見出し画像

djangoで作る本格的なSNSアプリケーション Part1

djangoで簡単なTodoアプリ作成等は作成できたけれども、オリジナルWebアプリを作る前にもう少しだけ本格的なアプリをチュートリアル形式で取り組みたい方を対象としています。

最近は、djangoの書籍も増えてきてSNSアプリを取り扱う書籍も複数あります。そのどれもが紙面の都合のせいか初心者には説明が不足しており、私も理解するのに苦労しました。はじめに作成するアプリの全体像をしっかりイメージしながらチュートリアル形式で学習する事により、オリジナルアプリ作成時の参考になるように記事を作成していきます。

Part1では、目次の①SNSプロジェクトの開始とアプリの作成と②ユーザーモデルとユーザー登録機能について説明いたします。

目標

 本格的なSNSアプリの作成を通じて以下の機能を実装できるようになります。
クラスベース汎用ビューを前提として開発を進めます。

・カスタムユーザー作成
・ログイン/ログアウトなどの認証
・記事の作成更新削除(CRUD操作)
・ユーザーのお気に入り登録・解除(Follow/Unfollow)
・記事のお気に入り機能(多対多のリレーション)

開発環境

・Python 3.9.4(2021年6月時点で最新)
・django 3.2.4
・Mac M1 Big Sur
・Visual Studio Code
・ブラウザはSafari(もしくはChrome)
・miniforge(ARMアーキテクチャに最適化されているため)
↑は、Windows環境であればAnacondaでも構いません。

前提条件

・Pythonの文法およびdjangoで簡単なWebアプリを作った経験がある。
・HTML,CSS,Bootstrapの基本を理解している。
・ miniforge(WindowsやIntelMacであればAnacondaでも可)がインストールされ、Webアプリ開発用の環境構築が完了しており必要なパッケージをインストール出来る事。

SNSアプリケーションの全体像

 SNSプロジェクトには、1. accounts(主にユーザーと認証関連機能) と2. microposts(投稿モデルとその関連機能)の2つのアプリを作成します。以下の画像でイメージを掴んで下さい。

SNSアプリGIF


SNSアプリ概要.001
SNSアプリ概要.002

開発の手順

 以下の手順でSNSアプリの開発を進める事とします。①から⑤で基本的な機能を実装し、⑥⑦で少し発展的な機能(お気に入りやFollow )を追加していきます。

①SNSプロジェクトの開始とアプリの作成
②ユーザーモデルとユーザー登録機能
③ログイン、ログアウト機能
④ユーザー情報の更新と一覧表示
⑤ポストモデルとCRUD操作
⑥ポストのお気に入り機能追加
⑦ポストのお気に入り機能追加ユーザのFollow/Unfollow機能の追加

Visual Studioの設定

・SQlite
・Rainbow Brackets
・indent-rainbow
・django
・Bootstrap 4, Font awesome 4, Font Awesome 5 Free & Pro snippets
・Material Icon Theme

仮想環境と必要なパッケージ

django_env(=python3.9.4)という仮想環境を作成し、以下のパッケージをインストールします。インストール後のパッケージ一覧は画像を参照下さい。

・django_envという仮想環境を作成し
・django3.2.4
・Pillow
・django-bootstrap4
・bcrypt
・beautifulsoup4

画像17

以上で環境構築までは完了です。以降は、実際にSNSアプリを作成していきます。

①SNSプロジェクトの開始と初期設定

 SNSプロジェクトを開始し、accountsアプリとmicropostsアプリを作成します。まずは、SNSプロジェクトを保存するフォルダーにターミナルで移動します。今回は、django_projectsというフォルダー内にsnsプロジェクトを作成します。

/Users/myname/django_projects
django-admin startproject sns

visual studio codeを開き、以下画像の手順で作成したsnsプロジェクトを開きます。

スクリーンショット 2021-06-06 午後11.17.28 午前

ターミナルで仮想環境をアクティベートし、python manage.py startapp アプリ名でアプリをsnsプロジェクト内に作成します。visual studio codeのエクスプローラーに作成した2つのアプリが表示されている事を確認してください。

