Шаблони «Джанго»: багатослівна версія вибору


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 яйця"), тому що він використовує

{% 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 модельного об'єкта :) Я трохи лінивий для написання не загальних шаблонів;) Більше того, документи говорять це метод модельного примірника. Тому це повинно бути типовою формою, прив'язаною до вже існуючого об'єкта, що не так, а також не є загальним.
Артур Гайові

2
Зауважте, що це використання не обмежується переглядами, get_FOO_display () - це метод на самому об'єкті моделі, тому ви можете використовувати його і в коді моделі! Наприклад, у __unicode __ () це дуже зручно
Богатир

51

Найкращим рішенням вашої проблеми є використання допоміжних функцій. Якщо вибір зберігається у змінній CHOICES, а поле моделі, що зберігає вибраний вибір, є " izbora ", то ви можете безпосередньо використовувати

 {{ x.get_choices_display }}

у вашому шаблоні. Тут х - екземпляр моделі. Сподіваюся, це допомагає.


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 для згадування вашого шляху # annoyances / 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 з того часу. Дуже прикро, що йому все ще потрібен сторонній фільтр (не включений у Джанго) фільтр (інакше ти б сказав нам, Іван, чи не так?)) ...
Артур Гайові

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

Вгору: Я не впевнений, достатньо це рішення «пітонічне» та «джанго», але воно працює. :)


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.