使用inlineformset_factory我可以添加/删除与单个客户相关的电话号码。唯一的问题是,我想要为每位客户提供至少1个有效的电话号码。
以下是一些演示代码:
型号:
class Customer( models.Model ):
name = models.CharField( max_length=255 )
class PhoneNumber( models.Model ):
customer = models.ForeignKey( Customer )
number = models.CharField( max_length=10 )
形式:
class CustomerForm( ModelForm ):
class Meta:
model = Customer
fields = ['name']
class PhoneNumberForm( ModelForm ):
class Meta:
model = PhoneNumber
fields = ['number']
好的,所以这很直接。 然后在我看来:
class Create( View ):
template_name = 'path_to_template'
CustomerForm = forms.CustomerForm
PhoneNumberFormSet = inlineformset_factory (
parent_model = Customer,
model = PhoneNumber,
form = PhoneNumberForm,
extra = 1,
)
def get(self, request):
# Return empty forms
context = {
'customer_form': self.CustomerForm,
'phone_number_formset': self.PhoneNumberFormSet
}
render( request, self.template_name, context)
def post(self, request):
this_customer_form = self.CustomerForm( request.POST )
if this_customer_form.is_valid():
new_customer.save(commit=False)
this_phone_number_formset = self.PhoneNumberFormSet(request.POST, instance=new_customer)
if this_phone_number_formset.is_valid():
new_customer.save()
this_phone_number_formset.save()
return HttpResponseRedirect(reverse_lazy('customer-detail', kwargs={'pk': new_customer.pk}))
# Something is not right, show the forms again
this_phone_number_formset = self.PhoneNumberFormSet(request.POST)
context = {
'customer_form': this_customer_form,
'phone_number_formset': this_phone_number_formset
}
render( request, self.template_name, context)
你认为我明白了这一点。对于客户的编辑/更新视图也是如此。只有这样才能预先填充表格。
此时我需要的是每个客户至少需要1个有效PhoneNumber的方法。 我找到了类似的东西:
class RequiredFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
来自https://stackoverflow.com/questions/2406537/django-formsets-make-first-required的但是当我在BaseInlineFormSet类上应用它时,它似乎无法工作。
Django 1.7似乎回答了我的意愿,但到目前为止还没有回答InlineModelFormSet。
有什么想法吗?
答案 0 :(得分:0)
谢谢kezabella(django irc)。 似乎我通过继承BaseInlineFormset找到了solution:
class RequiredFormSet(BaseInlineFormSet):
def clean(self):
for form in self.initial_forms:
if not form.is_valid() or not (self.can_delete and form.cleaned_data.get('DELETE')):
return
for form in self.extra_forms:
if form.has_changed():
return
raise ValidationError("No initial or changed extra forms")
顺便说一句,这些验证错误不会显示在{{formset.error}}中,而是显示在:
{{ formset.non_form_errors }}
答案 1 :(得分:0)
如果您只想设置最小值或最大值,则可以直接在inlineformset_factory
中进行设置,这是我的最少输入一项的代码
from django.forms import inlineformset_factory
SubUnitFormSet = inlineformset_factory(
Unit, SubUnit, form=SubUnitForm, min_num=1, validate_min=True, extra=0)
您需要在自己的视图中正确处理此问题。我正在使用CBV,这是我的代码供您参考
class UnitCreateView(PermissionRequiredMixin, SuccessMessageMixin, CreateView):
permission_required = "core.add_unit"
model = Unit
form_class = UnitForm
template_name = 'core/basic-info/unit_form.html'
success_url = reverse_lazy('core:units')
success_message = _("%(code)s was added successfully")
def get_context_data(self, **kwargs):
data = super(UnitCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['subunits'] = SubUnitFormSet(self.request.POST, )
else:
data['subunits'] = SubUnitFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
subunits = context['subunits']
with transaction.atomic():
if subunits.is_valid():
self.object = form.save()
subunits.instance = self.object
subunits.save()
else:
return self.render_to_response(self.get_context_data(form=form))
return super(UnitCreateView, self).form_valid(form)