编程语言
首页 > 编程语言> > python-如何使用每个选择的模板覆盖ModelChoiceField / ModelMultipleChoiceField默认小部件

python-如何使用每个选择的模板覆盖ModelChoiceField / ModelMultipleChoiceField默认小部件

作者:互联网

背景

我有两个模型,运行和订单.一次运行将完成许多订单,因此我的订单和运行之间存在多对一关系,表示为订单上的外键.

我想构建一个UI以创建运行.它应该是某人选择要运行的订单的形式.我想显示复选框列表以及有关每个订单的信息.我现在正在使用django crispy forms.

views.py

class createRunView(LoginRequiredMixin, CreateView):
    model = Run
    form_class = CreateRunForm
    template_name = 'runs/create_run.html'

表格

class CreateRunForm(forms.ModelForm):
    class Meta:
       model = Run
       fields = ['orders',]

    orders = forms.ModelMultipleChoiceField(queryset=Order.objects.filter(is_active=True, is_loaded=False))

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper()
        self.helper.form_method = 'post'
        self.helper.layout = Layout(
            Field('orders', template="runs/list_orders.html"),
            Submit('save', 'Create Run'),
            Button('cancel', 'Cancel'),
    )

问题

>我不确定list_orders.html模板中可以使用哪些本地人.似乎有{{field}},也许还有form.visible_fields,但是如果我深入研究其中一个,就会得到TypeError:’SubWidget’对象是不可迭代的,几乎没有在线记录.
>以上内容表明,尽管根据crispy docs,Field(‘orders’,template =“ runs / list_orders.html”)应该防止出现这种情况,但我仍可能在模板中获取小部件:

Field: Extremely useful layout object. You can use it to set attributes in a field or render a specific field with a custom template. This way you avoid having to explicitly override the field’s widget and pass an ugly attrs dictionary:

>我已经看到this answer建议使用label_from_instance.但是我不确定如何将一堆html填充到label_from_instance中.我确实希望拥有一个模板,该模板生成一堆html来显示有关整个订单对象的详细信息,而不是使用其他标签,所以我不确定这种方法是否有效.
> this question中的答案大部分使我感到困惑,但似乎无法接受. (也许是Django版本问题,还是脆皮表格问题?)

TL; DR

如何在ModelMultipleChoiceField中使用来自每个模型的数据呈现模板?

解决方法:

小部件控制如何以HTML形式呈现字段. Select小部件(及其变体)具有两个属性template_name和option_template_name. option_template_name提供用于选择选项的模板的名称.您可以将选择窗口小部件子类化以覆盖这些属性.使用子类(例如CheckboxSelectMultiple)可能是一个不错的起点,因为默认情况下,它不会在< select>中显示选项.元素,因此您的样式将更容易.

默认情况下,CheckboxSelectMultiple option_template_name为'django/forms/widgets/checkbox_option.html'.

您可以提供自己的模板,该模板将根据需要呈现订单的详细信息. IE在您的forms.py中

class MyCheckboxSelectMultiple(forms.CheckboxSelectMultiple):
    option_template_name = 'myapp/detail_options.html'

class CreateRunForm(forms.ModelForm)
    ...
    orders = ModelMultipleChoiceField(..., widget=MyCheckboxSelectMultiple)

假设myapp / detail_options.html包含以下内容

{# include the default behavior #}
{% include "django/forms/widgets/input_option.html" %} 
{# add your own additional div for each option #}
<div style="background-color: blue">
    <h2>Additional info</h2>
</div>

您会在每个标签/输入之后看到蓝色的div.像这样

Custom option template

现在,技巧将是如何使对象可用于小部件名称空间.默认情况下,小部件上仅存在某些属性,如小部件的get_context method返回.

您可以使用自己的MultipleModelChoiceField子类并重写label_from_instance来完成此操作. label_from_instance返回的值最终将作为label属性提供给小部件,该属性在呈现{{widget.label}}时用于模型表单中的可见文本.

只需重写label_from_instance以返回整个对象,然后将该子类用于您的字段.

class MyModelMultipleChoiceField(forms.ModelMultipleChoiceField):
    def label_from_instance(self, obj):
        return obj

class CreateRunForm(forms.ModelForm):
    ...
    orders = MyModelMultipleChoiceField(..., widget=MyCheckboxSelectMultiple)

因此,现在在myapp / detail_options模板中,您可以使用widget.label直接访问对象并根据需要设置自己的内容格式.例如,可以使用以下选项模板

{% include "django/forms/widgets/input_option.html" %}
{% with order=widget.label %}
    <div style="background-color: blue">
        <h2>Order info</h2>
        <p style="color: red">Order Active: {{ order.is_active }}</p>
        <p style="color: red">Order Loaded: {{ order.is_loaded }}</p>
    </div>
{% endwith %}

并且将产生以下效果.

object hack

无论在何处使用widget.label,这也不会破坏小部件标签文本的默认行为.请注意,在上图中,标签文本(例如,订单对象(1))与我们将更改应用于label_from_instance之前相同.这是因为模板呈现中的默认设置是在与模型对象一起显示时使用str(obj).默认情况下,默认label_from_instance可以完成相同的操作.

TL; DR

>创建您自己的ModelMultiplechoiceField子类,以让label_from_instance返回该对象.
>创建自己的SelectMultiple小部件子类以指定自定义option_template_name.
>指定的模板将用于呈现每个选项,其中widget.label将是每个对象.

标签:django-crispy-forms,django-forms,python,django
来源: https://codeday.me/bug/20191109/2012258.html