DjangoでSNSアプリを作る – その1

その1では、プロジェクトの作成から、テンプレートの作成までを行います。


プロジェクト作成

デスクトップにmyappディレクトリを作成し、その中にプロジェクトを作ります。

$ cd desktop
$ mkdir myapp

VScodeでmyappを開き、VScodeのターミナルで仮想環境を作ります。

ついでに、pipとsetuptoolsのアップグレードもしておきます。

$ python -m venv env
$ source env/bin/activate

(env)$ pip install --upgrade pip setuptools

Djangoをインストールします。

(env)$ pip install django

さらに必要なパッケージを一気にインストールします。

  • django-allauth
    ログイン等の認証関係を手軽に実装するためのパッケージ
  • django-autoslug
    URLにslugを使うための便利パッケージ
  • pillow
    画像を扱うためのパッケージ。ImageFieldを使う場合は必要です。
(env)$ pip install django-allauth django-autoslug pillow

念の為に、pipの依存関係をチェック

(env)$ pip check
No broken requirements found.

「sampleapp」プロジェクトと「sns」アプリを作ります。

(env)$ django-admin startproject sampleapp

(env)$ cd sampleapp
(env)$ python manage.py startapp sns

基本設定

settings.pyでアプリの追加、日本化、必要であればデータベースの設定もします。

# snsとallauth関係を追加
INSTALLED_APPS = [
    'sns', # snsを追加
    ...
    'django.contrib.sites', # 追加
    'allauth', # 追加
    'allauth.account', # 追加
    'allauth.socialaccount', # 追加
]

# allauthに必要なので追加
AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'allauth.account.auth_backends.AuthenticationBackend',
]

# 日本語に変更
LANGUAGE_CODE = 'ja'

# タイムゾーンを変更
TIME_ZONE = 'Asia/Tokyo'

# staticを使うのに必要
STATIC_URL = 'static/'
STATICFILES_DIRS = [BASE_DIR / 'static']

#メディアルーティング
MEDIA_ROOT =  BASE_DIR / 'media'
MEDIA_URL = 'media/'

# allauthに必要
SITE_ID = 1

# allauthをMyUserモデルに対応させる
AUTH_USER_MODEL = 'sns.MyUser'

AUTH_USER_MODELのMyUserに関しては次の項目でモデルを定義します。


ユーザーモデルの設定

django-allauthを使う場合は、初回migrate前にユーザーモデルを定義しておかないといけないので作成します。

django-allauthはDjangoのUser、AbstractUser、AbstractBaseUserを元にユーザーモデルが作成されるので、AbstractBaseUserモデルを定義する。

AbstractBaseUserは、少し難しく感じるかもしれませんが自由にフィールドを設定できるので使い慣れておくと良いです。Django公式でもAbstractBaseUserの使用を薦めています。

作成するフィールド

Twitter風のSNSを作るので次のフィールドを作成します。

  • ユーザーネーム(@usernameの部分)
  • email
  • ニックネーム
  • 誕生日
  • プロフィール画像
  • webサイトのurl
  • 自己紹介
  • 登録日
  • スラッグ(usernameをurlに使う)
  • フォロー
  • フォロワー
from django.db import models
from django.contrib.auth.models import BaseUserManager, PermissionsMixin, AbstractBaseUser
from django.core.validators import MinLengthValidator, RegexValidator

from autoslug import AutoSlugField

class MyUserManager(BaseUserManager):
    def create_user(self, username, email, password=None):
        if not username:
            raise ValueError('Users must have an username')
        if not email:
            raise ValueError('Users must have an email address')

        user = self.model(
            username=username,
            email=self.normalize_email(email),
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, username, email, password):
        user = self.create_user(
            username=username,
            email=self.normalize_email(email),
            password=password,
        )
        user.is_admin=True
        user.is_staff=True
        user.is_superuser=True
        user.save(using=self._db)
        return user

