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?

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


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.


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

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!


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

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

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


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 }}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.