简体   繁体   English

具有 OneToOne 关系和相关字段的 django factory boy factory

[英]django factory boy factory with OneToOne relationship and related field

I am using Factory Boy to create test factories for my django app.我正在使用Factory Boy为我的 Django 应用程序创建测试工厂。 The model I am having an issue with is a very basic Account model which has a OneToOne relation to the django User auth model (using django < 1.5):我遇到问题的模型是一个非常基本的 Account 模型,它与 django User auth 模型有 OneToOne 关系(使用 django < 1.5):

# models.py
from django.contrib.auth.models import User
from django.db import models

class Account(models.Model):
    user = models.OneToOneField(User)
    currency = models.CharField(max_length=3, default='USD')
    balance = models.CharField(max_length="5", default='0.00') 

Here are my factories:这是我的工厂:

# factories.py
from django.db.models.signals import post_save
from django.contrib.auth.models import User

import factory

from models import Account


class AccountFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = Account

    user = factory.SubFactory('app.factories.UserFactory')
    currency             = 'USD'
    balance              = '50.00'

class UserFactory(factory.django.DjangoModelFactory):
    FACTORY_FOR = User

    username = 'bob'
    account = factory.RelatedFactory(AccountFactory)

So I am expecting the factory boy to create a related UserFactory whenever AccountFactory is invoked:所以我希望工厂男孩在调用 AccountFactory 时创建一个相关的 UserFactory:

# tests.py 
from django.test import TestCase

from factories import AccountFactory

class AccountTest(TestCase):

    def setUp(self):
        self.factory = AccountFactory()

    def test_factory_boy(self):
        print self.factory.id

When running the test however, it looks like multiple User models are being create, and I am seeing an integriy error:但是,在运行测试时,似乎正在创建多个用户模型,并且我看到了一个完整的错误:

IntegrityError: column username is not unique

The documentation does mention watching out for loops when dealing with circular imports, but I am not sure whether that is whats going on, nor how I would remedy it.该文档确实提到在处理循环导入时要注意循环,但我不确定这是否发生了,也不知道如何补救。 docs 文档

If anyone familiar with Factory Boy could chime in or provide some insight as to what may be causing this integrity error it would be much appreciated!如果任何熟悉 Factory Boy 的人都可以插话或提供一些可能导致此完整性错误的见解,将不胜感激!

I believe this is because you have a circular reference in your factory definitions.我相信这是因为您的工厂定义中有一个循环引用。 Try removing the line account = factory.RelatedFactory(AccountFactory) from the UserFactory definition.尝试从UserFactory定义中删除行account = factory.RelatedFactory(AccountFactory) If you are always going to invoke the account creation through AccountFactory, then you shouldn't need this line.如果您总是要通过 AccountFactory 调用帐户创建,那么您应该不需要这一行。

Also, you may consider attaching a sequence to the name field, so that if you ever do need more than one account, it'll generate them automatically.此外,您可以考虑将一个序列附加到名称字段,以便如果您确实需要多个帐户,它会自动生成它们。

Change: username = "bob" to username = factory.Sequence(lambda n : "bob {}".format(n)) and your users will be named "bob 1", "bob 2", etc.将: username = "bob"更改为username = factory.Sequence(lambda n : "bob {}".format(n))并且您的用户将被命名为“bob 1”、“bob 2”等。

To pass result of calling UserFactory to AccountFactory you should use factory_related_name ( docs )要将调用UserFactory结果传递给AccountFactory您应该使用factory_related_name ( docs )

Code above works next way:上面的代码以下面的方式工作:

  • AccountFactory for instantiating needs SubFactory(UserFactory) . AccountFactory用于实例化需要SubFactory(UserFactory)
  • UserFactory instantiates User. UserFactory实例化用户。
  • UserFactory after instantiating calls RelatedFactory(AccountFactory)实例化后的UserFactory调用RelatedFactory(AccountFactory)
  • Recursion,.. that is broken due to unique username constraint (you probably want to generate usernames via FuzzyText or Sequence )递归,.. 由于唯一的用户名约束而被破坏(您可能希望通过FuzzyTextSequence生成用户名)

So you need write UserFactory like this:所以你需要像这样编写UserFactory

class UserFactory(factory.django.DjangoModelFactory):
    account = factory.RelatedFactory(AccountFactory, factory_related_name='user')
    username = factory.Sequence(lambda a: 'email%04d@somedomain.com' % a)
    # rest of code

But you can still experience issues with already written tests.但是您仍然会遇到已经编写好的测试的问题。 Imagine you have in tests places like next:想象一下,你在测试的地方有这样的下一个:

user = UserFactory()
account = Account(user=user)

Then adding RelatedFactory will break tests.然后添加相关RelatedFactory将破坏测试。 If you haven't lots of tests and contributors in your project, you could rewrite them.如果你的项目中没有很多测试和贡献者,你可以重写它们。 But if not, it is not an option.但如果没有,这不是一个选择。 Here is how it could be handled:以下是它的处理方式:

class UserFactory(factory.django.DjangoModelFactory):
    class Params:
        generate_account = factory.Trait(
            account=factory.RelatedFactory(AccountFactory, factory_related_name='user')
        )

Then code above won't be broken, because default call of UserFactory won't instantiate AccountFactory .那么上面的代码不会被破坏,因为UserFactory默认调用不会实例化AccountFactory To instantiate user with account:要使用帐户实例化用户:

user_with_account = UserFactory(generate_account=True)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM