Django 模板:选择的详细版本

Posted

技术标签:

【中文标题】Django 模板:选择的详细版本【英文标题】:Django templates: verbose version of a choice 【发布时间】:2010-11-09 11:46:02 【问题描述】:

我有一个模型:

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 中可用了吗?

【问题讨论】:

【参考方案1】:

在 Django 模板中,您可以使用“get_FOO_display()”方法,该方法将返回该字段的可读别名,其中“FOO”是该字段的名称。

注意:如果标准的FormPreview 模板没有使用它,那么您始终可以为该表单使用provide your own templates,其中将包含 form.get_meal_display 之类的内容。

【讨论】:

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

解决您的问题的最佳方法是使用辅助函数。 如果选项存储在变量 CHOICES 中,并且存储所选选项的模型字段是“choices”,那么您可以直接使用

  x.get_choices_display 

在您的模板中。这里,x 是模型实例。 希望对您有所帮助。

【讨论】:

为什么在有用的答案已经到位 2 年后你会这样回答?谁会投票呢?仅仅 2 年后,它的答案与 @roberto 相同.... @Mark0978 赞成这个答案的原因是(对我来说)它更清楚地遵循“最高投票”的答案。 YMMV。【参考方案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】:

根据 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

我不确定是否可以为此目的使用过滤器。如果有人有更好的解决方案,我会很高兴看到它:) 谢谢诺亚!

【讨论】:

+1 用于提及您的路径 #annoyances/templatetags/... LOL ... 我使用 get_FOO_display(),它在表单文档的底部提到。 在选择中使用 hasattr 是个好主意!【参考方案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)

【讨论】:

似乎很普遍 :) 不能确定,因为从那时起我没有做过太多的 Python 或 Django。不过,很遗憾,它仍然需要一个 3rd 方(不包含在 Django 中)过滤器(否则你会告诉我们,Ivan,不是吗?;))... @ArturGajowy 是的,到目前为止,Django 中还没有这样的默认功能。我已经提议了,who knows, maybe it will be approved. 完美!奇迹般有效!自定义模板过滤器 ROX!谢谢! :-)【参考方案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】:

在你的 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”解决方案是否足够,但它有效。 :)

【讨论】:

【参考方案8】:

你有 Model.get_FOO_display() 其中 FOO 是有选择的字段的名称。

在您的模板中执行此操作:

 scoop.get_flavor_display 

【讨论】:

【参考方案9】:

Noah 和 Ivan 的解决方案的扩展扩展版本。还修复了 Noah 对 Django 3.1 的解决方案,因为 ModelChoiceIteratorValue 现在是不可散列的。

@register.filter
def display_value(value: Any, arg: str = None) -> str:
    """Returns the display value of a BoundField or other form fields"""
    if not arg:  # attempt to auto-parse
        # Returning regular field's value
        if not hasattr(value.field, 'choices'): return value.value()
        # Display select value for BoundField / Multiselect field
        # This is used to get_..._display() for a read-only form-field
        # which is not rendered as Input, but instead as text
        return list(value.field.choices)[value.value()][1]

    # usage:  field|display_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)()
        return getattr(value, arg)

    return value.get(arg) or ''

【讨论】:

以上是关于Django 模板:选择的详细版本的主要内容,如果未能解决你的问题,请参考以下文章

模板结果和模板选择的Select2参数不同(django)

从模型中显示(在模板中)选择 - Django

Django:维护在 HTML 模板中选择的选项

Django:在模板中选择带有默认字段的选项

Django - 不使用表单或模型表单时在模板中呈现模型选择

基于选择的 Django 模板/ChoiceField 显示字段