У меня довольно сложная форма Django, которая влияет на 3 модели, часть из которых включает встроенный набор форм. Я нашел хорошее решение для создания формы по адресу https://dev.to/zxenia/django-inline-formsets-with-class-based-views-and-crispy-forms-14o6. Я расширил это решение и добавил третью модель аналогично тому, как был добавлен набор форм (используя настраиваемую Django Crispy Form и вставив ее с помощью функций Crispy Forms Layout).
Моя проблема в том, что любая проверка Ошибки, возникшие в любой из двух вставленных форм (набор форм и небольшая подчиненная форма), просто игнорируются - основная форма правильно отправляет сообщения, а возникшие ошибки ValidationErrors отображаются в форме как ошибки, позволяя пользователю исправить любые ошибки, и его данные правильно сохраняются в база данных. Если подчиненная форма и набор форм действительны, их данные также сохраняются правильно. Однако, если данные в подчиненной форме и наборе форм недействительны, форма никогда не отображает ошибки, чтобы дать пользователю возможность исправить свою ошибку, а данные просто игнорируются и никогда не сохраняются в базе данных - данные основной модели сохраняются нормально. хотя.
Мой вопрос: как мне заставить форму обновлять sh с ошибками, отображаемыми в добавленной подчиненной форме и наборе форм, позволяя пользователю исправить свои ошибки?
Большая часть кода ниже - из неплохого сообщения, упомянутого выше, с добавлением третьей модели
Модели:
from django.db import models
from django.contrib.auth.models import User
class Collection(models.Model):
subject = models.CharField(max_length=300, blank=True)
owner = models.CharField(max_length=300, blank=True)
note = models.TextField(blank=True)
created_by = models.ForeignKey(User,
related_name="collections", blank=True, null=True,
on_delete=models.SET_NULL)
def __str__(self):
return str(self.id)
class CollectionTitle(models.Model):
"""
A Class for Collection titles.
"""
collection = models.ForeignKey(Collection,
related_name="has_titles", on_delete=models.CASCADE)
name = models.CharField(max_length=500, verbose_name="Title")
language = models.CharField(max_length=3)
Class CollectionTxn(models.Model):
"""
A Class for Collection transactions.
"""
collection = models.ForeignKey(Collection,
related_name="has_txn", on_delete=models.CASCADE)
number_received= models.IntegerField()
date_received= models.DateField()
class Meta:
'''
If 2 rows are entered with the same information, a validation error is raised, but it just
doesn't save the data at all instead of refreshing the form showing the error.
'''
unique_together = ('number_received', 'date_received')
forms.py:
from django import forms
from .models import Collection, CollectionTitle
from django.forms.models import inlineformset_factory
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Fieldset, Div, Row, HTML, ButtonHolder, Submit
from .custom_layout_object import Formset, Subform
import re
class CollectionTitleForm(forms.ModelForm):
class Meta:
model = CollectionTitle
exclude = ()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
formtag_prefix = re.sub('-[0-9]+$', '', kwargs.get('prefix', ''))
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
Field('name'),
Field('language'),
Field('DELETE'),
css_class='formset_row-{}'.format(formtag_prefix)
)
)
CollectionTitleFormSet = inlineformset_factory(
Collection, CollectionTitle, form=CollectionTitleForm,
fields=['name', 'language'], extra=1, can_delete=True
)
class CollectionForm(forms.ModelForm):
class Meta:
model = Collection
exclude = ['created_by', ]
def __init__(self, *args, **kwargs):
super(CollectionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3 create-label'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout(
Div(
Field('subject'),
Field('owner'),
Fieldset('Add titles',
Formset('titles')),
Field('note'),
Subform('transactions'),
HTML("<br>"),
ButtonHolder(Submit('submit', 'Save')),
)
)
class CollectionTxnForm(forms.ModelForm):
class Meta:
model = CollectionTxn
exclude = ()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['collection'].widget = HiddenInput()
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Row(
Field('number_received'),
Field('date_received'),
)
)
views.py:
from .models import *
from .forms import *
from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from django.db import transaction
class CollectionCreate(CreateView):
model = Collection
template_name = 'mycollections/collection_create.html'
form_class = CollectionForm
success_url = None
def get_context_data(self, **kwargs):
data = super(CollectionCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['titles'] = CollectionTitleFormSet(self.request.POST)
data['transactions'] = CollectionTrxForm(self.request.POST)
else:
data['titles'] = CollectionTitleFormSet()
data['transactions'] = CollectionTrxForm()
return data
def form_valid(self, form):
context = self.get_context_data()
titles = context['titles']
transactions = context['transactions']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if titles.is_valid():
titles.instance = self.object
titles.save()
if transactions.is_valid():
transactions.save()
return super(CollectionCreate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('mycollections:collection_detail', kwargs={'pk': self.object.pk})
custom_layout_object.py
from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
from django.shortcuts import render
from django.template.loader import render_to_string
class Formset(LayoutObject):
template = "mycollections/formset.html"
def __init__(self, formset_name_in_context, template=None):
self.formset_name_in_context = formset_name_in_context
self.fields = []
if template:
self.template = template
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
formset = context[self.formset_name_in_context]
return render_to_string(self.template, {'formset': formset})
class SubForm(LayoutObject):
template = "mycollections/subform.html"
def __init__(self, subform_name_in_context, template=None):
self.subform_name_in_context = subform_name_in_context
self.fields = []
if template:
self.template = template
def render(self, subform, form_style, context, template_pack=TEMPLATE_PACK):
subform = context[self.subform_name_in_context]
return render_to_string(self.template, {'subform': subform})
набор форм. html
{% load crispy_forms_tags %}
{% load staticfiles %}
<style type="text/css">
.delete-row {
align-self: center;
}
</style>
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
{% for hidden in form.hidden_fields %}
{{ hidden|as_crispy_field }}
{% endfor %}
{% crispy form %}
{% endfor %}
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="{% static 'mycollections/libraries/django-dynamic-formset/jquery.formset.js' %}"></script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addText: 'add another',
deleteText: 'remove',
prefix: '{{ formset.prefix }}',
});
</script>
подчиненная форма. html
{% load crispy_forms_tags %}
{% crispy subform %}
collection_create. html
{% extends "mycollections/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header">
Create collection
</div>
<div class="card-body">
{% crispy form %}
</div>
</div>
</div>
{% endblock content %}
В основном, для полей, связанных с набором форм и подчиненной формой, добавленной в макет, ошибки проверки все еще возникают, но они не всплывают на уровень формы, чтобы показать ошибки, они просто игнорируется, и данные n когда-либо сохраненный. «Основная» модель работает нормально, и для ее полей отображаются ошибки валидации. Если нет недопустимых данных, основная форма, подчиненная форма и данные набора форм сохраняются правильно. Если в наборе форм или подчиненной форме есть недопустимые данные, у пользователя никогда не будет возможности исправить эти данные.
Любая помощь относительно того, где я мог бы добавить необходимый код, чтобы, если какие-либо недопустимые данные, введенные в набор форм, или подчиненная форма приведет к тому, что форма будет обновлять sh с отображением ошибок вместо того, чтобы просто игнорировать и не сохранять недопустимые данные.