#django #django-models #django-views #django-forms #django-templates
#django #django-модели #django-представления #django-forms #django-шаблоны
Вопрос:
У меня есть 2 модели: Applicant
и Application
. Application
подключен внешним ключом к Applicant
. Для любого заявителя может быть много заявок.
models.py
class Applicant(models.Model):
first_name = models.CharField(max_length=150, null=False, blank=False)
last_name = models.CharField(max_length=150, null=False, blank=False)
email = models.EmailField(blank=False, null=True)
...
class Application(models.Model):
applicant = models.ForeignKey(Applicant, null=False, blank=False, on_delete=models.CASCADE)
...
Я не могу понять, как сделать так, чтобы 2 формы для 2 моделей отображались в 1 шаблоне таким образом, чтобы при отправке оба Applicant
и Application
могли быть полностью заполнены и сохранены. Я пробовал inlineformsets, но я продолжал получать ошибки, которые, по сути Application
, должны иметь applicant
экземпляр. Затем я попытался просто создать две отдельные формы и сохранить их отдельно, но я получил ту же ошибку. Как я могу это исправить?
forms.py
class ApplicantForm(ModelForm):
veterinaryContactPermission = forms.BooleanField(widget=forms.CheckboxInput)
class Meta:
model= Applicant
fields = '__all__'
class ApplicationForm(ModelForm):
class Meta:
model = Application
fields = '__all__'
ApplicantFormset = inlineformset_factory(
Applicant, Application, form=ApplicationForm,
fields=['applicant'], can_delete=False
)
views.py
class ApplicantApplication(CreateView):
model = Applicant
# form1 = ApplicantForm
# form2 = ApplicationForm
form_class = ApplicantForm
template_name = 'animals/register.html'
success_url = reverse_lazy('animals:applicant_profile_complete')
def get_context_data(self, **kwargs):
data = super(ApplicantApplication, self).get_context_data(**kwargs)
data['form_applicant'] = ApplicantFormset()
if self.request.POST:
data['form_applicant'] = ApplicantFormset(self.request.POST, self.request.FILES)
# context = data
else:
data['form_applicant'] = ApplicantFormset()
# context = {'data':data, 'form1': self.form1, 'form2': self.form2}
return data
def form_valid(self, form):
context = self.get_context_data()
form_app = context['form_applicant']
with transaction.atomic():
self.object = form.save()
if form_app.is_valid():
form_app.instance = self.object
form_app.save()
return super(ApplicantApplication, self).form_valid(form)
ValidationError at /animals/register/
['ManagementForm data is missing or has been tampered with']
Request Method: POST
Request URL: http://127.0.0.1:8000/animals/register/
Django Version: 3.1.4
Exception Type: ValidationError
Exception Value:
['ManagementForm data is missing or has been tampered with']
Exception Location: /Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/forms/formsets.py, line 94, in management_form
Python Executable: /Users/zb/Desktop/animalDirectoryTemplate/.venv/bin/python
Python Version: 3.7.1
Python Path:
['/Users/zb/Desktop/animalDirectoryTemplate',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload',
'/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages',
'/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/setuptools-39.1.0-py3.7.egg',
'/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg']
Server time: Mon, 18 Jan 2021 00:50:08 0000
Traceback Switch to copy-and-paste view
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/core/handlers/exception.py, line 47, in inner
response = await sync_to_async(response_for_exception, thread_sensitive=False)(request, exc)
return response
return inner
else:
@wraps(get_response)
def inner(request):
try:
response = get_response(request) …
except Exception as exc:
response = response_for_exception(request, exc)
return response
return inner
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/core/handlers/base.py, line 179, in _get_response
if response is None:
wrapped_callback = self.make_view_atomic(callback)
# If it is an asynchronous view, run it in a subthread.
if asyncio.iscoroutinefunction(wrapped_callback):
wrapped_callback = async_to_sync(wrapped_callback)
try:
response = wrapped_callback(request, *callback_args, **callback_kwargs) …
except Exception as e:
response = self.process_exception_by_middleware(e, request)
if response is None:
raise
# Complain if the view returned None (a common error).
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/views/generic/base.py, line 70, in view
return self.dispatch(request, *args, **kwargs) …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/views/generic/base.py, line 98, in dispatch
return handler(request, *args, **kwargs) …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/views/generic/edit.py, line 172, in post
return super().post(request, *args, **kwargs) …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/views/generic/edit.py, line 142, in post
return self.form_valid(form) …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/animals/views.py, line 321, in form_valid
if form_app.is_valid(): …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/forms/formsets.py, line 308, in is_valid
self.errors …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/forms/formsets.py, line 288, in errors
self.full_clean() …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/forms/formsets.py, line 329, in full_clean
for i in range(0, self.total_form_count()): …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/forms/formsets.py, line 112, in total_form_count
return min(self.management_form.cleaned_data[TOTAL_FORM_COUNT], self.absolute_max) …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/utils/functional.py, line 48, in __get__
res = instance.__dict__[self.name] = self.func(instance) …
▶ Local vars
/Users/zb/Desktop/animalDirectoryTemplate/.venv/lib/python3.7/site-packages/django/forms/formsets.py, line 94, in management_form
code='missing_management_form', …
▶ Local vars
Request information
USER
zbrenner
GET
No GET data
POST
Variable Value
csrfmiddlewaretoken
'MoRtdL69wHGAiSL2cEX3sTu1ODUtspeH6z86bMMEtjjWr9SIanAkiuiNWdeU8DUk'
first_name
'Z'
last_name
'B'
phone
'644-777-2222'
email
'zbreadkwelkdak@gmail.com'
streetAddress
'8843 all ln'
city
'San Clemente'
state
'CA'
country
'United States'
age
'51'
job
'Physician'
kids
'0'
kidDeets
''
adults
'0'
adultDeets
''
cats
'1'
dogs
'2'
otherAnimals
'0'
animalDeets
''
veterinaryClinic
'Clinic'
veterinaryContactPermission
'on'
veterinaryPhone
'900-444-0000'
homeOwnership
'on'
landlordApproves
'on'
landlordContactPermission
'on'
landlordName
'Landlord'
landlordPhone
'555-444-3333'
hoursAway
'3'
haveBackYard
'on'
yardSizeInAcres
'150'
haveFence
'on'
fenceDeets
'6 Feet Tall, Mixed wire and wood'
yardPhoto
''
housePhoto
''
FILES
No FILES data
COOKIES
Variable Value
sessionid
'2uyil6sff65o46phqvayxewigoy3p01e'
csrftoken
'IFiwj7RpZ998CENmWAbfLJIewayhTEwS2Qz9h8xUWLMuLVU2UjOwBkw0EKSIzScv'
META
Variable Value
Apple_PubSub_Socket_Render
'/private/tmp/com.apple.launchd.zHVr0NmLOt/Render'
BASH_FUNC_generate_command_executed_sequence%%
"() { printf '\e\7'n}"
CONTENT_LENGTH
'655'
CONTENT_TYPE
'application/x-www-form-urlencoded'
CSRF_COOKIE
'IFiwj7RpZ998CENmWAbfLJIewayhTEwS2Qz9h8xUWLMuLVU2UjOwBkw0EKSIzScv'
DJANGO_SETTINGS_MODULE
'animalDirectoryTemplate.settings'
register.html
{% extends 'base.html' %}
{% load static %}
{% load bootstrap %}
{% block head %}
<link rel="stylesheet" href="{% static 'node_modules/bootstrap/dist/css/bootstrap.min.css' %}">
{% endblock %}
{% block content %}
<h2>Application</h2>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
<table>
{# {{ form1.as_table }}#} {# The only way I've been able to display both forms which means I'm not using formset...? #}
{{ form.as_table }} {# Displays Applicant form #}
</table>
<table>
{{ form_applicant.as_table }} {# Should display Application form but it doesn't. It doesn't display anything visible #}
{# {{ form2.as_table }}#} {# The only way I've been able to display both forms #}
</table>
<input type="submit" value="Submit">
<input type="submit" onclick="window.location='{% url 'animals:animals' %}'; return false;" value="Cancel">
</form>
{% endblock %}
Комментарии:
1. Не могли бы вы также включить свой HTML, пожалуйста? Похоже, вы что-то упускаете из виду, основываясь на этой ошибке.
2. @markwalker_ Хорошо, да. Мне не удалось отобразить обе формы, не передавая в контекст обе формы, а также formset, что для меня не имеет смысла.
3. @markwalker_ Я только что понял это. По какой-то причине я индексировал поле foreignkey, а не другие поля. Я исправил параметры inlineformset, и теперь он работает. Спасибо, что помогли мне продумать это.
4. @markwalker_ Оказывается, я этого не понял. Форма заявителя сохраняется в БД, а приложение — нет. По какой-то причине он не проверяется, поэтому view отправляет только форму заявителя. Я не могу понять, почему это так.
5. Да, как указал @NKSM в своем ответе, это потому, что вам не хватает формы управления. Я подозревал это.
Ответ №1:
Вы должны добавить {{ formset.management_form }}
.
Django, использующий набор форм в представлениях и шаблонах
Пример:
<form method="post">
{% csrf_token %}
{{ form.as_table }}
<hr>
{{ form_applicant.management_form }}
<table>
{% for form in form_applicant %}
{{ form }}
{% endfor %}
</table>
</form>
Дополнительные
def post(self, request, *args, **kwargs):
"""
Handles POST requests, instantiating a form instance and its inline
formsets with the passed POST variables and then checking them for
validity.
"""
form_class = self.get_form_class()
form = self.get_form(form_class)
form_applicant = ApplicantFormset(self.request.POST, self.request.FILES)
if (form.is_valid() and form_applicant.is_valid()):
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
form_applicant = ApplicantFormset(self.request.POST, self.request.FILES)
self.object = form.save()
....
return HttpResponseRedirect(self.get_success_url())
def get_context_data(self, **kwargs):
data = super(ApplicantApplication, self).get_context_data(**kwargs)
data['form_applicant'] = ApplicantFormset()
if self.request.POST:
data['form_applicant'] = ApplicantFormset(self.request.POST, self.request.FILES)
# context = data
else:
data['form_applicant'] = ApplicantFormset()
# context = {'data':data, 'form1': self.form1, 'form2': self.form2}
return data
Комментарии:
1. Спасибо. Я добавил ‘formset.management_form`, но это не помогло. Кроме того, если я использую
{% for form in formset %} {{form}} {% endfor %}
, ничего не отображается .. только кнопка отправки.2. Разве это не то, что делает мой
def get_context_data(self, **kwargs):
метод?if self.request.POST: else
Я попробовал предложенный вами код и оценил его, но я получил AttributeError иSignature of method 'ApplicantApplication.form_invalid()' does not match signature of base method in class 'FormMixin
3. get_context_data запускается после метода post . Итак, в get_context_data форма уже недействительна.
4. Спасибо за вашу помощь. Я внес изменения и теперь получаю ошибку в строке 109
return self.form_invalid(form, form_applicant)
TypeError takes 2 arguments but was given 3
. строка 109 находится в методе POST и указывает, что форма недействительна, и именно здесь у меня также возникли проблемы сget_context_data
методом. Изначально это был один из моих источников инструкций: dev.to/zxenia /…5. Удалите form_applicant из return self.form_invalid(форма, form_applicant . Я забыл его удалить.