Factory Boy 使用字段选项“选择”随机选择字段

问题描述 投票:0回答:5

当 Django 模型中的字段具有选项 Choices 时,请参阅 Django Choices 字段选项,它利用包含 2 个项目的可迭代对象的可迭代对象来定义允许哪些值。例如:

型号

class IceCreamProduct(models.Model):
    PRODUCT_TYPES = (
        (0, 'Soft Ice Cream'),
        (1, 'Hard Ice Cream'),
        (2, 'Light Ice Cream'),
        (3, 'French Ice Cream'),
        (4, 'Italian-style Gelato'),
        (5, 'Frozen Dairy Dessert'),
    )
    type = models.PositiveSmallIntegerField('Type', choices=PRODUCT_TYPES, default=0)

要在 Factory Boy 中生成随机值作为选择,我会使用factory.fuzzy.FuzzyChoice,但这只会选择 2 个项目的可迭代项。它不能采用所选可迭代对象的第一项。例如:

工厂

class IceCreamProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = IceCreamProduct

    type = factory.fuzzy.FuzzyChoice(IceCreamProduct.PRODUCT_TYPES)

错误

TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple'

获取元组的第一项是不可能的。例如:

工厂

class IceCreamProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = IceCreamProduct

    type = factory.fuzzy.FuzzyChoice(IceCreamProduct.PRODUCT_TYPES)[0]

错误

TypeError: 'FuzzyChoice' object does not support indexing

使用默认的 Python 随机迭代器是可能的,但这会在声明时生成一个值,因此每个工厂对象都将具有相同的随机值。例如:

工厂

class IceCreamProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = IceCreamProduct

    type = random.choice(IceCreamProduct.PRODUCT_TYPES)][0]

Factory Boy 中如何解决这个问题?我需要创建自定义 FuzzyAttribute 吗? (如果是的话请举个例子)

python django factory django-testing factory-boy
5个回答
36
投票

您不需要 FuzzyAttribute。

您可以通过执行以下操作来限制可能的值,并仅将每种产品类型的 int 值提供给 FuzzyChoice:

from factory import fuzzy

PRODUCT_IDS = [x[0] for x in IceCreamProduct.PRODUCT_TYPES]
class IceCreamProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = IceCreamProduct

    type = fuzzy.FuzzyChoice(PRODUCT_IDS)

它应该可以完成工作。

请注意,模糊模块最近已被弃用,请参阅模糊属性文档,您可能想使用 LazyFunction 代替。


11
投票

你就可以这么简单

class IceCreamProductFactory(factory.django.DjangoModelFactory):
    icecream_flavour = factory.Faker(
        'random_element', elements=[x[0] for x in IceCreamProduct.PRODUCT_TYPES]
    )

    class Meta:
        model = IceCreamProduct

PS。不要使用

type
作为属性,使用内置函数名称作为属性是不好的做法


9
投票

这是我如何按照 lothiraldan 的建议使用

factory.LazyFunction
来做到这一点:

import random

...


def get_license_type():
    "Return a random license type from available choices."
    lt_choices = [x[0] for x in choices.LICENSE_TYPE_CHOICES]
    return random.choice(lt_choices)


def get_line_type():
    "Return a random line type from available choices."
    lt_choices = [x[0] for x in choices.LINE_TYPE_CHOICES]
    return random.choice(lt_choices)


class ProductFactory(ModelFactory):
    name = factory.Faker('name')
    description = factory.Faker('text')
    license_type = factory.LazyFunction(get_license_type)
    line_type = factory.LazyFunction(get_line_type)

    class Meta:
        model = 'products.ProductBaseV2'

6
投票

因为我必须对相当多的模型执行此操作,所以我想出了 erichonkanen 解决方案的更抽象版本。我定义了一个辅助类,将其放入项目的顶级测试目录中并将其导入到包含工厂的模块中:

test/helpers.py

import factory
import random


class ModelFieldLazyChoice(factory.LazyFunction):
    def __init__(self, model_class, field, *args, **kwargs):
        choices = [choice[0] for choice in model_class._meta.get_field(field).choices]
        super(ModelFieldLazyChoice, self).__init__(
            function=lambda: random.choice(choices),
            *args, **kwargs
        )

并在

app/factories.py

from app.models import IceCreamProduct
from test.helpers import ModelFieldLazyChoice

class IceCreamProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = IceCreamProduct

    type = ModelFieldLazyChoice(IceCreamProduct, 'type')

3
投票

如果您基于类别做出选择...

class IceCreamProduct(models.Model):
    class ProductTypes(models.TextChoices):
        soft_ice_crem = (0, 'Soft Ice Cream')
        hard_ice_cream = (1, 'Hard Ice Cream')
        ...

class IceCreamProductFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = IceCreamProduct

    type = factory.fuzzy.FuzzyChoice(IceCreamProduct.ProductTypes)
    ...
© www.soinside.com 2019 - 2024. All rights reserved.