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です
django-admin startproject django_rest_framework_test
cd django_rest_framework_test/
python manage.py startapp blog
3-3. モデルを定義する
通常のDjangoと同様にモデルを定義します
ここではブログのデータをイメージして、ユーザーと記事エントリーのモデルをつくっています
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に登録します
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します
python manage.py makemigrations
python manage.py migrate
3-6. Django管理者ユーザーを設定する
管理者ユーザーを登録します
python manage.py createsuperuser
3-7. 管理者画面に項目を追加する
モデルが管理者画面に表示されるようにします
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を起動します
python manage.py runserver
管理者画面でモデルが出来ているか確認します
http://localhost:8000/admin
ここまで普通のDjangoと同じなのでいつも使っている方は普通に書けるところだと思います
4. APIをつくる
あとはDjango REST APIを使えば以下の3つを追加すればAPIが動きます
Serializer
ViewSet
URL pattern
4-1. serializer.py
まずSerializerをつくります
最小構成ではモデルとフィールドを指定するだけで動いてくれます
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とモデルを呼んできて指定するだけです
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が表示できます
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)),
]
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. 動作確認する
起動します
python manage.py runserver
urls.pyで指定した通り http://localhost/api 以下にAPIが準備されています

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

4-5. APIを使ってみる
4-5-1. 取得する
PythonからhttpでGETリクエストを送ってみます
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リクエストを送ってみます
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が入って表示されます
url = 'http://localhost:8000/entries'
res = requests.get(url)
print(res)
print(res.status_code)
values = json.loads(res.text)
print(values[0])
{'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. リレーションモデルを使う
以下のように取得登録できるようにします
{'title': 'てすと1',
'body': 'ほげほげほげほげ',
'created_at': '2024-02-01T09:12:04.079115Z',
'status': 'public',
'author': {'id': 1, 'name': 'ほげ太郎', 'mail': 'xxxx@gmail.com'}}
{'title': '投稿てすと',
'body': 'ららららい',
'status': 'public',
'author_id': 1}
取得時のauthorはUserSerializerから取得した情報をそのまま使いますので、単に author = UserSerializer()と上書きしてやるだけです。これを取得時のみ使いたいのでread_only=Trueにしておきます。一方、登録時はauthor_idをUserのプライマリキーとして受け取るのでPrimaryKeyRelatedFieldで設定してやります。author要素は既に使われているのでクライアント側は名前を変えてauthor_idとして、モデル側はauthorとして返さないといけないのでcreate()の中で置き換えてやります。create()メソッドが受け取る引数は辞書型なので扱いは簡単です
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. 取得
url = 'http://localhost:8000/entries'
res = requests.get(url)
values = json.loads(res.text)
print(values[0])
5-3-2. 登録
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回に表示するレコード数を指定すればペジネーションしてくれます
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 2
}
6-2. 取得してみる
指定した数だけ返してくれます。nextとpreviousのURLも付いてくるので次に進むときはそのURLで呼ぶだけです。これは本当にありがたいですね
7. フィルターをつかう
django-filterを使うとユーザーID=1に限定するなど、フィルタをかけて一覧を取得することができます
7-1. settings.pyにdjango_filtersを登録する
django_filtersをINSTALLED_APPSに追加します
INSTALLED_APPS = [
...
'django_filters',
]
7-2. Viewにフィルタを設定する
django_filters.FilterSetを作ってViewSetに入れてやるとフィルタを適用することができます
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
まとめ
公式ドキュメントが分かりにくくて苦戦しましたが、フレームワークはすごく良く出来ていてかなり楽に実装できそうです。まだ把握出来てない機能がかなりありそうなので使っていくのが楽しみです。レッツトライ