class MyUser(AbstractBaseUser, PermissionsMixin):
    username = models.CharField(verbose_name = 'username', max_length = 50, unique = True, validators=[MinLengthValidator(5,), RegexValidator(r'^[a-zA-Z0-9]*$',)])
    email = models.EmailField(verbose_name = 'Email', max_length = 50, unique = True)
    nickname = models.CharField(verbose_name = 'ニックネーム', max_length = 50, blank = False, null = False)
    date_of_birth = models.DateField(verbose_name = "誕生日", blank = True, null = True )
    image = models.ImageField(verbose_name = 'プロフィール画像', upload_to = "myimage", blank = True, null = True) 
    website = models.URLField(verbose_name = 'webサイト', blank = True, null = True)
    introduction = models.TextField(verbose_name = '自己紹介', max_length = 140, blank = True, null = True)
    date_joined = models.DateTimeField(verbose_name = '登録日', auto_now_add = True )
    
    slug = AutoSlugField(populate_from='username', always_update=True)

    follow = models.ManyToManyField("self", symmetrical=False, blank=True, related_name='follows')
    follower = models.ManyToManyField("self", symmetrical=False, blank=True, related_name='followers')

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_admin = models.BooleanField(default=False)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ["username"]

    def __str__(self):
        return f"{self.nickname}({self.username})"

    def get_absolute_url(self):
        return f"/{self.slug}"

誰かをfollowした時、相手が自分を自動でfollowしてしまわないように、manytomanyフィールドにsymmetrical=Falseとrelated_name=”を設定して非対処にしています。

BaseUserManager

AbstractBaseUserを使うには、BaseUserManagerが必要なので設定します。基本はコピペで大丈夫です。

MyUser内で、objects = MyUserManager()と書き呼び出します。

USERNAME_FIELD

ログインする時のユーザーネームをemailに変えます。

REQUIRED_FIELDS

アカウント作成時に必須の項目を指定。

def get_absolute_url(self):

ユーザーのurlを呼び出す時に使います。


データベースに反映

ユーザーモデルが定義できたので、makemigrationsとmigrateを実行します。

$ python manage.py makemigrations

$ python manage.py migrate

スーパーユーザー作成

メールアドレス、ユーザー名、パスワードは自由に設定してください。

$ python manage.py createsuperuser

Email: *******@mail.com
Username: ******
Password: ********
Password (again): ********
Superuser created successfully.

これでadminページにログインできるようになりました。


adminページに追加

snsのadmin.pyにMyUserモデルを追加します。

MyUserAdminで、MyUserのページを見やすくカスタマイズしています。

from django.contrib import admin

from sns.models import MyUser

class MyUserAdmin(admin.ModelAdmin):
    filter_horizontal = ('follow', 'follower')
    fieldsets = [
        ("基本情報", {'fields': ('username', 'password', 'email', 'nickname', 'date_of_birth','image', 'website', 'introduction')}),
        ('フォロー', {'fields': ('follow',)}),
        ('フォロワー', {'fields': ('follower',)}),
    ]

admin.site.register(MyUser, MyUserAdmin)

http://127.0.0.1:8000/adminにアクセスしログインします。

My Usersという項目があるので、そこで自分のニックネームと自己紹介を編集しておきましょう。


URLの設定

sampleapp/urls.pyに以下の内容に修正してください。

from django.contrib import admin
from django.urls import path, include

#メディアファイル保存
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('allauth.urls')),
    path('', include("sns.urls")),
]

if settings.DEBUG:の部分は「開発環境であれば」settingsのMEDIA_ROOT(media)から画像を探すようになっています。

DEBUG=Trueだとエラーの内容が詳しく表示されるので開発環境でのみTrueに設定し、本番環境ではエラーを詳しく表示するとセキュリティー的に問題なのでFalseに設定します。

snsアプリにurls.pyを作成しpathを設定します。

from django.urls import path
from . import views

app_name = "sns"

urlpatterns = [
    path('', views.index, name = "index"),
]

エラーが分かるように仮想サーバーを起動しておきます。

$ python manage.py runserver

viewの設定

sns/views.pyにindexページのviewを作成しておきます。

from django.shortcuts import render

def index(request):
    context = {}
    return render(request, 'sns/index.html', context)

templatesの設定

snsアプリ内にtemplatesディレクトリを作成。さらにその中にsnsのディレクトリを作成します。

作成したsnsディレクトリの中に、index.htmlを作成します。

<p>hello world</p>

これで、http://127.0.0.1:8000/にアクセスすれば「hello world」が表示されるはずです。


これで最低限の設定ができました。

次は、Twitter風のテンプレートを用意し、ツイート一覧、ツイート機能、フォロー機能を実装していきます。