LoginSignup
2
4

Django REST framework

Last updated at Posted at 2024-02-02

1. やりたいこと

ReactとVueに入門したのでバックエンドAPIも覚えたい

2. 実現方法

DjangoでAPIが書けるDjango REST frameworkを使うと簡単につくることが出来るようです
https://www.django-rest-framework.org/

DjangoはDBをモデルとして扱うORMを備えていて、モデルから受け取った値をViewを使って表示するという仕組みになっていますが、Django REST frameworkはモデルから受け取った値をシリアライズしてJSONとして渡してやる、あるいはシリアライズ済みのJSONを受け取ってデシリアライズして処理する、という仕組みで動作になっており、どのURLがどんなAPIになるかについてもフレームワークにまとめて処理できるようになっているようです

こちらの記事が分かりやすかったのでコードをお借りしてやってみます
https://qiita.com/kimihiro_n/items/86e0a9e619720e57ecd8

3. プロジェクトをつくりモデルを設定する

モデルをつくって書き込めるようにします

3-1. 必要なパッケージのインストール

pip install django
pip install djangorestframework
pip install django-filter 

3-2. プロジェクトをつくる

適当なフォルダを作ってプロジェクトとアプリをつくります
プロジェクト名がdjango_rest_framework_test、アプリ名がblogです

terminal
django-admin startproject django_rest_framework_test
cd django_rest_framework_test/
python manage.py startapp blog

3-3. モデルを定義する

通常のDjangoと同様にモデルを定義します
ここではブログのデータをイメージして、ユーザーと記事エントリーのモデルをつくっています

/blog/models.py
from django.db import models

class User(models.Model):
    name = models.CharField(max_length=32)
    mail = models.EmailField()

    def __repr__(self):
        # 主キーとnameを表示させて見やすくする
        # ex) 1: Alice
        return "{}: {}".format(self.pk, self.name)

    __str__ = __repr__  # __str__にも同じ関数を適用    


class Entry(models.Model):
    STATUS_DRAFT = "draft"
    STATUS_PUBLIC = "public"
    STATUS_SET = (
            (STATUS_DRAFT, "下書き"),
            (STATUS_PUBLIC, "公開中"),
    )
    title = models.CharField(max_length=128)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    status = models.CharField(choices=STATUS_SET, default=STATUS_DRAFT, max_length=8)
    author = models.ForeignKey(User, related_name='entries', on_delete=models.CASCADE)

Userクラスで__repr__と__str__に書いている処理は、管理者画面でEntryの内容を編集する際に設定しないままだと「User Object」表示されてしまうのを見たい形で表示させる為に書いています

3-4. settings.pyに登録

追加したアプリとrest_frameworkをsettings.pyに登録します

/django_rest_framework_test/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
    'rest_framework',
]

3-5. DBをマイグレートする

追加したモデルを追加してmigrateします

terminal
python manage.py makemigrations
python manage.py migrate

3-6. Django管理者ユーザーを設定する

管理者ユーザーを登録します

terminal
python manage.py createsuperuser

3-7. 管理者画面に項目を追加する

モデルが管理者画面に表示されるようにします

django_rest_framework_test/admin.py
from django.contrib import admin
from .models import User, Entry

@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    pass

@admin.register(Entry)
class Entry(admin.ModelAdmin):
    pass

3-8. 動作確認

Djangoを起動します

terminal
python manage.py runserver

管理者画面でモデルが出来ているか確認します
http://localhost:8000/admin

ここまで普通のDjangoと同じなのでいつも使っている方は普通に書けるところだと思います

image.png

4. APIをつくる

あとはDjango REST APIを使えば以下の3つを追加すればAPIが動きます

Serializer
ViewSet
URL pattern

4-1. serializer.py

まずSerializerをつくります
最小構成ではモデルとフィールドを指定するだけで動いてくれます

/blog/serializer.py
from rest_framework import serializers
from .models import User, Entry

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('name', 'mail')

class EntrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Entry
        fields = ('title', 'body', 'created_at', 'status', 'author')

4-2. views.py

ViewSetsをつくります
views.pyからさっき作ったserializerとモデルを呼んできて指定するだけです

/blog/views.py
from rest_framework import viewsets, filters

from .models import User, Entry
from .serializer import UserSerializer, EntrySerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

class EntryViewSet(viewsets.ModelViewSet):
    queryset = Entry.objects.all()
    serializer_class = EntrySerializer

4-3. urls.py

URL patternをつくります
routersにViewSetを渡せばURL patternを作ってくれるので、これをincludeしてurls.pyで指定してやればAPIが表示できます

/django_rest_framework_test/urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import include
from blog.urls import router as blog_router

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include(blog_router.urls)),
]
/blog/urls.py
from rest_framework import routers
from .views import UserViewSet, EntryViewSet

router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
router.register(r'entries', EntryViewSet)

4-4. 動作確認する

起動します

terminal
python manage.py runserver

urls.pyで指定した通り http://localhost/api 以下にAPIが準備されています

それぞれのAPIの取得例を表示できます

4-5. APIを使ってみる

4-5-1. 取得する

PythonからhttpでGETリクエストを送ってみます

python
import requests
import json

url = 'http://localhost:8000/'
res = requests.get(url)
print(res)
print(res.status_code)
values = json.loads(res.text)
print(values)

ちゃんと取れました

4-5-2. 登録する

PythonからhttpでPOSTリクエストを送ってみます

