Djangoプロジェクトの各種初期設定
DjangoのMVCはMTV(モデル、テンプレート、ビュー(実質コントローラー))で「1つの機能 = 1つのアプリケーション」となっています。
そして、複数のアプリを連携させて1つのシステムを構築するスタイルです。
目次
1. インストール & プロジェクトの生成
2. 初期設定(タイムゾーン/データベース/日本語化)
3. 各種コマンド(アプリの作成/シェル)
4. モデル、マイグレーション、テーブルの作成
5. 管理ツール(http://localhost:8000/admin/)
6. デバッグ、ダンプ、SQLの出力
7. アプリケーションの名前空間の設定
8. 組み込みタグとフィルタの一覧
9. ページネーション
10. バリデーションエラーの一覧を表示
11. LEFT JOIN/外部キー/select_related()
12. 共通(templates/static/module/自作フィルタ/パッケージ)
13. 本番環境用の設定
--- 以下はDjango開発に役立つミニ情報。
14. ジェネリックビュー(クラス)の一覧
15. 自作バリデーションの作成(NGワード)
16. 自作バリデーションの作成(クラス用)
17. 自作のログイン機能(@login_required)
18. 自作のログイン機能(LoginRequiredMixin)
19. シーディング(Seeding)
20. AjaxのX-CSRF-TOKENについて
21. 複数のデータベースを使用する
22. よくあるエラーの一覧
23. フロントエンド(Vue.js)
1. インストール & プロジェクトの生成
2. 初期設定(タイムゾーン/データベース/日本語化)
タイムゾーンやデータベースの設定、エラーメッセージなどの日本語化です。
2-1. settings.py
[アプリケーションの登録]
INSTALLED_APPS = [ # ココに作成したアプリケーションを登録していく # ※各アプリのapps.pyに記述されている情報を用いる (例1)書式 : (クラス名.name).apps.(クラス名) 'hello.apps.HelloConfig', (例2)書式 : アプリケーション名 'hello', # Django apps 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
[データベース設定]
次はMySQL/MariaDBの設定方法です。
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'データベース名', 'USER': 'ユーザー名', 'PASSWORD': 'パスワード', 'HOST': 'localhost', 'PORT': '3306', # MySQL/MariaDBの絵文字対応 'OPTIONS': { 'charset': 'utf8mb4', } } }
[日本語化/タイムゾーン]
# エラーメッセージや管理ツールなどが日本語になる LANGUAGE_CODE = 'ja' # タイムゾーン TIME_ZONE = 'Asia/Tokyo'
2-2. MySQL/MariaDBの設定
Django推奨のドライバをインストールする。
pip install mysqlclient
3. 各種コマンド(アプリの作成/シェル)
# アプリケーションの作成 python manage.py startapp アプリケーション名 (例)python manage.py startapp hello # Djangoシェルを起動して任意のコードを実行する python manage.py shell
シェルはDjangoのプロジェクトで定義したクラスやモデルなどをimportして実行可能です。モデルも扱えるのでDB操作が可能です。
4. モデル、マイグレーション、テーブルの作成
Djangoのマイグレーションはモデルの編集から始まります。
4-1. モデル(models.py)の編集
対象アプリケーションのモデル(models.py)にデータ構造を記述する。
[モデルの例]
from django.db import models from django.core.validators import MaxLengthValidator # モデルのクラス名は単数形 class User(models.Model): class Meta: db_table = 'bbs_users' # テーブル名 verbose_name = 'ユーザー' # モデル名 # 名前 name = models.CharField(verbose_name='名前', \ max_length=50, \ validators=[MaxLengthValidator(50)]) # メール email = models.EmailField(verbose_name='メールアドレス', \ max_length=250, unique=True, \ validators=[MaxLengthValidator(250)]) # パスワード password = models.CharField(verbose_name='パスワード', \ max_length=250, \ validators=[MaxLengthValidator(250)]) # 作成日時 ※レコードを作成時に自動設定 created_at = models.DateTimeField(auto_now_add=True) # 更新日時 ※レコードを更新時に自動設定 updated_at = models.DateTimeField(auto_now=True) def __str__(self): return '<id=' + str(self.id) + ', name=' + self.name + '>'
4-2. マイグレーションファイルの作成
python manage.py makemigrations アプリケーション名 (例)python manage.py makemigrations hello ※migrations\0001_initial.pyが作成される
4-3. マイグレーションの実行
python manage.py migrate
4-4. マイグレーション履歴を確認
python manage.py showmigrations
4-5. ロールバック
次のコマンドを実行するとテーブルが削除されますのでご注意ください。ロールバックした際は不要なマイグレーションファイル(migrations/0001_initial.pyなど)が残るので手動で削除します。
python manage.py migrate アプリケーション名 zero (例)python manage.py migrate hello zero
5. 管理ツール(http://localhost:8000/admin/)
# 管理者の作成 python manage.py createsuperuser
次に管理ツールで各モデルを扱えるようにadmin.pyを編集します。
[app/admin.py]
from django.contrib import admin from .models import モデル名 admin.site.register(モデル名)
後はhttp://localhost:8000/adminにアクセスするだけです。
6. デバッグ、ダンプ、SQLの出力
[デバッグ、ダンプ出力]
print()を使用するとコンソール(黒い画面)に出力されます。
print(数値) print('文字列') print(オブジェクト)
[SQLの出力]
7. アプリケーションの名前空間の設定
各アプリケーション毎にurls.pyで「名前空間」(app_name)を設定すれば、「同名の名称」(name='index'など)を区別できます。
[project/books/urls.py]
app_name = 'books' urlpatterns = [ path('', views.index, name='index'), ];
[project/notes/urls.py]
app_name = 'notes' urlpatterns = [ path('', views.index, name='index'), ];
[project/urls.py]
urlpatterns = [ path('books/', include('books.urls')), path('notes/', include('notes.urls')), ]
[テンプレート]
<a href="{% url 'books:index' %}">本</a> <a href="{% url 'notes:index' %}">ノート</a>
8. 組み込みタグとフィルタの一覧
9. ページネーション
10. バリデーションエラーの一覧を表示
<ol> {% for item in form %} {% if item.errors.as_text != '' %} <li>{{ item.name }}:{{ item.errors.as_text }}</li> {% endif %} {% endfor %} </ol>
11. LEFT JOIN/外部キー/select_related()
DjangoはLaravelのように「モデル名::leftJoin」がありません。
LEFT JOINをする為には「モデルで外部キーを使用」する方法か「モデル名.objects.raw(sql)」で生SQLを直に実行します。ただし、raw()を使う場合はfilter()やorder_by()などは使えなくなります。
モデルで外部キーを使ってLEFT JOINをする場合
※実際はINNER JOINとなりますが結果は同じです。
このモデルの標準機能をそのまま使うと、for文でループしている場合はその分だけSQLの発行回数が多くなります。そこで、select_related() (公式)を使用すると最初にメモリに展開させるのでSQL発行回数が減ります。
[使用例]
ビュー | 40行目 |
テンプレート | 55行目以降 |
モデル | Body |
モデル | Question |
※例がややこしくてすみませんです。
models.ForeignKey(to, on_delete, **options)で外部キーを設定します。toはモデル名。on_deleteは外部キー制約です。MySQL/MariaDBのデフォルトはRESTRICTなのでmodels.DO_NOTHINGを指定してもmodels.PROTECTと同様となりますのでご注意ください。
外部キー制約でテーブルのレコードを削除できない場合は、次例を参照。
# MySQL(MariaDB)にログインする mysql -u root -p SHOW DATABASES; use データベース # 無効にする set foreign_key_checks = 0; # テーブルのレコードを全て削除する truncate table テーブル名; # 有効にする set foreign_key_checks = 1;
12. 共通
(templates/static/module/フィルタ/パッケージ)
共通のtemplates/static/module/自作フィルタの設定方法です。
12-1. templates(テンプレート)
主に共通レイアウトやページネーションなどのテンプレートの設定方法。
[settings.py]
import os TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(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', ], }, }, ]
これで「project/project/app/templates」の他に「project/templates」に共通のテンプレートを配置できるようになります。
共通レイアウト
「project/templates/layouts/layout.html」にベースとなるレイアウトファイルを配置して他のテンプレートから呼び出す方法は次のようになります。
{% extends 'layouts/layout.html' %}
共通ページネーション
「project/templates/pagination.html」に配置した場合。
{% include 'pagination.html' %}
12-2. static(静的ファイル)
共通の静的ファイル(CSS/JS/PNG/GIF/JPGなど)の設定方法。
[settings.py]
import os STATIC_URL = '/static/' # プロジェクト共通用 STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), )
これで「project/static」に静的ファイルを配置可能となります。
次は「project/static/css/bootstrap.min.css」の使用方法。
{% load static %} <link rel="stylesheet" media="all" href="{% static '/css/bootstrap.min.css' %}">
12-3. module(モジュール)
共通で使用するモジュールの作成方法です。
project/module/__init__.py |
project/module/common.py |
を作成します。__init__.pyは0バイトでcommon.pyは任意の名称です。
[common.py]
def trim(s): return s.replace(' ',' ').strip()
これを使用するには次のようにインポートするだけです。
from module.common import trim
12-4. カスタムフィルタ(自作フィルタ)
共通で使用するテンプレート用の自作フィルタの作成方法です。
project/app1/templatetags/__init__.py |
project/app1/templatetags/nbsp.py |
__init__.pyは0バイトです。nbsp.pyのファイル名はフィルタ名(nbsp)となります。次はスペース、タブを「 」に変換するフィルタの例です。
[nbsp.py]
from django import template from django.utils.safestring import mark_safe register = template.Library() # スペース、タブを「 」に変換する @register.filter(name='nbsp') def nbsp(value): # RAWになるのでhtmlspecialchars()と同様の処理も付加 value = value.replace('&', '&') \ .replace('"', '"') \ .replace('\'', ''') \ .replace('<', '<') \ .replace('>', '>') \ .replace(' ', ' ') \ .replace(' ', ' ') return mark_safe(value)
[使い方]
{% load nbsp %} <p>{{ item.body|nbsp }}</p>
注意点として「project/app1」で自作フィルタを定義しても「project/app2」など他のアプリでも使用可能です。
12-5. Pythonパッケージの管理
チームなどでパッケージを共有する場合は次のように簡単に共通化できます。
# パッケージの一覧をpackages.txtへ保存する pip freeze > packages.txt # packages.txtを用いてパッケージをインストール pip install -r packages.txt
13. 本番環境用の設定
13-1. settings.py
DEBUGをFalse。ALLOWED_HOSTSにはドメインを設定する。
DEBUG=Flase ALLOWED_HOSTS = ['www.example.com',]
13-2. 本番環境の構築
Djangoの本番環境を構築する [CentOS + uWSGI + Nginx] |
Djangoの本番環境を構築する [CentOS + Gunicorn + Nginx] |
Djangoの本番環境を構築する [Ubuntu + mod_wsgi +Apache] |
14. ジェネリックビュー(クラス)の一覧
ビルトインのクラスベースビュー API (公式)
各クラスの呼び出し方は__init__.pyのソースをみると
from django.views.generic import ListView, CreateView, DetailView from django.views.generic import UpdateView, TemplateView, View
で良い。「django.views.generic.edit」などではなくて良い。
# .editは不要 from django.views.generic.edit import CreateView
15. 自作バリデーションの作成(NGワード)
[project/module/common.py]
from django.core.validators import ValidationError # 禁止用語 # ※各自で追加して下さい NG_WORDS =[ 'カジノ', 'ギャンブル', ] # NGワード def NgWord(value): for ng in NG_WORDS: if value.find(ng) != -1: raise ValidationError('禁止用語が含まれています。')
[使い方]
from module.common import NgWord class Answer(models.Model): name = models.CharField(verbose_name='名前', \ max_length=50, db_index=True, default='', \ validators=[MaxLengthValidator(50), NgWord])
16. 自作バリデーションの作成(クラス用)
ジェネリックビューのクラスベースで開発している場合はバリデーション成功時のform_valid()をオーバーライドしてやると良い。
# バリデーションの成功 def form_valid(self, form): # カスタムバリデーション if form.instance.debit_account_id == form.instance.credit_account_id: form.add_error('debit_account_id', \ '借方と貸方に同一科目は登録できません。') form.add_error('credit_account_id', \ '借方と貸方に同一科目は登録できません。') return self.form_invalid(form) try: with transaction.atomic(): form.save() messages.success(self.request, "登録しました。") except Exception as e: messages.error(self.request, \ "エラーが発生しました。管理者に問い合わせてください。") return redirect('fsjs_journals:index')
debit_account_idとcredit_account_idはフォームの任意のオブジェクトです。form.add_error()でエラーを追加できます。エラー時はフォームバリデーションの失敗であるform_invalid()を返します。
17. 自作のログイン機能(@login_required)
@login_requiredのデコレータによる「リダイレクト機能」の自作とパスワードのハッシュ化、ハッシュ化されたパスワードの認証をする方法です
[デコレータ]
# ログインのリダイレクト用 def login_required (function): def actual_decorator(*args, **kwargs): request = args[0] # ココでリダイレクト処理 if not 'name' in request.session: return redirect('bbs_login:login') # ココまで return function(*args, **kwargs) return actual_decorator
[使い方]
関数の上に@login_requiredを記述するだけです。
def index(request): pass @login_required def create(request): pass @login_required def edit(request, id): pass
[パスワードのハッシュ化と認証]
from django.contrib.auth.hashers import check_password from django.contrib.auth.hashers import make_password # ハッシュ化 xxx=make_password('abc') # 認証チェック if check_password('abc', xxx): print('認証に成功しました。') else: print('認証に失敗しました。')
18. 自作のログイン機能(LoginRequiredMixin
[ミックスイン]
# ログインのリダイレクト用 class LoginRequiredMixin(): def dispatch(self, request, *args, **kwargs): # ココでリダイレクト処理 if not 'name' in request.session: return redirect('bbs_login:login') # ココまで return super().dispatch(request, *args, **kwargs)
[使い方]
class JournalListView(ListView): pass class JournalCreateView(LoginRequiredMixin, CreateView): pass class JournalUpdateView(LoginRequiredMixin, UpdateView): pass class JournalDeleteView(LoginRequiredMixin, View): pass
パスワードのハッシュ化と認証は17章をご覧ください。
19. シーディング(Seeding)
dumpdataでバックアップ、loaddataでDBへ登録となります。
# プロジェクトフォルダに対象のJSONデータ(xxx.json)を作成する python manage.py dumpdata アプリ名.モデル名 > xxx.json # xxx.jsonのデータをインポートする # ※元のデータは消えますのでご注意ください。 python manage.py loaddata xxx.json
20. AjaxのX-CSRF-TOKENについて
Djangoでは「X-CSRF-TOKEN」ではなく「X-CSRFToken」を用います。「X-CSRF-TOKEN」だと「403 (Forbidden)」のエラーとなります。
[テンプレート]
<meta name="csrf-token" content="{{ csrf_token }}">
[JavaScript]
21. 複数のデータベースを使用する
今回は書き込み用(default)と読み込み用(readonly)をご紹介します。
[settings.py]
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'データベース名', 'USER': 'ユーザー名', 'PASSWORD': 'パスワード', }, 'readonly': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'データベース名', 'USER': 'ユーザー名', 'PASSWORD': 'パスワード', } }
書き込み用は通常のコードのままでOKです。読み込み用はdb_manager()でDB設定を指定するだけとなります。
LangType.objects.db_manager('readonly').filter(id=lang_id)
22. よくあるエラーの一覧
22-1. TemplateDoesNotExist at /???/
settings.pyのINSTALLED_APPSにアプリを登録していません。
22-2. Could not parse the remainder: '[0].???' from 'items[0].???'
テンプレートではitems[0]のように添え字でアクセスできません。
22-3. 'ModelFormOptions' object has no attribute 'concrete_fields'
有名な参考書のコードに外部キーを含めるとこのエラーが発生します。
(ダメな例) def create() item = LangType() langtype = LangTypeForm(data, instance=item) (良い例) def create() langtype = LangTypeForm(data)
(ダメな例)のinstanceを用いる手法は既存のモデルインスタンスを更新するときに渡します。その場合はitemにはモデル名.objects.get(id=id)で取得したモデルインスタンスを設定します。
23. フロントエンド(Vue.js)
Vue CLIのインストールと使い方
Django + Vue CLI(Vue.js + Axios)でCRUDのサンプル (学習用)