conda activate django_env
python manage.py startapp accounts
python manage.py startapp microposts
アプリ作成後のVSC画面

staticフォルダー、templateフォルダー、mediaフォルダー、各アプリのurls.pyファイルなどを最初に作成してしまいます。作成後のVSCodeの画面は以下画像のようになります。

必要フォルダー作成後

設定ファイル(settings.py)の編集

 これまでに作成したアプリ、パッケージ、フォルダーなどが使えるようにsettings.pyを変更していきます。また、メッセージタグも後々使用していきますのでこの場で設定します。まずは、以下をインポートして下さい。

import os #追加
from django.contrib.messages import constants as messages #追加
from django.contrib.messages import constants as message_constants #追加

 次にアプリケーションをINSTALLED_APPSに登録します。ここで、先ほど作成したアプリに加えてbootstrap4とそのjQueryも使えるように設定していきます。

INSTALLED_APPS = [
   'django.contrib.admin',
   'django.contrib.auth',
   'django.contrib.contenttypes',
   'django.contrib.sessions',
   'django.contrib.messages',
   'django.contrib.staticfiles',
   'accounts',  # 追加
   'microposts', # 追加
   'bootstrap4', # 追加
]
# Bootstrap4 jqueryを使用するため追加
BOOTSTRAP4 = {
   'include_jquery': True,
}

 先程作成したtemplatesフォルダーとbootstrap4の設定をTEMPLATESの部分に設定します。

TEMPLATES = [
   {
       'BACKEND': 'django.template.backends.django.DjangoTemplates',
       'DIRS': [BASE_DIR / 'templates'], # 変更
       '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',
           ],
           # Bootstrap4を使用するために追加
           'builtins': [
               'bootstrap4.templatetags.bootstrap4',
           ],
       },
   },
]

 今回は、認証機能も追加していくためパスワードの暗号化設定をしてセキュリティーレベルを強化します。AUTH_PASSWORD_VALIDATORSの上に以下のPASSWORD_HASHERESを追加していきます。既にbcryptはanaconda.orgからインストール済です。

# 追加 パスワードのセキュリティーレベルをアップさせるためにBcryptを使用
PASSWORD_HASHERS = [
   'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
   'django.contrib.auth.hashers.BCryptPasswordHasher',
   'django.contrib.auth.hashers.PBKDF2PasswordHasher',
   'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
]

 言語とタイムゾーン設定を変更します。

LANGUAGE_CODE = 'ja' # 変更
TIME_ZONE = 'Asia/Tokyo' # 変更

 staticフォルダとmediaフォルダの設定を追加します。及びメッセージタグの設定を追加していきます。

# 追加
STATICFILES_DIRS = (
   os.path.join(BASE_DIR, "static"),
)
# 追加 mediaを扱うための設定
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 追加 メッセージタグの設定
MESSAGE_TAGS = {
   messages.ERROR: 'alert alert-danger',
   messages.WARNING: 'alert alert-warning',
   messages.SUCCESS: 'alert alert-success',
   messages.INFO: 'alert alert-info'
}

 最後にsnsプロジェクトのurls.pymediaフォルダを使用するための設定を行います。※この段階では、accountsmicropostsのパスはまだ通さないで下さい。モデルのmigration時にエラーが発生します。

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static  # mediaを使うために追加
from . import settings  # mediaを使うために追加
urlpatterns = [
   path('admin/', admin.site.urls),   
]
# mediaを使うために追加
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

以上で最初の設定は完了となります。次からは、実際にaccountsアプリを作成していきます。

②ユーザーモデルとユーザー登録機能

 この章では、カスタムユーザモデルを作成しユーザー登録機能までを実装していきます。カスタムユーザーモデルを作成するためには、BaseUserManager,とAbstractBaseUserを継承する必要がありますのでimportします。

from django.contrib.auth.models import (
   BaseUserManager, AbstractBaseUser, PermissionsMixin
)
from django.urls import reverse_lazy

 BaseUserManagerを継承した UserManagerクラスを作成します。
