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.像这样
现在,技巧将是如何使对象可用于小部件名称空间.默认情况下,小部件上仅存在某些属性,如小部件的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 %}
并且将产生以下效果.
无论在何处使用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