今時、YoutubeやInstagramレベルのクオリティを個人で作れて当たり前な時代な中どうやってウェブサイトを作られているのか疑問に思ったことはありませんか?そこで実際InstagramやYoutubeでも使われたとされるDjangoを利用して簡易的なSNSを制作する方法をご紹介します。動画投稿サイトの制作スキルをもとに作られています。これをもとに応用すればYoutubeと同レベルのサービスを展開することも可能です。
今回は僕が忘れないようにメモとして残します。
Table of Contents
使用環境
今回使う環境は以下の通りです。
- Django ==3.2
- python ==3.7.3
- Visual Studio Code
デフォルトではsqlite3ではありますが、このサイトの骨組みを利用して実際にサービスとして公開していく場合はsqlite3はDjango公式が非推奨しています。そのためmysqlやPostgreSQLを採用することをオススメします。django3.2ではdjango2.2と違いモジュールsixが反応しなかったり、staticfileの指定の仕方が異なっているので注意が必要です。
sixが反応しないのは、djangoが3.0にする際「今時python2 使う人なんていないしpython公式もpython3にしろとか言っているから2と3を繋げるmodule sixはいらないよね?」と言った感じでdjangoから消されたみたいです。もっともmodule six が必要な場面はSNSログインを実装するときに起きるぐらいなので、あまり遭遇するとは思いませんが…
実装するもの
SNSを作るにしてもサービスは何をどうゆう風に作っていくのかを考える必要があります。
今回は非常にシンプルなものとして文字だけを入力して画像を一枚まで投稿できる機能にしましょう。リストとしてはこんな感じです。
- ユーザー作成機能
- ログイン機能
- ユーザー作成にて行うメール送信機能
- 投稿機能
の4つの機能を実装しましょう、これを抑えればInstagramやTwiiterなどのSNSや果てはYoutubeと同等レベルのものが作れます。この記事ではユーザー作成までを行います。
では実装してきましょう
プロジェクトの作成
まず好きなところにVisual Studio Codeで開きます。その次にメニューバーのターミナルからNew Terminal をクリックすると楽にコマンド入力ができます。
$ django-admin startproject プロジェクト名
入力が完了すると以下の画像のようにフォルダと必要なファイルが作成されます。
これだけでもサイトの確認が可能で以下のコマンドを入力することで実際に仮想環境で表示することが可能です。
$python manage.py runserver
これを入力すると以下の返事が返ってきます。
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
January 16, 2022 - 03:41:37
Django version 3.2.11, using settings 'blog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
特に「You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.」は赤文字で返ってきてびっくりすると思いますが、これは「まだデータベースを登録してないよ大丈夫?」という確認です。そのため無視しても問題はありません。
Starting development server at http://127.0.0.1:8000/
のところの「http://127.0.0.1:8000/」をコピー&ペーストもしくはMacだと⌘押しながらクリックを押すと以下のサイトが出てくると思います。
アプリケーションの作成
ターミナルから下記のコマンド入力するだけで簡単にできます。このラインより上のエリアが無料で表示されます。
$ django-admin startapp アプリ名
すると以下のようなフォルダとファイル名が表示されます。
ここからはアプリケーションを作成していく上で必要なものを実装するものを書いていきます。
UserModelのカスタマイズ
Models.pyを開いてユーザー作成およびログインの仕方を変更させます。
デフォルトだとユーザー名+パスワードですがセキュリティの観点からメールアドレス +パスワードに変更させます。
コードとしては以下の通りになります。
rom django.db import models
# Create your models here.
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin,UserManager
from django.contrib.auth import get_user_model
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy
from django_countries.fields import CountryField
from django.utils import timezone
import random ,string
# Settings User Login
User = get_user_model
def user_random_url():
chars = '0123456789abcdefghijkmnopqrstuvwxzyABCDEFGHIJKLMNOPQRSTUVWXYZ'
url = ''.join(random.choice(chars)for i in range(28))
return url
GENDER_CHOICES = (
('1', 'Woman'),
('2', 'Man'),
)
class CustomUserManager(UserManager):
"""ユーザーマネージャー"""
use_in_migrations = True
def _create_user(self, email, password, **extra_fields):
if not email:
raise ValueError('The given email must be set')
email = self.normalize_email(email)
user = self.model(email=email, **extra_fields)
user.set_password(password)
user.save(using=self._db)
return user
def create_user(self, email, password=None, **extra_fields):
extra_fields.setdefault('is_staff', False)
extra_fields.setdefault('is_superuser', False)
return self._create_user(email, password, **extra_fields)
def create_superuser(self, email, password, **extra_fields):
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
if extra_fields.get('is_staff') is not True:
raise ValueError('Superuser must have is_staff=True.')
if extra_fields.get('is_superuser') is not True:
raise ValueError('Superuser must have is_superuser=True.')
return self._create_user(email, password, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(('Email_Adress'),unique=True)
name = models.CharField(('Username'),max_length=50,unique=True,)
icon = models.ImageField('Icon',upload_to='media/user/icon', null=True ,blank=True)
profile = models.TextField('Profile',blank=True)
birthday =models.DateField('Birthday',null=True,default=timezone.now)
create =models.DateField('Create',null=True,default=timezone.now)
slug = models.SlugField(unique = True,blank=True,default=random_url)
country = CountryField('Where you live')
gender = models.CharField("Gender", max_length=2, choices=GENDER_CHOICES, blank=True)
creater = models.BooleanField('creater',default=False, help_text='If you publick game movie, colud you check on the checkbox')
def get_absolute_url(self):
return reversed('user', kwargs={'slug': self.slug})
is_staff = models.BooleanField(
('staff status'),
default=False,
help_text=(
'Designates whether the user can log into this admin site.'),
)
is_active = models.BooleanField(
('active'),
default=True,
help_text=(
'Designates whether this user should be treated as active. '
'Unselect this instead of deleting accounts.'
),
)
date_joined = models.DateTimeField(('date joined'), default=timezone.now)
objects = CustomUserManager()
EMAIL_FIELD = 'email'
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = ('user')
verbose_name_plural = ('users')
def email_user(self, subject, message, from_email=None, **kwargs):
"""Send an email to this user."""
send_mail(subject, message, from_email, [self.email], **kwargs)
Loginの機能の追加
ログインを行うにあたってまずsettings.pyを開きログイン画面をどこに設定するのかとログイン後どこのページに飛ぶのかを設定します。場所自体はどこでもいいのでご自身がわかりやすいところで書けば問題ないです。
LOGIN_URL='practice:login'
LOGIN_REDIRECT_URL = 'practice:top'
このままやっても「practice:login」や「practice:top」の[practice]なんてないよと返されるので、settings.pyのフォルダにあるurl.pyに[practice]をしていきます。
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('prctice.urls')),
]
これで指定が完了したので、アプリケーションフォルダにurls.pyを作って接続できるようにします。
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('practice.urls')),
]
views.pyがあるフォルダにurls,pyファイルを作り記述します。
from django.urls import path
from . import views
app_name ='practice'
urlpatterns =[
path('',views.Top.as_view(),name='top'),
path('login/',views.Login.as_view(),name='login'),
path('logout/',views.Logout.as_view(),name='logout')
]
Login用の入力項目を作成する
views.pyから入力する事も可能ですが、見やすさのためforms.pyというファイルを作成してコードを書いていきます。
from django.contrib.auth.forms import (AuthenticationForm)
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__( *args, **kwargs)
for field in self.fields.values():
field.widget.attrs['class'] = 'form-control'
field.widget.attrs['placeholder'] = field.label
LoginページとLogoutページの所在をviews.pyに書き込む
views.pyでLoginページとLogoutページのhtmlがどこにあるのかを伝えます。views.pyはmodels.pyと連携させて投稿したものの視聴回数やコメントを追加させたりが可能です。
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import(LoginView,LogoutView)
from django.views import generic
from.forms import LoginForm
# Create your views here.
class Top(generic.TemplateView):
template_name ='page/top.html'
class Login(LoginView):
form_class = LoginForm
template_name ='page/login.html'
class Logout(LogoutView):
template_name='page/top.html'
Loginページを作成する
全ページにメニューバーの表示とcssやjavascriptを読み込む場所を作成する
表示する場所を指定したのであとはページを表示させますが、一つ一つのページが違ったデザインならまだしもホームボタンなど、どんなページでも必ず表示される項目を設定する必要があります。そこでbase.htmlを作り、一回限りに絞ります。{% if user.is_authenticated %}を使用して、ログイン済みならログアウト用のリンクを、そうでなければログイン用のリンクを表示させています。templatesフォルダを作成しhtmlを書いていきます。
<!DOCTYPE html>
<head>
<meta charset= "utf-8">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light " >
<a class ="navbar-brand" href="{% url 'practice:top'%}">
Top
</a>
<div class="collapse navbar-collapse">
</ul>
</div>
<div class="collapse navbar-collapse" id="navbarSupportedContent"></div>
<ul class="navbar-nav card col-md-3">
{% if user.is_authenticated %}
<li>
<li class="nav-item dropdown outline-info">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<H4>{{ user.email}}</H4>
</a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
<a class="dropdown-item"href="#">Mypage</a>
<a class="dropdown-item" href="{% url 'practice:logout' %}">Logout</a>
</div>
</li>
</li>
{% else %}
<li class="nav-item">
<a class="nav-item btn btn-outline-info" href="{% url 'practice:login' %}">
Login
</a>
</li>
{% endif %}
</ul>
</nav>
<div class="position">
{% block content %}{% endblock %}
</div>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<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.0/umd/popper.min.js"
integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"
integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"
crossorigin="anonymous"></script>
</body>
Topページの作成
次にトップページの作成を行います。まだ何も設定する予定はないのでシンプルに書きます。
{% extends "base.html"%}
{% block content %}
{% load static %}
{% endblock %}
Loginページの作成
次にログインページの作成を行います。bootstrap使いながら実装します。
{% extends "base.html"%}
{% block content %}
<head>
<meta charset="utf-8">
<title>LOGIN</title>
</head>
<h2>UserLogin</h2>
<div class="card col-md-6" id="form1">
<div class="card-body">
<form action="{% url 'practice:login' %}" method="POST">
{{ form.non_field_errors }}
{% for field in form %}
{{ field.label }}<br>
{{ field }}
{{ field.errors }}
<hr>
{% endfor %}
<button type="submit" class="btn btn-success btn-info btn-block" >Login</button>
{% csrf_token %}
</form>
</div>
</div>
</div>
{% endblock %}
Applicationをプロジェクトに認知してもらう
これでページの表示が完了しましたがこのまま起動してもそんなものないよと返されるのでsettings.pyを開き INSTALLAPPSに追加します。ついでにカスタマイズしたログイン方法を伝えます。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'practice'#追加
]
AUTH_USER_MODEL = 'practice.User'#変更したログイン方法を伝える
これで以下の画像の感じでページを見るだけなら完了します。
アカウントを作成する
実際にログインを行う前にまずUserすら登録していないので先にアカウントを作ります。よく使うであろうメールアドレスを記入して仮登録、届いたメールのURLをクリックしたらアカウントが完了する仕組みは別記事に書きますが、今回はデフォルトで入っている管理者用のアカウントを作成します。
データベースを登録する
こういうデーターベースがありますよとsqllite3に登録します。Terminalを開いて以下のコマンドを打ち込みます。
python manage.py makemigrations practice
次にこのコマンドを入力して登録は完了します
python manage.py migrate
エラーがなく動作したら次は管理者アカウントを作成します。以下のコマンドを記入します。
python manage.py createsuperuser
メールアドレスやパスワードを入力したらログインまでは終わりです。実際にログインしてみるとこんな感じになるはずです。
最後に
これでログインまでの機能は終了となります。次はメール送信の機能を取り付けてメール送信からアカウント作成を行ってみたいと思います。