雑念ストレージ

プログラミング関連のメモとか

【Django】Factory Boyで、カテゴリを外部テーブルに持つようなレコードを簡単に作成する

以下のような、「本」と「本の種別(マスターデータ)」を表すModelがあるとする。

class BookCategory(models.Model):
    name = models.TextField()

class Book(models.Model):
    name = models.TextField()
    book_category = models.ForeignKey(BookCategory, on_delete=models.DO_NOTHING)

実データの例は以下のような感じ。

ER図にすると以下のようになる。

BookのレコードをFactory Boyで作りたいとする。
単純に作ると、以下のようになる。

class BookFactory(DjangoModelFactory):
    class Meta:
        model = models.Book

    name = "Test Name"
    book_category = BookCategory.objects.get(name='comic')

この例では、book_categoryのデフォルト値を'comic'のレコードにしている。
ただ、ユニットテストではカテゴリを色々と変えてレコードを作りたい。

# カテゴリがデフォルトの'comic'ならこれでいいけど
book1 = BookFactory.create()

# 別のカテゴリを設定したいときは、マスターデータを取得してくる必要がある。ちょっと面倒。
category = BookCategory.objects.get(name='science')
book2 = BookFactory.create(book_category=category)

こういうときは、excludeとlazy_attributeを組み合わせて、factory側でマスターデータを取得すると便利。

class BookFactory(DjangoModelFactory):
    class Meta:
        model = models.Book
        exclude = ["category_name"]

    name = "Test Name"

    # Bookのmodelに存在しない、カテゴリを取得するキーとして使うだけのフィールド
    category_name = "comic"

    book_category = factory.LazyAttribute(lambda o: BookCategory.objects.get(name=o.category_name))

category_nameというフィールドを追加し、BookCategoryを検索するキーとして使用している。
book_categoryはLazyAttributeとして遅延評価させて取得している。

category_nameはBookには存在しないフィールドなので、excludeにに指定する必要がある。
(excludeに指定することで、factory内で使うだけのフィールドだということを明示している)

book2 = BookFactory.create(category_name='history')

Bookの生成は以上のようになるため、スッキリ書けるようになった。