web-dev-qa-db-fra.com

Django modèles: version détaillée d'un choix

J'ai un modèle:

from Django.db import models

CHOICES = (
    ('s', 'Glorious spam'),
    ('e', 'Fabulous eggs'),
)

class MealOrder(models.Model):
    meal = models.CharField(max_length=8, choices=CHOICES)

J'ai un formulaire:

from Django.forms import ModelForm

class MealOrderForm(ModelForm):
    class Meta:
        model = MealOrder

Et je veux utiliser formtools.preview. Le modèle par défaut imprime la version courte du choix ('e' au lieu de 'Fabulous eggs'), car il utilise

{% for field in form %}
<tr>
<th>{{ field.label }}:</th>
<td>{{ field.data }}</td>
</tr>
{% endfor %}.

Je voudrais un modèle aussi général que celui mentionné, mais en imprimant à la place des "œufs fabuleux".

[comme j'avais des doutes où est la vraie question, je l'ai mise en gras pour nous tous:)]

Je sais comment obtenir la version verbeuse d'un choix d'une manière qui est elle-même laide:

{{ form.meal.field.choices.1.1 }}

La vraie douleur est que j'ai besoin d'obtenir le choix sélectionné, et la seule façon qui me vient à l'esprit est d'itérer à travers les choix et de vérifier {% ifequals currentChoice.0 choiceField.data %}, ce qui est encore plus laid.

Peut-on le faire facilement? Ou il a besoin d'une programmation de balises de modèle? Cela ne devrait-il pas être disponible dans Django déjà?

116
Artur Gajowy

Dans les modèles Django vous pouvez utiliser la méthode " get_FOO_display() ", qui renverra l'alias lisible pour le champ, où 'FOO' est le nom du champ.

Remarque: dans le cas où les modèles standard FormPreview ne l'utilisent pas, vous pouvez toujours fournir vos propres modèles pour ce formulaire, qui contiendra quelque chose comme {{ form.get_meal_display }}.

237
rob

La meilleure solution à votre problème consiste à utiliser des fonctions d'assistance. Si les choix sont stockés dans la variable [[# #]] choix [~ # ~] et que le champ modèle stockant le choix sélectionné est 'choix' alors vous pouvez directement utiliser

 {{ x.get_choices_display }}

dans votre modèle. Ici, x est l'instance de modèle. J'espère que ça aide.

43
Reema

Je m'excuse si cette réponse est redondante avec l'une des réponses ci-dessus, mais il semble que celle-ci n'ait pas encore été proposée, et elle semble assez propre. Voici comment j'ai résolu cela:

from Django.db import models

class Scoop(models.Model):
    FLAVOR_CHOICES = [
        ('c', 'Chocolate'),
        ('v', 'Vanilla'),
    ]

    flavor = models.CharField(choices=FLAVOR_CHOICES)

    def flavor_verbose(self):
        return dict(Scoop.FLAVOR_CHOCIES)[self.flavor]

Ma vue transmet un Scoop au modèle (note: pas Scoop.values ​​()), et le modèle contient:

{{ scoop.flavor_verbose }}
39
Dan Kerchner

Sur la base de la réponse de Noah, voici une version à l'abri des champs sans choix:

#annoyances/templatetags/data_verbose.py
from Django import template

register = template.Library()

@register.filter
def data_verbose(boundField):
    """
    Returns field's data or it's verbose version 
    for a field with choices defined.

    Usage::

        {% load data_verbose %}
        {{form.some_field|data_verbose}}
    """
    data = boundField.data
    field = boundField.field
    return hasattr(field, 'choices') and dict(field.choices).get(data,'') or data

Je ne sais pas si c'est correct d'utiliser un filtre à cette fin. Si quelqu'un a une meilleure solution, je serai heureux de la voir :) Merci Noah!

9
Artur Gajowy

Nous pouvons étendre la solution de filtrage par Noah pour être plus universel dans le traitement des types de données et de champs:

<table>
{% for item in query %}
    <tr>
        {% for field in fields %}
            <td>{{item|human_readable:field}}</td>
        {% endfor %}
    </tr>
{% endfor %}
</table>

Voici le code:

#app_name/templatetags/custom_tags.py
def human_readable(value, arg):
    if hasattr(value, 'get_' + str(arg) + '_display'):
        return getattr(value, 'get_%s_display' % arg)()
    Elif hasattr(value, str(arg)):
        if callable(getattr(value, str(arg))):
            return getattr(value, arg)()
        else:
            return getattr(value, arg)
   else:
       try:
           return value[arg]
       except KeyError:
           return settings.TEMPLATE_STRING_IF_INVALID
register.filter('human_readable', human_readable)
6
Ivan Kharlamov

Je ne pense pas qu'il existe un moyen intégré de le faire. Un filtre pourrait faire l'affaire, cependant:

@register.filter(name='display')
def display_value(bf):
    """Returns the display value of a BoundField"""
    return dict(bf.field.choices).get(bf.data, '')

Ensuite, vous pouvez faire:

{% for field in form %}
    <tr>
        <th>{{ field.label }}:</th>
        <td>{{ field.data|display }}</td>
    </tr>
{% endfor %}
5
Noah Medling

Ajoutez à votre models.py une fonction simple:

def get_display(key, list):
    d = dict(list)
    if key in d:
        return d[key]
    return None

Maintenant, vous pouvez obtenir la valeur détaillée de champs de choix comme celui-ci:

class MealOrder(models.Model):
    meal = models.CharField(max_length=8, choices=CHOICES)

    def meal_verbose(self):
        return get_display(self.meal, CHOICES)    

Upd .: Je ne sais pas, est-ce que cette solution "Pythonic" et "Django-way" est suffisante ou non, mais ça marche. :)

3
Igor Pomaranskiy