0
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?

ツイートアプリを作成しながら学習 1-1【Django + React】

Last updated at Posted at 2025-01-26

初めに

こんにちは。
Django + React でツイートアプリみたいなのを作り
学んだことをアウトプットしていこうと思います。
長くなると思うので、投稿は何個かに分けます。

転職活動も始まり、ポートフォリオとして活用できる頑張ります。
間違ってる部分などあればコメントで教えてください。

目次

ざっくりこんな感じで進めていきますが、
追加機能の追加など変わっていくと思います。

1.リポジトリを新規作成

2.Docker / Devcontainer セットアップ

3.Djangoプロジェクト初期化

4.User関連をセットアップ

5.JWT 認証 + CORS 設定

リポジトリを新規作成

この辺は説明いりませんね。
tweetapp_ver1みたいな名前つけて作ってください。

Gitの初期化

git init
このコマンドで、このフォルダがGitリポジトリとして扱われるようになります。

.git/ という隠しフォルダが作られて、そこにGitの管理情報が入ります。
ここから先、ファイルを作ったり修正したら「git add」「git commit」でバージョン管理できるようになます。

.gitignore というファイルを作る

tweetapp_ver1/  touch .gitignore

このファイルに「Gitで追跡したくないファイルやフォルダのパターン」を書いていく。
いったんこんな感じで。


# python関連
__pycache__/
*.pyc
db.sqlite3

