正在父工厂上存储工厂男孩RelatedFactory对象



我有两个Django模型(CustomerCustomerAddress(,它们彼此都包含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时,即使创建了ExampleCustomerAddressdefault_billto也是

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

首先,一个简单的经验法则:当你遵循ForeignKey时,总是更喜欢SubFactory;CCD_ 16旨在遵循相反的关系。

让我们轮流参观每个工厂。

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_generation
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()

在这里,我们将新创建的客户的价值设置为";我们";;注意,该逻辑也可以移动到CCD_ 18。

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',
)

该工厂将按以下方式运行:

  1. 使用default_billto=None创建ExampleCustomer实例
  2. 与新创建的客户一起致电ExampleCustomerAddressFactory(customer=obj)
  3. 该工厂将与该客户创建一个ExampleCustomerAddress
  4. 然后,该工厂中的后生成挂钩将检测到客户没有default_billto,并将覆盖它

备注

  • 我没有测试这个,所以可能会出现一些打字错误或小错误
  • 由您决定首先声明哪个工厂,使用目标工厂的路径而不是直接引用
  • 如上所述,当客户的默认账单地址为空并且向该客户添加了地址时,设置该地址的逻辑可以移动到模型的.save()方法中

最新更新