3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【DRF×Next】環境構築 ~ Googleログインまで

Last updated at Posted at 2023-07-02

はじめに

今回は、バックエンドをDjangoRestFrameworkで実装し、
フロントはNext(TypeScript)でreact-oauthというサードパーティライブラリを使用して簡単にWebアプリ開発の土台を作成していきます。

環境

Ububtu 20.04

構成概要

PROJ #作成するプロジェクト
│── back # Django REST framework(以下DRF)のAPI
│   ├── api # モデル、ビュー、APIのルーティングなど
│   ├── env # venv
│   ├── manage.py # DRFのサーバ起動やマイグレーション
│   ├── project # DRFの設定もろもろの中核
│   └── requirements.txt # 必要なPythonパッケージ
│── front # フロントエンド
     └── client # Reactアプリケーション

データベース

※SQLiteを使用する場合は無視してください。
DRFはデフォルトでSQLiteです。

(sudo apt update && apt upgrade)

sudo apt install mysql-server

rootユーザのパスワード設定

sudo mysql_secure_installation

mysql -uroot -p

mysql > create database 任意のデータベース名;

mysql > quit

バックエンド

Django REST framework(DRF)を使用します。
Djangoとは別物と考えてください。

sudo apt install python3-django

必要ライブラリのインストール

python -m venv env

(「env」は任意)

source env/bin/activate

requirements.txt
Django
djangorestframework
django-cors-headers
drf_social_oauth2
mysqlclient

※django-cors-headersはポートの異なるフロントエンドからの通信を許可するのに必要です。

pip install -r requirements.txt

後から追加ライブラリがあればrequirements.txtを更新します。

pip freeze > requirements.txt

②Djangoプロジェクトを作成

django-admin startproject project .

(project名「project」は任意)

appを作成

python3 [manage.py](http://manage.py/) startapp api

(app名「api」は任意)

設定ファイルを修正

  • 使用するライブラリと上で作成したapp名を登録
  • db設定(dbエンジン, host, user, password)
  • 言語、地域設定(日本・アジア・東京)

以下にスニペットを示します。

project/settings.py
INSTALLED_APPS = [
   'app',
   'rest_framework',
   'corsheaders',
]
MIDDLEWARE = [
   'corsheaders.middleware.CorsMiddleware'
]
DATABASES = {
    #デフォのSQLite設定はコメントアウトまたは削除
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 上で作成した任意のDB名,
        'USER': 'root',
        'PASSWORD': 上で設定したrootのパスワード,
    }
}
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
CORS_ALLOWED_ORIGINS = [
   "http://localhost:3000",
]

管理者を作成

python3 manage.py createsuperuser

上で設定したdbのauth_userテーブルに作成されます。

※以下api直下での作業です。

モデルを定義

models.pyに作成するテーブルを記述します。

以下、例を示します。

models.py
from django.db import models

# Create your models here.
class User(models.Model):
    email = models.EmailField(
        verbose_name='Eメールアドレス',
        max_length=255,
        unique=True,
    )
    name = models.CharField(max_length=100)
    family_name = models.CharField(max_length=100)
    given_name = models.CharField(max_length=100)
    picture = models.CharField(max_length=200)
    active = models.BooleanField(default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name # 管理画面での一覧に表示する値。なくてもいいし別の値でもいい。 

マイグレート

python3 manage.py makemigrations api

python3 manage.py migrate

appのモデルにテーブルを新しく定義する場合は再度上記2コマンド実行してください。

管理画面で操作できるようにする

管理したいモデルを追加します。

以下、Userテーブルを追加する例です。

admin.py
from django.contrib import admin
from .models import User

# ここに使用するモデルを設定してください
admin.site.register(User)

リクエストを受けてCRUD操作をするviewsを作成

serializer.py

まずシリアライザを設定します。

Djangoにはない機能。DRF独自なのでファイルは新規作成してください。

シリアライザは複雑なデータをDBレコード⇔モデル間で橋渡ししてくれます。

以下、例です。

serializer.py
from .models import User
from rest_framework import serializers
# from django.db.models import fields

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
       fields = ('id','email','name','family_name','given_name','picture','active','created_at','updated_at')

views.py

以下、クラスベースのビューを採用する場合の例です。(関数ベースでも定義可能)

DRF公式によると以下のメリットがある。

・ 特定の HTTP メソッド (GET、POSTなど) に関連するコードの集まりを、条件分岐を使ってかき分けるのではなく、それぞれに独立したメソッドを割り当てることができる。

・ ミックスイン (多重継承) などのオブジェクト指向のテクニックを使って、コードを再利用可能なコンポーネントに分解できる。

views.py
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.generics import get_object_or_404

from .models import User
from .serializer import UserSerializer
 
class UserListCreateAPIView(APIView): # 全データ取得とデータ追加についてのクラス
    def get(self, request): # 全データ取得
        articles = User.objects.filter(active=True)
        serializer = UserSerializer(articles, many=True)
        return Response(serializer.data)
 
    def post(self, request): # データ追加
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class UserDetailAPIView(APIView): # idをもとに個別データ取得
    err_msg = {
        "error": {
            "code": 404,
            "message": "Article not found",
        }}

    def get_object(self, pk):
        user = get_object_or_404(User, pk=pk)
        return user

    def get(self, request, pk):
        user = self.get_object(pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)

    def put(self, request, pk):
        user = self.get_object(pk)
        serializer = UserSerializer(user, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk):
        user = self.get_object(pk)
        user.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

⑩APIのエンドポイントと管理画面のルーティング

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('app.urls')) # APIのバージョンを表記するのがREST界隈での慣習らしいです。
]
api/urls.py
from django.urls import path
from .views import (
    UserListCreateAPIView,
    UserDetailAPIView
)

