[英]django - dynamic form fieldsets
表格将吐出未知数量的问题以供回答。 每个问题都包含一个提示,一个值字段和一个单位字段。 表单是在运行时在formclass的init方法中构建的。
编辑:每个问题都会收到一个用作标签的唯一提示,以及select元素的唯一单元列表。
这似乎是一个适用于可迭代表单字段集的案例,可以轻松设置样式。 但由于字段集 - 例如django-form-utils中的字段集被定义为元组,它们是不可变的......我找不到在运行时定义它们的方法。 这是可能的,还是另一种解决方案?
编辑:
具有initial_data的formset不是答案 - initial_data仅允许为formset中的表单字段设置默认值。 无法通过initial_data将项目列表发送到choicefield构造函数。
......除非我错了。
查看表单集 。 您应该能够将N个问题中的每个问题的数据作为初始数据传递。 这些方面的东西:
question_data = []
for question in your_question_list:
question_data.append({'prompt': question.prompt,
'value': question.value,
'units': question.units})
QuestionFormSet = formset_factory(QuestionForm, extra=2)
formset = QuestionFormSet(initial=question_data)
老问题,但我遇到了类似的问题。 到目前为止,我发现的最接近的事情是这个片段基于Malcom几年前做过的帖子。 http://djangosnippets.org/snippets/1955/
原始代码段没有解决模板方面并将它们拆分为字段集,但是将每个表单添加到其自己的字段集应该可以实现。
forms.py
from django.forms.formsets import Form, BaseFormSet, formset_factory, \
ValidationError
class QuestionForm(Form):
"""Form for a single question on a quiz"""
def __init__(self, *args, **kwargs):
# CODE TRICK #1
# pass in a question from the formset
# use the question to build the form
# pop removes from dict, so we don't pass to the parent
self.question = kwargs.pop('question')
super(QuestionForm, self).__init__(*args, **kwargs)
# CODE TRICK #2
# add a non-declared field to fields
# use an order_by clause if you care about order
self.answers = self.question.answer_set.all(
).order_by('id')
self.fields['answers'] = forms.ModelChoiceField(
queryset=self.answers())
class BaseQuizFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
# CODE TRICK #3 - same as #1:
# pass in a valid quiz object from the view
# pop removes arg, so we don't pass to the parent
self.quiz = kwargs.pop('quiz')
# CODE TRICK #4
# set length of extras based on query
# each question will fill one 'extra' slot
# use an order_by clause if you care about order
self.questions = self.quiz.question_set.all().order_by('id')
self.extra = len(self.questions)
if not self.extra:
raise Http404('Badly configured quiz has no questions.')
# call the parent constructor to finish __init__
super(BaseQuizFormSet, self).__init__(*args, **kwargs)
def _construct_form(self, index, **kwargs):
# CODE TRICK #5
# know that _construct_form is where forms get added
# we can take advantage of this fact to add our forms
# add custom kwargs, using the index to retrieve a question
# kwargs will be passed to our form class
kwargs['question'] = self.questions[index]
return super(BaseQuizFormSet, self)._construct_form(index, **kwargs)
QuizFormSet = formset_factory(
QuestionForm, formset=BaseQuizDynamicFormSet)
views.py
from django.http import Http404
def quiz_form(request, quiz_id):
try:
quiz = Quiz.objects.get(pk=quiz_id)
except Quiz.DoesNotExist:
return Http404('Invalid quiz id.')
if request.method == 'POST':
formset = QuizFormSet(quiz=quiz, data=request.POST)
answers = []
if formset.is_valid():
for form in formset.forms:
answers.append(str(int(form.is_correct())))
return HttpResponseRedirect('%s?a=%s'
% (reverse('result-display',args=[quiz_id]), ''.join(answers)))
else:
formset = QuizFormSet(quiz=quiz)
return render_to_response('quiz.html', locals())
模板
{% for form in formset.forms %}
<fieldset>{{ form }}</fieldset>
{% endfor %}
这是我用于类似情况的(一组变量的字段集,每个字段集包含一组变量字段)。
我使用了type()
函数来构建我的Form类,以及来自django-form-utils的 BetterBaseForm
类。
def makeFurnitureForm():
"""makeFurnitureForm() function will generate a form with
QuantityFurnitureFields."""
furnitures = Furniture.objects.all()
fieldsets = {}
fields = {}
for obj in furnitures:
# I used a custom Form Field, but you can use whatever you want.
field = QuantityFurnitureField(name = obj.name)
fields[obj.name] = field
if not obj.room in fieldsets.keys():
fieldsets[obj.room] = [field,]
else:
fieldsets[obj.room].append(field)
# Here I use a double list comprehension to define my fieldsets
# and the fields within.
# First item of each tuple is the fieldset name.
# Second item of each tuple is a dictionnary containing :
# -The names of the fields. (I used a list comprehension for this)
# -The legend of the fieldset.
# You also can add other meta attributes, like "description" or "classes",
# see the documentation for further informations.
# I added an example of output to show what the dic variable
# I create may look like.
dic = [(name, {"fields": [field.name for field in fieldsets[name]], "legend" : name})
for name in fieldsets.keys()]
print(dic)
# Here I return a class object that is my form class.
# It inherits from both forms.BaseForm and forms_utils.forms.BetterBaseForm.
return (type("FurnitureForm",
(forms.BaseForm, form_utils.forms.BetterBaseForm,),
{"_fieldsets" : dic, "base_fields" : fields,
"_fieldset_collection" : None, '_row_attrs' : {}}))
以下是dic
外观示例:
[('fieldset name 1',
{'legend': 'fieldset legend 2',
'fields' ['field name 1-1']}),
('fieldset name 2',
{'legend': 'fieldset legend 2',
'fields' : ['field 1-1', 'field 1-2']})]
由于本文建议使用BaseForm
而不是Form
我使用BetterBaseForm
而不是BetterForm
。
这篇文章很有意思,即使它很旧,并解释了如何做动态表单(使用可变字段集)。 它还提供了其他方法来实现动态表单。
虽然它没有解释如何使用fieldsets,但它激发了我如何去做,并且原理保持不变。
在视图中使用它非常简单:
return (render(request,'main/form-template.html', {"form" : (makeFurnitureForm())()}))
并在模板中:
<form method="POST" name="myform" action=".">
{% csrf_token %}
<div>
{% for fieldset in form.fieldsets %}
<fieldset>
<legend>{{ fieldset.legend }}</legend>
{% for field in fieldset %}
<div>
{% include "main/furniturefieldtemplate.html" with field=field %}
</div>
{% endfor %}
</fieldset>
{% endfor %}
</div>
<input type="submit" value="Submit"/>
</form>
我使用下面的技巧来创建动态formset。 从视图中调用create_dynamic_formset()函数。
def create_dynamic_formset(name_filter):
"""
-Need to create the classess dynamically since there is no other way to filter
"""
class FormWithFilteredField(forms.ModelForm):
type = forms.ModelChoiceField(queryset=SomeType.objects.filter(name__icontains=name_filter))
class Meta:
model=SomeModelClass
return modelformset_factory(SomeModelClass, form=FormWithFilteredField)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.