繁体   English   中英

django - 动态表格字段集

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM