web-dev-qa-db-ja.com

フィールドオプション「choices」を使用したフィールドのFactoryBoyランダム選択

Djangoモデルのフィールドにオプションの選択肢がある場合、 Djangoの選択肢のフィールドオプション を参照してください。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]

ファクトリーボーイでこれをどのように解決できますか?カスタムFuzzyAttributeを作成する必要がありますか? (もしそうなら、例を挙げてください)

12
Robin

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を使用することをお勧めします。

13
Boris Feld

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'
7
erichonkanen

あなたはこれと同じくらい簡単に行うことができます

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

    class Meta:
        model = IceCreamProduct

PS。属性としてtypeを使用しないでください

5

かなり多くのモデルでそれをしなければならなかったので、エリコンカネンのソリューションのより抽象的なバージョンを思いつきました。ヘルパークラスを定義します。これをプロジェクトの最上位のテストディレクトリに配置し、ファクトリを含むモジュールにインポートします。

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
lmr2391