gpt4 book ai didi

django - FactoryBoy/Django - OneToOneField 重复键错误

转载 作者:行者123 更新时间:2023-12-01 12:07:00 29 4
gpt4 key购买 nike

我正在为具有多个应用程序的大型 Django 应用程序编写测试。作为这个过程的一部分,我逐渐为 Django 项目中不同应用程序的所有模型创建工厂。

但是,我在使用 FactoryBoy 时遇到了一些令人困惑的行为

我们的应用程序使用 Profiles链接到默认 auth.models.User带有 OneToOneField 的型号

class Profile(models.Model):
user = models.OneToOneField(User)
birth_date = models.DateField(
verbose_name=_("Date of Birth"), null=True, blank=True)
( ... )

我为这两种模型创建了以下工厂:
@factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):

class Meta:
model = profile_models.Profile

user = factory.SubFactory('yuza.factories.UserFactory')
birth_date = factory.Faker('date_of_birth')
street = factory.Faker('street_name')
house_number = factory.Faker('building_number')
city = factory.Faker('city')
country = factory.Faker('country')
avatar_file = factory.django.ImageField(color='blue')
tenant = factory.SubFactory(TenantFactory)


@factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):

class Meta:
model = auth_models.User

username = factory.Faker('user_name')
first_name = factory.Faker('first_name')
last_name = factory.Faker('last_name')

email = factory.Faker('email')
is_staff = False
is_superuser = False
is_active = True
last_login = factory.LazyFunction(timezone.now)

profile = factory.RelatedFactory(ProfileFactory, 'user')

然后我运行以下测试:
class TestUser(TestCase):

def test_init(self):
""" Verify that the factory is able to initialize """
user = UserFactory()
self.assertTrue(user)
self.assertTrue(user.profile)
self.assertTrue(user.profile.tenant)


class TestProfile(TestCase):

def test_init(self):
""" Verify that the factory is able to initialize """
profile = ProfileFactory()
self.assertTrue(profile)

TestUser 中的所有测试通过,但 TestProfile出厂初始化 ( profile = ProfileFactory()) 失败并引发以下错误:
IntegrityError: duplicate key value violates unique constraint "yuza_profile_user_id_key"
DETAIL: Key (user_id)=(1) already exists.

我不清楚为什么已经存在重复的用户,(应该只有一个调用来创建一个权限?,特别是因为任何干扰信号都已被禁用)

我的代码基于 the example来自 FactoryBoy 文档,该文档还处理通过 OneToOneKey 连接的用户/配置文件

有谁知道我做错了什么?

更新

根据 Bruno 和 ivissani 的建议,我更改了 user ProfileFactory 中的行到
user = factory.SubFactory('yuza.factories.UserFactory', profile=None)

现在上面描述的所有测试都成功通过了!

但是我仍然遇到以下问题 - 当其他工厂调用 UserFactory
IntegrityError: duplicate key value violates unique constraint "yuza_profile_user_id_key"
DETAIL: Key (user_id)=(1) already exists.

仍然返回。

我已经包含了一个调用 UserFactory 的工厂的示例。下面,但它发生在每个拥有 user 的工厂中 field 。
class InvoiceFactory(factory.django.DjangoModelFactory):

class Meta:
model = Invoice

user = factory.SubFactory(UserFactory)
invoice_id = None
title = factory.Faker('catch_phrase')
price_paid = factory.LazyFunction(lambda: Decimal(0))
tax_rate = factory.LazyFunction(lambda: Decimal(1.21))
invoice_datetime = factory.LazyFunction(timezone.now)

更改 user InvoiceFactory 上的字段到
user = factory.SubFactory(UserFactory, profile=None)

帮助它通过了一些测试,但最终遇到了麻烦,因为它不再有与之关联的配置文件。

奇怪的是(在工厂之前声明用户)确实有效:
self.user = UserFactory()
invoice_factory = InvoiceFactory(user=self.user)

我不清楚为什么我仍然会遇到 IntegrityError在这里,调用 UserFactory()现在工作正常。

最佳答案

我想这是因为你的 ProfileFactory创建一个 User例如,使用 UserFactory它本身试图创建一个新的 Profile使用 ProfileFactory 的实例.

您需要打破这个循环,如 in the documentation you link to 所述:

# We pass in profile=None to prevent UserFactory from 
# creating another profile (this disables the RelatedFactory)
user = factory.SubFactory('yuza.factories.UserFactory', profile=None)

如果这对您不起作用并且您需要更高级的处理,那么我建议实现 post_generation hook在那里你可以做更高级的事情。

编辑:

另一种选择是告诉 Factory Boy 不要重新创建 Profile。如果 User 已经有一个通过使用 django_get_or_create option :

@factory.django.mute_signals(post_save)
class ProfileFactory(factory.django.DjangoModelFactory):

class Meta:
model = profile_models.Profile
django_get_or_create = ('user',)

如果这样做,您也许可以删除 profile=None我之前建议的。

编辑2:

这也可能有帮助,更改 UserFactory.profile使用 post_generation钩:

@factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):

class Meta:
model = auth_models.User

...

# Change profile to a post_generation hook
@factory.post_generation
def profile(self, create, extracted):
if not create:
return
if extracted is None:
ProfileFactory(user=self)

编辑 3

我刚刚意识到 username您的 UserFactory 中的字段不同于工厂男孩的文档,它是 unique在 Django 中。我想知道这是否不会导致一些旧实例被重用,因为用户名是相同的。

您可能想尝试将此字段更改为工厂中的序列:

@factory.django.mute_signals(post_save)
class UserFactory(factory.django.DjangoModelFactory):

class Meta:
model = auth_models.User

# Change to sequence to avoid duplicates
username = factory.Sequence(lambda n: "user_%d" % n)

关于django - FactoryBoy/Django - OneToOneField 重复键错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55647801/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com