if not email:のところでは、ユーザー作成の段階でE-mailが設定されなかった場合はエラーを発生させています。それ以降のところで、パスワード設定、dbへのユーザー保存を実施しています。

class UserManager(BaseUserManager):
   def create_user(self, username, email, password=None):
       if not email:
           raise ValueError('Enter Email') # エラーメッセージ
       user = self.model(
           username=username,
           email=email
       )
       user.set_password(password) # passwordを引数にとってパスワード設定
       user.save(using=self._db) # データベースへユーザーを保存
       return user

 次にカスタムユーザーとしてUserクラスを定義し、AbstractBaseUserPermissionsMixinを継承します。usernameemailは長さを50としユニーク制約を付けています。また、プロフィール画像も登録できるようにしたいため、avatarというフィールドを追加します。ただし、プロフィール画像の登録は任意とするのでblank=True, null = Trueを設定します。
 objectsは、上で作成した UserManagerを指定します。ここで、objectsを設定しないとモデルマネージャーを利用したデータ取得が出来なくなりますので注意して下さい(例えば、User.objects.all()が使えなくなります)。

class User(AbstractBaseUser, PermissionsMixin):
   username = models.CharField(max_length=50, unique=True)
   email = models.EmailField(max_length=50, unique=True)
   is_active = models.BooleanField(default=True)
   is_staff = models.BooleanField(default=False)
   # プロフィール画像をavatarとして設定
   avatar = models.ImageField(blank=True, null=True)  
   EMAIL_FIELD = 'email'
   USERNAME_FIELD = 'username'
   REQUIRED_FIELDS = ['email'] 
   
   objects = UserManager()
   
   def get_absolute_url(self):
       return reverse_lazy('accounts:home')

 作成したカスタムユーザーをプロジェクト内で使用できるようにするため、settings.pyに以下を追加します。

AUTH_USER_MODEL = 'accounts.User'

 ここまででモデル作成は完了しましたので、ターミナルからaccountsアプリのマイグレーションを実施していきます。画像のように成功しましたら、データベースが作成されているかを確認していきます。

python manage.py makemigrations accounts
python manage.py migrate
accountsマイグレーション

VSCodeのエクセスプローラー内のdb.sqlite3を選択し、Open DatabaseをクリックすればSQLITE EXPLORERが開いてuserテーブルが作成され指定したフィールドが設定されていることが確認出来ます。 VSCodeの拡張機能でSQLiteをインストールしていないと確認出来ませんので、インストールがまだの場合はここでインストールをしておきましょう。

マイグレーション結果

汎用クラスベースビューで各Viewを実装する。

モデルが出来たので、次はviews.pyをクラスベースビューで作成していく。まずは、Home画面とユーザー登録画面を作成する。出来上がりは、以下の画像となるのでイメージを掴んで下さい。

画像9
画像10

views.pyHomeViewクラスとRegistUserViewクラスをそれぞれ、Template ViewCreateViewを継承し作成するので、それらをimportしておく。

from django.views.generic.edit import CreateViewことに
from django.views.generic.base import TemplateView


class HomeView(TemplateView):
   template_name = 'accounts/home.html'

class RegistUserView(CreateView):
   template_name = 'accounts/regist.html'
   form_class = RegistForm

template(HTML)は、templates/ accountsフォルダー内に作成していく事にする。ファイル名をhome.htmlregistration.htmlとするため、template_nameにファイルのパスを指定する。settings.pytemplatesフォルダを設定したため、パスを通すときは、templatesフォルダー以下を指定すれば良い。
 RegistUserViewでは、ユーザー登録フォームが必要となるためform_classとしてRegistFormを設定します。forms.pyaccountsフォルダー直下にに作成し、formを作成します。

from django import forms
from .models import User
from django.contrib.auth.password_validation import validate_password

