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

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

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


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

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

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

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

プロジェクト名（ここでは`myproject`）と同じ名前のベースディレクトリがあり、その中に同名の設定ディレクトリができています。
（ちなみに、学習に使用している[現場で使える Django の教科書《基礎編》](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Django-%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8%E3%80%8A%E5%9F%BA%E7%A4%8E%E7%B7%A8%E3%80%8B-%E6%A8%AA%E7%80%AC-%E6%98%8E%E4%BB%81/dp/4802094744)の書籍の中では、区別がつきやすいよう

- ベースディレクトリ＝`django-admin startproject` で作られるプロジェクトのディレクトリ
- 設定ディレクトリ＝`settings.py`のあるディレクトリ

と呼んでいるので、その呼び方に準じることにします）

<br>
で、構造をわかりやすくするために設定ディレクトリの方の名前を変えてやる方法があります。

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

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

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

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

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



## （２）ユーザーモデルを拡張しておく
DjangoにはデフォルトでUserモデルが用意されており、簡単にログイン機能などが実装できるようになっています。
それだけでとても便利なのですが、よりUserモデルを使いやすくするために、カスタマイズしたユーザーモデルを作っておくことが[公式でも推奨](https://docs.djangoproject.com/ja/2.0/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project)されているようです。

Userモデルを拡張する主な方法は３種類あって、

1.`AbstractBaseUser`を継承する
2.`AbstractUser`を継承する
3. 別モデルを作って`OneToOneField`で関連させる

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

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

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

①`app/models.py`に記述（`app`はアプリケーションディレクトリ名）
②`config/settings.py`に`AUTH_USER_MODEL`を設定（`config`はプロジェクト作成時に作られる設定ディレクトリの名前）
③ マイグレートする


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

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

```python3:app/models.py
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参照）

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

```python3:config/settings.py
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`がアプリケーション名）のコマンドで作られるやつ

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

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

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

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



## おわりに

一通りチュートリアル的な教材を終えた後は、途中にも紹介させていただきましたが

[現場で使える Django の教科書《基礎編》](https://www.amazon.co.jp/%E7%8F%BE%E5%A0%B4%E3%81%A7%E4%BD%BF%E3%81%88%E3%82%8B-Django-%E3%81%AE%E6%95%99%E7%A7%91%E6%9B%B8%E3%80%8A%E5%9F%BA%E7%A4%8E%E7%B7%A8%E3%80%8B-%E6%A8%AA%E7%80%AC-%E6%98%8E%E4%BB%81/dp/4802094744)

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

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

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

```python3: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'
```