我有两个Django模型(Customer
和CustomerAddress
),它们彼此包含ForeignKey
。我正在使用factory-boy来管理这些模型的创建,并且无法将子工厂实例保存到父工厂中(使用通过RelatedFactory
类定义的关系)。
我的两个模型:
class ExampleCustomerAddress(models.Model):
# Every customer mailing address is assigned to a single Customer,
# though Customers may have multiple addresses.
customer = models.ForeignKey('ExampleCustomer', on_delete=models.CASCADE)
class ExampleCustomer(models.Model):
# Each customer has a single (optional) default billing address:
default_billto = models.ForeignKey(
'ExampleCustomerAddress',
on_delete=models.SET_NULL,
blank=True,
null=True,
related_name='+')
我有两个工厂,每个工厂一个:
class ExampleCustomerAddressFactory(factory.django.DjangoModelFactory):
class Meta:
model = ExampleCustomerAddress
customer = factory.SubFactory(
'ExampleCustomerFactory',
default_billto=None) # Set to None to prevent recursive address creation.
class ExampleCustomerFactory(factory.django.DjangoModelFactory):
class Meta:
model = ExampleCustomer
default_billto = factory.RelatedFactory(ExampleCustomerAddressFactory,
'customer')
在创建ExampleCustomerFactory
时,即使创建了default_billto
,ExampleCustomerAddress
也是 None :
In [14]: ec = ExampleCustomerFactory.build()
In [15]: ec.default_billto is None
Out[15]: True
(使用create()
时,数据库中存在一个新的ExampleCustomerAddress
。我在这里使用build()
来简化示例)。
创建ExampleCustomerAddress
的工作符合预期,并且自动创建了Customer
:
In [22]: eca = ExampleCustomerAddressFactory.build()
In [23]: eca.customer
Out[23]: <ExampleCustomer: ExampleCustomer object>
In [24]: eca.customer.default_billto is None
Out[24]: True <-- I was expecting this to be set to an `ExampleCustomerAddress!`.
我觉得我在这里疯了,缺少一些非常简单的东西。由于两个模型之间相互包含ForeignKeys
,因此给人留下我遇到此错误的印象。
答案 0 :(得分:2)
首先,有一条简单的经验法则:当您遵循ForeignKey
时,总是会偏爱SubFactory
; RelatedFactory
旨在遵循相反的关系。
让我们轮流走每个工厂。
ExampleCustomerAddressFactory
当我们在没有客户的情况下呼叫该工厂时,我们想要获取一个地址,该地址链接到一个客户,并用作该客户的默认地址。
但是,当我们与客户通话时,请不要更改它。
以下将起作用:
class ExampleCustomerAddressFactory(factory.django.DjangoModelFactory):
class Meta:
model = ExampleCustomerAddress
# Fill the Customer unless provided
customer = factory.SubFactory(
ExampleCustomerFactory,
# We can't provide ourself there, since we aren't saved to the database yet.
default_billto=None,
)
@factory.post_declaration
def set_customer_billto(obj, create, *args, **kwargs):
"""Set the default billto of the customer to ourselves if empty"""
if obj.customer.default_billto is None:
obj.customer.default_billto = obj
if create:
obj.customer.save()
在这里,我们将新创建的客户的价值设置为“我们”;请注意,此逻辑也可以移至ExampleCustomerAddress.save()
。
ExampleCustomerFactory
对于该工厂,规则更简单:创建客户时,请创建默认的帐单邮寄地址(除非已提供值)。
class ExampleCustomerFactory(factory.django.DjangoModelFactory):
class Meta:
model = ExampleCustomer
# We can't use a SubFactory here, since that would be evaluated before
# the Customer has been saved.
default_billto = factory.RelatedFactory(
ExampleCustomerAddressFactory,
'customer',
)
该工厂将按以下方式运行:
ExampleCustomer
创建default_billto=None
实例; ExampleCustomerAddressFactory(customer=obj)
; ExampleCustomerAddress
; default_billto
,并将对其进行覆盖。.save()
方法中。