Modelos do Django: versão detalhada de uma escolha

127

Eu tenho um modelo:

from django.db import models

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

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

Eu tenho um formulário:

from django.forms import ModelForm

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

E eu quero usar formtools.preview. O modelo padrão imprime a versão curta da escolha ('e' em vez de 'Ovos fabulosos'), pois usa

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

Eu gostaria de um modelo tão geral quanto o mencionado, mas imprimindo 'Ovos fabulosos'.

[como eu duvidava de qual é a verdadeira questão, coloquei-a em negrito para todos nós :)]

Eu sei como obter a versão detalhada de uma escolha de uma maneira que é feia:

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

A verdadeira dor é que preciso ter a opção selecionada, e a única maneira que me vem à mente é iterar através de escolhas e verificações {% ifequals currentChoice.0 choiceField.data %}, o que é ainda mais feio.

Isso pode ser feito facilmente? Ou ele precisa de alguma programação de tag de modelo? Isso já não deveria estar disponível no django?

Artur Gajowy
fonte

Respostas:

258

Nos modelos do Django, você pode usar o get_FOO_display()método " ", que retornará o alias legível para o campo, onde 'FOO' é o nome do campo.

Nota: caso os FormPreviewmodelos padrão não o estejam usando, você sempre poderá fornecer seus próprios modelos para esse formulário, que conterá algo como {{ form.get_meal_display }}.

roubar
fonte
1
Sim eu conheço. Porém, não é tão geral (universal) - a menos que você saiba como iterar em um modelo em todos os métodos get_FOO_display de um objeto de modelo :) Estou com preguiça de escrever modelos não genéricos;) Além disso, os documentos dizem é o método de uma instância de modelo. Portanto, teria que ser um modelo vinculado a um objeto existente, o que não é o caso e também não é geral.
7898 Artur Gajowy
2
Observe que esse uso não se limita às visualizações, get_FOO_display () é um método no próprio objeto de modelo, para que você possa usá-lo também no código do modelo! Por exemplo, em __unicode __ (), é muito útil
Bogatyr 27/02
51

A melhor solução para o seu problema é usar as funções auxiliares. Se as opções estiverem armazenadas na variável CHOICES e o campo modelo que armazena a opção selecionada for ' escolhas ', você poderá usar diretamente

 {{ x.get_choices_display }}

no seu modelo. Aqui, x é a instância do modelo. Espero que ajude.

Reema
fonte
3
Por que você responderia assim 2 anos após uma resposta útil já estar em vigor? E quem votaria? É a mesma resposta que @roberto apenas 2 anos mais tarde ....
boatcoder
15
@ Mark0978 a razão para a votação positiva desta resposta é porque (para mim) ficou mais claro seguir a resposta "mais votada". YMMV.
Nir Levy
49

Peço desculpas se esta resposta for redundante com qualquer uma das listadas acima, mas parece que essa ainda não foi oferecida e parece bastante limpa. Aqui está como eu resolvi isso:

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]

Minha visão passa um Scoop para o modelo (note: not Scoop.values ​​()), e o modelo contém:

{{ scoop.flavor_verbose }}
Dan Kerchner
fonte
10

Baseando-se na resposta de Noah, aqui está uma versão imune a campos sem opções:

#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

Não tenho certeza se está tudo bem em usar um filtro para esse fim. Se alguém tiver uma solução melhor, ficarei feliz em vê-la :) Obrigado Noah!

Artur Gajowy
fonte
+1 por mencionar seu caminho # aborrecimentos / etiquetas de modelos / ... LOL ... eu uso get_FOO_display (), mencionado na parte inferior dos documentos do formulário.
fmalina
ótima idéia com o uso de hasattr nas escolhas!
oden
7

Podemos estender a solução de filtro da Noah para ser mais universal ao lidar com tipos de dados e campos:

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

Aqui está o código:

#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)
Ivan Kharlamov
fonte
Parece bastante universal :) Não posso ter certeza, porque não fiz muito Python ou Django desde então. É muito triste, porém, que ainda precisa de um filtro de 3rd party (não incluído no Django) (caso contrário você nos diria, Ivan, não é;?)) ...
Artur Gajowy
@ArturGajowy Sim, a partir de hoje não existe esse recurso padrão no Django. Eu propus, quem sabe, talvez seja aprovado .
Ivan Kharlamov
PERFEITO! FUNCIONA COMO UM ENCANTO! FILTROS DE MODELO PERSONALIZADOS ROX! OBRIGADO! :-)
CeDeROM
5

Eu não acho que exista uma maneira embutida de fazer isso. Um filtro pode fazer o truque, no entanto:

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

Então você pode fazer:

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

Adicione ao seu models.py uma função simples:

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

Agora, você pode obter um valor detalhado dos campos de escolha como este:

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

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

Upd .: Não tenho certeza, essa solução é “pythonic” e “django-way” o suficiente ou não, mas funciona. :)

Igor Pomaranskiy
fonte
0

Você possui Model.get_FOO_display () em que FOO é o nome do campo que tem opções.

No seu modelo, faça o seguinte:

{{ scoop.get_flavor_display }}
Mohamed OULD EL KORY
fonte