Modèles Django: version verbeuse d'un choix


127

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 imprimant plutôt des «œufs fabuleux».

[comme j'avais des doutes sur 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 moche:

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

La vraie douleur est que j'ai besoin d'obtenir le choix choisi, et le seul moyen qui me vient à l'esprit est d'itérer les choix et les vérifications {% ifequals currentChoice.0 choiceField.data %}, ce qui est encore plus laid.

Cela peut-il être fait facilement? Ou cela a-t-il besoin d'une programmation de template-tag? Cela ne devrait-il pas déjà être disponible dans django?

Réponses:


258

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

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


1
Oui je sais. Ce n'est pas aussi général (universel), cependant - à moins que vous ne connaissiez un moyen d'itérer dans un modèle sur toutes les méthodes get_FOO_display d'un objet de modèle :) Je suis un peu trop paresseux pour écrire des modèles non génériques;) De plus, la documentation dit c'est la méthode d'une instance de modèle. Il faudrait donc qu'il s'agisse d'une forme modèle liée à un objet existant, ce qui n'est pas le cas et non plus général.
Artur Gajowy

2
Notez que cette utilisation n'est pas limitée aux vues, get_FOO_display () est une méthode sur l'objet de modèle lui-même afin que vous puissiez également l'utiliser dans le code du modèle! Par exemple, dans __unicode __ (), c'est très pratique
Bogatyr

51

La meilleure solution à votre problème est d'utiliser des fonctions d'assistance. Si les choix sont stockés dans la variable CHOICES et que le champ de modèle stockant le choix sélectionné est `` choix '', vous pouvez utiliser directement

 {{ x.get_choices_display }}

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


3
Pourquoi répondriez-vous ainsi 2 ans après qu'une réponse utile soit déjà en place? Et qui voterait? C'est la même réponse que @roberto seulement 2 ans plus tard ....
boatcoder

15
@ Mark0978 la raison pour laquelle cette réponse a été votée à la hausse est que (pour moi) il était plus clair de suivre alors la réponse «le plus voté». YMMV.
Nir Levy

49

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

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 passe un Scoop au modèle (note: pas Scoop.values ​​()), et le modèle contient:

{{ scoop.flavor_verbose }}

10

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 vous pouvez utiliser un filtre à cette fin. Si quelqu'un a une meilleure solution, je serai heureux de la voir :) Merci Noah!


+1 pour mentionner votre chemin # ennuis / templatetags / ... LOL ... J'utilise get_FOO_display (), qui est mentionné au bas de la documentation du formulaire.
fmalina

super idée avec l'utilisation de hasattr sur les choix!
oden

7

Nous pouvons étendre la solution de filtrage de Noah pour qu'elle soit plus universelle dans le traitement des données et des types 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)

Cela semble assez universel :) Je ne peux pas le dire avec certitude, car je n'ai pas trop fait de Python ou de Django depuis ce temps. C'est assez triste, cependant, qu'il ait encore besoin d'un filtre tiers (non inclus dans Django) (sinon vous nous le diriez, Ivan, n'est-ce pas
?;

@ArturGajowy Oui, à ce jour, il n'y a pas de telle fonctionnalité par défaut dans Django. Je l'ai proposé, qui sait, il sera peut-être approuvé .
Ivan Kharlamov

PARFAIT! FONCTIONNE COMME UN CHARME! FILTRES GABARIT SUR MESURE ROX! MERCI! :-)
CeDeROM

5

Je ne pense pas qu'il y ait de moyen intégré de faire cela. Un filtre pourrait cependant faire l'affaire:

@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 %}

3

Ajoutez à vos 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 des 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 suis pas sûr, est-ce que cette solution «pythonique» et «django-way» est suffisante ou pas, mais ça marche. :)


0

Vous avez Model.get_FOO_display () où FOO est le nom du champ qui a des choix.

Dans votre modèle, procédez comme suit:

{{ scoop.get_flavor_display }}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.