我在验证InlineFormSet的数据时遇到麻烦。我想要的是至少需要在表单集中输入一个资格。但是每次我用空的Qualification按下提交按钮时,都不会引发ValidationError。
这是我的代码:
forms.py
class QualificationForm(forms.ModelForm):
class Meta:
model = Qualification
fields = ['qualification']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.disable_csrf = True
self.helper.help_text_inline = True
self.helper.label_class = 'col-sm-4'
self.helper.field_class = 'col-sm-8'
self.helper.layout = Layout(
Field('qualification')
)
class QualificationCustomInlineFormSet(forms.BaseInlineFormSet):
def clean(self):
cleaned_data = super().clean()
for form in self.forms:
qualification = cleaned_data.get('qualification', '').strip()
if not qualification:
msg = "Please enter qualification."
self.add_error('qualification', msg)
raise forms.ValidationError(msg, "error")
return cleaned_data
views.py
class JobAddView(LoginRequiredMixin, SuccessMessageMixin, FormView):
template_name = 'cepalco_website_admin/job_form.html'
form_class = forms.JobForm
success_url = reverse_lazy('cepalco_website_admin:home')
success_message = "Successfully added %(job_title)s!"
head_title = "Add new job"
title_text = head_title
description = "Enter the following job information"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['head_title'] = self.head_title
context['title_text'] = self.title_text
context['description'] = self.description
QualificationInlineFormSet = inlineformset_factory(
Job, Qualification,
form=forms.QualificationForm, formset=forms.QualificationCustomInlineFormSet,
extra=0, can_delete=False, min_num=1
)
WorkAssignmentInlineFormSet = inlineformset_factory(
Job, WorkAssignment, form=forms.WorkAssignmentForm,
extra=0, can_delete=False, min_num=1
)
context['qualification_inlineformset'] = QualificationInlineFormSet
context['work_assignment_inlineformset'] = WorkAssignmentInlineFormSet
return context
模板
{% extends 'cepalco_website_admin/base_admin_main.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block head_title %}{{ head_title }} | {{ block.super }}{% endblock head_title %}
{% block head_css %}
{{ block.super }}
{% include 'cepalco_website_admin/no_asteriskfield.html' %}
{% endblock head_css %}
{% block content_main %}
<div class="title-bar">
<h1 class="title-bar-title">
<span class="d-ib">{{ title_text }}</span>
</h1>
<p class="title-bar-description">
<small>{{ description }}</small>
</p>
</div>
<div class="row">
<div class="col-md-8">
<div class="demo-form-wrapper">
<form action="{% url 'cepalco_website_admin:job_add' %}" class="form form-horizontal" id="id_jobform" method="post">
{% csrf_token %}
<div class="divider">
<div class="divider-content"><h4>Job Information</h4></div>
</div>
<!-- <legend>Job Information</legend> -->
{% crispy form %}
<div class="divider">
<div class="divider-content"><h4>Qualifications</h4></div>
</div>
<!-- <legend>Qualifications</legend> -->
<div id="id_{{ qualification_inlineformset.prefix }}">
{% crispy qualification_inlineformset qualification_inlineformset.form.helper %}
</div>
<div class="divider">
<div class="divider-content"><h4>Work Assignments</h4></div>
</div>
<!-- <legend>Work Assignments</legend> -->
<div id="id_{{ work_assignment_inlineformset.prefix }}">
{% crispy work_assignment_inlineformset work_assignment_inlineformset.form.helper %}
</div>
<div class="form-group">
<input type="submit" name="save" value="Save" class="btn btn-primary col-sm-offset-4" id="submit-id-save" />
<input type="reset" name="reset" value="Reset" class="btn btn-inverse" id="reset-id-reset" />
</div>
</form>
</div>
</div>
</div>
{% endblock content_main %}
{% block footer_javascript %}
{{ block.super }}
<script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
<script src="{% static 'js/jquery.formset.js' %}"></script>
<script type="text/javascript">
$(function() {
$('#id_{{ qualification_inlineformset.prefix }}').formset({
prefix: "{{ qualification_inlineformset.prefix }}",
formCssClass: "{{ qualification_inlineformset.prefix }}",
addText: 'Add another',
deleteText: 'Remove',
addCssClass: 'add-qualification label label-success col-sm-offset-4',
deleteCssClass: 'delete-qualification label arrow-up arrow-primary col-sm-offset-4'
})
});
</script>
<script type="text/javascript">
$(function() {
$('#id_{{ work_assignment_inlineformset.prefix }}').formset({
prefix: "{{ work_assignment_inlineformset.prefix }}",
formCssClass: "{{ work_assignment_inlineformset.prefix }}",
addText: 'Add another',
deleteText: 'Remove',
addCssClass: 'add-work-assignment label label-success col-sm-offset-4',
deleteCssClass: 'delete-work-assignment label arrow-up arrow-primary col-sm-offset-4'
})
});
</script>
{% endblock footer_javascript %}
希望有人可以提供帮助。
谢谢。
答案 0 :(得分:1)
表单集clean()
方法中的逻辑不太正确。 super()
方法的返回值不包含cleaned_data
-该方法始终返回null
。您需要分别检查每个表单的清理数据。像这样:
class QualificationCustomInlineFormSet(forms.BaseInlineFormSet):
def clean(self):
# The line below isn't going to work - you also don't need to call super().
# cleaned_data = super().clean()
for form in self.forms:
# use form.cleaned_data
qualification = form.cleaned_data.get('qualification', '').strip()
if not qualification:
msg = "Please enter qualification."
raise forms.ValidationError(msg, "error")
答案 1 :(得分:0)
我刚刚设法找到解决问题的方法。原来,我不需要实现InlineFormSet clean方法。
单击提交时,FormView类仅验证在form_class FormView属性中指定的表单。它不会自动验证我添加的其他inlineformset。所以我要做的是验证视图的form_valid方法中的每个inlineformset
views.py
class JobAddView(LoginRequiredMixin, SuccessMessageMixin, FormView):
template_name = 'cepalco_website_admin/job_form.html'
form_class = forms.JobForm
success_url = reverse_lazy('cepalco_website_admin:home')
success_message = "Successfully added %(job_title)s!"
head_title = "Add new job"
title_text = head_title
description = "Enter the following job information"
QualificationInlineFormSet = inlineformset_factory(
Job, Qualification,
form=forms.QualificationForm,
extra=0, can_delete=False, min_num=1
)
WorkAssignmentInlineFormSet = inlineformset_factory(
Job, WorkAssignment, form=forms.WorkAssignmentForm,
extra=0, can_delete=False, min_num=1
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['head_title'] = self.head_title
context['title_text'] = self.title_text
context['description'] = self.description
context['qualification_inlineformset'] = self.QualificationInlineFormSet
context['work_assignment_inlineformset'] = self.WorkAssignmentInlineFormSet
return context
def form_valid(self, form):
qualification_formset = self.QualificationInlineFormSet(self.request.POST)
if qualification_formset.is_valid():
return super().form_valid(form)
return render(self.request, self.template_name, {
'form': form,
'head_title': self.head_title,
'title_text': self.title_text,
'description': self.description,
'qualification_inlineformset': qualification_formset,
'work_assignment_inlineformset': self.WorkAssignmentInlineFormSet
})
因此,即使不添加自定义inlineformset clean方法,该代码仍会验证表单集是否为空,并会提供一条错误消息,指出必须填写此字段。
希望这也可以帮助其他遇到相同问题的人。