django(pythonのwebフレームワーク)で、単純なSNSを作ってみた(ログインとテキスト投稿のみ)
機能一覧
1, ユーザー管理(ユーザ登録、ログイン、ログアウト)
2, 投稿(テキストのみ)
3, フォロー
4, いいね
5, コメント
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
sns_project/ ← プロジェクト全体(設定・ルートURL管理) ├── sns_project/ ← プロジェクト設定(settings.py, urls.py など) │ └── settings.py ← 設定ファイル ├── sns_app/ ← あなたのSNS機能を担当するアプリ │ ├── models.py ← データベースの定義 │ ├── views.py ← ロジック処理(投稿一覧など) │ ├── forms.py ← フォーム定義 │ ├── urls.py ← sns_app用のURLルーティング │ └── templates/ │ └── sns_app/ ← 投稿一覧や投稿フォームのテンプレート │ ├── index.html │ └── create_post.html ├── templates/ ← 共通テンプレート(base.html、login.html など) │ ├── base.html ← 共通レイアウト(全体の枠組み) │ ├── registration/ │ │ ├── login.html ← ログインページ(認証機能用) │ │ └── logged_out.html ← ログアウト後の表示 │ └── sns_app/ │ ├── index.html ← 投稿一覧ページ(中身) │ └── create_post.html ← 投稿作成ページ(中身) └── manage.py ← 開発用コマンド実行スクリプト |
プロジェクト生成。
1 2 3 |
django-admin startproject sns_project cd sns_project python manage.py startapp sns_app |
settings.pyを修正。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 日本語化しておく # LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'ja' # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Tokyo' USE_I18N = True # 多言語対応する(日本語を表示する) USE_L10N = True # 日本式のフォーマットで表示する(例:日付) USE_TZ = True # UTCで記録、日本時間で表示 # ログイン後はルートにリダイレクト。指定しないとaccounts/profile/にリダイレクトされる LOGIN_REDIRECT_URL = '/' # ログアウトも管理画面のログアウトにリダイレクトされたりするので指定する LOGOUT_REDIRECT_URL = '/' # INSTALLED_APPS に sns_app を追加します。 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'sns_app', # これを追加!! ] |
sns_app/models.py に投稿モデルを定義(ユーザID、投稿テキスト、作成日時)
1 2 3 4 5 6 7 8 9 10 |
from django.db import models from django.contrib.auth.models import User class Post(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) content = models.TextField(verbose_name="本文") created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return f'{self.user.username} - {self.created_at.strftime("%Y-%m-%d %H:%M")}' |
migrateコマンドでDBテーブルを作る
1 2 |
python manage.py makemigrations python manage.py migrate |
sns_app/forms.pyを新規作成。Webフォームを作る
ModelForm を使うと、モデルに基づいたフォームが簡単に作れる!
djangoって、機能アプリ分割しているので、内部のmodels.py, forms.py, views.pyは1ファイルに統合しているのね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# sns_app/forms.py from django import forms from .models import Post from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.models import User # ユーザ登録 class SignUpForm(UserCreationForm): email = forms.EmailField(required=True, label='メールアドレス') class Meta: model = User fields = ("username", "email", "password1", "password2") # 投稿フォーム class PostForm(forms.ModelForm): class Meta: model = Post fields = ['content'] |
既存のsns_app/views.pyに、ビューを書く(投稿一覧、ユーザ登録、投稿フォーム)
laravelのcontrollerは、djangoだとviewって表現するのでややこしい。
laravelのbladeビューは、djangoだとtemplateって表現する!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# sns_app/views.py from django.shortcuts import render, redirect from django.contrib.auth.decorators import login_required from .models import Post from .forms import PostForm from django.contrib.auth import login from django.contrib.auth.forms import UserCreationForm from .forms import SignUpForm # 投稿一覧と投稿フォームを渡す def index(request): posts = Post.objects.all().order_by('-created_at') form = PostForm() if request.user.is_authenticated else None return render(request, 'sns_app/index.html', {'posts': posts, 'form': form}) def signup(request): if request.method == 'POST': form = SignUpForm(request.POST) if form.is_valid(): user = form.save() login(request, user) # 登録と同時にログインさせる return redirect('index') else: form = SignUpForm() return render(request, 'registration/signup.html', {'form': form}) @login_required def create_post(request): if request.method == 'POST': form = PostForm(request.POST) if form.is_valid(): post = form.save(commit=False) post.user = request.user post.save() return redirect('index') else: form = PostForm() return render(request, 'sns_app/create_post.html', {'form': form}) |
sns_appフォルダとsns_projectフォルダの両方のurls.pyに、ルーティングを記述する
1 2 3 4 5 6 7 8 |
# sns_app/urls.py from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), path('create/', views.create_post, name='create_post'), ] |
1 2 3 4 5 6 7 8 9 10 11 |
# sns_project/urls.py に include を追加 from django.contrib import admin from django.urls import path, include from sns_app import views as sns_views urlpatterns = [ path('admin/', admin.site.urls), path('', include('sns_app.urls')), path('accounts/', include('django.contrib.auth.urls')), # ログイン/ログアウト用 path('accounts/signup/', sns_views.signup, name='signup'), ] |
テンプレートを生成
base.htmlが文字通り、大元のテンプレート扱いで、各テンプレートは、それをベースに色々拡張している
ヘッダ・フッタ、ログイン・ログアウトなど全画面で使う表示を定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!-- templates/base.html --> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>単純なSNS</title> </head> <body> {% if user.is_authenticated %} <form action="{% url 'logout' %}" method="post" style="display: inline;"> {% csrf_token %} <button type="submit">ログアウト</button> </form> {% else %} <a href="{% url 'login' %}">ログイン</a> | <a href="{% url 'signup' %}">新規登録</a> {% endif %} <hr> {% block content %}{% endblock %} </body> </html> |
ルート画面(トップ画面)。投稿一覧と投稿フォーム
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!-- sns_app/templates/sns_app/index.html --> {% extends "base.html" %} {% block content %} <h2>投稿一覧</h2> {% for post in posts %} <p><strong>{{ post.user.username }}</strong>: {{ post.content }} ({{ post.created_at }})</p> {% empty %} <p>投稿がまだありません。</p> {% endfor %} {% if user.is_authenticated %} <form method="post" action="{% url 'create_post' %}"> {% csrf_token %} {{ form.as_p }} <button type="submit">投稿</button> </form> {% else %} <p><a href="{% url 'login' %}">ログイン</a>すると投稿できます。</p> {% endif %} {% endblock %} |
ユーザ登録
1 2 3 4 5 6 7 8 9 10 |
<!-- templates/registration/signup.html --> {% extends "base.html" %} {% block content %} <h2>ユーザー登録</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">登録</button> </form> {% endblock %} |
ログイン画面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- templates/registration/login.html --> {% extends "base.html" %} {% block content %} <h2>ログイン</h2> {% if form.errors %} <p style="color:red;">ユーザー名またはパスワードが間違っています。</p> {% endif %} <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">ログイン</button> </form> {% endblock %} |
ログアウト画面
1 2 3 4 5 6 7 |
<!-- templates/registration/logged_out.html --> {% extends "base.html" %} {% block content %} <p>ログアウトしました。</p> <a href="{% url 'login' %}">再ログイン</a> {% endblock %} |
投稿画面。トップ画面に統合しちゃったので不要になった・・・。
1 2 3 4 5 6 7 8 9 10 11 |
<!-- sns_app/templates/sns_app/create_post.html --> {% extends "base.html" %} {% block content %} <h2>投稿する</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">投稿</button> </form> {% endblock %} |
1 2 |
# 開発サーバ起動 python manage.py runserver |