DjangoでREST APIを簡単に作れるフレームワーク(djangorestframework)を使ってみた。 サクッと作れるので、いい感じ!
インストール
とりあえず、pipでインストール
$ pip install django djangorestframework
インストールしてたら、rest_framework
を追加
INSTALLED_APPS = (
...
'rest_framework',
)
概要
構成要素
- Model ・・・ DjangoのModel
- Serializer ・・・Modelをどのようにシリアライズ(・デシリアライズ)するかを決めるためのもの
- ViewSet ・・・ APIのクエリーをどう解釈するかを決めるためのもの
- URL Pattern ・・・ DjangoにURLのパターンを教えるためのもの
依存関係は、「Model ⇐ Serializer ⇐ ViewSet ⇐ URL Pattern」という感じ。
Model(models.py)
from django.db import models class Todo(models.Model): title = models.CharField('タイトル', max_length=20) done = models.BooleanField('完了', default=False)
Serializer(serializer.py)
from rest_framework import serializers from .models import Todo class TodoSerializer(serializers.ModelSerializer): class Meta: model = Todo fields = '__all__'
ViewSet(views.py)
from rest_framework import viewsets from .models import Todo from .serializer import TodoSerializer class TodoViewSet(viewsets.ModelViewSet): queryset = Todo.objects.all() serializer_class = TodoSerializer
URL pattern(urls.py)
from rest_framework import routers from .views import TodoViewSet router = routers.DefaultRouter() router.register(r'todos', TodoViewSet)
from django.conf.urls import url, include from api.urls import router as api_router urlpatterns = [ path('api/', include(api_router.urls)), # api.urlsをincludeする ]
これで、「 http://api/todos/ 」にアクセスすると、todoの一覧が取得できるようになる!
小ネタ
MenyToManyFieldやForeignKeyを展開する
デフォルトのだと、外部キーや多対多のModelは展開されず、pkで表現される。 参照しているRelationは展開してほしい。。
展開する場合は、Serializerを変更するとよい感じに。
models.py
from django.db import models class Category(models.Model): name = models.CharField('カテゴリ名', max_length=20) class Todo(models.Model): title = models.CharField('タイトル', max_length=20) done = models.BooleanField('完了', default=False) categories = models.ManyToManyField(Category, verbose_name='カテゴリ')
serializer.py
from rest_framework import serializers from .models import Todo, Category class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = '__all__' class TodoSerializer(serializers.ModelSerializer): # 対象のフィールドのSerializerを置き換えると、CategorySerializerを使って展開される # ManyToManyのように複数の場合は「many=True」をつける # contextを設定すると、URLの展開などをしてくれる categories = CategorySerializer(many=True, context=self.context) class Meta: model = Todo fields = '__all__'
MenyToManyFieldごとCreate/Updateしたい
デフォルトのままだと、Todoを追加/更新する場合に、同時にCategoryも追加されてしまう。。 というか、その前のvalidateで、「Categoryがすでに存在します」みたいになる。。 Categoryが存在したらcreateして、存在しなかったらgetしてほしい。。
既存のcreate/update処理をカスタマイズする場合も、Serializerを変更する
models.py
from django.db import models class Category(models.Model): name = models.CharField('カテゴリ名', max_length=20) class Todo(models.Model): title = models.CharField('タイトル', max_length=20) done = models.BooleanField('完了', default=False) categories = models.ManyToManyField(Category, verbose_name='カテゴリ')
serializer.py
from rest_framework import serializers from .models import Todo, Category class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = ['id', 'name'] extra_kwargs = { # validationを無効にする 'id': {'validators': []}, 'name': {'validators': []}, } def validate_name(self, data): # 独自のvalidationを追加。なにもしない return data class TodoSerializer(serializers.ModelSerializer): categories = CategorySerializer(many=True) class Meta: model = Todo fields = '__all__' # create時の処理をカスタマイズ def create(self, validated_data): # categoriesの部分をpopして退避 categories_data = validated_data.pop('categories') # 残りのvalidated_dataからTodoをcreate todo = Todo.objects.create(**validated_data) todo.save() # 退避したcategories_dataからcategoriesを追加 # idのcategoryがあれば、取得して追加。なければ、createして追加 for category_data in categories_data: category = Category.objects.filter(id=category_data['id']).first() if category is None: category = Category.objects.create(**category_data) todo.categories.add(category) return todo # update時の処理をカスタマイズ def update(self, instance, validated_data): # categoriesの部分をpopして退避 categories_data = validated_data.pop('categories') # Todo部分の更新処理 instance.name = validated_data.get('name', instance.name) instance.done = validated_data.get('done', instance.name) # 一旦、タグを全削除 instance.tags.clear() # 追加時と同様に、退避したcategories_dataからcategoriesを追加 for category_data in categories_data: category = Category.objects.filter(id=category_data['id']).first() if category is None: category = Category.objects.create(**category_data) instance.categories.add(category) instance.save() return instance
参考サイト
- In the Django Rest Framework, how do you add ManyToMany related objects? - Stack Overflow
- Serializers - Django REST framework
- Django-Rest-Framework many to many : django
RESTっぽくない、Web APIを追加したい
RESTだとModelと対応したAPIの形になるので、ちょっとロジックを入れ込んだAPIとかを用意しにくい。。 これも、「ViewSetにactionを追加する」という形で独自のエントリポイントを追加できる。
from rest_framework import status, viewsets from rest_framework.decorators import action from rest_framework.response import Response from .models import Todo from .serializer import TodoSerializer class TodoViewSet(viewsets.ModelViewSet): queryset = Todo.objects.all() serializer_class = TodoSerializer @action(detail=False) # pkを取らないactionの場合は、「detail=False」にする def is_done(self, request): try: name = request.query_params.get('name') # GETパラメタを取得 todo = Todo.objects.filter(name=name).first() # 独自の形式のresponse response = { 'data': TodoSerializer(todo, context={'request': request}).data, # serializeしたdictを取得・設定 'is_done': todo.is_done } return Response(response) except: return Response({'message': 'error'}, status=status.HTTP_400_BAD_REQUEST)
これで「 http://api/todos/is_done/?name=AAA 」にアクセスすると、is_doneが呼び出せるようになる!
参考サイト
以上!!
参考になる書籍
- 作者: 関根裕紀,新井正貴
- 出版社/メーカー: シーアンドアール研究所
- 発売日: 2019/07/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
- 作者: 掌田津耶乃
- 出版社/メーカー: 秀和システム
- 発売日: 2018/06/09
- メディア: 単行本
- この商品を含むブログを見る
- 作者: 横瀬明仁
- 出版社/メーカー: NextPublishing Authors Press
- 発売日: 2018/08/26
- メディア: オンデマンド (ペーパーバック)
- この商品を含むブログを見る
- 作者: 横瀬明仁
- 発売日: 2018/12/08
- メディア: Kindle版
- この商品を含むブログを見る
参考にしたサイト様
- Django REST Frameworkを使って爆速でAPIを実装する
- Django REST framework 超入門 - らっちゃいブログ
- Django REST frameworkで爆速API開発 導入編 - OfferBox Tech Blog
- 【Django入門】REST FrameworkでAPIを作ってみよう | 侍エンジニア塾ブログ | プログラミング入門者向け学習情報サイト
- django-rest-frameworkのチュートリアルを超意訳してみた。第01回「シリアライズ」 - すなぶろ
- django-rest-frameworkのチュートリアルを超意訳してみた。第02回「リクエストとレスポンス」 - すなぶろ
- Home - Django REST framework
- [Django REST Framework] View の使い方をまとめてみた - くろのて
- Django REST framework カスタマイズ方法 - チュートリアルの補足
- Viewsets - Django REST framework
- In the Django Rest Framework, how do you add ManyToMany related objects? - Stack Overflow
- Serializers - Django REST framework
swagger
- django-filter