Djangoで複数アプリ構築時、事前に考慮したほうが良い部分
この記事は、Qiita Advent Calendar 20189日目の記事になります。16日目とはなりますが、投稿がなかったため、代わりに投稿しました。
Djangoを使って、Webアプリを開発しております。
当初から同時に二つのアプリケーション(同じ場所で使うが、機能がだいぶ異なるので分かれている)を構築することとなっており、一つ作った後にもう一つ、というときにいろいろ躓いたため、どこで困ったか書いてみます。
Djangoは、本番環境で動かす際どうするかについて書いてある記事がそれほど多くない印象です。その一助になれば幸いです。
主にファイル管理関連です。
バージョン等
| 名称 | バージョン |
|---|---|
| Python | 3.7.0 |
| Django | 2.1.1 |
サンプル

プロジェクトとしてdjango_complex_sampleというものを作り、その中にapp1、app2というアプリケーションを作っています。
事前準備
settings.pyのINSTALLED_APPSにapp1とapp2を追加します。DBは特に使わないですが、SQLiteにしておきます。また、URLも指定しておきます。
- settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app1',
'app2',
]
- urls.py
from django.contrib import admin from django.urls import path from app1 import views as v1 from app2 import views as v2 urlpatterns = [ path('admin/', admin.site.urls), path('app1/', v1.HomeView.as_view(), name="app1home"), path('app2/', v2.HomeView.as_view(), name="app2home"), ]
テンプレート
結論
標準のまま使うなら、
/templates/アプリ名というフォルダを作り、そこにファイルを格納する- ビューからテンプレートを参照する場合、
アプリ名/ファイル名と記述する
根拠
テンプレート | Django documentation | Django
テンプレートは、viewでパス指定されて、ページを描画する際に使われます。
標準のまま使う場合、各サイトにtemplatesというフォルダを作って、そこにHTMLを配置すると、サイト表示時にそのテンプレートを表示します。
ただ、このテンプレートの決定方法は、フォルダを探索して最初に見つかったファイルを使用するという動作をしています。自動的にアプリ別に判定、とはなっていません。
よって、確実にファイルを判別するために、templates直下にアプリ名のフォルダを作り、そこを参照するようにしたほうが良いです。
例
例として、app2のtemplates直下にhome.htmlを配置します。app1の同フォルダは空にします。そして、app1のビューでテンプレート名home.htmlを指定します。

- app1/views.py
from django.shortcuts import render from django.views.generic import TemplateView # Create your views here. class HomeView(TemplateView): template_name = 'home.html' # app1のhome.htmlを期待しているが、まだ作っていない def get(self, request, *args, **kwargs): return render(request, self.template_name)
- app1/home.html
<h1>Hello App1!</h1>
- app2/home.html
<h1>Hello App2!</h1>
- デバッグ結果

上記のように、app2のhome.htmlを参照して、表示してしまいます。これは、templatesの検索で、app2にあるhome.htmlが見つかるためです。
これを避けるために、全てのアプリで以下のようにフォルダを切り、

- app1/views.py
from django.shortcuts import render from django.views.generic import TemplateView # Create your views here. class HomeView(TemplateView): template_name = 'app1/home.html' # templates/app1フォルダを参照 def get(self, request, *args, **kwargs): return render(request, self.template_name)
フォルダを固定で指定できるようにしましょう*1。
静的ファイル(static)
結論
- 各アプリケーション直下に
/static/アプリ名というフォルダを作り、そこにファイルを格納する - テンプレートでファイルを参照する場合、
アプリ名/ファイル名と記述する
根拠
静的ファイル (画像、JavaScript、CSS など) を管理する | Django documentation | Django
静的ファイルとは、JavaScriptや画像など、ページに埋め込む形で使うファイルを指します。開発時は、各アプリ直下にstaticというフォルダを作り、そこにファイルを格納します。
このファイル、本番環境では一か所に集めて管理します。collectstatic*2コマンドで、静的ファイルを集められます。
試しに、staticフォルダに一つだけファイル(main.css)を置いて、collectstaticを実行します。

