LoginSignup
4
5

More than 1 year has passed since last update.

Docker+Django+Next+TypeScript+ECSでアプリを作った話(2) ~ Djangoの初期設定からModel作成編 ~

Last updated at Posted at 2021-07-11

はじめに

前回の続きです。

前の記事: Docker+Django+Next+TypeScript+ECSでアプリを作った話(1) ~ 準備編 ~
次の記事: Docker+Django+Next+TypeScript+ECSでアプリを作った話(3) ~ Djangoのスキーマ作成からデータ取得まで~

今回はDjangoプロジェクトの初期設定から、Model作成までを書きました。  
バックエンドとしてDjangoを採用しましたが、DjangoをAPI化するのにgraphene-djangoを採用しました。  
graphene-djangoに関しては以下の記事を参考にさせていただきました。

1.初期設定

開発・テスト環境用と本番環境用のsettingsを作成します。また、本番環境用にSECRET_KEYを管理する.envを作成し、django-environというパッケージにて管理します。

myProject/
    app/
+   .env
        app/
        settings.py
+          dev_settings.py
+          prod_settings.py
        ...
  • settings.py  

全ての開発環境で共通する部分を記載しています。SECRET_KEYやDEBUGは環境別settingsに記載しています。
ALLOWED_HOSTSは環境ごとに変更できるように、環境変数を入れています。
その他に、graphene-djangoとDBの設定の変更や、言語設定を日本語に、タイムゾーンを日本に変更しています。
また、AUTH_USER_MODELを後に作成するUserモデルに設定しています。

settings.py
+ import os
  from pathlib import Path

  BASE_DIR = Path(__file__).resolve().parent.parent
-
- SECRET_KEY = '1234567890**************************'
- DEBUG = True 
- 
- ALLOWED_HOSTS = []
+ ALLOWED_HOSTS = [os.environ['ALLOWED_HOST']]
 
  INSTALLED_APPS = [
      'django.contrib.admin',
      'django.contrib.auth',
      'django.contrib.contenttypes',
      'django.contrib.sessions',
      'django.contrib.messages',
      'django.contrib.staticfiles',
+     'graphene_django',
+     'corsheaders',
+     'api.apps.ApiConfig',
  ]

  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_ORIGIN_WHITELIST = [
+     os.environ['FRONT_URI']
+ ]
+
+ GRAPHWL_JWT = {
+     'JWT_VERIFY_EXPORATION': False,
+ }
+
+ GRAPHENE = {
+     'SCHEMA':
+         'app.schema.schema',
+         'MIDDLEWARE': [
+             'graphql_jwt.middleware.JSONWebTokenMiddleware',
+         ],
+ }
+ 
+ AUTHENTICATION_BACKENDS = [
+     'graphql_jwt.backends.JSONWebTokenBackend',
+     'django.contrib.auth.backends.ModelBackend',
+ ]

  ROOT_URLCONF = 'app.urls'

  TEMPLATES = [
      {
          'BACKEND': 'django.template.backends.django.DjangoTemplates',
          'DIRS': [],
          'APP_DIRS': True,
          'OPTIONS': {
              'context_processors': [
                  'django.template.context_processors.debug',
                  'django.template.context_processors.request',
                  'django.contrib.auth.context_processors.auth',
                  'django.contrib.messages.context_processors.messages',
              ],
          },
      },
  ]

  WSGI_APPLICATION = 'app.wsgi.application'

  DATABASES = {
      'default': {
-          'ENGINE': 'django.db.backends.sqlite3',
+          'ENGINE': 'django.db.backends.postgresql',
-          'NAME': BASE_DIR / 'db.sqlite3',
+          'NAME': os.environ['DB_NAME'],
+          'USER': os.environ['DB_USER'],
+          'HOST': os.environ['DB_HOST'],
+          'PORT': 5432,
       }
  }

  AUTH_PASSWORD_VALIDATORS = [
      {
          'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
      },
      {
          'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
      },
      {
          'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
      },
      {
          'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
      },
  ]

