動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

DjangoでCreateApiViewのミニマムな使い方と効かない場合に試してみること

バイブス父さん
現役の業務SE
2020年8月3日7 min read
DjangoでCreateApiViewのミニマムな使い方と効かない場合に試してみること

みなさんこんにちは!ヒロポンです!

最近個人開発でDjangoRestFrameworkでapiを作ってReactと連携させてます!

今回はそんなDjangoRestFramworkからCreateApiViewの使い方と、効かないときに試してみることを書いていきます!

CreateApiViewの超絶ミニマムな使い方

CreateApiViewの超絶ミニマムな使い方 (sql)#13aa00d89635
from rest_framework import generics
from .serializers import TodoSeriarizer
 
class CreateTodoViewSet(generics.CreateAPIView):
    serializer_class = TodoSeriarizer
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

超絶シンプルに書くと上記の4行で終わります。

シリアライザーを作って、generics.CreateAPIViewを継承したクラスのシリアライザークラスにシリアライザーをセットするだけ。

DjangoRestFrameWorkのCreateApiViewの使い方

TodoModelが下記のような感じであったとします。

DjangoRestFrameWorkのCreateApiViewの使い方 (sql)#08aa96828022
from django.db import models
from django.contrib.auth.models import User
 
# Todoは保持しているユーザー、タイトル、created,updatedを持っている。
class Todo(models.Model):
    user = models.ForeignKey(User, related_name='user', on_delete=models.CASCADE, default=1)
    title = models.CharField(max_length=100)
    done = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

ものすごくシンプルなModelですね!

ユーザーに紐づくTodoでです!

このTodoのシリアライザーが下記です。

DjangoRestFrameWorkのCreateApiViewの使い方 (sql)#047a48499eda
from rest_framework import serializers
from core.models import Todo
 
 
class TodoSeriarizer(serializers.ModelSerializer):
 
    created = serializers.DateTimeField(format='%Y-%m-%d', read_only=True)
    updated = serializers.DateTimeField(format='%Y-%m-%d', read_only=True)
 
    class Meta:
        model = Todo
        fields = ['id', 'user', 'title', 'created', 'updated']
 
        extra_kwargs = {'user': {'write_only': True}}
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

createdとupdatedはModel側で自動更新の設定をしています。

またuserは今後ログインしているユーザーをバックエンド側で紐づけるので、write_onlyとしておきます。

っで!!!!

djangoのviewはどんな感じかというと。

下記。

DjangoRestFrameWorkのCreateApiViewの使い方 (sql)#bd64c9e090d7
from django.shortcuts import render
from core.models import Todo
from rest_framework import viewsets,generics
from .serializers import TodoSeriarizer
 
 
class TodoViewSet(viewsets.ModelViewSet):
    queryset = Todo.objects.all()
    serializer_class = TodoSeriarizer
 
 
class CreateTodoViewSet(generics.CreateAPIView):
    serializer_class = TodoSeriarizer
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

viewsets.ModelViewSetを継承したTodoViewSetは、上記のコードだけでCRUD全てを持つことができます。

ここで本題のgenerics.CreateApiViewのミニマムな使い方ですが。

下記のやり方でできます。

class CreateTodoViewSet(generics.CreateAPIView):
    serializer_class = TodoSeriarizer

たった2行。

この2行でうまく動かなければ、原因は他にあります。

DjangoRestFrameworkのgenericsはrouterが使えない

今回CreateApiViewを作りましたが、これを作ったら次にしないといけないのはルーティングです。

ですが、genericsはrouterに直接追加できません。

DjangoRestFrameworkのgenericsはrouterが使えない (sql)#b36e79797695
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import TodoViewSet,CreateTodoViewSet
 
app_name = 'todo'
router = DefaultRouter()
 
# api/todo/
router.register('', TodoViewSet)
router.register('create', CreateTodoViewSet)
 
urlpatterns = [
    path('', include(router.urls))
]
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

上記のようなコードを書くと下記のような実行時エラーが出ます。

Exception in thread django-main-thread:
Traceback (most recent call last):
  File "C:\Users\Hiroshi\Desktop\todoapp\todoApp\env\lib\site-packages\django\urls\resolvers.py", line 590, in url_patterns
    iter(patterns)
TypeError: 'module' object is not iterable

じゃあどうすればいいのか?

routerを使わずに追加します。

DjangoRestFrameworkのgenericsはrouterが使えない (sql)#6953355c619f
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import TodoViewSet,CreateTodoViewSet
 
app_name = 'todo'
router = DefaultRouter()
 
# api/todo/
router.register('', TodoViewSet)
 
urlpatterns = [
    path('', include(router.urls), name='crud'),
    path('create/', CreateTodoViewSet.as_view(),name='create')
]
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

generics系のクラス(createApiViewとかとか)を継承したクラスはas_view()というメソッドを持ってます。

これをpath関数にぶん投げます。

また同じシリアライザーを複数のviewで共有する場合、name属性を指定する必要があります。

DjangoRestFrameworkのCreateApiViewが効かないときに試してみること

上記のコードでローカルサーバーは立ち上がります。

ですが、正しくTodoの追加はできません。

http://localhost:8000/api/todo/create/

にアクセスすると下記のような画面になって、postができません。

この原因はシンプルにルーティングのミスです。

viewsets.ModelViewSetを継承したviewを作ってrouterに追加した場合下記のようなルーティングが生成されます。

  • get => 指定したパス/
  • post => 指定したパス/
  • get => 指定したパス/<id>
  • put => 指定したパス/<id>
  • delete => 指定したパス/<id>

今回作成したルーティングは下記のような感じでした。

DjangoRestFrameworkのgenericsはrouterが使えない (sql)#b36e79797695
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import TodoViewSet,CreateTodoViewSet
 
app_name = 'todo'
router = DefaultRouter()
 
# api/todo/
router.register('', TodoViewSet)
router.register('create', CreateTodoViewSet)
 
urlpatterns = [
    path('', include(router.urls))
]
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

TodoViewSetと同じ階層にcreateがあるのが味噌です。

今回上手くいかなかったパスは下記。

localhost:8000/api/todo/create

はい。ルーティングのミスです。

todo/createのcreate部分がidとみなされてdelete,put,getのみ有効とされているためpostメソッドが呼べないのです!

この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について