python
json_data = {"name": "ほが三郎",
             "mail": "xxxx@fugamail.com"}
url = 'http://localhost:8000/users/'
response = requests.post(url, data = json_data)
response

追加できました

5. リレーションモデル

こちらの記事を参考にやってみました
https://sakataharumi.hatenablog.jp/entry/2018/10/20/010806

5-1. 現状

現状でentriesを取得すると、author欄にはuser IDが入って表示されます

python
url = 'http://localhost:8000/entries'
res = requests.get(url)
print(res)
print(res.status_code)
values = json.loads(res.text)
print(values[0])
取得されるJSON
{'title': 'てすと1',
 'body': 'ほげほげほげほげ\r\nふがふがふがふが\r\nほげほげほげほげ\r\nふがふがふがふが\r\nほげほげほげほげ\r\nふがふがふがふが',
 'created_at': '2024-02-01T09:12:04.079115Z',
 'status': 'public',
 'author': 1}

知りたいのがユーザー名であるような場合、別途ユーザーIDをAPIで取得しなければならずわりとイマイチですので、リレーションモデルを使って記事エントリーを取得する際に著者情報も取れるようにしましょう

5-2. リレーションモデルを使う

以下のように取得登録できるようにします

取得するときのJSON
{'title': 'てすと1',
 'body': 'ほげほげほげほげ',
 'created_at': '2024-02-01T09:12:04.079115Z',
 'status': 'public',
 'author': {'id': 1, 'name': 'ほげ太郎', 'mail': 'xxxx@gmail.com'}}
登録するときのJSON
{'title': '投稿てすと',
 'body': 'ららららい',
 'status': 'public',
 'author_id': 1}

取得時のauthorはUserSerializerから取得した情報をそのまま使いますので、単に author = UserSerializer()と上書きしてやるだけです。これを取得時のみ使いたいのでread_only=Trueにしておきます。一方、登録時はauthor_idをUserのプライマリキーとして受け取るのでPrimaryKeyRelatedFieldで設定してやります。author要素は既に使われているのでクライアント側は名前を変えてauthor_idとして、モデル側はauthorとして返さないといけないのでcreate()の中で置き換えてやります。create()メソッドが受け取る引数は辞書型なので扱いは簡単です

/blog/serializer.py
from rest_framework import serializers
from .models import User, Entry

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('id', 'name', 'mail')

class EntrySerializer(serializers.ModelSerializer):
    author = UserSerializer(read_only=True)
    author_id = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), write_only=True)
    class Meta:
        model = Entry
        fields = ('title', 'body', 'created_at', 'status', 'author', 'author_id')

    def create(self, validated_data):
        validated_data['author'] = validated_data.get('author_id', None)

        if validated_data['author'] is None:
            raise serializers.ValidationError("user not found.") 

        del validated_data['author_id']

        return Entry.objects.create(**validated_data)

5-3. APIを使ってみる

5-3-1. 取得

python
url = 'http://localhost:8000/entries'
res = requests.get(url)
values = json.loads(res.text)
print(values[0])

image.png

5-3-2. 登録

python
json_data = {'title': '投稿てすと',
             'body': 'ららららい',
             'status': 'public',
             'author_id': 1}

url = 'http://localhost:8000/entries/'
response = requests.post(url, data = json_data)

6. ペジネーション

一覧を取得するAPIはこのままだと全レコードを送ってしまうのでえらい事になります。定番の対処方法がペジネーションすなわちページ分けです

6-1. settings.pyに設定を書く

使用するPaginationクラスと、1回に表示するレコード数を指定すればペジネーションしてくれます

/django_rest_framework_test/settings.py
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
    'PAGE_SIZE': 2
}

6-2. 取得してみる

指定した数だけ返してくれます。nextとpreviousのURLも付いてくるので次に進むときはそのURLで呼ぶだけです。これは本当にありがたいですね

image.png

7. フィルターをつかう

django-filterを使うとユーザーID=1に限定するなど、フィルタをかけて一覧を取得することができます

7-1. settings.pyにdjango_filtersを登録する

django_filtersをINSTALLED_APPSに追加します

/django_rest_framework_test/settings.py
INSTALLED_APPS = [
    ...
    'django_filters',
]

7-2. Viewにフィルタを設定する

django_filters.FilterSetを作ってViewSetに入れてやるとフィルタを適用することができます

/blog/views.py
import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import viewsets
from .models import User, Entry
from .serializer import UserSerializer, EntrySerializer

class EntryFilter(django_filters.FilterSet):
    author = django_filters.NumberFilter()
    status = django_filters.CharFilter()

    class Meta:
        model = Entry
        fields = ('author', 'status')

class EntryViewSet(viewsets.ModelViewSet):
    queryset = Entry.objects.all()
    serializer_class = EntrySerializer
    filter_backends = [DjangoFilterBackend]
    filterset_class = EntryFilter

7-3. 取得してみる

?author=1&status=public などでフィルタをかけられるようになりました

url = 'http://localhost:8000/entries?author=1&status=public'
res = requests.get(url)
print(res)
print(res.status_code)
values = json.loads(res.text)
values

image.png

まとめ

公式ドキュメントが分かりにくくて苦戦しましたが、フレームワークはすごく良く出来ていてかなり楽に実装できそうです。まだ把握出来てない機能がかなりありそうなので使っていくのが楽しみです。レッツトライ

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4