이 글은 Grapehene Docs를 읽고 정리한 것입니다(조금 더 정확히는 graphene-django).
Graphene은 파이썬에서 code-first approach를 통해 GraphQL API를 구현하는 데 사용하는 라이브러리이다.
Code-First approach란?
django project에 쿼리를 생성하기 위해서는 다음의 두 가지가 필요하다.
1. Object types가 정의된 스키마
2. 쿼리를 input으로 받고 그 결과를 반환하는 view
GraphQL은 graph 구조를 사용하기 때문에 Graphene은 그래프에 나타나게 될 각 객체의 타입을 알아야 한다.
또한, 그래프는 모든 접근의 시작점(진입점)이 될 root type도 알아야 한다.
- 이 진입점은 Query
클래스를 정의하면 된다.
라이브러리 다운로드
$ pip install graphene-django
graphene_django 추가
# settings.py
INSTALLED_APPS = [
...
"graphene_django"
]
graphql URL 추가
# urls.py
'''
for Django 2.0 and above
'''
from django.urls import path
from graphene_django.views import GraphQLView
urlpatterns = [
# ...
path("graphql", GraphQLView.as_view(graphiql=True)),
]
GraphiQL이란?
위의 urlpatterns에서 확인할 수 있듯이 graphiql=True
로 설정 되어 있다. 그런데 GraphiQl은 무엇일까?
http://localhost:8000/___graphql
로 접근할 수 있다.schema의 위치 명시
# settings.py
GRAPHENE = {
"SCHEMA": "django_root.schema.schema"
}
모델
# cookbook/ingredients/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Ingredient(models.Model):
name = models.CharField(max_length=100)
notes = models.TextField()
category = models.ForeignKey(
Category, related_name="ingredients", on_delete=models.CASCADE
)
def __str__(self):
return self.name
장고 모델 각각에 대한 GraphQL 타입을 생성하려면 DjangoObjectType
을 상속 받은 타입 클래스를 만들어야 한다.
- DjangoObjectType
: 장고 모델의 필드에 대응하는 GraphQL 필드를 자동으로 정의한다.
# cookbook/schema.py
import graphene
from graphene_django import DjangoObjectType
from cookbook.ingredients.models import Category, Ingredient
class CategoryType(DjangoObjectType):
class Meta:
model = Category
fields = ("id", "name", "ingredients")
class IngredientType(DjangoObjectType):
class Meta:
model = Ingredient
fields = ("id", "name", "notes", "category")
그 다음, 위에서 생성한 타입들을 Query
클래스에 나열하면 된다.
- schema.py
는 top-level의 urls.py
와 같은 개념이다.
# cookbook/schema.py
import graphene
from graphene_django import DjangoObjectType
...(위에서 정의한 Type들)...
class Query(graphene.ObjectType):
all_ingredients = graphene.List(IngredientType)
category_by_name = graphene.Field(CategoryType, name=graphene.String(required=True))
def resolve_all_ingredients(root, info):
# We can easily optimize query count in the resolve method
return Ingredient.objects.select_related("category").all()
def resolve_category_by_name(root, info, name):
try:
return Category.objects.get(name=name)
except Category.DoesNotExist:
return None
schema = graphene.Schema(query=Query)
직접 쿼리해 테스트 해 보면 알 수 있지만, Graphene은 js와의 호환을 위해 snakecase로 작성된 모든 필드명을 자동으로 camelcase로 변환한다.