はじめに
Strawberry Djangoでは、@strawberry.django.type
デコレータをつけたクラスを作成すると、DjangoのModelに対応したGraphqlのtypeを生成します。この時各フィールドの型は auto
を指定するとModelの定義に対応した型を自動で設定してくれます。
Defining Types - Strawberry Django
この記事は、DjangoのModelのchoicesを使用したフィールドを、自動でGraphQLのEnumに変換してくれる GENERATE_ENUMS_FROM_CHOICES
というオプションの紹介です
ドキュメントでは、今回紹介しているGENERATE_ENUMS_FROM_CHOICES
の代替案として、django-choices-field
というライブラリを使うよう推奨する記述があります。
A better option is to use Django's TextChoices/IntegerChoices with the django-choices-field integration.
しかしこちらのライブラリについては、以下の点より、導入は慎重に検討する必要がありそうです。
- Django本体のModel側のフィールド定義を変更する必要がある
-
django-choices-field
は現状Strawberry Djangoのメンテナーの方の個人のリポジトリとなっており、Star数も少ない
Strawberry Djangoとは
StrawberryはPython製のGraphQLライブラリ。
そのDjango向け拡張がStrawberry Djangoです。
(ライブラリ名としてはstrawberry-graphql-django
)
コード例
動作確認バージョン
- django: 4.2.8
- strawberry-graphql: 0.217.0
- strawberry-graphql-django: 0.28.2
settings.py
STRAWBERRY_DJANGO = {
"GENERATE_ENUMS_FROM_CHOICES": True,
}
models.py
Taskモデルのpriority
フィールドにchoices
パラメータを設定
class Task(models.Model):
PRIORITY_CHOICES = [
("L", "LOW"),
("M", "MEDIUM"),
("H", "HIGH"),
]
title = models.CharField(max_length=200)
priority = models.CharField(
max_length=1,
choices=PRIORITY_CHOICES,
default="M",
)
もしくは、TextChoices
を使ったEnumライクな指定の仕方もできます。
class PriorityChoices(models.TextChoices):
LOW = "L", "LOW"
MEDIUM = "M", "MEDIUM"
HIGH = "H", "HIGH"
class Task(models.Model):
title = models.CharField(max_length=200)
priority = models.CharField(
max_length=2,
choices=PriorityChoices.choices,
default=PriorityChoices.MEDIUM,
)
schema.py
tasks
クエリを作成。カスタムのリゾルバ処理は作成せず、単にTaskを取ってくるだけ
@strawberry_django.type(Task)
class TaskType:
title: strawberry.auto
priority: strawberry.auto
@strawberry.type
class Query:
tasks: list[TaskType] = strawberry.django.field()
schema = strawberry.Schema(query=Query,)
出力されるスキーマ
以下のように、Enumが自動生成される。
choicesパラメータに渡した各タプルの1つ目が値、2つ目がフィールドのdescriptionとして扱われている
enum MyappTaskPriorityEnum {
"""Low"""
L
"""Medium"""
M
"""High"""
H
}
type Query {
tasks: [TaskType!]!
}
type TaskType {
title: String!
priority: MyappTaskPriorityEnum!
}
今回のコード例ではCharFieldに対してchoicesを指定しています。
IntegerField
に対してchoices
を指定してauto
で変換する形では、Enum変換されず、単にIntになるようです。
GENERATE_ENUMS_FROM_CHOICES
を使わない場合
ちなみに、この設定を使わずにchoicesをスキーマ上でEnumで表現したい場合、以下のようにEnumクラスとその生成処理を自前で書く必要がある。
(プロジェクトによっては汎用変換処理を作ってそう)
schema.py:
@strawberry.enum
class PriorityEnum(strawberry.Enum):
LOW = 'L'
MEDIUM = 'M'
HIGH = 'H'
@strawberry.type
class TaskType:
title: str
description: str
priority: PriorityEnum
def convert_task(task: Task) -> TaskType:
"""TaskTypeへの変換処理"""
priority = task.priority
if priority == PriorityChoices.LOW:
converted_priority = PriorityEnum.LOW
elif priority == PriorityChoices.MEDIUM:
converted_priority = PriorityEnum.MEDIUM
elif priority == PriorityChoices.HIGH:
converted_priority = PriorityEnum.HIGH
else:
converted_priority = None
return TaskType(
title=task.title,
description=task.description,
priority=converted_priority,
)
class Query:
@strawberry.field
def tasks(self, info) -> List[TaskType]:
tasks = Task.objects.all()
return [convert_task(task) for task in tasks]