urlpatterns = [
    path("user/", UserListCreateAPIView.as_view(), name="article-list"),
    path("user/<int:pk>", UserDetailAPIView.as_view(), name="article-detail"),
]

⑪起動

python3 [manage.py](http://manage.py) runserver

でサーバを起動して、

localhost:8000/admin

の管理画面でテーブルを直接変更したり、

localhost:8000/api/v1/user

localhost:8000/api/v1/user/(ユーザid)

でhttp通信をしてみてください。

(get, post, put, deleteメソッドすべてやってみてください。)

あとでReact側からaxiosというhttp通信ライブラリでhttpリクエストをして、
レスポンスが帰ってくる感覚が簡単に実感できるかと思います。


フロントエンド

※googleアカウントで認証してDRFにpostメソッドでユーザ追加できるようにするところまでの環境構築方法

筆者のnode環境

node -v

v18.12.1

npm -v

8.19.2

npx -v

8.19.2

①アプリ作成

npx create-react-app client

or

npx create-next-app client

next.jsを使用すればルーティングがpages直下のフォルダ構成と連動するので楽です。

しかし今回はまだnext.jsの特徴であるバックエンド機能は使用しません。

create-appがうまく行かなければキャッシュクリア&.npm削除してみてください。

npm cache clean --force

rm -rf ~/.npm

②googleAOuth認証⇒DRFにリクエスト に必要なnpmパッケージをインストール

npm i @react-oauth/google@latest axios jwt-decode

  • react-oauth
    ReactでGoogleOAuth認証するためのライブラリ。
    react-google-loginというライブラリもあるが、非推奨。
  • axios
    javascriptでのhttp通信を簡単に実装できるライブラリ。
    Promissベース(処理が完了するまで次の処理に移行しない。)
  • jwt-decode
    react-oauthで認証成功した際のレスポンスにcredentialというキーがある。
    JWTという符号化やデジタル署名の仕組みを規定した標準規格であるためデコードする必要がある。
    デコードするとユーザ名、メアド、プロフィール写真のURLなどを取得可能。

③OAuthクライアントIDの取得

以下の記事を見れば簡単に行うことができます。

ReactとDjangoでGoogle OAuthログイン【前編】Google Developer ConsoleからクライアントIDとクライアントシークレットを取得する

④画面実装

以下、Next.js + TypeScriptで実装する場合の例です。

pages/login.tsx
import { GoogleOAuthProvider } from '@react-oauth/google';
import { GoogleLogin } from '@react-oauth/google';
import axios from "axios";
import jwt_decode from 'jwt-decode'

const googleClientId: string = クライアントID;
const baseURL: string = "localhost:8000/api/v1";

const handleGoogleLogin = (response: any) => {
  console.log("response",response)
  var decoded: any = jwt_decode(response.credential)
  console.log("decoded",decoded)

  const body = {
    "email": decoded.email,
    "name": decoded.name,
    "family_name": decoded.family_name,
    "given_name": decoded.given_name,
    "picture": decoded.picture
  }

  axios.post(`${baseURL}/user/`, body)
  .then((res) => {
      console.log("Success Google login", res)
      window.location.href = "/"
  })
  .catch((err) => {
      console.log("Error Google login", err);
  });
};

export default function Login() {
  return (
      <div>
        <GoogleOAuthProvider clientId={googleClientId}>
          <GoogleLogin
            onSuccess={(response) => handleGoogleLogin(response)}
            onError={() => {
              console.log('Login Failed');
            }}
          />
        </GoogleOAuthProvider>
      </div>
  )
}

⑤起動&GoogleOAuth認証

npm run dev

認証してDBを確認してください。

api_userテーブルにユーザが追加されていればOKです。

参考

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?