class RegistForm(forms.ModelForm):
   username = forms.CharField(label='ユーザーネーム')
   email = forms.EmailField(label='E-mail アドレス')
   password = forms.CharField(label='パスワード', widget=forms.PasswordInput())
   class Meta:
       model = User
       fields = ['username', 'email', 'password']
   def save(self, commit=False):
       user = super().save(commit=False)
       validate_password(self.cleaned_data['password'], user)
       user.set_password(self.cleaned_data['password'])
       user.save()
       return user

最初の3行で必要なformクラスと自身で設定したUserモデルをインポート、パスワードの長さや複雑さが正しいかどうか検証を行うため、validate_passowrdもインポートします。
 RegistFormクラスは、form.ModelFormを継承して作成します。登録フォームには、usernameemailpassowrdを設定します。この時、emailにはCharFieldではなくEmailFieldを設定しています。EmailFieldは、使用可能なemailかどうかのValidatorが付属していますのでEmailValidationは不要になります。

username = forms.CharField(label='ユーザーネーム')
email = forms.EmailField(label='E-mail アドレス')
password = forms.CharField(label='パスワード', widget=forms.PasswordInput())

class Metaで使用するモデルとフィールドを設定します。ユーザー新規作成時は、プロフィール画像を登録する必要がありませんので、fields内にavatarは設定しません。

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

パスワードを暗号化する必要があるため、保存時の処理をsave以下に記載していきます。validate_passwordで入力されたパスワードを検証し、問題が無ければ暗号化したパスワードを設定し保存します。

    def save(self, commit=False):
       user = super().save(commit=False)
       validate_password(self.cleaned_data['password'], user)
       user.set_password(self.cleaned_data['password'])
       user.save()
       return user

以上でRegistFormが作成できたので、views.pyで使えるようにviews.pyにインポートする。

from django.views.generic.edit import CreateView
from django.views.generic.base import TemplateView
from .forms import RegistForm # 追加

次にurlを通すため、accounts/urls.pyを開き先ほど作成したRegistUserViewHomeViewをインポートする。app_nameaccountsとし、urlpatternsを設定しパスを設定していきます。

from django.urls import path
from .views import (
   RegistUserView,HomeView
)
app_name = 'accounts'
urlpatterns = [
   path('home/', HomeView.as_view(), name='home'),
   path('regist/', RegistUserView.as_view(), name='regist'),
]

プロジェクトのurls.pyaccounts/urls.pyへのパスを通すためsns/urls.pyを開きurlspatternsaccountsへのパスを通します。

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static  # mediaを使うために追加
from . import settings  # mediaを使うために追加

urlpatterns = [
   path('admin/', admin.site.urls),
   path('accounts/', include('accounts.urls')), # 追加
]

パスが通ったため、templateフォルダー内に登録画面(regist.html)やホーム画面(home.html)を作成していきます。ただ、htmlファイルで共通となる部分はbase.htmlとしてtemplatesフォルダーに最初に作成してしまいます。templateフォルダー内に以下のhtmlファイルが作成されている事を確認して下さい。

templatesフォルダ内

base.htmlには共通部分を記載していきますが、共通となる部分は以下の画像で緑枠で囲んだナビゲーションバー部分となります。ログイン前後で表示内容を切り替えられるようにするためif文とis.authenticatedメソッドも用います。html全体はbootstrap4で装飾する事にするためCDNでbootstrapのCSSやjavascriptなどを読み込むところもbase.html内に記載していきます。

画像13



