Django inlineformset_factory 验证器不工作
Django inlineformset_factory validators not working
代码片段
Django 版本:3.1.7
models.py
# custom validator
is_numeric = RegexValidator(r"^[0-9]*$", "Only numbers are allowed")
class Person(models.Model):
# all the boring fields
class PersonAddress(models.Model):
# all the boring fields
person = models.ForeignKey(
Person, null=True, on_delete=models.SET_NULL, related_name="person_address",
)
postcode = models.CharField(
blank=True, help_text="Include leading zero if exists",
max_length=10, validators=[is_numeric],
)
PersonAddress
将是 Person
的内联表单集,因为一个人可以有多个地址
forms.py
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
class PersonAddressForm(forms.ModelForm):
class Meta:
model = PersonAddress
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = "post"
PersonAddressFormSet = forms.models.inlineformset_factory(
Person, PersonAddress, form=PersonAddressForm, extra=1, can_delete=True,
)
# min_num, validate_min don't help even though I read from other Whosebug solution
# PersonAddressFormSet = forms.models.inlineformset_factory(
# Person, PersonAddress, form=PersonAddressForm, extra=1, can_delete=True, min_num=1, validate_min=True,
# )
views.py
# Person CreateView
class PersonCreateView(SuccessMessageMixin, LoginRequiredMixin, CreateView):
model = Person
form_class = PersonForm
def get_context_data(self, **kwargs):
context = super(PersonCreateView, self).get_context_data(**kwargs)
if self.request.POST:
context["addresses"] = PersonAddressFormSet(self.request.POST, self.request.FILES)
else:
context["addresses"] = PersonAddressFormSet()
return context
# I think the problem is here, not 100% sure
def form_valid(self, form):
context = self.get_context_data()
addresses = context["addresses"]
self.object = form.save()
if addresses.is_valid():
addresses.instance = self.object
addresses.save()
#
# my attempt, got this error
# TypeError: join() argument must be str, bytes, or os.PathLike object, not 'NoneType'
# else:
# return render(self.request, self.template_name, context)
return super(PersonCreateView, self).form_valid(form)
# PersonAddress CreateView
class PersonAddressCreateView(SuccessMessageMixin, LoginRequiredMixin, CreateView):
model = PersonAddress
form_class = PersonAddressForm
问题
如果我使用非数字字符串 postcode
创建 PersonAddress
对象,PersonAddressCreateView
HTML 表单将 return 验证错误。
但是如果我在 PersonCreateView
HTML 表单中做同样的事情,就不会有任何验证错误。 Person
对象将被创建,PersonAddress
将不会被创建,因为 postcode
无效。我期望 PersonCreateView
HTML 表单向我显示验证错误而不是保存表单。
我认为解决方案应该与PersonCreateView
的form_valid
有关,但我不确定。
提前致谢!
确实,问题出在您的 form_valid
实施中。确切地说,当 PersonAddressFormSet
无效时,您什么都不做。要解决该问题,请尝试:
def form_valid(self, form):
context = self.get_context_data()
addresses = context["addresses"]
self.object = form.save()
if addresses.is_valid():
addresses.instance = self.object
addresses.save()
else:
return self.form_invalid(form)
return super(PersonCreateView, self).form_valid(form)
代码片段
Django 版本:3.1.7
models.py
# custom validator
is_numeric = RegexValidator(r"^[0-9]*$", "Only numbers are allowed")
class Person(models.Model):
# all the boring fields
class PersonAddress(models.Model):
# all the boring fields
person = models.ForeignKey(
Person, null=True, on_delete=models.SET_NULL, related_name="person_address",
)
postcode = models.CharField(
blank=True, help_text="Include leading zero if exists",
max_length=10, validators=[is_numeric],
)
PersonAddress
将是 Person
的内联表单集,因为一个人可以有多个地址
forms.py
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
class PersonAddressForm(forms.ModelForm):
class Meta:
model = PersonAddress
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = "post"
PersonAddressFormSet = forms.models.inlineformset_factory(
Person, PersonAddress, form=PersonAddressForm, extra=1, can_delete=True,
)
# min_num, validate_min don't help even though I read from other Whosebug solution
# PersonAddressFormSet = forms.models.inlineformset_factory(
# Person, PersonAddress, form=PersonAddressForm, extra=1, can_delete=True, min_num=1, validate_min=True,
# )
views.py
# Person CreateView
class PersonCreateView(SuccessMessageMixin, LoginRequiredMixin, CreateView):
model = Person
form_class = PersonForm
def get_context_data(self, **kwargs):
context = super(PersonCreateView, self).get_context_data(**kwargs)
if self.request.POST:
context["addresses"] = PersonAddressFormSet(self.request.POST, self.request.FILES)
else:
context["addresses"] = PersonAddressFormSet()
return context
# I think the problem is here, not 100% sure
def form_valid(self, form):
context = self.get_context_data()
addresses = context["addresses"]
self.object = form.save()
if addresses.is_valid():
addresses.instance = self.object
addresses.save()
#
# my attempt, got this error
# TypeError: join() argument must be str, bytes, or os.PathLike object, not 'NoneType'
# else:
# return render(self.request, self.template_name, context)
return super(PersonCreateView, self).form_valid(form)
# PersonAddress CreateView
class PersonAddressCreateView(SuccessMessageMixin, LoginRequiredMixin, CreateView):
model = PersonAddress
form_class = PersonAddressForm
问题
如果我使用非数字字符串 postcode
创建 PersonAddress
对象,PersonAddressCreateView
HTML 表单将 return 验证错误。
但是如果我在 PersonCreateView
HTML 表单中做同样的事情,就不会有任何验证错误。 Person
对象将被创建,PersonAddress
将不会被创建,因为 postcode
无效。我期望 PersonCreateView
HTML 表单向我显示验证错误而不是保存表单。
我认为解决方案应该与PersonCreateView
的form_valid
有关,但我不确定。
提前致谢!
确实,问题出在您的 form_valid
实施中。确切地说,当 PersonAddressFormSet
无效时,您什么都不做。要解决该问题,请尝试:
def form_valid(self, form):
context = self.get_context_data()
addresses = context["addresses"]
self.object = form.save()
if addresses.is_valid():
addresses.instance = self.object
addresses.save()
else:
return self.form_invalid(form)
return super(PersonCreateView, self).form_valid(form)