Django-Vorlagen: ausführliche Version einer Auswahl

127

Ich habe ein Modell:

from django.db import models

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

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

Ich habe ein Formular:

from django.forms import ModelForm

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

Und ich möchte formtools.preview verwenden. Die Standardvorlage druckt die Kurzversion der Auswahl ('e' anstelle von 'Fabulous Eggs'), da sie verwendet wird

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

Ich hätte gerne eine Vorlage, die so allgemein ist wie die erwähnte, aber stattdessen 'Fabulous Eggs' druckt.

[Da ich Zweifel hatte, wo die eigentliche Frage ist, habe ich sie für uns alle kühn gemacht :)]

Ich weiß, wie man die ausführliche Version einer Wahl auf eine Weise erhält, die selbst hässlich ist:

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

Der wahre Schmerz ist, dass ich die ausgewählte Wahl treffen muss, und der einzige Weg, der mir in den Sinn kommt, besteht darin, durch Entscheidungen und Überprüfungen zu iterieren {% ifequals currentChoice.0 choiceField.data %}, was noch hässlicher ist.

Kann es leicht gemacht werden? Oder braucht es eine Template-Tag-Programmierung? Sollte das nicht schon in Django verfügbar sein?

Artur Gajowy
quelle

Antworten:

257

In Django-Vorlagen können Sie die get_FOO_display()Methode " " verwenden, die den lesbaren Alias ​​für das Feld zurückgibt, wobei 'FOO' der Name des Felds ist.

Hinweis: Falls die Standardvorlagen FormPreviewes nicht verwenden, können Sie jederzeit Ihre eigenen Vorlagen für dieses Formular bereitstellen , die so etwas wie enthalten {{ form.get_meal_display }}.

rauben
quelle
1
Ja, ich weiß. Es ist jedoch nicht so allgemein (universell) - es sei denn, Sie kennen eine Möglichkeit, eine Vorlage über alle get_FOO_display-Methoden eines Modellobjekts zu iterieren :) Ich bin etwas zu faul, um nicht generische Vorlagen zu schreiben;) Außerdem heißt es in den Dokumenten Es ist die Methode einer Modellinstanz. Daher müsste es sich um eine Modellform handeln, die an ein vorhandenes Objekt gebunden ist, was nicht der Fall und auch nicht allgemein ist.
Artur Gajowy
2
Beachten Sie, dass diese Verwendung nicht auf die Ansichten beschränkt ist. Get_FOO_display () ist eine Methode für das Modellobjekt selbst, sodass Sie sie auch im Modellcode verwenden können! Zum Beispiel in __unicode __ () ist es sehr praktisch
Bogatyr
51

Die beste Lösung für Ihr Problem ist die Verwendung von Hilfsfunktionen. Wenn die Auswahlmöglichkeiten in der Variablen CHOICES gespeichert sind und das Modellfeld, in dem die ausgewählte Auswahl gespeichert ist, " Auswahlmöglichkeiten " lautet, können Sie sie direkt verwenden

 {{ x.get_choices_display }}

in Ihrer Vorlage. Hier ist x die Modellinstanz. Ich hoffe es hilft.

Reema
quelle
3
Warum würden Sie 2 Jahre, nachdem bereits eine nützliche Antwort vorliegt, so antworten? Und wer würde darüber abstimmen? Es ist die gleiche Antwort wie @roberto nur 2 Jahre später ....
Bootscodierer
15
@ Mark0978 Der Grund für die Abstimmung dieser Antwort ist, dass (für mich) es klarer war, der Antwort mit der höchsten Bewertung zu folgen. YMMV.
Nir Levy
49

Ich entschuldige mich, wenn diese Antwort mit einer der oben aufgeführten überflüssig ist, aber es scheint, dass diese noch nicht angeboten wurde, und sie scheint ziemlich sauber zu sein. So habe ich das gelöst:

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]

Meine Ansicht übergibt einen Scoop an die Vorlage (Hinweis: nicht Scoop.values ​​()), und die Vorlage enthält:

{{ scoop.flavor_verbose }}
Dan Kerchner
quelle
10

Basierend auf Noahs Antwort ist hier eine Version, die gegen Felder ohne Auswahlmöglichkeiten immun ist:

#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

Ich bin mir nicht sicher, ob es in Ordnung ist, einen Filter für diesen Zweck zu verwenden. Wenn jemand eine bessere Lösung hat, werde ich mich freuen, sie zu sehen :) Danke Noah!

Artur Gajowy
quelle
+1 für die Erwähnung Ihres Pfades # ärger / templatetags / ... LOL ... Ich verwende get_FOO_display (), das am Ende der Formulardokumente erwähnt wird.
Fmalina
Tolle Idee mit der Verwendung von Hasattr auf Entscheidungen!
Oden
7

Wir können die Filterlösung von Noah erweitern , um universeller mit Daten und Feldtypen umzugehen:

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

Hier ist der 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)
Ivan Kharlamov
quelle
Scheint ziemlich universell :) Kann ich nicht sicher sagen, weil ich seitdem nicht mehr zu viel Python oder Django gemacht habe. Es ist jedoch ziemlich traurig, dass es noch einen Filter eines Drittanbieters (nicht in Django enthalten) benötigt (sonst würden Sie uns sagen, Ivan, nicht
wahr
@ArturGajowy Ja, ab heute gibt es in Django keine solche Standardfunktion. Ich habe es vorgeschlagen, wer weiß, vielleicht wird es genehmigt .
Ivan Kharlamov
PERFEKT! KLAPPT WUNDERBAR! CUSTOM TEMPLATE FILTERS ROX! DANKE! :-)
CeDeROM
5

Ich glaube nicht, dass es dafür einen eingebauten Weg gibt. Ein Filter könnte jedoch den Trick machen:

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

Dann können Sie tun:

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

Fügen Sie Ihren models.py eine einfache Funktion hinzu:

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

Jetzt können Sie einen ausführlichen Wert für Auswahlfelder wie folgt erhalten:

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

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

Upd.: Ich bin mir nicht sicher, ob diese Lösung "pythonic" und "django-way" genug ist oder nicht, aber sie funktioniert. :) :)

Igor Pomaranskiy
quelle
0

Sie haben Model.get_FOO_display (), wobei FOO der Name des Felds ist, für das Auswahlmöglichkeiten bestehen.

Führen Sie in Ihrer Vorlage Folgendes aus:

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