当 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 吗? (如果是的话请举个例子)
您不需要 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 代替。
你就可以这么简单
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
作为属性,使用内置函数名称作为属性是不好的做法
这是我如何按照 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'
因为我必须对相当多的模型执行此操作,所以我想出了 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')
如果您基于类别做出选择...
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)
...