我有两个模型,运行和订单。一次运行将完成许多订单,因此我的订单和运行之间存在多对一关系,在我的订单上表示为外键。
我想构建一个用于创建运行的UI。它应该是某人选择要运行的订单的表单。我想显示一个复选框列表以及每个订单的相关信息。我现在正在使用django crispy forms。
views.py
class createRunView(LoginRequiredMixin, CreateView):
model = Run
form_class = CreateRunForm
template_name = 'runs/create_run.html'
forms.py
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' object is not iterable
,这在网上几乎没有记录。
以上建议我可能仍会在模板中获取一个小部件,尽管Field('orders', template="runs/list_orders.html"),
应根据crispy docs阻止该小部件:
字段:非常有用的布局对象。您可以使用它在字段中设置属性或使用自定义模板呈现特定字段。这样您就可以避免显式覆盖字段的窗口小部件并传递丑陋的attrs字典:
我看过this answer建议使用label_from_instance
。但是,我不确定如何将一堆html填入label_from_instance
。我没有使用不同的标签,而是希望有一个模板生成一堆html,其中显示了整个订单对象的详细信息,因此我不确定这种方法是否有效。
this question中的答案大多让我感到困惑,但接受的答案似乎并不奏效。 (可能是django版本问题,还是一个脆弱的表格问题?)
如何使用ModelMultipleChoiceField中每个模型的数据渲染模板?
答案 0 :(得分:3)
窗口小部件控制字段在HTML表单中的呈现方式。 “选择”窗口小部件(及其变体)具有两个属性template_name
和option_template_name
。 option_template_name
提供用于选择选项的模板名称。您可以将选择窗口小部件子类化以覆盖这些属性。使用子类(如CheckboxSelectMultiple)可能是一个很好的起点,因为默认情况下它不会在<select>
元素中呈现选项,因此您的样式将更容易。
默认情况下,CheckboxSelectMultiple option_template_name
为'django/forms/widgets/checkbox_option.html'
。
您可以提供自己的模板,以便按照您想要的方式呈现订单的详细信息。您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
的小部件标签文本的默认行为。请注意,在上图中,标签文字(例如Order object (1)
)与我们将更改应用于label_from_instance
之前相同。这是因为模板渲染中的默认值是在使用模型对象时使用str(obj)
;与以前由默认label_from_instance
完成的事情相同。
ModelMultiplechoiceField
子类,让label_from_instance
返回该对象。 option_template_name
。 widget.label
将是每个对象。