Шаблоны Django: подробная версия выбора - PullRequest
114 голосов
/ 09 июля 2009

У меня есть модель:

from django.db import models

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

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

У меня есть форма:

from django.forms import ModelForm

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

И я хочу использовать formtools.preview. Шаблон по умолчанию печатает краткую версию выбора ('e' вместо 'Fabulous eggs'), потому что он использует

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

Я хотел бы, чтобы шаблон был таким же общим, как упомянуто, но вместо этого печатал 'Fabulous eggs'.

[поскольку у меня были сомнения, где же настоящий вопрос, я выделил его для всех нас:)]

Я знаю, как получить подробную версию выбора таким образом, который сам по себе уродлив:

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

Настоящая боль в том, что мне нужно получить выбранный выбор, и единственный способ, который приходит мне в голову, - это перебирать варианты и проверять {% ifequals currentChoice.0 choiceField.data %}, что еще страшнее.

Можно ли сделать это легко? Или нужно какое-то программирование тегов-шаблонов? Разве это уже не доступно в django?

Ответы [ 7 ]

233 голосов
/ 10 июля 2009

В шаблонах Django вы можете использовать метод "get_FOO_display()", который будет возвращать читаемый псевдоним для поля, где 'FOO' - это имя поля.

Примечание: если стандартные шаблоны FormPreview не используют его, вы всегда можете предоставить свои собственные шаблоны для этой формы, которые будут содержать что-то вроде {{ form.get_meal_display }}.

42 голосов
/ 06 октября 2011

Лучшее решение вашей проблемы - использовать вспомогательные функции. Если варианты хранятся в переменной CHOICES , а поле модели, в котором хранится выбранный вариант, равно ' choices ', тогда вы можете напрямую использовать

 {{ x.get_choices_display }}

в вашем шаблоне. Здесь х - это экземпляр модели. Надеюсь, это поможет.

36 голосов
/ 26 ноября 2013

Приношу свои извинения, если этот ответ излишний с любым из перечисленных выше, но кажется, что этот еще не был предложен, и кажется довольно чистым. Вот как я решил это:

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]

Мое представление передает Scoop в шаблон (примечание: не Scoop.values ​​()), и шаблон содержит:

{{ scoop.flavor_verbose }}
9 голосов
/ 10 июля 2009

Основываясь на ответе Ноя, вот версия, невосприимчивая к полям без выбора:

#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

Я не уверен, нормально ли использовать фильтр для этой цели. Если у кого-нибудь есть лучшее решение, я буду рад его видеть :) Спасибо, Ной!

6 голосов
/ 27 сентября 2011

Мы можем расширить решение фильтра на Ноа , чтобы быть более универсальным в работе с типами данных и полей:

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

Вот код:

#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)
5 голосов
/ 09 июля 2009

Я не думаю, что есть какой-то встроенный способ сделать это. Однако фильтр может помочь:

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

Тогда вы можете сделать:

{% for field in form %}
    <tr>
        <th>{{ field.label }}:</th>
        <td>{{ field.data|display }}</td>
    </tr>
{% endfor %}
3 голосов
/ 12 января 2012

Добавьте в свой models.py одну простую функцию:

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

Теперь вы можете получить подробное значение полей выбора следующим образом:

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

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

Upd .: Я не уверен, достаточно ли этого решения «pythonic» и «django-way», но оно работает. :)

...