- LANGUAGE_CODE = 'en-us'
+ LANGUAGE_CODE = 'ja'
-
- TIME_ZONE = 'UTC'
+ TIME_ZONE = 'Asia/Tokyo'

  USE_I18N = True

  USE_L10N = True

  USE_TZ = True
+
+ AUTH_USER_MODEL = 'api.CustomUser'

  STATIC_URL = '/static/'
+
+ STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  • dev_settings.py

開発環境、テスト環境用のsettingsを記載しています。
SECRET_KEYは本番環境とは異なる値にしています。

dev_settings.py
from .settings import *

DEBUG = True

SECRET_KEY = '1234567890**************************'
  • prod_settings.py

本番環境用のsettingsを記載しています。
SECRET_KEYはdjango-environにて.envに記載しています。

dev_settings.py
import os
import environ

from .settings import *

env = environ.Env()
env.read_env(os.path.join(BASE_DIR, '.env'))

DEBUG = False

SECRET_KEY = env('SECRET_KEY')
  • .env  
    本番環境用のSECRET_KEYを記載しています。
.env
SECRET_KEY=abcdefghijk************************

2.Model作成

UserモデルとProfileモデルを作成します。
apiフォルダ直下のmodels.pyに記載します。

  • models.py
    UserモデルはDjango標準のAbstractBaseUserをオーバーライドして作成します。

    email、passwordでの認証にするため、name属性はProfileモデルに分け、Userモデルは必要最低限の属性にしています。
models.py

from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin

class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None):

        if not email:
            raise ValueError('email is must')

        user = self.model(email=self.normalize_email(email))
        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 CustomUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(max_length=50, unique=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

    objects = CustomUserManager()

    USERNAME_FIELD = 'email'

    def __str__(self):
        return self.email

class Profile(models.Model):
    nickname = models.CharField(max_length=20)
    user = models.OneToOneField(
        CustomUser, related_name="profile",
        on_delete=models.CASCADE
    )

    def __str__(self):
        return self.nickname

  • admin.py
    Django標準の管理画面にてUserモデルとProfileモデルをCRUD操作できるように記載します。
admin.py
from django.contrib import admin
from .models import CustomUser, Profile

admin.site.register(CustomUser)
admin.site.register(Profile)

  • docker-compose.yml
    Djangoコンテナ起動時に、開発環境用のsettingsを使用するように修正します。
docker-compose.yml
  version: "3"

  services:
    app:
      build:
        context: .
      ports:
        - "8000:8000"
      volumes:
        - ./app:/app
-      command: sh -c "python manage.py migrate &&
-       python manage.py runserver 0.0.0.0:8000"
+      command: sh -c "python manage.py migrate --settings app.dev_settings &&
+       python manage.py runserver 0.0.0.0:8000 --settings app.dev_settings"
      environment:
        - FRONT_URI=http://localhost:3000
        - ALLOWED_HOST=localhost
        - DB_HOST=db
        - DB_NAME=app
        - DB_USER=postgres
        - DB_PASS=supersecretpassword
      depends_on:
        - db
・・・

以下のコマンドにて、DBを更新して、管理者ユーザーを作成します。

$ make migrate
$ make admin

コンテナを再起動して、localhost:8000/adminにアクセスします。
コンテナが正しく動作していれば、管理者用のログインページが表示されるので、
先ほど作成した管理者にてログインします。
ログインすると、UserモデルとProfileモデルのCRUD操作ができるページに遷移します。

Django管理者後のログインページ

まとめ  

今回はsettingsの修正と UserモデルとProfileモデルの作成をしました。
次回はDjangoをGraphqlとして、スキーマを作成して、ブラウザでデータを取得する所までを書きたいと思います。

次の記事: Docker+Django+Next+TypeScript+ECSでアプリを作った話(3) ~ Djangoのスキーマ作成からデータ取得まで~

4
5
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
4
5