はじめに
今回は、Next.jsとDRFを用いて簡単なAPIを作成します。
DRFの基本的な使い方と、CORSについて説明します。
Next.jsについての説明は省きます。
制作物概要
イベントを追加できるカレンダーアプリを作ります。
カレンダーには、FullCalendarを使用します。
環境構築
DRFの準備
仮想環境に入る
$ python3 -m venv venv
$ source venv/bin/activate
必要なものをinstall
$ pip install django
$ pip install djangorestframework
プロジェクトとアプリケーションを作成
$ django-admin startproject config .
$ python3 manage.py startapp model # モデル用
$ python3 manage.py startapp api # Api用
初期設定
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework', # 追加
'api.apps.ApiConfig', # 追加
'model.apps.ModelConfig', # 追加
]
LANGUAGE_CODE = 'ja' # 修正
TIME_ZONE = 'Asia/Tokyo' # 修正
Next.jsの準備
Next.jsの構築
$ npx create-next-app calendar
$ npm run dev # サーバが立ち上がる
TypeScriptなどは追加していません
fullcalendar install
$ npm install --save \
@fullcalendar/core \
@fullcalendar/react \
@fullcalendar/daygrid \
@fullcalendar/interaction
fullcalendarの基本的なコード
これを参考にして下さい。
"use client"
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from "@fullcalendar/interaction"
export default function Home() {
const handleDateClick = (arg) => {
alert(arg.dateStr)
}
return (
<FullCalendar
plugins={[dayGridPlugin, interactionPlugin]}
initialView="dayGridMonth"
locale={"ja"}
weekends={false}
events={[
{ title: 'event 1', date: '2019-04-01' },
{ title: 'event 2', date: '2019-04-02' }
]}
dateClick={handleDateClick}
/>
);
}
DRF
今回は使用するDRFの機能は、大きく4つに分けられます。
- models.py (モデル)
- serializer.py (シリアライザ)
- views.py (ビュー)
- urls.py (URL)
models.py (モデル)
DRFには「ORM」という機能が備わっています。
「ORM」とはオブジェクトとリレーショナルデータベースをうまく対応付けてくれる機能です。
Djangoではmodel.pyにclassを作り、マイグレーションすることでデータベースが自動的に作成されます。
また、このおかげでSQL文を書く必要なく直感的な操作が可能となります。
import uuid
from django.db import models
class Event(models.Model):
class Meta:
db_table = 'event'
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(verbose_name='タイトル', max_length=20)
date = models.DateField(verbose_name='開催日')
created_at = models.DateField(verbose_name='投稿日', auto_now_add=True)
def __str__(self):
return self.title
マイグレーション
$ python3 manage.py makemigrations
$ python3 manage.py migrate
makemigrationで設計図を作成して
migrateで実際にテーブルを作成するイメージです
管理画面からモデルを登録しましょう
from django.contrib import admin
from .models import Event
admin.site.register(Event)
これにより、管理者画面でモデルの管理ができます。
$ python3 manage.py createsuperuser
で管理者を作成してサーバを起動して(http://127.0.0.1:8000/admin/)にアクセスしましょう
モデル作成がうまくいったと分かりました。
実際に何個か適当なイベントを追加しておいてください。
serializer.py (シリアライザ)
次にシリアライザについて説明します。これはDRF固有のもので、JSON文字列と、オブジェクトの相互変換をしてくれる機能になります。
今回は、自動で変換やバリデーションを行ってくれるModelserializerについてのみ説明します。
詳しく知りたい場合は以下を参照。
from rest_framework import serializers
from model.models import Event
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = ['id', 'title', 'date']
このように、ModelSerializerではほとんどを自動で行ってくれます。
views.py (ビュー)
これも、Djangoとあまり機能は変わりません。
from rest_framework import viewsets
from model.models import Event
from .serializers import EventSerializer
class EventViewSet(viewsets.ModelViewSet):
queryset = Event.objects.all()
serializer_class = EventSerializer
ModelViewSetを使うと、Modelに関連したCRUDを自動で実装してくれます。
urls.py (URL)
これもDjangoとほとんど変わりません。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
]
from django.urls import path, include
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register('event', views.EventViewSet)
app_name = 'api'
urlpatterns = [
path('', include(router.urls)),
]
apiの確認
http://127.0.0.1:8000/api/event/
にアクセスするとapiを作成できたことが分かります。
これで、DRFの作成は終了です。
Next.js
コードのみで説明は省略します。
"use client"
import React, { useState, useEffect } from 'react'
import AddEvent from './components/addEvent'
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from "@fullcalendar/interaction"
export default function Home() {
const [events, setEvents] = useState([])
const [showForm, setShowForm] = useState(false)
const [selectedDate, setSelectedDate] = useState(null)
useEffect(() => {
const fetchEvents = async () => {
try {
const response = await fetch("http://127.0.0.1:8000/api/event/")
const json = await response.json()
setEvents(json.map((item) => ({
title: item.title,
date: item.date,
})))
} catch (error) {
alert('データが取得できませんでした')
}
}
fetchEvents()
}, [])
const handleDateClick = (arg) => {
setSelectedDate(arg.dateStr)
setShowForm(true)
}
return (
<div>
<FullCalendar
plugins={[dayGridPlugin, interactionPlugin]}
dateClick={handleDateClick}
initialView="dayGridMonth"
weekends={false}
locale="ja"
events={events}
/>
{showForm && <AddEvent setShowForm={setShowForm} selectedDate={selectedDate} />}
</div>
)
}
"use client"
import React, { useState, useEffect } from 'react'
export default function AddEvent({setShowForm, selectedDate}) {
const [title, setTitle] = useState('')
// const [user, setUser] = useState()
const handleSubmit = async() => {
try{
const response = await fetch("http://127.0.0.1:8000/api/event/",{
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
title: title,
date: selectedDate,
})
})
setShowForm(false)
}catch{
alert("登録し直してください")
}
}
const handleCancelClick = () => {
setShowForm(false)
}
return (
<div>
<form onSubmit={handleSubmit}>
<input value={title} onChange={(e) => {
setTitle(e.target.value)
}} type="text" name="title" placeholder="イベント名" required />
<button>イベント追加</button>
</form>
<button onClick={handleCancelClick}>キャンセル</button>
</div>
)
}
ここまでで、プログラムは完成です。実際にサーバーを起動して操作を実行してみてください。
コンソールにcorsエラーが出ると思います。
CORS(Cross-Origin Resource Sharing)
ブラウザにはサンドボックスという考え方があり、同一オリジンポリシーという制約があります。
この制約により、異なるオリジン(ホスト・スキーム・ポート番号)間からのアクセスが規制されます。
しかし、この制限を超えたデータのやり取りのニーズに対応するために「CORS」が策定されました。
「CORS」ではheaderに情報を追加したりなどの作業が必要となりますが、Djangoでは、django-cors-headersを使用すると簡単に実装できます。
$ pip install django-cors-headers
を実行してから
setting.pyに以下を追加してください
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'api.apps.ApiConfig',
'model.apps.ModelConfig',
'corsheaders', # 追加
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware', # 追加
]
CORS_ALLOW_ALL_ORIGINS = False # 追加
CORS_ALLOWED_ORIGINS = [
'http://localhost:3000',
] # 追加
これですべての作業が終了しました。
実際にサーバを起動してみてください。
このように、セルを選択すると入力欄が出てきてイベントを追加できることが分かります。
さいごに
DRFを使うと簡単にAPIを作成できることが分かりました。
認証などについても便利な機能がたくさんあるのでぜひDRFについて勉強してみてください。