Шаблоны Django: расширенная версия выбора

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

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 %}, что даже уродливее.

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

Ответ 1

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

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

Ответ 2

Лучшим решением для вашей проблемы является использование вспомогательных функций. Если выбор сохраняется в переменной ВЫБОР, и поле модели, в котором сохранено выбранное значение, будет < > , вы можете напрямую использовать

 {{ x.get_choices_display }}

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

Ответ 3

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

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

Ответ 4

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

#annoyances/templatetags/data_verbose.py
from django import template

register = template.Library()

@register.filter
def data_verbose(boundField):
    """
    Returns field data or it 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

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

Добавьте к вашим model.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)    

Обновление: я не уверен, что это решение "pythonic" и "django-way" достаточно или нет, но оно работает.:)

Ответ 8

Использование тега шаблона.

Почти то же самое, что и Артур Гавёв, но этот вариант предназначен для выбора, в котором ТОЛЬКО используют значения Integer вместо строк.

# 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 data or it 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