概要
バックエンド(Django)の作成
$ pip install Django
$ pip install djangorestframework
$ pip install djangorestframework-simplejwt==4.1.2 (バージョン4.1.2のjwtを導入)
$ pip install djoser (トークン関係)
$ pip install pillow (画像関係)
$ pip install django-cors-headers (クロスオリジンで繋げられるようにしたいから)
$ Django-admin startproject プロジェクト名 (プロジェクト作成される?)
$ Django-admin startapp アプリケーション名 (アプリケーションが作成される?)
- Manage.pyを右クリックで”run manage”を選択
- parametersをrunserverに変更すると、再生ボタンを押下した時にローカル環境が立ち上がるように修正
INSTALLED_APPSに以下を追加
settings.py
INSTALLED_APPS = [
(省略) #以下の4行を追加
'rest_framework',
'api.apps.ApiConfig', (apiディレクトリの中にある、apps.pyの中にある、ApiConfigをさす)
'corsheaders'
,
'djoser',
]
MIDDLEWAREの箇所に以下を追加
settings.py
'conrsheaders.middleware.CorsMiddleware' (フロントエンドとの連携のため?)
フロントエンドからのアクセスを許可するために以下を追加
CORS_ORIGIN_WHITELIST = ['http://localhost:3000']
認証の権限を指定
settings.py
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}
- 通常はview事に権限を書くが、settings.pyに書いておくとベースができる、ベースの権限に対してカスタマイズした権限を与えたい場合の差分のみ各viewに書く
jwtの設定(今回の認証はjwtの認証を使いたいので以下を追加)
settings.py
from datetime import timedelta
SIMPLE_JWT = {
'AUTH_HEADER_TYPES': ('JWT',),
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
}
-
認証が通ったユーザーがviewを見ることができるというパーミッションをデフォルトで定義
-
認証にはJWTを使うことを定義
-
ヘッダーのタイプがjwt
-
トークンの有効期限(ex timedeltaで有効期限を30分に指定している)
画像データを扱えるようにする
settings.py
(省略) #以下の2行を追加
MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
- プロジェクトの直下に画像データの保管場所としてmediaディレクトリを作成
カスタマイズされたモデルを使用することを明示する
AUTH_USER_MODEL = 'api.User'
- apiディレクトリの中のUserモデルを参照するように明示する
DjangoとReactのパスを紐付ける
プロジェクト名/urls.py
(省略) #以下の1行を追加
from django.conf.urls import include
urlpatterns = [
#以下の2行を追加
path('api/', include('api.urls')),
path('authen/', include('djoser.urls.jwt')),
]
- 'api/'にアクセスがあった際に'api/urls'を参照する (アプリケーション名ディレクトリにurls.pyを作成)
- 'authen/'にpostメソッドでメールアドレスとパスワードを渡すとjwtのトークンを返す
アプリケーション名/urls.py
from rest_framework import routers
from django.urls import path
from django.conf.urls import include
router = routers.DefaultRouter()
urlpatterns = [
path('', include(router.urls)),
]
MEDIA_URLとMEDIA_ROOTを追加
settings.py
from django.conf.urls.static import static
from django.conf import settings
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
データベースの作成
models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin
import uuid
def load_path_video(instance, filename):
return '/'.join(['video', str(instance.title)+str(".mp4")])
def load_path_thum(instance, filename):
ext = filename.split('.')[-1]
return '/'.join(['thum', str(instance.title)+str(".")+str(ext)])
class UserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Email address is must')
user = self.model(email=self.normalize_email(email), **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, password):
user = self.create_user(email, password)
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
class User(AbstractBaseUser, Permissions Mixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
email = modelsEmailField(max_length=255, unique=True)
username = modelsCharField(max_length=255, blank=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
def __str__(self):
return self.email
class Video(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
title = models.CharField(max_length=255, unique=True)
video = models.FileField(blank=False, upload_to=load_path_video)
thum = models.ImageField(blank=False, upload_to=load_path_thum)
like = models.IntegerField(defualt=0)
dislike = models.IntegerField(defualt=0)
def __str__(self):
return self.title
- AbstractBaseUserとBaseUserManagerはuseModelの編集に必要
- uuidはuniversal unique idの略で自動で生成される数字のpkを文字列に変える?
- 継承元のBaseUserManagerにはcreateuserやcreatesuperuserが予め定義されているがuser.nameをベースとして認証になっているので, user.emailをベースに認証するようにオーバーライドする
- normalize_emailは渡されたメールアドレスを正規化して小文字にしている
- primary_key=Trueはuuidで生成される文字列をpkとして扱う宣言になり、editableは編集を不可にする宣言
- mediaディレクトリの中に'video'というサブディレクトリを作成し、選択された動画タイトル名と拡張子(.mp4)をつけて保存する
- サムネイル(thum)は拡張子が今後変わる可能性があるので、拡張子を抜き出す(split関数で引数に渡されたfilenameの後ろから初めにヒットする.(ドット)までを取り出し、ext変数に代入)
- mediaディレクトリにサブディレクトリとして'thum'を作成し、動画タイトル名と.(ドット)とext(取り出した拡張子)を結合して保存
admin siteの構築
admin.py
(省略) #以下の3行を追記
from .models import Video, User
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.translation import gettext as _
class UserAdmin(BaseUserAdmin):
ordering = ['id']
list_display = ['email', 'password']
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Personal Info'), {'fields': ('username',)}),
(
_('Permissions'),
{
'fields': (
'is_active',
'is_staff',
'is_superuser',
)
}
),
(_('Important dates'), {'fields': ('last_login',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')
}),
)
admin.site.register(User, UserAdmin)
admin.site.register(Video)
- admin.pyにadmin siteで管理したいモデルをimportし、admin.site.registerの引数に管理したいモデルを渡すとadmin siteで管理可能になる
- UserAdminだとuser.nameを元に認証するのでuser.emailを元に認証するようにオーバーライドさせる
シリアライザーの作成
serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from .models import User, Video
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = ('email', 'password', 'username', 'id')
extra_kwargs = {'password': {'write_only': True, 'min_length': 5}}
#渡されたパスワードをハッシュ化して保存している
def create(self, validated_data):
user = get_user_model().objects.create_user(**validated_data)
return user
class VideoSerializer(serializers.ModelSerializer):
class Meta:
model = Video
fields = ['id', 'title', 'video', 'thum', 'like', 'dislike']
- アプリケーションディレクトリ?の直下にserializer.pyを作成
- metaでserializerを装飾することができる?
- create_userはmodels.pyで定義していたUserManagerのcreate_userメソッドの事
- ser_passwordメソッドは引数に渡されたパスワードをハッシュ化している
views.pyの作成
views.py
from rest_framework import viewsets
from rest_framework import generics
from .models import Video
from .serializers import VideoSerializer, UserSerializer
class CreateUserView(generics.CreateAPIView):
serializer_class = UserSerializer
permission_classes = (AllowAny, )
class VideoViewSet(viewsets.ModelViewSet):
queryset = Video.objects.all()
serializer_class = VideoSerializer
- Userを新規で作成するviewを作成する
- 動画の一覧の取得や作成、削除などを取り扱うviewを作成する
- genetics(汎用api)のCreateAPIViewを継承したCreateUserViewを作成
viewsとurlsを紐づける
urls.py
(省略) #以下を追加
from .views import VideoViewSet, CreateUserView
router = routers.DefaultRouter()
router.register('videos', VideoViewSet)
urlpatterns = [
path('create', CreateUserView.as_view(), name='create'),
path('', include(router.urls)),
]
- VideoViewSetをrouterに追加