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

この記事は約 5 分で読めます。

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

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

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

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

from rest_framework import generics
from .serializers import TodoSeriarizer

class CreateTodoViewSet(generics.CreateAPIView):
    serializer_class = TodoSeriarizer

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

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

DjangoRestFrameWorkのCreateApiViewの使い方

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

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)

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

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

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

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}}

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

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

っで!!!!

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

下記。

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

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

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

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

class CreateTodoViewSet(generics.CreateAPIView):
    serializer_class = TodoSeriarizer

たった2行。

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

DjangoRestFrameworkのgenericsはrouterが使えない

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

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

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))
]

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

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を使わずに追加します。

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')
]

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>

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

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))
]

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

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

localhost:8000/api/todo/create

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

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

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA