英文:
Separate form fields to "parts"; render part with loop, render part with specific design, render part with loop again
问题
# forms.py
class HappyIndexForm(forms.Form):
pizza_eaten = forms.IntegerField(label="Pizzas eaten")
# 5 more fields
minutes_outside = forms.IntegerField(label="Minutes outside")
class TherapyNeededForm(HappyIndexForm):
had_therapy_before = forms.BooleanField()
# about 20 more fields
class CanMotivateOthers(HappyIndexForm):
has_hobbies = forms.BooleanField()
# about 20 more fields
<!-- template.html -->
{% for field in first_ten_fields_therapy_needed %}
{{ field }}
{% endfor %}
{% include happyindexform.html %}
{% for field in second_ten_fields_therapy_needed %}
{{ field }}
{% endfor %}
任务:
循环遍历 TherapyNeededForm
的前十个字段,然后显示我为来自 HappyIndexForm
的字段编写的特定模板,称为 'happyindexform.html',然后循环遍历 TherapyNeededForm
的后十个字段。
我的问题:
如果我循环遍历字段并包含我的 'happyindexform.html',那么从继承中获得的字段将显示两次。我当然也可以为 TherapyNeededForm
的所有20个字段编写特定模板,但这很重复,与DRY不符。
注意:我正在使用Django的基于类的视图。这就是为什么我觉得使用2个表单比继承方法更加紧张的原因。
我觉得我不知道如何将表单分成块并将它们分别发送到模板。
英文:
I want to render part of the form via a loop in template and a part with specific "design".
# forms.py
class HappyIndexForm(forms.Form):
pizza_eaten = forms.IntegerField(label="Pizzas eaten")
# 5 more fields
minutes_outside = forms.IntegerField(label="Minutes outside")
class TherapyNeededForm(HappyIndexForm):
had_therapy_before = forms.BooleanField()
# about 20 more fields
class CanMotivateOthers(HappyIndexForm):
has_hobbies = forms.BooleanField()
# about 20 more fields
The only purpose of HappyIndexForm
is to pass the 7 fields to other forms that need to work with the "HappyIndex" (it is a made up example).
I have designed a very nice and complex template for the HappyIndexForm
fields. The fields of TherapyNeededForm
exclusive of the HappyIndexForm
fields I want to simply loop over.
<!-- template.html -->
{% for field in first_ten_fields_therapy_needed %}
{{ field }}
{% endfor %}
{% include happyindexform.html %}
{% for field in second_ten_fields_therapy_needed %}
{{ field }}
{% endfor %}
Task:
Loop over first ten fields of TherapyNeededForm
, then display the specific template I wrote for the fields coming from HappyIndexForm
called 'happyindexform.html', then loop over second ten fields of TherapyNeededForm
.
My problem:
If I loop over the fields and include my 'happyindexform.html' the fields coming from inheritance get displayed twice. I for sure can also write the specific template for all the 20 fields of TherapyNeededForm
but that is very repetitive and not in any way aligned to DRY.
Note: I am using django's class-based views. That's why I feel using 2 forms would be more stressful than the inheritance approach.
I feel like I am somehow missing how I can devide my form to blocks and ship them to the template separately.
答案1
得分: 2
我会翻译代码部分,以下是代码的翻译:
from itertools import islice
class TherapyNeededView(FormView):
template_name = 'template.html'
form_class = TherapyNeededForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = context['form']
# 获取所有字段名
all_fields = list(form.fields.keys())
# 获取前10个字段名
first_ten_fields = list(islice(all_fields, 10))
# 获取从HappyIndexForm继承的字段名
happy_index_fields = [name for name in all_fields if name in HappyIndexForm.base_fields]
# 获取剩余的字段名
remaining_fields = list(islice(all_fields, len(happy_index_fields) + len(first_ten_fields), None))
# 更新上下文
context.update({
'first_ten_fields_therapy_needed': first_ten_fields,
'happy_index_fields': happy_index_fields,
'second_ten_fields_therapy_needed': remaining_fields,
})
return context
在模板中,你可以使用这些新变量:
<!-- template.html -->
{% for field_name in first_ten_fields_therapy_needed %}
{{ form[field_name] }}
{% endfor %}
{% include happyindexform.html %}
{% for field_name in second_ten_fields_therapy_needed %}
{{ form[field_name] }}
{% endfor %}
在你的'happyindexform.html'中,你可以遍历字段:
<!-- happyindexform.html -->
{% for field_name in happy_index_fields %}
{{ form[field_name] }}
{% endfor %}
这样,来自'HappyIndexForm'的字段将只显示一次。
英文:
I would assume the HappyIndexForm
is used as a base form class for other form classes. Here, you have two other form classes - TherapyNeededForm
and CanMotivateOthersForm
- that inherit from HappyIndexForm
, thereby inheriting its fields.
When you instantiate TherapyNeededForm
or CanMotivateOthersForm
, they will have all the fields from HappyIndexForm
along with their own additional fields.
The goal would be therefore to separate out the fields inherited from HappyIndexForm
from the rest of the fields in TherapyNeededForm
and CanMotivateOthersForm
, which allows you to treat them differently in your templates.
In Django, form fields are stored in a dictionary-like object, which ensures that the fields are always iterated over in the order (by default) they were defined in the form class (see "Notes on field ordering").
You could take advantage of this to separate the fields into three parts:
- the first 10,
- the inherited fields from
HappyIndexForm
, - and the remaining fields.
To do this, you can use Python's built-in itertools
library, specifically the []islice
function](https://docs.python.org/3/library/itertools.html#itertools.islice), which returns an iterator that returns selected elements from the iterable.
In your view, you can slice the fields into two parts, the first ten and the remaining fields.
Then, in the template, you can include the 'happyindexform.html' after the first loop.
However, the key here is to remember that Django form fields need to be accessed in a [bound state](https://docs.djangoproject.com/en/4.2/ref/forms/api/#bound-and-unbound-forms] in order to be rendered properly in a template.
When you iterate over a form object directly, you get bound fields, which are the field objects bound to the form and its data. These bound fields can be directly included in templates, and Django will render the appropriate HTML.
That means a code like this one would be incorrect:
// Incorrect
first_ten_fields = list(islice(form, 10))
remaining_fields = list(islice(form, 10, None))
This is incorrect, because slicing the form object gives us the fields themselves, not the bound fields, which are needed for rendering.
You need first to get a list of all field names, and then to slice this list of names:
// Correct
all_fields = list(form.fields.keys())
first_ten_fields = list(islice(all_fields, 10))
second_ten_fields = list(islice(all_fields, 10, None))
And you need to be careful with the template themselves:
<!-- Incorrect -->
{% for field in first_ten_fields %}
{{ field }}
{% endfor %}
That does not work because field
is not a bound field, it's just the field itself: you need to use the field names (form[field_name]
) to access the corresponding bound fields:
<!-- Correct -->
{% for field_name in first_ten_fields %}
{{ form[field_name] }}
{% endfor %}
The code would be:
from itertools import islice
class TherapyNeededView(FormView):
template_name = 'template.html'
form_class = TherapyNeededForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = context['form']
# Get all field names
all_fields = list(form.fields.keys())
# Get the first 10 field names
first_ten_fields = list(islice(all_fields, 10))
# Get the field names inherited from HappyIndexForm
happy_index_fields = [name for name in all_fields if name in HappyIndexForm.base_fields]
# Get the remaining field names
remaining_fields = list(islice(all_fields, len(happy_index_fields) + len(first_ten_fields), None))
# Update the context
context.update({
'first_ten_fields_therapy_needed': first_ten_fields,
'happy_index_fields': happy_index_fields,
'second_ten_fields_therapy_needed': remaining_fields,
})
return context
In your template, you can use these new variables:
<!-- template.html -->
{% for field_name in first_ten_fields_therapy_needed %}
{{ form[field_name] }}
{% endfor %}
{% include happyindexform.html %}
{% for field_name in second_ten_fields_therapy_needed %}
{{ form[field_name] }}
{% endfor %}
And in your 'happyindexform.html
', you can loop over the fields:
<!-- happyindexform.html -->
{% for field_name in happy_index_fields %}
{{ form[field_name] }}
{% endfor %}
This way, the fields from the HappyIndexForm
will only be displayed once.
The line in the view:
happy_index_fields = [name for name in all_fields if name in HappyIndexForm.base_fields]
This line is going through the names of the fields of the form in the view (either TherapyNeededForm
or CanMotivateOthersForm
), and picking out the ones that are part of HappyIndexForm
.
These field names are then passed to the template separately from the other field names.
In the template, we use these names to access the corresponding bound fields from the form. Django will then render these bound fields correctly.
This approach allows you to use your special template for the HappyIndexForm
fields while avoiding the issue of displaying these fields twice.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论