2009-07-09 22 views
98

tengo un modelo:las plantillas de Django: la versión detallada de una elección

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 una forma:

from django.forms import ModelForm 

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

Y quiero usar formtools.preview. La plantilla por defecto imprime la versión corta de la elección ('e' en lugar de 'huevos' Fabulous), becuase que utiliza

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

Me gustaría tener una plantilla tan general como el mencionado, pero la impresión 'huevos Fabulous 'en cambio.

[ya que tenía dudas dónde está la verdadera cuestión, que en negrita que para todos nosotros :)]

Yo sé cómo conseguir la versión detallada de la elección de una manera que es en sí misma fea:

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

el dolor real es que necesito para obtener la opción seleccionada, y la única manera que viene a la mente es iteración a través de decisiones y el control de {% 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 estar ya disponible en django?

Respuesta

4

No creo que haya ninguna forma incorporada de hacerlo. Un filtro puede hacer el truco, sin embargo:

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

entonces usted puede hacer:

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

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

Nota: en el caso de los estándares FormPreview plantillas no lo están utilizando, entonces siempre se puede provide your own templates de esa forma, que contendrá algo así como {{ form.get_meal_display }}.

+1

sí, lo sé. Aunque no es tan general (universal), a menos que sepa una forma de iterar en una plantilla sobre todos los métodos get_FOO_display de un objeto modelo :) Soy demasiado perezoso para escribir plantillas no genéricas;) Además, la docs dicen que es el método de una instancia modelo. Por lo tanto, tendría que ser un formulario modelo vinculado a un objeto existente que no es el caso y tampoco es general. –

+0

Siempre me olvido de este – Sevenearths

+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 del modelo. Por ejemplo, en __unicode __() es muy útil – Bogatyr

9

Basándose en la respuesta de Noé, aquí es una versión inmune a 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 carnero que es aceptable utilizar un filtro para tal fin. Si alguien tiene una solución mejor, estaré encantado de verla :) ¡Gracias, Noah!

+0

+1 para mencionar tu ruta # molestias/templatetags/... LOL ... utilizo get_FOO_display(), que se menciona en la parte inferior de los documentos de formulario. –

+0

¡gran idea con el uso de hasattr on choices! – oden

6

podemos extender la solución de filtro por Noah ser más universal en el tratamiento de datos y tipos de campos:

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

Parece bastante universal :) No puedo asegurarlo, porque desde entonces no he hecho mucho Python o Django. Es bastante triste, sin embargo, que todavía necesita un filtro de terceros (no incluido en Django) (de lo contrario, nos lo dirías, Ivan, ¿verdad?;)) ... –

+0

@ArturGajowy Sí, a partir de hoy no existe tal característica predeterminada en Django. Lo he propuesto, [quién sabe, quizás sea aprobado] (https://groups.google.com/group/django-developers/browse_thread/thread/9ff2222931b381c7). –

35

La mejor solución para su problema consiste en usa funciones de ayuda. Si las opciones se almacenan en las variables OPCIONES y el campo de modelo de almacenamiento de la opción seleccionada es '' opciones entonces se puede utilizar directamente

{{ x.get_choices_display }} 

en su plantilla. Aquí, x es la instancia modelo. Espero que ayude.

+3

¿Por qué respondería así 2 años después de que ya haya una respuesta útil? ¿Y quién lo votaría? Es la misma respuesta que @roberto solo 2 años después ... – boatcoder

+10

@ Mark0978 La razón para subir la votación de esta respuesta es porque (para mí) fue más claro seguir luego la respuesta "más votados". YMMV. –

2

Añadir a su models.py una función simple:

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

Ahora, se puede obtener el valor detallado de los campos de selección por el estilo:

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 "pythonic" y "django-way" suficiente o no, pero funciona. :)

25

Disculpe si esta respuesta es redundante con cualquiera de las enumeradas anteriormente, pero parece que esta no se ha ofrecido aún, 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 punto de vista pasa una cucharada a la plantilla (nota: no Scoop.values ​​()), y la plantilla contiene:

{{ scoop.flavor_verbose }} 
+0

gracias :) esto era exactamente lo que estaba buscando. –

-2

Con el uso de una etiqueta de plantilla.

Casi lo mismo que Arthur Gawjowj, pero este es para opciones que SÓLO usan valores enteros en lugar de cadenas.

# project/templatetags/project_tags.py 

from django.template.base import Library 


register = Library() 

@register.filter(name='pretty_form_value') 
def pretty_form_value(field): 
    """ 
    Returns field's data or it's verbose version 
    for a field with choices defined. 

    Usage:: 

     {% load project_tags %} 
     {{form.some_field|pretty_form_value}} 
    """ 
    if hasattr(field.field, 'choices'): 
     try: 
      return dict(field.field.choices)[int(field.data)] 
     except ValueError: 
      pass 
    return field.data 
Cuestiones relacionadas