# Devcontainer関連
.devcontainer/*.log

# Node関連
node_modules/

環境変数ファイル
.env

Docker / Devcontainer セットアップ

Visual Studio CodeのDevContainer機能を使う場合、.devcontainer/ というディレクトリを作り、その中に設定ファイルをまとめましょう。

tweetapp_ver1/ mkdir .devcontainer
cd .devcontainer
tweetapp_ver1/.devcontainer/ touch devcontainer.json
// .devcontainer/devcontainer.json

{
    "name": "TweetApp Devcontainer", //なんでもいい

    "build": {
        "dockerfile": "Dockerfile",
        // Dockerfileのパス ここ読んでbuildします
        // (相対パス指定なので同じ.devcontainerフォルダ内にあるDockerfileを使う)

        "context": ".."
        // Dockerイメージをビルドするときのコンテキスト(作業ディレクトリ)
        // ".." を指定することで1つ上のフォルダ(=tweetapp_ver1ルート)全体がビルド対象になる
    },

    "customizations": {
        "vscode": {
            // VSCode拡張機能やエディタ設定をコンテナ内で使えるようにする
            "extensions": [
                "ms-python.python",
                "ms-python.vscode-pylance",
                "dbaeumer.vscode-eslint",
                "msjsdiag.vscode-react-native"
            ]
        }
    },

    "forwardPorts": [
        8000,
        3000
    ],
    // コンテナ内のポート 8000 (Django)  3000 (React開発サーバー) 
    // ホスト側にフォワードするための設定

    "remoteUser": "vscode"
    // VSCode推奨のコンテナ内ユーザー
}

tweetapp_ver1/.devcontainer/ touch Docekrfile
# .devcontainer/Dockerfile

FROM mcr.microsoft.com/vscode/devcontainers/python:3.10
# DevContainer用のPython3.10イメージをベースにする

# apt-get で必要なものを入れる
RUN apt-get update && apt-get install -y \
    curl \
    iputils-ping \
    # Redisなどを使うならここでredis-serverもインストール可
    && curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
    && apt-get install -y nodejs

# Pythonパッケージをインストール
RUN pip install --upgrade pip
RUN pip install django \
    djangorestframework \
    django-cors-headers \
    djangorestframework-simplejwt \
    channels \
    channels-redis

# 必要に応じてnpmでグローバルインストールするものがあればここで
# たとえば、create-react-appとかyarnとか
# RUN npm install -g yarn

# ホントはここで "COPY ./frontend/package*.json /tmp/frontend/" して
# RUN cd /tmp/frontend && npm install 
# みたいな記述を入れることもあるけど、
# DevContainer内で直接 "cd frontend && npm install" でもOK。(僕はこれ)

apt-getは、Debianとその派生物(Ubuntu、Linux Mintなど)で使用されるコマンドラインツールで、パッケージの管理を行うために使用されます。
apt-getを使用すると、ソフトウェアパッケージを検索、インストール、更新、削除することができます。また、依存関係の解決やパッケージのバージョン管理などの機能も提供されます。

Curlとは、コマンドラインからHTTPリクエストを送信するためのオープンソースのライブラリやツールのことです。

apt-get install -y nodejs
Node.jsも入れる。Reactの開発やnpm実行に必要。

pip install django ... channels-redis
Django本体やRESTFramework、JWT、Channelsなどを一括インストール。
channels-redis があるとRedisを使ったWebSocket通信のバックエンドをサポート。

コミットタイミングの例

ここまでの手順が終わったら、いったんGitにコミットしましょう。
現場のチーム開発では、ある程度意味のある単位でコミットするのがおすすめらしいです。
今回「プロジェクト初期化 + DevContainer設定」が1つの大きなまとまり。

tweetapp/ git add .
tweetapp/ git commit -m "chore: initialize project and devcontainer setup"

chore: は機能変更ではないが、環境や設定に関する変更をまとめるときに使われる。
もし feat:, fix:, docs: など機能追加やバグ修正、ドキュメント更新ならそちらを。
"initialize project and devcontainer setup" → やったことを簡潔に要約。
コミットメッセージは英語にしている現場も多いけど、日本語で書くチームもある。
そこはプロジェクトの方針に合わせてOK。
大事なのは**「何をやったか」**がパッと分かるようにすること。

ここまで出来たら vscodeの左下にある><みたいなマークから『新しい開発コンテンツ』を押してコンテナをbuildしてください。コンテナ内に入り開発していきましょう!

Djangoプロジェクト初期化

tweetapp_ver1/ mkdir backend
tweetapp_ver1/ cd backend
tweetapp_ver1/backend/ django-admin startproject backend .

すると以下のような構造になるはずです。

twitter-clone/
  ├─ .devcontainer/
  ├─ backend/
  │   ├─ backend/
  │   │   ├─ __init__.py
  │   │   ├─ asgi.py
  │   │   ├─ settings.py
  │   │   ├─ urls.py
  │   │   └─ wsgi.py
  │   ├─ manage.py
  └─ ... (その他は後で追加)

settings.pyの基本設定

backend/backend/settings.py を開きます。
ここにDjangoのメイン設定が書かれています。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # ここに追加予定
    # 'rest_framework',
    # 'corsheaders',
    # 'channels',
    # など
]

以下にも corsheaders.middleware.CorsMiddleware を追加予定。
CORS(クロスオリジン)を扱うときに必須。ReactとDjangoが別ポートで動く場合など。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # ここに 'corsheaders.middleware.CorsMiddleware', など
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

以下についても、デフォルトはSQLite。試験的に使うなら十分。
本番運用するならPostgreSQLやMySQLを選ぶことが多い。
とりあえず開発ではSQLiteでOK。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

ユーザーがアップロードした画像を保存するときに MEDIA_ROOT を指定する。
MEDIA_URL でURLのパスを定義している。

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

Django Channels を使うときに必要。
ここで 'backend.asgi.application' をASGIエントリポイントとして設定する。

WSGI_APPLICATION = "backend.wsgi.application" #これは初めからある
ASGI_APPLICATION = 'backend.asgi.application'

初回マイグレーション & 動作確認

設定が終わったら、一旦マイグレーションしてDBを準備します。
DevContainerが起動していれば以下コマンドをコンテナ内ターミナルで叩けるはずです。

tweetapp_ver1/backend/ python manage.py migrate

開発サーバーを起動してみましょう

tweetapp_ver1/backend/ python manage.py runserver 0.0.0.0:8000

ブラウザ開いて以下のように表示されたら成功です。

image.png

Git commitしよう

今回のステップだと、「Djangoプロジェクトを初期化して最低限の設定を入れた」 がまとまりになります。

tweetapp_ver1/ git add .
tweetapp_ver1/ git commit -m "feat: create Django project." #みたいな

User関連をセットアップ

この辺りでGitHubに共有もわすれずに

GitHubリポジトリの作成 & ローカル連携

まずはGitHubリポジトリをつくって、
今作業しているローカルリポジトリをプッシュできるようにしましょう。

1.1 GitHubリポジトリを作る

  • GitHubにアクセスしてログイン
  • Repositories から "New" をクリック
  • "Repository name" に "twitter-clone" とか「好きなリポジトリ名」を入力
  • "Public" or "Private"を選択
  • "Create repository" ボタンを押す
  • すると、「リモートリポジトリのURL」が表示される
  • だいたい https://github.com/ユーザー名/リポジトリ名.git みたいなやつ

1.2 ローカルリポジトリをGitHubにPush
今のローカルプロジェクトを、作ったリポジトリにPushする。

# 1) ローカルで現在いるブランチを確認
git branch

# 2) GitHubのリポジトリを"origin"という名前で追加
git remote add origin "https://github.com/ユーザー名/リポジトリ名.git"

# 3) プッシュ
git push -u origin main

DjangoでカスタムUserモデルを作るための準備

新しいアプリを作る
慣例的に"users"とか"accounts"といった名前でアプリを作ることが多いです。
わたしはusersですすめます。

tweetapp_ver1/backend/ python manage.py startapp users

settings.py で INSTALLED_APPS に追加

backend/backend/settings.py の INSTALLED_APPS に "users" を追加。

AUTH_USER_MODELの設定

デフォルトの django.contrib.auth.models.User を使わず、
独自Userを使うときは、settings.py に

# backend/backend/settings.py

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    ...
    "users",  # カスタムユーザーアプリ
]

AUTH_USER_MODEL = "users.User"

これは「ユーザーモデルはusersアプリのUserクラスを使いますよ」という宣言。

注意: プロジェクトで最初のマイグレーションより前に設定しておく必要があります。
既にマイグレート済みの場合でも、まだ実際にUserを使っていないなら大丈夫だけど、
本番運用後に変えるのは本当に面倒になります。

AbstractUserを継承したUserモデルの実装

# backend/users/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models

# Create your models here.

class User(AbstractUser):
    
    # DjangoのAbstractUserを継承したカスタムUserモデル。
    # AbstractUserは username, email, first_name, last_name, password など
    # すでに多くのフィールドが定義されている。
    
    # 追加でプロフィール画像や自己紹介欄などを1つのモデルにまとめてもOK
    # 例:
    # profile_image = models.ImageField(upload_to='profiles/', null=True, blank=True)
    # bio = models.TextField(blank=True)

    # ここに追加フィールドを書ける
    pass

    def __str__(self):
        return self.username
    #のように書くと、このUserインスタンス(レコード)を文字列として扱う際には
    # "ユーザー名(self.username)" が返ってくるということになる。
    #もし__str__を定義しないと、User object (1)みたいな、ちょっと味気ない表記になる。
    #__str__を定義すると、管理画面やデバッグ時のprintなどに、
    # usernameが表示されるようになり、分かりやすいというメリットがある。

なぜAbstractUserを使うかというと

  • AbstractUser は DjangoのデフォルトUser機能(認証、パスワードハッシュ、権限管理など)をまとめて持っている
  • それを継承するだけで「username」「email」「password」などのフィールドが使えるし、さらに自由にフィールド追加できる
  • AbstractBaseUser を使う場合は「usernameやpasswordなどを自分で定義する」必要があるため、より細かい制御ができる代わりに設定が大変
  • 今回は「標準のusernameとか使いたい+プロフィール情報を同じテーブルにまとめたい」ならAbstractUserが楽
  • django.contrib.auth.modelsの標準Userでもよいが、後で追加フィールドするときにsignal作ったりと面倒に感じたので、今回はCustomで試す

admin.py の編集

# backend/users/admin.py

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from.models import User
#.は同じ階層のという意味

# Register your models here.

@admin.register(User)
class CustomUserAdmin(UserAdmin):

    # カスタムUserモデルを管理画面で扱うためのAdmin設定
    # Django標準のUserAdminを継承すると、基本的なUIが確保される。

    pass

こうするとDjango管理画面でもusers.Userが表示されるようになり
あとは管理画面にアクセスしたときに、標準Userと同じような画面が利用可能です。

マイグレーション & テスト実行

rm db.sqlite3
python manage.py makemigrations
python manage.py migrate

スーパーユーザー作成

管理画面で確認するためにスーパーユーザーを1人作っておきましょう。

python manage.py createsuperuser

フォローする情報(username, email, passwordなど)を聞かれるので答えます。
emailは無視。
成功すると、管理画面(http://localhost:8000/admin/)にログイン。

Git コミットしておきましょう

JWT 認証 + CORS 設定

ここでは以下をやります。

Django REST Framework と JWT を settings.py に追加

まずは backend/backend/settings.py を開いて、下記のように修正します。

INSTALLED_APPS = [
    # 既存のDjango公式アプリ
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",

    # 追加するアプリ
    "rest_framework",           # Django REST Framework
    "corsheaders",             # CORSヘッダ処理
    "rest_framework_simplejwt", # JWT

    # 自作アプリ
    "users",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",

    # CORS (フロントからのリクエストを許可)
    "corsheaders.middleware.CorsMiddleware",

    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

なぜここに書くのか

  • INSTALLED_APPS に書くことで、Django がそれらのアプリを認識してマイグレーションなどを正しく処理できる。
  • MIDDLEWARE は、HTTPリクエストが Django に届いたとき、どんな順番で処理するかを指定するリスト
  • corsheaders.middleware.CorsMiddleware を入れると、別ドメイン・別ポートからのアクセスを受け入れたり、ヘッダーに Access-Control-Allow-Origin: ... を含められるようになる

この記事を読んでCORSを理解してください。

REST_FRAMEWORK の設定

同じく settings.py の下のほうに、REST_FRAMEWORK の設定をしましょう。

from datetime import timedelta

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ],
    "DEFAULT_PERMISSION_CLASSES": [
        "rest_framework.permissions.IsAuthenticatedOrReadOnly",
    ],
}

SIMPLE_JWT = {
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=30),
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
}
  • "DEFAULT_AUTHENTICATION_CLASSES" で JWT 認証をメインに使う宣言
  • "DEFAULT_PERMISSION_CLASSES" は「認証されてない場合は読み取り専用だけ許可、書き込み系は不可」にする設定
  • API 全体に対してのデフォルト方針なので、後ほど個別ビューで上書きできる
  • "SIMPLE_JWT" でアクセストークンとリフレッシュトークンの有効期限を定義
  • ここではアクセストークン 30分、リフレッシュトークン 1日としているけど、好みに合わせて変えてOK

JWTの記事とリフレッシュトークンの記事も用意してます。

CORSの設定

# backend/backend/settings.py

CORS_ALLOW_ALL_ORIGINS = False
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",
    "http://localhost:3001",
]
  • CORS_ALLOW_ALL_ORIGINS = True だと「あらゆるオリジンからのリクエストを許可」
  • ポート3000でReactを動かすことが多いので、必要なら上記のように localhost:3000 だけ許可

ユーザー登録 & JWTトークン発行のエンドポイントを作る

"カスタムUserモデル" を使っているので、「新規ユーザーを登録するView」を1つ作ります。

# backend/users/views.py

from rest_framework import generics, permissions
from.models import User
from.serializers import UserCreateSerializer

# Create your views here.

class UserCreateView(generics.CreateAPIView):
    # 新規ユーザーを作成するだけのAPI

    queryset = User.objects.all()
    serializer_class = UserCreateSerializer
    permission_classes = [permissions.AllowAny]
  • generics.CreateAPIView は「POST でデータを作るAPI」のための汎用ビュー
  • 下でUserCreateSerializer というシリアライザーを後で作る
  • permission_classes = [permissions.AllowAny] で、認証なしでもユーザー登録できるようにする

この記事読むとDRFの全体像なんとなくつかめますよ

# backend/users/serializers.py

from rest_framework import serializers
from.models import User

class UserCreateSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True)

    class Meta:
        model = User
        fields = ["id", "username", "email", "password"]

    def create(self, validated_data):
        # create_user() でパスワードをハッシュ化して登録できる
        user = User.objects.create_user(
            username = validated_data["username"],
            email = validated_data.get("email", ""), #emailは任意。メソッド呼び出し
            password = validated_data["password],
        )
        return user

ルーティング設定

# backend/users/urls.py

from django.urls import path
from.views import UserCreateView

urlpatterns = [
    path('users/register/', UserCreateView.as_view(), name="user_register"),
]
  • "api/users/register/" というURLで登録できるようにするイメージ。
    実際には "backend/urls.py" で include("users.urls") してあげる必要がある。

JWT発行APIの設定

"rest_framework_simplejwt" には、以下のようにトークン発行用の標準ビューがあります。

# backend/backend/urls.py

from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("users.urls")),
    path("api/token/", TokenObtainPairView.as_view(), name="token_obtain_pair"),
    path("api/token/refresh/", TokenRefreshView.as_view(), name="token_refresh"),
]
  • "api/token/" に POST すると "username" と "password" を送って access token と refresh token が返ってくる
  • "api/token/refresh/" に POST すると、リフレッシュトークンを使って新しいアクセストークンを取得できる

Gitコミット忘れずに!

今日はここまで。ありがとうございました。

0
1
1

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
0
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?