使用与 faker 集成的 factory boy 从名字列表中选择

Choosing from a list of names using factory boy integrated with faker

我正在尝试使用 factory.faker 从四家公司的列表中随机选择,并将它们用作生成名称列表的流量来源。我正在使用以下代码:

    from django.db import models
    import factory
    import factory.django
    from datetime import datetime
    from django.core.validators import MinValueValidator, MaxValueValidator
    from faker import Faker
    from faker.providers import BaseProvider
    import random

    fake = Faker()

    class User(models.Model):
        name = models.CharField(max_length=64)
        address = models.CharField(max_length=128)
        phone_number = models.CharField(max_length=32)
        login_date = models.DateTimeField(default=datetime.now(), blank=True)
        session_duration = models.IntegerField(default = 0, validators=  [
                           MinValueValidator(0),
                           MaxValueValidator(5)
                           ])
        traffic_source = models.CharField(max_length=32)

    class UserFactory(factory.django.DjangoModelFactory):
        class Meta:
            model = User

        name = factory.Faker('name')
        address = factory.Faker('address')
        phone_number = factory.Faker('phone_number')
        login_date = factory.Faker('date')
        session_duration = factory.Faker('random_int')



        traffic_source = random.choice(['XYZ', 'ABC', '123', '456'])

问题是,对于所有 200 次迭代,我在 python shell 中使用以下内容执行:

    for _ in range(200): 
        UserFactory.create()

我为每个名字找到了同一家公司,即 'XYZ' 为所有 200 个名字。

我错过了什么吗?我想为 200 次迭代中的每一次迭代找一家不同的公司。任何帮助深表感谢。谢谢!

这来自Python的解析规则。

为什么?

当你这样写的时候:

class UserFactory(factory.django.DjangoModelFactory):
    ...
    traffic_source = random.choice(['XYZ', 'ABC', '123', '456'])

Python 将执行以下步骤:

  1. 阅读class声明体;
  2. 到达第traffic_source = random.choice(['XYZ', 'ABC', '123', '456'])行;
  3. 评估对 random.choice 的调用,这可能 return 'ABC';
  4. 读取 class 正文的每一行(及其函数调用已评估)后,创建 class:
    UserFactory = type(
        name='UserFactory',
        bases=[factory.django.DjangoModelFactory],
        {'traffic_source': 'ABC', ...},
     )```
    
    

如您所见,在解析 class 声明时,对 random.choice 的调用仅执行 一次

基本上,这就是所有 factory.XXX 声明的原因:它们产生一个对象,该对象仅在从工厂构建实例时执行其特定规则。

那么,你应该怎么做?

在这里,你应该使用:

class UserFactory(factory.django.DjangoModelFactory):
    ...
    traffic_source = factory.Faker('random_choices', elements=['XYZ', 'ABC', '123', '456'])
    alt_traffic_source = factory.fuzzy.FuzzyChoice(['XYZ', 'ABC', '123', '456'])

factory.Faker('random_choices')factory.fuzzy.FuzzyChoices 之间的主要区别在于 factory.fuzzy.FuzzyChoices 支持延迟评估生成器;如果您想从查询集中进行选择,这很有用:

  • factory.Faker('random_choices', elements=Company.objects.all()) 将在导入时执行数据库查询;
  • factory.fuzzy.FuzzyChoice(Company.objects.all()) 只会在第一次调用 UserFactory.create() 时查询数据库。

虽然不是真正随机的,但您在从一组预先存在的记录中进行选择时寻找的效果也可以通过使用 FactoryBoy 的 Iterator 来实现,它也可以与 QuerySet 一起使用。例如,在这里我希望每个对象都由不同于现有假用户集的人创建:

from django.contrib.auth import get_user_model
...

# Then, within a factory class, for one of the fields:
created_by = factory.Iterator(get_user_model().objects.all())