すると、staticの直下に配置されてしまいます。これだと、複数のサイトで同じファイル名のファイルがあると、お互い上書きされます*3。
なので、各アプリのstatic直下に、アプリ名のフォルダを作成して、ファイルが衝突しないようにしましょう。
テンプレートで参照する場合も、
- app1/home.html
{% load static %} <script type="text/javascript" src="{% static 'app1/main.css' %}"></script>
のように、パスにアプリ名を付ける必要があるため、開発着手時から対応しておいたほうが良いです。
static内にアプリ名のフォルダを作って、collectstaticを実施すると、以下のようになります。

URL(urls.py)
結論
- アプリごとに、
urls.pyを作成する - 各アプリの
urls.pyで、一意のapp_nameを定義する - プロジェクトの
urls.pyで、各アプリの定義をincludeする - テンプレートでアプリ名を指定する
根拠
URL ディスパッチャ | Django documentation | Django
現状、プロジェクトにあるurls.pyは、以下のようになっています。
- django_complex_sample/urls.py
from django.contrib import admin from django.urls import path from app1 import views as v1 from app2 import views as v2 urlpatterns = [ path('admin/', admin.site.urls), path('app1/', v1.HomeView.as_view(), name="app1home"), path('app2/', v2.HomeView.as_view(), name="app2home"), ]
アプリでURLが増えた場合に、ここに定義を増やして対応することは可能です。
しかし、そうすると、
というような問題が発生します。
こういうときのためにDjangoでは、includeという機能が用意されています。
例
まず、各アプリの直下(どこでもよい)に、urls.pyを作成します。
そして、自身のアプリに対するURLを記述します。
- app1/urls.py
"""app1のURL定義""" from django.urls import path from .apps import App1Config as config from . import views as v app_name = config.name # app1が入る urlpatterns = [ path('', v.HomeView.as_view(), name='home'), ]
そして、プロジェクトのurls.pyでそれをincludeします。
- django_complex_sample/urls.py
from django.contrib import admin from django.urls import path, include from app1 import urls as v1 from app2 import views as v2 urlpatterns = [ path('admin/', admin.site.urls), path('app1/', include(v1)), # 変更(app1のURLを追加) path('app2/', v2.HomeView.as_view(), name="app2home"), ]
こうすると、自動的にapp1のURLへのルーティングを追加してくれます。
また、この機能を使った場合、viewでのURL指定方法が変わります。
まず、app1に適当なビューを追加します。
- app1/views.py
from django.shortcuts import render from django.views import View from django.views.generic import TemplateView from django.http import HttpResponse # Create your views here. class HomeView(TemplateView): template_name = 'app1/home.html' def get(self, request, *args, **kwargs): return render(request, self.template_name) class ApplicationView(View): def get(self, request, *args, **kwargs): return HttpResponse('GET request!')
- app1/urls.py
"""app1のURL定義""" from django.urls import path from .apps import App1Config as config from . import views as v app_name = config.name # app1が入る urlpatterns = [ path('', v.HomeView.as_view(), name='home'), path('app/', v.ApplicationView.as_view(), name='app'), # 追加 ]
このViewに対し、home.htmlからリンクを作成します。(いろいろ加筆しています)
- app1/templates/home.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
<link href="{% static 'app1/main.css' %}" rel="stylesheet">
</head>
<body>
<h1>Hello App1!</h1>
<a href={% url 'app1:app' %}>リンク</a>
</body>
重要なのは
<a href={% url 'app1:app' %}>リンク</a>
の部分です。urlというDjangoのタグで、指定した名前のURLを設定してくれますが、{% url '[アプリ名]:[name]' %}とhrefで指定すると、アプリ名を判定してそのリンクを作ってくれます。
これにより、アプリ間の名前競合を避けられます。
おわりに
まだまだ、複数アプリ開発での注意点はあるように思います。
現状、筆者が体験した中での話となっています。
原則としては、アプリの処理はアプリ内で完結させることと、アプリ外で扱いやすいような設計にしておくことの二つだと感じています。
理想は、pipでインストールしたときに簡単に扱えるようにしておくことです。
ほかにも気づいたことあったら、ちょっとずつ整理していきます。