2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Django REST Framework「実践入門」Part2

Posted at

はじめに

今回は、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用

初期設定

config/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', # 追加
]

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の基本的なコード

これを参考にして下さい。

page.js
"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文を書く必要なく直感的な操作が可能となります。

model/models.py
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で実際にテーブルを作成するイメージです
管理画面からモデルを登録しましょう

model/admin.py
from django.contrib import admin
from .models import Event

admin.site.register(Event)

これにより、管理者画面でモデルの管理ができます。

$ python3 manage.py createsuperuser

で管理者を作成してサーバを起動して(http://127.0.0.1:8000/admin/)にアクセスしましょう

image.png
モデル作成がうまくいったと分かりました。
実際に何個か適当なイベントを追加しておいてください。

serializer.py (シリアライザ)

次にシリアライザについて説明します。これはDRF固有のもので、JSON文字列と、オブジェクトの相互変換をしてくれる機能になります。
今回は、自動で変換やバリデーションを行ってくれるModelserializerについてのみ説明します。
詳しく知りたい場合は以下を参照。

api/serializer.py
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とあまり機能は変わりません。

api/views.py
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とほとんど変わりません。

config/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('api.urls')),
]
api/urls.py
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を作成できたことが分かります。
image.png
これで、DRFの作成は終了です。

Next.js

コードのみで説明は省略します。

page.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>
  )
}

components/addEvent.js
"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に以下を追加してください

config/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',
] # 追加

これですべての作業が終了しました。
実際にサーバを起動してみてください。
image.png

このように、セルを選択すると入力欄が出てきてイベントを追加できることが分かります。

さいごに

DRFを使うと簡単にAPIを作成できることが分かりました。
認証などについても便利な機能がたくさんあるのでぜひDRFについて勉強してみてください。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?