2020-05-22に更新

Djangoで開発を始める時にやっておくと良いこと

はじめに

Djangoで開発を始める時にやっておいたほうが良いこと。
後々になると変更が難しくなるので、必ずではないのですが初期段階のうちに設定しておいた方が良いことがあります。
「settings.pyが入っている設定ディレクトリ名の設定」「拡張ユーザーモデル」 を作っておくことです。

他にも色々あるかもしれませんが、現段階での備忘録として。

この記事の対象者

  • Djangoのチュートリアル的な教材を一通り終えたぐらいで、これからアプリ制作を始めようという人
  • ディレクトリ構造やプロジェクト、アプリケーションについて基礎知識を整理しておきたい人
  • ユーザーモデルをちょろっとカスタマイズしたいという人

(1)プロジェクト制作時に設定ディレクトリの名前を変更しておく

これは、プロジェクトのディレクトリ構造をわかりやすくするための措置です。
普通に$ django-admin startproject myprojectなとしてプロジェクト制作を始めると、ベースディレクトリ名と設定ディレクトリ名が同じ名前(ここではmyproject)で作られるため区別がつきづらい、という弊害が起こってしまいます。

どういうことかというと、このコマンドでプロジェクト制作を始めると、こんなディレクトリ構造になります。

myproject   <- ベースディレクトリ
|-- manage.py
`-- myproject  <- 設定ディレクトリ
    |-- __init__.py
    |-- settings.py
    |-- urls.py
    |-- manage.py
    `-- wsgi.py

プロジェクト名(ここではmyproject)と同じ名前のベースディレクトリがあり、その中に同名の設定ディレクトリができています。

(ちなみに[現場で使える Django の教科書《基礎編》]の書籍の中では、区別がつきやすいよう
- ベースディレクトリ=django-admin startproject で作られるプロジェクトのディレクトリ
- 設定ディレクトリ=settings.pyのあるディレクトリ
と呼んでいるので、その呼び方に準じることにします)

これだとわかりづらいので、設定ディレクトリの方の名前を変えてやる方法があります。

プロジェクト作成時に、ベースディレクトリを任意の名前(プロジェクト名)で作成した後にベースディレクトリの下へ移動し、第一引数に設定ディレクトリ名(ここではconfig)、第二引数に.を指定してstartprojectを実行します。

$ mkdir myproject(ここにプロジェクト名を入れます)
$ cd myproject/
$ django-admin startproject config .

これを実行することで、ディレクトリ構造は

myproject   <- ベースディレクトリ
|-- manage.py
`-- config  <- 設定ディレクトリ *この名前が変わる
    |-- __init__.py
    |-- settings.py
    |-- urls.py
    |-- manage.py
    `-- wsgi.py

このようになり、わかりやすい構成になるわけです。

(2)ユーザーモデルを拡張しておく

DjangoにはデフォルトでUserモデルが用意されており、簡単にログイン機能などが実装できるようになっています。
それだけでとても便利なのですが、よりUserモデルを使いやすくするために、カスタマイズしたユーザーモデルを作っておくことが公式でも推奨されているようです。

Userモデルを拡張する主な方法は3種類あって、
- 1.AbstractBaseUserを継承する
- 2.AbstractUserを継承する
- 3. 別モデルを作ってOneToOneFieldで関連させる

それぞれのやり方で長所短所があるのですが、
開発が進んでデータベースに既にデータがある場合は3のやり方が良さそうです。
アプリ制作を始める最初の段階なら1か2推奨ですが、1は入門〜初心者には難しそうなので、初めは2のやり方で慣れるのが無難かなというところです。

2の方法では、ユーザーモデルに独自のカラムを追加することができます。

カスタムユーザーモデルの作り方は簡単これだけ。

  1. models.pyに記述
  2. settings.pyAUTH_USER_MODELを設定
  3. マイグレート

app/models.pyに記述(appはアプリケーションディレクトリ名が入る)

ここにカスタムユーザーモデルを定義します。(AbstractUserというモデルにユーザーモデルのベースとなる情報が定義されているので、それを継承して使う形になります)

ここでテーブル名や、追加したいカラムを指定することができます。
例えば、custome_userというテーブルに外部キーを用いたCalendarというカラムを設定できます。

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    class Meta(AbstractUser.Meta):
        db_table = 'custom_user'
        swappable = 'AUTH_USER_MODEL'

    calendar = models.ForeignKey(Calendar, verbose_name='アクティブカレンダー', on_delete=models.PROTECT, blank=True, null=True)

