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?

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 }}.


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.


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

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!


+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)

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

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. :)


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 }}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.