{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
   <title>Microposts</title>
   {# bootstrapのCSS、自作のCSSを読み込む#}
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" 
   integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
   <link rel="stylesheet" href="{% static 'accounts/style.css' %}">
   {% load bootstrap4 %}
</head>
<body>
<header class="mb-4">
   <nav class="navbar navbar-expand-sm navbar-dark bg-dark">
       <a class="navbar-brand" href="#">Microposts</a>
       <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
           <span class="navbar-toggler-icon"></span>
       </button>
       <div class="collapse navbar-collapse" id="navbarSupportedContent">
           <ul class="navbar-nav navbar-left">{% if user.is_authenticated %}
               <li class="nav-item">
                   <a class="nav-link" href="#">All Posts</a>
               </li>
               <li class="nav-item">
                   <a class="nav-link" href="#">All Users</a>
               </li>
               <li class="nav-item">
                   <a class="nav-link" href="#">New post</a>
               </li>
               <li class="nav-item dropdown">
                   <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" 
                   data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                       {{ user.username }}'s detail
                   </a>
                   <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                       <a class="dropdown-item" href="#">My posts</a>
                       <a class="dropdown-item" href="#">Following</a>
                       <a class="dropdown-item" href="#">Follower</a>
                   </div>
               </li>{% endif %}
           </ul>
           <ul class="navbar-nav navbar-right">{% if user.is_authenticated %}
               <li class="nav-item">
                   <a class="nav-link" href="#">Logout</a>
               </li>{% else %}
               <li class="nav-item">
                   <a class="nav-link" href="#">Login</a>
               </li>
               <li class="nav-item">
                   <a class="nav-link" href="{% url 'accounts:regist' %}">Sign in</a>
               </li>{% endif %}
           </ul>
       </div>
   </nav>
</header>
{% block content %}
<!-- ここに各htmlの内容が反映される -->
{% endblock content %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS,Fontawasome -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
       integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
       crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
       integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
       crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
       integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
       crossorigin="anonymous"></script>
<script defer src="https://use.fontawesome.com/releases/v5.7.2/js/all.js"></script>

{% block footer %}
{% endblock footer %}
</body>
</html>

  かなり長いですが、少しづつ説明していきます。先頭の{% load static %}staticフォルダを読み込みます。これをしないとstaticフォルダ内のファイルにアクセス出来ませんので忘れないようにします。<head><title>〜</title>内に自作アプリケーションの名前を設定してください。今回は、Micropostsとしました。
   {# bootstrapのCSS、自作のCSSを読み込む#}以下は、bootstrap4を使用するためのCDNやstaticフォルダ内にこれから作成する自作cssの読み込みを行なっています。ここで忘れないうちにcssファイルを作成してしまいます。cssファイルの場所は、以下の画像で確認してください。
 最後の{% load bootstrap4 %}は、django-bootstrap4パッケージを読み込んでいます。

{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
   <title>Microposts</title>
   {# bootstrapのCSS、自作のCSSを読み込む#}
   <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" 
   integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
   <link rel="stylesheet" href="{% static 'accounts/style.css' %}">
   {% load bootstrap4 %}
</head>
cssファイル作成

<body>内も長く複雑に見えますが、bootstrap4のNavbarを使っているので長く見えるだけです。詳細は、本家の日本語リファレンスに譲り、ここではviewから受け取ったデータを表示する部分とどのようなデータをtemplateで表示するかについて説明を加えていきます。

<body>
<header class="mb-4">
   <nav class="navbar navbar-expand-sm navbar-dark bg-dark">
       <a class="navbar-brand" href="#">Microposts</a>
       <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
           <span class="navbar-toggler-icon"></span>
       </button>
       <div class="collapse navbar-collapse" id="navbarSupportedContent">

ここまでは、Navbarの設定ですので説明は省略します。

           <ul class="navbar-nav navbar-left">
           {% if user.is_authenticated %}
               <li class="nav-item">
                   <a class="nav-link" href="#">All Posts</a>
               </li>
               <li class="nav-item">
                   <a class="nav-link" href="#">All Users</a>
               </li>
               <li class="nav-item">
                   <a class="nav-link" href="#">New post</a>
               </li>
               <li class="nav-item dropdown">
                   <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" role="button" 
                   data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                       {{ user.username }}'s detail
                   </a>
                   <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
                       <a class="dropdown-item" href="#">My posts</a>
                       <a class="dropdown-item" href="#">Following</a>
                       <a class="dropdown-item" href="#">Follower</a>
                   </div>
               </li>
           {% endif %}
           </ul>
           <ul class="navbar-nav navbar-right">
           {% if user.is_authenticated %}
               <li class="nav-item">
                   <a class="nav-link" href="#">Logout</a>
               </li>
           {% else %}
               <li class="nav-item">
                   <a class="nav-link" href="#">Login</a>
               </li>
               <li class="nav-item">
                   <a class="nav-link" href="{% url 'accounts:regist' %}">Sign in</a>
               </li>
            {% endif %}
           </ul>

 ログインの前後で画像のように表示を変える処理を設定していきます。

ログイン前後

 最初の<ul></ul>タグで囲まれたところは、ログインが完了した後にナビゲーションバーに表示させたい項目を記載しています。ログイン完了後の判定は、djangoのis_authenticationファンクションとif文を使用します。
 {% if user.is_authenticated %}{% endif%}で表示/非表示を切り替えたい部分を記載します。ログイン状態であれば、これから作成していく事になるMy postsや詳細ページへのリンクを表示させます。今の段階では、いずれのhtmlも作成していませんのでhref="#"としてリンク先は記載しません。
 <a></a>タグで囲まれた部分で{{ user.username }}'s detailとしてユーザーが設定したusernameを呼び出します。usernameichiroであれば、ichiro's detailと表示されます。
 次の<ul></ul>タグ内にも{% if user.is_authenticated %}〜{% endif%}で表示/非表示を切り替えます。ログインされている場合には、Logoutを表示します。ログインされていない場合はLoginSign inを表示させるよう{% else %}内にそれぞれのリンクを<a>タグで作成します。regist.htmlはすでに作成していますのでdjango template languageの特殊な記法を用いてhref属性でリンクを貼ります。
 href="{% url 'accounts:regist' %}” とすることで、accounts/urls.py で設定したアプリケーションの名前空間app_name='accounts'と詳細ビューの名前空間name='regist'をころん:で繋ぎ設定する事でRegistUserViewへリンクを貼ることが出来ます。RegistUserViewには、templateとしてregistration.htmlが設定されていましたので、実際に表示される画面はregistration.htmlとなります。

urls使い方
class RegistUserView(CreateView):
   template_name = 'accounts/regist.html'
   form_class = RegistForm

<body>内の</header>以降には、各htmlの内容を反映させる部分を{% block content %}{% endblock content %}として記載します。

</header>
{% block content %}
<!-- ここに各htmlの内容が反映される -->
{% endblock content %}

最後は、jQueryやjavascriptを読み込みます。Navbarはjqueryとjavascriptが必要になりますので、記載を忘れると動作しなくなってしまいます。また、お気に入りボタンとしてfontawesomeを使いますのでここで読み込みます。

<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS,Fontawasome -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
       integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
       crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
       integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
       crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
       integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
       crossorigin="anonymous"></script>
<script defer src="https://use.fontawesome.com/releases/v5.7.2/js/all.js"></script>

これでbase.htmlが完成しました。かなり長くなってしまいましたが、ほとんどがbootstrap4やそのナビゲーションバーを使う為の定型文ですのでbootstrapの部分は少し分からなくても問題ありません。base.htmlが出来ましたので、次はhome.htmlを作成していきます。コードと実際の画面は以下のようになります。

{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
   <div class="center jumbotron">
       <div class="text-center">
           <h1>Welcome to the Microposts</h1>
           <a class="btn btn-outline-secondary btn-sm"
                          href="{% url 'accounts:regist' %}"
                          role="button">Sign in Now!</a>
       </div>
   </div>
{% endblock %}
画像16

    最初の{% extends 'base.html' %}base.htmlを読み込んでいます。次の{% load bootstrap4 %}では、django-bootstrap4を読み込みます。ここまではお決まりとして、以降の全てのtemplate(html)で使用していきます。
 次の{% block content %}{% endblock %}内にhome.htmlで実際に表示させたいhtmlをコーディングしていきます。

   <div class="center jumbotron">
       <div class="text-center">
       
       </div>
   </div>

この部分は、bootstrap4のjumbtronを利用しています。Welcome to the Micropostsという文字を<h1>で強調表示させています。

<a class="btn btn-outline-secondary btn-sm"
    href="{% url 'accounts:regist' %}" 
    role="button">Sign in Now!</a>

<a>タグで囲まれた部分は、bootstrapのアウトラインボタンを配置してhref属性と{% url %} テンプレートタグでリンクを貼ります。登録画面に遷移させるため、href="{% url 'accounts:regist' %}” とし、RegistUserView(regist.html)を表示させます。
 以上でhome.htmlの作成は完了ですので、ターミナルからpython manage.py runserverを入力し正しく画面が表示されるかを確認して下さい。以下の画面が表示されれば成功です。今の時点では、Sign in Now!ボタンを押しても真っ白な画面が表示されるだけですが、アドレスバーにhttp://127.0.0.1:8000/accounts/regist/が表示されている事を確認して下さい。

画像17

 regist.htmlを作成していきます。コードと実際の画面は以下のようになります。

{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
   <div class="row">
       <div class="col-sm-6 offset-sm-3">
           <h1>ユーザー登録</h1>
           <form method="post">
               {% csrf_token %}
               {% bootstrap_form form %}
               <button type="submit" class="btn btn-primary" name="button">
                   ユーザー登録
               </button>
           </form>
       </div>
   </div>
{% endblock %}
画像18

 先ほどのhome.htmlと同じですが、最初の{% extends 'base.html' %}base.htmlを読み込み次の{% block content %}{% endblock %}内にregist.htmlで実際に表示させたいhtmlをコーディングしていきます。

   <div class="row">
       <div class="col-sm-6 offset-sm-3">
           <h1>ユーザー登録</h1>
           ・・省略・・
       </div>
   </div>

 この部分は、bootstrap4の設定となります。ユーザー登録という文字を<h1>で強調表示させています。

<form method="post">
    {% csrf_token %}
    {% bootstrap_form form %}
     <button type="submit" class="btn btn-primary" name="button">
         ユーザー登録
     </button>
</form>

 <form>タグで囲まれた範囲にユーザー登録フォームを設置しています。サーバ側のデータを更新するフォーム送信しますので、method="post"となります。
djangoでは、postフォームには全て{% csrf_token %}テンプレートタグを使いますので、セットで覚えておきます。
 次の{% bootstrap_form form %}は、django-bootstrap4のテンプレートタグになります。これから表示させるformにbootstrap4を適用します。これだけでaccounts/forms.pyRegistFormで設定したusername, email, passowrdフィールド全てが表示されます。

class RegistForm(forms.ModelForm):
   username = forms.CharField(label='ユーザーネーム')
   email = forms.EmailField(label='E-mail アドレス')
   password = forms.CharField(label='パスワード', widget=forms.PasswordInput())

これでregist.htmlは完成です。サーバーを立ち上げて、http://127.0.0.1:8000/ accounts/regist/  にアクセスして下さい。正しく表示されたでしょうか?
 これまでの設定が正しいのか確かめるためにも、試しにユーザー登録をしてみてください。まだログイン後の画面を作成していませんので、ログインは出来ませんが問題が無ければユーザーは登録されるはず。

スクリーンショット 2021-06-08 午後00.03.06 午前

各フィールドのバリデーションが正しく作動するかも確認してみましょう。
E -mailフィールドに@を含まない不適切な設定を行います。以下のようにメールアドレスを入力して下さい。というエラーメッセージが表示されます。 

エラーメッセージ

 次に同じユーザーを登録してみましょう。usernameemailにはユニーク制約を付けていましたので、登録が出来ないようになっています。画像のように、ユーザーネーム、Eーmailアドレスどちらにも以下のエラーメッセージが正しく表示されるのを確認して下さい。
 このUsernameを持ったUserが既に存在します。
 このEmailを持ったUserが既に存在します。

class User(AbstractBaseUser, PermissionsMixin):
   username = models.CharField(max_length=50, unique=True)
   email = models.EmailField(max_length=50, unique=True)
エラーメッセージ2

以上で②ユーザーモデルとユーザー登録機能までが完了しました。次は、クラスベースビューを用いたログイン、ログアウト機能を実装していきます。




この記事が気に入ったらサポートをしてみませんか?