Python+Django REST FrameworkでAPIを作ってみた

環境構築

インストール

pip install Django
pip install djangorestframework

Djangoのプロジェクトを作成

django-admin startproject myproject .

Djangoのアプリケーションを作成

django-admin startapp myapp

VS Code用のデバッグ設定

launch.jsonに以下の記述をしておけば、F5押すだけで「manage.py runserver」を実行してくれます。

{
  // IntelliSense を使用して利用可能な属性を学べます。
  // 既存の属性の説明をホバーして表示します。
  // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: Current File",
      "type": "python",
      "request": "launch",
      "program": "${workspaceFolder}\\manage.py",
      "args": [
        "runserver"
      ],
      "console": "integratedTerminal"
    }
  ]
}

VS Code用のデバッグ設定(anaconda用)

settings.jsonに以下を追記すると、デバッグ実行時に自動でactivateしてくれます。
一旦「conda deactivate」すると、次にデバッグ実行してもactivateしてくれないようです。

{
  "python.pythonPath": "C:\\Users\\ababa51515\\Anaconda3\\envs\\my_python_env\\python.exe"
}

settings.pyの編集

INSTALLED_APPS = [
    'rest_framework',            # 追加
    'myapp',                     # 追加
]

マイグレーション

マイグレーションファイルの作成

python manage.py makemigrations

マイグレーションの実行

python manage.py migrate

マイグレーションの取消

python manage.py migrate myapp zero

管理者ユーザーの作成

python manage.py createsuperuser

管理サイトへのアクセス

python manage.py runserver

http://127.0.0.1:8000/admin にアクセスし、
python manage.py createsuperuser で作成したユーザ、パスワードでログインする。

モデル

\myproject\myapp\models.pyを削除してmodelsフォルダを作成し、その中にmodelを作成していきます。

from .pokemon import Pokemon
from .pokemon_name import PokemonName

こんな感じにしました。

from django.db import models

class Pokemon(models.Model):
  class Meta:
    db_table = 'pokemon'
    ordering = ['order']
    verbose_name = verbose_name_plural = 'ポケモン'

  id = models.CharField(primary_key=True, max_length=5)
  no = models.IntegerField(verbose_name='No')
  is_default = models.BooleanField(verbose_name='デフォルトフォルム')

  def __str__(self):
    return str(self.no)
from django.db import models

from . import Pokemon

class PokemonName(models.Model):
  class Meta:
    db_table = 'pokemon_name'
    ordering = ['pokemon_id', 'local_language_id']
    verbose_name = verbose_name_plural = 'ポケモン名'
    constraints = [
      models.UniqueConstraint(
        fields=["pokemon_id", "local_language_id"],
        name="unique_pokemon_name"
      ),
    ]

  id = models.CharField(primary_key=True, max_length=7)
  local_language_id = models.IntegerField(verbose_name='言語')
  name = models.CharField(verbose_name='ポケモン名', max_length=10)
  form_name = models.CharField(verbose_name='フォルム名', max_length=20, null=True, blank=True)
  pokemon = models.ForeignKey(Pokemon, verbose_name='ポケモン', on_delete=models.CASCADE)

  def __str__(self):
    name = self.name
    form_name = '' if not self.form_name else '({})'.format(self.form_name)
    return name + form_name

\myproject\myapp\admin.pyに記述する事で、管理者サイトからデータの更新・参照等ができるようになります。

from django.contrib import admin

from api.models import Pokemon, PokemonName

admin.site.register(Pokemon)
admin.site.register(PokemonName)

前述のマイグレーションを実行する事で、modelに応じたテーブルが作成されます。

シリアライザ

serializerもserializersフォルダを作成し、serializerを作成していきます。

from .pokemon_serializer import PokemonSerializer, PokemonListSerializer
from .pokemon_name_serializer import PokemonNameSerializer, PokemonNameListSerializer
from rest_framework import serializers
from ..models import Pokemon, PokemonName 

class PokemonSerializer(serializers.ModelSerializer):
  class Meta:
    model = Pokemon
    fields = ['id', 'no', 'is_default']

class PokemonListSerializer(serializers.ListSerializer):
  child = PokemonSerializer()
from rest_framework import serializers
from ..models import Pokemon, PokemonName 

class PokemonNameSerializer(serializers.ModelSerializer):
  pokemon_no = serializers.ReadOnlyField(source='pokemon.no', read_only=True)

  class Meta:
    model = PokemonName
    fields = ['id', 'local_language_id', 'name', 'form_name', 'pokemon_id', 'pokemon_no']

class PokemonNameListSerializer(serializers.ListSerializer):
  child = PokemonNameSerializer()

ビュー

viewsフォルダを作成し、viewを作成していきます。

from .pokemon_view import PokemonAPIView, PokemonListAPIView
from .pokemon_name_view import PokemonNameAPIView, PokemonNameListAPIView
from django.http import Http404
from rest_framework import status, views
from rest_framework.response import Response
from ..models import Pokemon
from ..serializers import PokemonSerializer, PokemonListSerializer

class PokemonListAPIView(views.APIView):  
  def get(self, request, format=None):
    pokemons = Pokemon.objects.all()
    serializer = PokemonListSerializer(pokemons)
    return Response(serializer.data, status.HTTP_200_OK)

class PokemonAPIView(views.APIView):
  def get(self, request, pk, format=None):
    try:
      pokemon = Pokemon.objects.get(pk=pk)
    except Pokemon.DoesNotExist:
      raise Http404("data not found")

    serializer = PokemonSerializer(pokemon)
    return Response(serializer.data, status.HTTP_200_OK)
from django.http import Http404
from rest_framework import status, views
from rest_framework.response import Response
from ..models import Pokemon, PokemonName
from ..serializers import PokemonNameSerializer, PokemonNameListSerializer

class PokemonNameListAPIView(views.APIView):  
  def get(self, request, format=None):
    print(request.query_params)
    
    local_language_id = request.query_params.get('local_language_id')
    if local_language_id:
      pokemonNames = PokemonName.objects.filter(
        local_language_id=local_language_id
      )

    name = request.query_params.get('name')
    if name:
      pokemonNames = PokemonName.objects.filter(
        name__icontains=name
      )

    serializer = PokemonNameListSerializer(pokemonNames)
    return Response(serializer.data, status.HTTP_200_OK)

class PokemonNameAPIView(views.APIView):
  def get(self, request, pk, format=None):
    try:
      pokemonName = PokemonName.objects.get(pk=pk)
    except Pokemon.DoesNotExist:
      raise Http404("data not found")

    serializer = PokemonNameSerializer(pokemonName)
    return Response(serializer.data, status.HTTP_200_OK)

Urls.py

myappのUrls.py

from django.urls import path
import myapp.views as views

urlpatterns = [
  path('pokemons/', views.PokemonListAPIView.as_view()),
  path('pokemons/<pk>/', views.PokemonAPIView.as_view()),
  path('pokemon-names/', views.PokemonNameListAPIView.as_view()),
  path('pokemon-names/<pk>/', views.PokemonNameAPIView.as_view()),
]

myprojectのUrls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('myapp.urls'))
]

実行

起動後、以下でアクセスできます。

http://127.0.0.1:8000/api/pokemons/1
http://127.0.0.1:8000/api/pokemon-names/?name=ピカチュウ