2010-04-12 15 views
5

Форма будет выплеснуть неизвестное количество вопросов для ответа. каждый вопрос содержит подсказку, поле значения и поле единицы. Форма создается во время выполнения в методе init класса formclass.django - динамические формы полей

изменить: каждый вопрос получает уникальное приглашение для использования в качестве метки, а также уникальный список единиц для выбранного элемента.

Это похоже на случай, идеально подходит для итерационных форм полей, которые можно легко стилизовать. но поскольку наборы полей - например, в django-form-utils, определены как кортежи, они неизменяемы ... и я не могу найти способ определить их во время выполнения. возможно ли это, или, возможно, другое решение?

Edit:

FormSets с initial_data не ответ - initial_data только позволяет устанавливать значения по умолчанию для полей формы в formset. список элементов не может быть отправлен в конструктор поля выбора через initial_data.

... если я ошибаюсь.

+0

не было бы лучше использовать [FormSets] (HTTP: //docs.djangoproject .com/en/dev/themes/forms/formsets /) вместо полей? Пользовательский класс формы для вопроса (с атрибутом 'prompt'), затем вы загружаете данные вопроса с помощью [' initial' ключевого слова] (http://docs.djangoproject.com/en/dev/topics/forms/formsets/# с использованием-начальных данных-с-formset)? –

+0

формы не делают трюка. по крайней мере, не данные formet_factory. Я должен быть в состоянии предоставить некоторые параметры фактическим конструкторам полей для каждой формы в наборе форм - ярлык/приглашение для поля значения и список единиц для поля выбора. –

ответ

2

Отъезд formsets. Вы должны иметь возможность передавать данные по каждому из вопросов N как initial data. Что-то в этом стихе:

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) 
+0

Исходные данные предназначены для предоставления значений по умолчанию элементу формы, а не для предоставления данных для построения формы в наборе форм. –

0

Я использовал трюк внизу, чтобы создать динамический набор форм. Вызовите функцию 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) 
1

Старый вопрос, но я сталкиваюсь с аналогичной проблемой. Самое близкое, что я нашел до сих пор, это этот фрагмент, основанный на сообщении, которое 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 %} 
0

Вот что я использовал для подобного случая (переменный набор fieldsets, каждый из которых содержащий переменный набор полей).

Я использовал функцию type(), чтобы построить свой класс формы, и BetterBaseForm класс от django-form-utils.

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']})] 

Я использовал BetterBaseForm, а не BetterForm по той же причине this article предлагает использовать BaseForm, а не Form.

Эта статья интересна, даже если она старая, и объясняет, как делать динамические формы (с переменным набором полей). Он также дает другие способы достижения динамических форм.

Это не объясняет, как это сделать с помощью полей, но это вдохновило меня найти, как это сделать, и принцип остается тем же.

С его помощью в представлении довольно просто:

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> 
Смежные вопросы