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

127

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

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 egg»), поскольку в нем используется

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

Я хотел бы иметь такой же общий шаблон, как упомянутый, но вместо этого печатал бы «Сказочные яйца».

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

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

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

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

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

Артур Гайовы
источник

Ответы:

258

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

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

грабить
источник
1
Да, я знаю. Однако это не так универсально (универсально) - если вы не знаете, как выполнить итерацию в шаблоне по всем методам get_FOO_display объекта модели :) Мне немного лень писать неуниверсальные шаблоны;) Более того, в документации говорится это метод экземпляра модели. Следовательно, это должна быть модельная форма, привязанная к существующему объекту, что не так и не является общим.
Artur Gajowy 09
2
Обратите внимание, что это использование не ограничивается представлениями, get_FOO_display () - это метод самого объекта модели, поэтому вы также можете использовать его в коде модели! Например, в __unicode __ () это очень удобно
Богатырь
51

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

 {{ x.get_choices_display }}

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

Reema
источник
3
Почему вы ответите так через 2 года после того, как полезный ответ уже есть? И кто проголосует за это? Тот же ответ, что и @roberto, всего 2 года спустя ....
boatcoder
15
@ Mark0978 причина поддержки этого ответа в том, что (для меня) было более ясно следовать ответу, получившему наибольшее количество голосов. YMMV.
Нир Леви
49

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

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 }}
Дэн Кершнер
источник
10

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

#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

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

Артур Гайовы
источник
+1 за упоминание вашего пути # раздражает / templatetags / ... LOL ... Я использую get_FOO_display (), который упоминается в нижней части документа формы.
fmalina
отличная идея с использованием hasattr на выбор!
oden
7

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

<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)
Иван Харламов
источник
Кажется довольно универсальным :) Не могу сказать наверняка, потому что с тех пор я не слишком много писал на Python или Django. Тем не менее, довольно грустно, что ему все еще нужен сторонний (не включенный в Django) фильтр (иначе вы бы сказали нам, Иван, не так ли?;)) ...
Артур Гайови
@ArturGajowy Да, на сегодняшний день в Django нет такой функции по умолчанию. Я это предложил, кто знает, может быть, он будет одобрен .
Иван Харламов
ИДЕАЛЬНЫЙ! РАБОТАЕТ КАК ОЧАРОВАНИЕ! ПОЛЬЗОВАТЕЛЬСКИЕ ШАБЛОННЫЕ ФИЛЬТРЫ ROX! СПАСИБО! :-)
CeDeROM
5

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

@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

Добавьте в свой 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», но оно работает. :)

Игорь Помаранский
источник
0

У вас есть Model.get_FOO_display (), где FOO - это имя поля, в котором есть варианты.

В вашем шаблоне сделайте следующее:

{{ scoop.get_flavor_display }}
Мохамед УЛД ЭЛЬ КОРИ
источник