Plantillas Django: versión detallada de una elección

127

Tengo un 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)

Tengo un formulario:

from django.forms import ModelForm

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

Y quiero usar formtools.preview. La plantilla predeterminada imprime la versión corta de la opción ('e' en lugar de 'Huevos fabulosos'), porque usa

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

Me gustaría una plantilla tan general como la mencionada, pero imprimiendo 'Huevos fabulosos' en su lugar.

[Como tenía dudas de dónde está la verdadera pregunta, la en negrita para todos nosotros :)]

Sé cómo obtener la versión detallada de una elección de una manera fea:

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

El verdadero dolor es que necesito obtener la opción seleccionada, y la única forma de recordar es iterar a través de las elecciones y la verificación {% ifequals currentChoice.0 choiceField.data %}, lo que es aún más feo.

¿Se puede hacer fácilmente? ¿O necesita alguna programación de etiqueta de plantilla? ¿No debería ya estar disponible en django?

Artur Gajowy
fuente

Respuestas:

258

En las plantillas de Django puede usar el get_FOO_display()método " ", que devolverá el alias legible para el campo, donde 'FOO' es el nombre del campo.

Nota: en caso de que las FormPreviewplantillas estándar no lo usen, siempre puede proporcionar sus propias plantillas para ese formulario, que contendrá algo así como {{ form.get_meal_display }}.

robar
fuente
1
Sí, lo sé. Sin embargo, no es tan general (universal), a menos que conozca una forma de iterar en una plantilla sobre todos los métodos get_FOO_display de un objeto modelo :) Soy un poco flojo para escribir plantillas no genéricas;) Además, los documentos dicen Es el método de una instancia modelo. Por lo tanto, tendría que ser una forma modelo vinculada a un objeto existente, que no es el caso y tampoco es general.
Artur Gajowy
2
Tenga en cuenta que este uso no se limita a las vistas, ¡get_FOO_display () es un método en el objeto modelo en sí mismo para que pueda usarlo también en el código modelo! Por ejemplo, en __unicode __ () es muy útil
Bogatyr
51

La mejor solución para su problema es usar funciones auxiliares. Si las opciones se almacenan en las OPCIONES variables y el campo del modelo que almacena la opción seleccionada es ' opciones ', puede usar directamente

 {{ x.get_choices_display }}

en tu plantilla Aquí, x es la instancia del modelo. Espero eso ayude.

Reema
fuente
3
¿Por qué respondería así 2 años después de que una respuesta útil ya esté en su lugar? ¿Y quién lo votaría? Es la misma respuesta que @roberto solo 2 años después ...
boatcoder
15
@ Mark0978, la razón para votar a favor de esta respuesta es porque (para mí) era más claro seguir la respuesta "más votada". YMMV.
Nir Levy
49

Mis disculpas si esta respuesta es redundante con cualquiera de las enumeradas anteriormente, pero parece que aún no se ha ofrecido, y parece bastante limpia. Así es como he resuelto esto:

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]

Mi vista pasa un Scoop a la plantilla (nota: no Scoop.values ​​()), y la plantilla contiene:

{{ scoop.flavor_verbose }}
Dan Kerchner
fuente
10

Basándonos en la respuesta de Noah, aquí hay una versión inmune a los campos sin opciones:

#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

No estoy seguro de si está bien usar un filtro para tal fin. Si alguien tiene una solución mejor, me alegrará verla :) ¡Gracias Noah!

Artur Gajowy
fuente
+1 por mencionar su ruta # molestias / templatetags / ... LOL ... Uso get_FOO_display (), que se menciona en la parte inferior de los documentos del formulario.
fmalina
¡Gran idea con el uso de hasattr en las opciones!
oden
7

Podemos extender la solución de filtro de Noah para que sea más universal al tratar con datos y tipos de campo:

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

Aquí está el 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
fuente
Parece bastante universal :) No puedo decir con certeza, porque no he hecho demasiado Python o Django desde ese momento. Sin embargo, es bastante triste que todavía necesite un filtro de terceros (no incluido en Django) (de lo contrario nos lo
dirías
@ArturGajowy Sí, a partir de hoy no existe dicha función predeterminada en Django. Lo he propuesto, quién sabe, tal vez sea aprobado .
Ivan Kharlamov
¡PERFECTO! ¡FUNCIONA DE MARAVILLA! FILTROS DE PLANTILLA PERSONALIZADOS ROX! ¡GRACIAS! :-)
CeDeROM
5

No creo que haya una forma integrada de hacerlo. Sin embargo, un filtro podría hacer el truco:

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

Entonces puedes hacer:

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

Agregue a sus modelos.py una función simple:

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

Ahora, puede obtener un valor detallado de los campos de elección como ese:

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

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

Upd .: No estoy seguro, es esa solución "pitónica" y "django-way" suficiente o no, pero funciona. :)

Igor Pomaranskiy
fuente
0

Tiene Model.get_FOO_display () donde FOO es el nombre del campo que tiene opciones.

En su plantilla haga esto:

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