2009-07-09 8 views
98

j'ai un modèle:modèles Django: la version verbeux d'un choix

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 une forme:

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 des « œufs fabuleux »), becuase 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 mentionné, mais l'impression « œufs fabuleux ' au lieu.

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

Je sais comment obtenir la version verbeux d'un choix d'une manière qui lui-même est laid:

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

la vraie douleur est que je dois obtenir le choix sélectionné, et la seule façon de venir à mon esprit Itère choix et le contrôle {% ifequals currentChoice.0 choiceField.data %}, ce qui est encore plus laid.

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

Répondre

4

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

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

Dans Django modèles que 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 provide your own templates pour ce formulaire, qui contiendra quelque chose comme {{ form.get_meal_display }}.

+1

oui, je sais. Cependant, ce n'est pas aussi général (universel), sauf si vous connaissez un moyen d'itérer dans un modèle sur toutes les méthodes get_FOO_display d'un objet modèle :) Je suis un peu trop paresseux pour écrire des modèles non génériques;) les docs disent que c'est la méthode d'une instance de modèle. Par conséquent, il doit s'agir d'un formulaire modèle lié à un objet existant, ce qui n'est pas le cas et n'est pas général. –

+0

J'oublie toujours celui-ci – Sevenearths

+2

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

9

Fonder sur la réponse de Noé, voici une version immunitaire aux 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 wether il est correct d'utiliser un filtre à cette fin. Si quelqu'un a une meilleure solution, je serai heureux de le voir :) Merci Noah!

+0

+1 pour mentionner votre chemin # ennoyances/templatetags/... LOL ... J'utilise get_FOO_display(), qui est mentionné au bas du formulaire docs. –

+0

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

6

On peut étendre la solution de filtre par Noah être plus universel 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) 
+0

Semble assez universel :) Je ne peux pas dire à coup sûr, parce que je n'ai pas fait trop de Python ou Django depuis ce temps. Il 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?;)) ... –

+0

@ArturGajowy Oui, à partir de aujourd'hui, il n'y a pas de fonctionnalité par défaut dans Django. Je l'ai proposé, [qui sait, peut-être qu'il sera approuvé] (https://groups.google.com/group/django-developers/browse_thread/thread/9ff2222931b381c7). –

35

La meilleure solution pour votre problème est de utiliser des fonctions d'aide. Si les choix sont stockés dans les variables et CHOICES 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 du modèle. J'espère que ça aide.

+3

Pourquoi répondriez-vous comme ça 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

+10

@ Mark0978 la raison de l'upvoting de cette réponse est parce que (pour moi) il était plus clair de suivre alors la réponse "top voté". YMMV. –

2

Ajouter à 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 verbeux des champs de choix comme ça:

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" suffit ou pas, mais cela fonctionne. :)

25

Mes excuses si cette réponse est redondante avec l'une des listes ci-dessus, mais il semble que celui-ci n'a pas encore été offert, et il semble assez propre. Voici comment je l'ai résolu ce problème:

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] 

Mon point de vue passe un scoop au modèle (note: pas Scoop.values ​​()), et le modèle contient:

{{ scoop.flavor_verbose }} 
+0

merci :) c'était exactement ce que je cherchais. –

-2

Avec la l'utilisation d'un tag de modèle.

Presque le même que Arthur Gawjowj, mais celui-ci est pour les choix qui utilisent UNIQUEMENT des valeurs entières au lieu de chaînes.

# project/templatetags/project_tags.py 

from django.template.base import Library 


register = Library() 

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

    Usage:: 

     {% load project_tags %} 
     {{form.some_field|pretty_form_value}} 
    """ 
    if hasattr(field.field, 'choices'): 
     try: 
      return dict(field.field.choices)[int(field.data)] 
     except ValueError: 
      pass 
    return field.data 
Questions connexes