Динамическое определенное поле на форме в 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, и это может быть не лучшее решение, но оно работает для меня и является относительно простым.