Modelli di Django: versione dettagliata di una scelta


127

Ho un modello:

from django.db import models

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

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

Ho un modulo:

from django.forms import ModelForm

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

E voglio usare formtools.preview. Il modello predefinito stampa la versione breve della scelta ('e' invece di 'Uova favolose'), perché utilizza

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

Vorrei un modello generale come quello menzionato, ma invece stampa "Uova favolose".

[dato che avevo dei dubbi su dove fosse la vera domanda, l'ho messa in grassetto per tutti noi :)]

So come ottenere la versione dettagliata di una scelta in un modo che è brutto:

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

Il vero dolore è che ho bisogno di ottenere la scelta selezionata, e l'unico modo che mi viene in mente è iterare attraverso le scelte e il controllo {% ifequals currentChoice.0 choiceField.data %}, il che è ancora più brutto.

Può essere fatto facilmente? O ha bisogno di una programmazione con tag modello? Non dovrebbe già essere disponibile in Django?

Risposte:


258

Nei modelli Django puoi usare il get_FOO_display()metodo " ", che restituirà l'alias leggibile per il campo, dove "FOO" è il nome del campo.

Nota: nel caso in cui i FormPreviewmodelli standard non lo utilizzino, è possibile fornire sempre i propri modelli per quel modulo, che conterrà qualcosa di simile {{ form.get_meal_display }}.


1
si, lo so. Non è così generale (universale), però - a meno che tu non conosca un modo per iterare in un modello su tutti i metodi get_FOO_display di un oggetto modello :) Sono un po 'troppo pigro per scrivere modelli non generici;) Inoltre, dicono i documenti è il metodo di un'istanza del modello. Pertanto dovrebbe essere una forma modello legata a un oggetto esistente che non è il caso e anche non generale.
Artur Gajowy,

2
Nota che questo utilizzo non è limitato alle viste, get_FOO_display () è un metodo sull'oggetto modello stesso, quindi puoi usarlo anche nel codice modello! Ad esempio, in __unicode __ () è molto utile
Bogatyr,

51

La migliore soluzione per il tuo problema è utilizzare le funzioni di supporto. Se le scelte sono memorizzate nella variabile SCELTE e il campo del modello che memorizza la scelta selezionata è ' scelte ', è possibile utilizzare direttamente

 {{ x.get_choices_display }}

nel tuo modello. Qui, x è l'istanza del modello. Spero che sia d'aiuto.


3
Perché dovresti rispondere in questo modo 2 anni dopo che è già stata creata una risposta utile? E chi lo voterebbe? È la stessa risposta di @roberto solo 2 anni dopo ....
Boatcoder

15
@ Mark0978 il motivo della votazione di questa risposta è perché (per me) è stato più chiaro seguire la risposta "votata più in alto". YMMV.
Nir Levy,

49

Mi scuso se questa risposta è ridondante con quelle sopra elencate, ma sembra che questa non sia stata ancora offerta e sembra abbastanza chiara. Ecco come ho risolto questo:

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]

La mia vista passa uno Scoop al modello (nota: non Scoop.values ​​()) e il modello contiene:

{{ scoop.flavor_verbose }}

10

Sulla base della risposta di Noè, ecco una versione immune ai campi senza scelta:

#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

Non sono sicuro che vada bene usare un filtro per questo scopo. Se qualcuno ha una soluzione migliore, sarò felice di vederlo :) Grazie Noah!


+1 per menzionare il tuo percorso # fastidi / templatetags / ... LOL ... Uso get_FOO_display (), che è menzionato in fondo ai documenti del modulo.
Fmalina,

ottima idea con l'uso di hasattr sulle scelte!
Oden

7

Siamo in grado di estendere la soluzione di filtro di Noah per essere più universali nella gestione di dati e tipi di campo:

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

Ecco il codice:

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

Sembra abbastanza universale :) Non posso dirlo con certezza, perché non ho fatto troppo Python o Django da quel momento. È piuttosto triste, tuttavia, che abbia ancora bisogno di un filtro di terze parti (non incluso in Django) (altrimenti ci diresti, Ivan, vero?;)) ...
Artur Gajowy,

@ArturGajowy Sì, ad oggi non esiste una funzionalità predefinita di questo tipo in Django. L'ho proposto, chissà, forse sarà approvato .
Ivan Kharlamov,

PERFETTO! FUNZIONA COME UN FASCINO! FILTRI SU MODELLO PERSONALIZZATI ROX! GRAZIE! :-)
CeDeROM

5

Non credo che ci sia un modo integrato per farlo. Un filtro potrebbe fare il trucco, però:

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

Quindi puoi fare:

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

3

Aggiungi ai tuoi modelli.py una semplice funzione:

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

Ora puoi ottenere un valore dettagliato dei campi di scelta come questo:

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

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

Aggiornamento: Non sono sicuro, è quella soluzione "pythonic" e "django-way" abbastanza o no, ma funziona. :)


0

Hai Model.get_FOO_display () dove FOO è il nome del campo che ha delle scelte.

Nel tuo modello fai questo:

{{ scoop.get_flavor_display }}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.