Django 模板:选择项的详细版本

140

我有一个模型:

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

我想要一个与提到的那个模板一样通用的模板,但是要打印出“ 精彩的鸡蛋 ”。

[由于我不确定真正的问题在哪里,所以为了我们所有人都能看清楚,我加粗了它 :) ]

我知道如何以本身丑陋的方式获取选择项的冗长版本:

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

真正的痛点在于我需要获取所选的选项,而我想到的唯一方法是遍历选择并检查{% ifequals currentChoice.0 choiceField.data %},这甚至更加丑陋。

是否可以轻松完成?还是需要一些模板标签编程?这不应该已经在Django中可用了吗?

11个回答

289
在Django模板中,您可以使用"get_FOO_display()"方法,该方法将返回字段的可读别名,其中'FOO'是字段的名称。
注意:如果标准的FormPreview模板没有使用它,则您始终可以为该表单提供自己的模板,其中将包含类似于{{ form.get_meal_display }}的内容。

1
是的,我知道。不过这并不是很通用,除非你知道一种在模板中迭代模型对象的所有get_FOO_display方法的方法 :) 我有点懒得写非通用模板 ;)此外,文档说它是模型实例的方法。因此,它必须是绑定到现有对象的模型表单,但这不是情况,也不是通用的。 - Artur Gajowy
2
请注意,此用法不仅限于视图,get_FOO_display()是模型对象本身的方法,因此您也可以在模型代码中使用它!例如,在__unicode__()中非常方便。 - Bogatyr

67
你的问题最好的解决方案是使用帮助函数。 如果选项存储在变量CHOICES中,存储所选选项的模型字段是'choices',那么你可以直接使用。
 {{ x.get_choices_display }}

在你的模板中。这里,x是模型实例。


3
为什么你会在有一个有用的回答已经存在的情况下两年后才回答这样?投票支持的人是谁?这和@roberto的回答一模一样,只不过比他晚了两年... - boatcoder
17
@Mark0978 之所以给这个答案点赞,是因为(对我来说)它比“得票最高”的答案更容易理解。可能因人而异。 - Nir Levy
请参阅文档和https://code.djangoproject.com/ticket/4023。 - undefined

53

如果我的回答与已经列出的任何答案重复,我表示道歉,但似乎还没有提供这个答案,而且看起来相当简洁。以下是我是如何解决这个问题的:

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

11

基于 Noah 的回答,这里提供了一个版本,可以避免没有选择的字段:

#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

我不确定是否可以使用过滤器来实现这样的目的。如果有更好的解决方案,我很乐意看到 :) 谢谢Noah!


+1,因为提到了你的路径 #annoyances/templatetags/... 哈哈... 我使用get_FOO_display(),这在表单文档底部有提到。 - fmalina
使用 hasattr 在 choices 上是个好主意! - oden

7
我们可以通过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)

似乎相当通用 :) 不能确定,因为自那时以来我没有做过太多Python或Django。然而很遗憾,它仍然需要第三方(不包括在Django中)的过滤器(否则你会告诉我们,伊万,不是吗?;))... - Artur Gajowy
是的,截至今天,在Django中没有这样的默认功能。我已经提出了建议,谁知道,也许它会被批准 - Ivan Kharlamov
1
完美!像魔法一样好用!自定义模板过滤器太棒了!谢谢! :-) - CeDeROM

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

4

在模型中有一个方法 Model.get_FOO_display(),其中 FOO 是具有选项的字段的名称。

在您的模板中,请执行以下操作:

{{ scoop.get_flavor_display }}

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)    

更新:我不确定这个解决方案是否足够“pythonic”和“django-way”,但它可以工作。:)


0
<select class="form-select">
    {% for key, value in form.meal.field.choices %}
        {% if form.meal.value == key %}
            <option value="{{ form.key }}" selected>{{ value }}</option>
        {% else %}
            <option value="{{ key }}">{{ value }}</option>
        {% endif %}
    {% endfor %}
</select>

你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

0

嘿,那个方式怎么样?

在models.py中

class MealOrder(models.Model):
    CHOICES = (
        ('s', 'Glorious spam'),
        ('e', 'Fabulous eggs'),
    )
    meal = models.CharField(max_length=8, choices=CHOICES)
    meal_value = models.CharField(max_length=1, blank=True, null=True, 
        editable=False)
    
    def save(self, *args, **kwargs):
        if self.meal == "s":
            self.meal_value = "Glorious spam"
        elif self.meal == "e":
            self.meal_value = "Fabulous eggs"
        super(MealOrder, self).save(*args, **kwargs)

in views.py

from .models import MealOrder

def meal_order(request):
    meals = MealOrder.objects.all()

    return render(request, "meals.html", {
        "meals": meals,
    })

在meals.html文件中

{% for meal in meals %}
    {{meal.meal_value }}
{%endfor%}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接