Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factory Boy random choice for a field with field option "choices"

When a field in a Django model has the option choices, see Django choices field option, it utilises an iterable containing iterables of 2 items to define which values are allowed. For example:

Models

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)

To generate a random value in Factory Boy for choices I would utilise factory.fuzzy.FuzzyChoice, but this only chooses an iterable of 2 items. It can not take the first item of the chosen iterable. For example:

Factories

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

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

Error

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

Getting the first item of the tuple is not possible. For example:

Factories

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

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

Error

TypeError: 'FuzzyChoice' object does not support indexing

It is possible with the default Python random iterator, but this generates a value on declaration time and so every factory object will have the same random value. For example:

Factories

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

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

How can this be solved in Factory Boy? Do I need to create a custom FuzzyAttribute? (If so, please give an example)

like image 395
Robin Avatar asked Apr 15 '16 08:04

Robin


2 Answers

You'll not need a FuzzyAttribute.

You can either restrict the values possible and only give the int value of each product type to FuzzyChoice by doing something like this:

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)

It should do the work.

Please be aware that fuzzy module has been deprecated recently, see ( https://factoryboy.readthedocs.org/en/latest/fuzzy.html), you may want to use a LazyFunction instead.

like image 74
Boris Feld Avatar answered Oct 06 '22 00:10

Boris Feld


You can do as easy as this

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. Don't use type as attribute, it is a bad practice to use a built-in function name as an attribute

like image 37
Artem Bernatskyi Avatar answered Oct 05 '22 23:10

Artem Bernatskyi