これは僕の実際のコードの例なので、ユーザーモデルにCalendarというモデルを紐づけていますが、例えばユーザーモデルに性別や年齢、メールアドレス、パスワードなどを持たせたいときには、ここでカラム指定してあげると良いかと思います。
デフォルトのUserモデルにカラムを追加する形になるので、もともとあるカラムも残ります(Userモデルの詳細はMEMO参照)

config/settings.pyAUTH_USER_MODELを設定(configはプロジェクト作成時に作られる設定ディレクトリの名前)

ここに使用するカスタムユーザーモデルを定義します。「アプリ名.モデル名」のように記述します

AUTH_USER_MODEL = app.CustomUser

③マイグレート

モデルを変更したら定番のデータベースのマイグレート。

$ Python manage.py makemigrations
$ Python manage.py migrate

Django入門向けのいろいろ

バージョンについて

どのバージョンを指定するかですが、LTS (Long-Term Support)(=長期間のセキュリティサポートがあるバージョンのこと)に該当するバージョンを採用すると間違いがなさそう。

  • 2.2 => 2022年4月までサポート
  • 1.11 => 2020年4月までサポート

ちなみに2019年12月にDjango3系が出ているのですが、LTSに当たる3.2は2021年の4月にリリース予定とのこと。

特に理由がなければ現時点で最新のLTSで始めておけば間違いがないのかな?という印象です。
(もしデプロイ先が決まっているなら、そのサーバーが対応しているかなどは調査の必要があるのかも)

プロジェクトとアプリケーションについて

Djangoには「プロジェクト」と「アプリケーション」という概念があるので、少し整理しておきます。

プロジェクト
WEBサービスの全体のプロジェクト
$ django-admin startproject myproject
myprojectがプロジェクト名)のコマンドで作られる
 
 
アプリケーション
小さな機能のひとまとまり
python3 manage.py startapp myapp
myappがアプリケーション名)のコマンドで作られる

プロジェクトが大きな箱で、アプリケーションはその箱の中に機能ごとに分けて作られるイメージです。

1つのWEBサービスを作る際に1つのプロジェクトを作り、小さなサービスならその中に1つのアプリケーションを作成して実装していきます。
プロジェクトの規模が大きくなってくると、1つのプロジェクトに複数のアプリケーションを作って運用することもできます。

  • アプリケーションは他のアプリケーションとなるべく依存しないように作るとよい
  • プロジェクトにアプリケーションを次々と追加する形で機能を追加していくのが基本スタイル

複数のアプリケーションを作る場合は、このように考慮するとメンテナンスがしやすそうですね。

おわりに

一通りチュートリアル的な教材を終えた後は

現場で使える Django の教科書《基礎編》

こちらの本がとてもわかりやすく勉強が捗ります!

基礎的なことが広く網羅されていて、知識の定着や理解のために良いです。つまみ食いしながら読んでもよし。
今回まとめたようなベストプラクティスな手法が多く掲載されていて、かなり重宝しております。

MEMO:デフォルトのUserモデルについて

参考までに、Django 2.1.7で確認したUserモデルのフィールドを記載。
デフォルトのUserモデルはAbstractUserモデルを継承している形になるので、AbstractUserモデルのカラム名が、カスタムユーザーモデルを作った際にも引き継がれます。

lib/django/contrib/auth/models.py

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username and password are required. Other fields are optional.
    """
    username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _('username'),
        max_length=150,
        unique=True,
        help_text=_('Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[username_validator],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    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 = UserManager()

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

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')
        abstract = True

    def clean(self):
        super().clean()
        self.email = self.__class__.objects.normalize_email(self.email)

    def get_full_name(self):
        """
        Return the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    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)


class User(AbstractUser):
    """
    Users within the Django authentication system are represented by this
    model.

    Username and password are required. Other fields are optional.
    """
    class Meta(AbstractUser.Meta):
        swappable = 'AUTH_USER_MODEL'
Originally published at qiita.com
ツイッターでシェア
みんなに共有、忘れないようにメモ

Massa

北海道でアプリ制作に取り組んでるノンプログラマな農夫。仕事や日常生活で感じる小さな不便を解消すべく趣味と実益を兼ねて遊んでます ■Python・GAS + LINE bot

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント