我在Django 1.11
我有三个模型,business
,business_address
,business_phone
其中business_address
和business_phon
与business
相关联。
我想在添加新商家时为business_address
以及business_phone
和business
创建字段。
为实现这一目标,我实施了`
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
地址`模型的详细信息,甚至没有错误。
答案 0 :(得分:1)
你说有错误,但你没有在你的问题中显示它。错误(以及整个回溯)比你写的任何东西都重要(除了可能来自forms.py和views.py)
由于表单集和在同一CreateView
上使用多个表单,您的情况有点棘手。互联网上的例子并不多(或不多)。直到您在django代码中挖掘内联表单集是如何工作的,您将遇到麻烦。
好的直截了当。您的问题是,formset未使用与主窗体相同的实例进行初始化。当您的amin表单将数据保存到数据库时,formset中的实例不会更改,并且最后您没有主对象的ID以便作为外键放置。在init之后更改表单属性的instance
属性不是一个好主意。
is_valid
之后点击它,那么在正常形式中,您将获得不可预测的结果。instance
属性的表单集,因为表单集中的表单已经使用某个实例进行了初始化,之后更改它将无济于事。好消息是,在初始化Formset之后,您可以更改instance
的属性,因为在初始化formset之后,所有表单的instance
属性都将指向同一个对象。
您有两种选择:
instance
属性,而只设置instance.pk
。 (这只是一个猜测,我从来没有这样做,但我认为它应该有效。问题是它看起来像黑客)。is_valid()
方法时,应验证所有fomrs。当调用save()
方法时,必须保存所有表单。然后,您需要将form_class
的{{1}}属性设置为该表单类。唯一棘手的部分是,在初始化主表单后,您需要使用第一个表单的CreateView
初始化其他表单(formsests)。您还需要将表单/表单集设置为表单的属性,以便在模板中访问它们。当我需要创建一个包含所有相关对象的对象时,我使用第二种方法。这会将业务逻辑与视图逻辑分开,因为从视图的角度来看,您只需要一个可以的表单:
instance
is_valid()
有效时保存。您保留了表单界面,如果您正确地创建了表单,您甚至可以将其用于创建,但不仅可以将对象与相关对象一起更新,而且视图也非常简单。
答案 1 :(得分:0)
我猜business_address.is_valid()
返回False,可能是因为业务未传递给表单而且它抱怨它是必填字段。