Динамическое определенное поле на форме в Django?

У меня есть форма, которая содержит более 10 полей. Теперь мне нужно определенное поле, скажем, "требования". Это может быть более одного требования, я могу сделать это с помощью текстового редактора, попросив пользователя ввести все требования в виде упорядоченного списка. Но для лучшего удобства пользователей я прошу об этом!

Я собираюсь держать кнопку под полем "требования", чтобы пользователь мог нажать на эту кнопку для получения нового поля. При этом я хочу, чтобы все поля были объединены в дикту типа

requirements = {'field1','extrafield1'} and etc

Как это сделать? Я не могу использовать набор форм (так как я просто добавляю динамическое поле, а не всю форму).

Как решить эту проблему с формами django?

У меня была похожая проблема, и я пришел к следующему решению (оно основано в основном на этом ответе, но я смог избежать любого javascript):

Форма

содержит скрытое поле для хранения количества полей requirement; по умолчанию форма имеет одно такое поле, но нажатие кнопки "add_requirement" увеличивает это количество в TestView.post(). На __init__ форма добавляет новые поля с соответствующими индексами, если это необходимо, а на save() она перебирает все индексы требований, чтобы собрать значения из соответствующих полей. TestView.post() работает по-разному для двух кнопок формы: "add_requirement" просто увеличивает количество полей и снова отображает форму с добавленным полем; кнопка по умолчанию "submit" сохраняет действующую форму и перенаправляет на success_url или повторно отображает недействительную.

forms.py

class SimpleForm(forms.Form):   
 
    requirements_count = forms.CharField(widget=forms.HiddenInput(), initial=1)     
    stable_field = forms.CharField(label='Stable field', required=False)     
    other_stable_field = forms.CharField(label='Other field', required=False)
    requirements_1 = forms.CharField(label='Requirements', required=False)
    
    def __init__(self, *args, **kwargs):   
        super(SimpleForm, self).__init__(*args, **kwargs)
        if self.is_bound:
            requirements_count = int(self.data.get('requirements_count', 1))
        else:
            requirements_count = int(self.initial.get('requirements_count', 1))
        if requirements_count > 1:   
            for index in range(2, requirements_count + 1):
                self.fields.update(
                    {'requirements_' + str(index): 
                    forms.CharField(label='Requirements', required=False)}
                    )
                
    def save(self, *args, **kwargs):
        form = self.cleaned_data
        requirements_count = int(form.get('requirements_count', 1))
        requirements_list = []
        for index in range(1, requirements_count + 1):
            requirements = form.get('requirements_' + str(index), '')
            if requirements:            
                requirements_list.append(requirements)

views.py

class TestView(FormView):
    template_name = 'testtemplate.html'
    form_class = SimpleForm
    form = None
    success_url = reverse_lazy('home')
        
    def post(self, request, *args, **kwargs):
        request_POST = request.POST
        requirements_count = int(request_POST.get('requirements_count', 1))
        if 'add_requirement' in request_POST:
            new_initial = get_initial(request_POST)
            new_initial['requirements_count'] = requirements_count + 1
            self.form = SimpleForm(initial=new_initial)
            context = self.get_context_data(form=self.form)
            response = render(request, self.template_name, context)
        else:    
            self.form = SimpleForm(data=request_POST)
            context = self.get_context_data(form=self.form)
            if self.form.is_valid():
                response = self.form_valid(self.form)
            else:
                response = render(request, self.template_name, context)  
        return response
        
    def form_valid(self, form):
        form.save()
        return super().form_valid(self.form)

# helper function
def get_initial(previous_data):
    new_initial = {}
    for key, value in previous_data.items():
        if value != '' and key != 'csrfmiddlewaretoken':
            new_initial.update({key: value})
    return new_initial

testtemplate.html

<form action="" method="post">
  {% csrf_token %}
  <table border="1">
    {{ form.as_table }}
  </table>
  <input type="submit">
  <input type="submit" name="add_requirement" value="Add another requirement">
  {{ form.non_field_errors }}
</form>

Я ни в коем случае не эксперт по Django, и это может быть не лучшее решение, но оно работает для меня и является относительно простым.

Вернуться на верх