Factory Boy 随机选择带有字段选项的字段 "choices"
Factory Boy random choice for a field with field option "choices"
当 Django 模型中的字段具有选项选项时,请参阅 Django choices field option,它利用包含 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:
PRODUCT_IDS = [x[0] for x in IceCreamProduct.PRODUCT_TYPES]
class IceCreamProductFactory(factory.django.DjangoModelFactory):
class Meta:
model = IceCreamProduct
type = factory.fuzzy.FuzzyChoice(PRODUCT_IDS)
它应该完成工作。
请注意模糊模块最近已被弃用,请参阅(https://factoryboy.readthedocs.org/en/latest/fuzzy.html),您可能想改用 LazyFunction。
以下是我如何按照 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 解决方案的更抽象版本。我定义了一个助手 class,我把它放在项目的顶级测试目录中,并将其导入到包含工厂的模块中:
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 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
作为属性,it is a bad practice to use a built-in function name as an attribute
如果您做出选择 class-based...
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)
...
当 Django 模型中的字段具有选项选项时,请参阅 Django choices field option,它利用包含 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:
PRODUCT_IDS = [x[0] for x in IceCreamProduct.PRODUCT_TYPES]
class IceCreamProductFactory(factory.django.DjangoModelFactory):
class Meta:
model = IceCreamProduct
type = factory.fuzzy.FuzzyChoice(PRODUCT_IDS)
它应该完成工作。
请注意模糊模块最近已被弃用,请参阅(https://factoryboy.readthedocs.org/en/latest/fuzzy.html),您可能想改用 LazyFunction。
以下是我如何按照 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 解决方案的更抽象版本。我定义了一个助手 class,我把它放在项目的顶级测试目录中,并将其导入到包含工厂的模块中:
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 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
作为属性,it is a bad practice to use a built-in function name as an attribute
如果您做出选择 class-based...
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)
...