Django内联formset数据未保存

时间:2017-09-21 18:56:11

标签: django django-forms django-1.11

我在Django 1.11

工作

我有三个模型,businessbusiness_addressbusiness_phone其中business_addressbusiness_phonbusiness相关联。

我想在添加新商家时为business_address以及business_phonebusiness创建字段。

为实现这一目标,我实施了`

models.py

class Business(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    business_type = models.ForeignKey(BusinessType, on_delete=models.CASCADE)

    class Meta:
        db_table = 'businesses'

    def __str__(self):
        return self.name


class BusinessAddress(models.Model):
    business = models.OneToOneField(Business, on_delete=models.CASCADE)
    line_1 = models.CharField(max_length=200)
    line_2 = models.CharField(max_length=200)

    class Meta:
        db_table = 'business_addresses'

    def __str__(self):
        return '%s, %s, %s, %s' % (self.line_1, self.line_2, self.city, self.state)


class BusinessPhone(models.Model):
    business = models.ForeignKey(Business, on_delete=models.CASCADE)
    phone_number = models.CharField(max_length=15, default=None)

    class Meta:
        db_table = 'business_phones'

    def __str__(self):
        return self.phone_number

forms.py

class BusinessForm(ModelForm):
    class Meta:
        model = Business
        exclude = ()

class BusinessAddressForm(ModelForm):
    class Meta:
        model = BusinessAddress
        fields = '__all__'

class BusinessPhoneForm(ModelForm):
    class Meta:
        model = BusinessPhone
        exclude = ()

BusinessAddressFormSet = inlineformset_factory(
    Business,
    BusinessAddress,
    form=BusinessAddressForm,
    extra=1,
    can_delete=False
)

BusinessPhoneFormSet = inlineformset_factory(
    Business,
    BusinessPhone,
    form=BusinessPhoneForm,
    extra=1,
    can_delete=False
)

views.py

class BusinessCreate(CreateView):
    model = Business
    fields = ['name', 'business_type']

    def get_context_data(self, **kwargs):
        data = super(BusinessCreate,self).get_context_data(**kwargs)

        if self.request.POST:
            data['business_address'] = BusinessAddressFormSet(self.request.POST)
            data['business_phone'] = BusinessPhoneFormSet(self.request.POST)
        else:
            data['business_address'] = BusinessAddressFormSet()
            data['business_phone'] = BusinessPhoneFormSet()
        return data

    def form_valid(self, form):
        context = self.get_context_data()
        form.instance.user = self.request.user
        business_address = context['business_address']
        business_phone = context['business_phone']
        with transaction.atomic():
            self.object = form.save()

            if business_address.is_valid():
                business_address.instance = self.object
                business_address.save()
            if business_phone.is_valid():
                business_phone.instance = self.object
                business_phone.save()

        return super(BusinessCreate, self).form_valid(form)

    def get_success_url(self):
        messages.success(self.request, 'Business Added Successfully')
        return reverse('business:list')

并在 template.html

<form action="POST">
  {% csrf_token %}
  {% form.as_p %}

  // address
  {{ business_address.management_form }}
  // business fields
  {% render_field business_address.form.line_1 %}
  {% render_field business_address.form.line_2 %}


  // phone field
  {{ business_phone.management_form }}
  {% render_field business_phone.form.phone_number %}
</form>

但是当我填写所有详细信息时,只会填写business is saving in database and there is no record created for电话and地址`模型的详细信息,甚至没有错误。

2 个答案:

答案 0 :(得分:1)

你说有错误,但你没有在你的问题中显示它。错误(以及整个回溯)比你写的任何东西都重要(除了可能来自forms.py和views.py)

由于表单集和在同一CreateView上使用多个表单,您的情况有点棘手。互联网上的例子并不多(或不多)。直到您在django代码中挖掘内联表单集是如何工作的,您将遇到麻烦。

好的直截了当。您的问题是,formset未使用与主窗体相同的实例进行初始化。当您的amin表单将数据保存到数据库时,formset中的实例不会更改,并且最后您没有主对象的ID以便作为外键放置。在init之后更改表单属性的instance属性不是一个好主意。

  • 如果您在is_valid之后点击它,那么在正常形式中,您将获得不可预测的结果。
  • 对于直接在init之后直接更改instance属性的表单集,因为表单集中的表单已经使用某个实例进行了初始化,之后更改它将无济于事。

好消息是,在初始化Formset之后,您可以更改instance的属性,因为在初始化formset之后,所有表单的instance属性都将指向同一个对象。

您有两种选择:

  1. 如果是formset,则不设置instance属性,而只设置instance.pk。 (这只是一个猜测,我从来没有这样做,但我认为它应该有效。问题是它看起来像黑客)。
  2. 创建一个将立即初始化所有表单/表单集的表单。当调用is_valid()方法时,应验证所有fomrs。当调用save()方法时,必须保存所有表单。然后,您需要将form_class的{​​{1}}属性设置为该表单类。唯一棘手的部分是,在初始化主表单后,您需要使用第一个表单的CreateView初始化其他表单(formsests)。您还需要将表单/表单集设置为表单的属性,以便在模板中访问它们。
  3. 当我需要创建一个包含所有相关对象的对象时,我使用第二种方法。这会将业务逻辑与视图逻辑分开,因为从视图的角度来看,您只需要一个可以的表单:

    • 使用一些数据初始化(在本例中为POST数据)
    • 使用instance
    • 检查有效性
    • 可以在is_valid()有效时保存。

    您保留了表单界面,如果您正确地创建了表单,您甚至可以将其用于创建,但不仅可以将对象与相关对象一起更新,而且视图也非常简单。

答案 1 :(得分:0)

我猜business_address.is_valid()返回False,可能是因为业务未传递给表单而且它抱怨它是必填字段。