Django 2.0にて、プロジェクトの urls.py
を
from django.urls import path, include urlpatterns = [ path('old/', include('myapp.urls', namespace='old')), ... ]
アプリの urls.py
を
from django.urls import path from django.views.generic import TemplateView urlpatterns = [ path('namespace/', TemplateView.as_view(template_name='myapp/url_with_include.html'), name='without_app'), ]
としたところ、以下のエラーが発生しました。
'Specifying a namespace in include() without providing an app_name ' django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.
この時に調査・対応したことをメモしておきます。
目次
環境
調査
エラーメッセージより、
- includeで呼ばれるモジュールに、
app_name
を設定 (URLパターン, 名前空間)
のタプルをinclude()に渡す
のどちらかを実装すれば良さそうでした。
また、 django.urls.include()
の公式ドキュメントを見たところ、引数 namespace
を使いたい場合の記載もありました。
include() | django.urls functions for use in URLconfs | Django documentation | Django
ただ、エラーメッセージで示されたどちらかの実装をしていないと、引数 namespace
が使えないようです。
次に、いつから app_name
が導入されたのかを調べてみたところ、 1.9のようでした。
https://docs.djangoproject.com/en/1.9/topics/http/urls/#url-namespaces-and-included-urlconfs
1.8以前は、include()の引数に namespace
を使う方法が書かれていました。
https://docs.djangoproject.com/en/1.8/topics/http/urls/#url-namespaces-and-included-urlconfs
チュートリアルも、1.9から書き直されていました。
1.8:https://docs.djangoproject.com/en/1.8/intro/tutorial03/#namespacing-url-names
1.9:https://docs.djangoproject.com/en/1.9/intro/tutorial03/#namespacing-url-names
最後に、いつ頃からエラーが発生する用になったのかと思い、挙動を確認したところ、
でした。
Django 2.0のリリースノートにある
Support for setting a URL instance namespace without an application namespace is removed.
https://docs.djangoproject.com/en/2.0/releases/2.0/#features-removed-in-2-0
のあたりが関係していそうでした。
以上より、冒頭のソースコードはDjango2.0で1.8時代の実装をしていたために発生していたようでした。
対応
調査方法でも書きましたが、主な対応方法として以下の3つが考えられました。
- プロジェクトのurls.pyのinclude()では
namespace
引数を使用せず、アプリのurls.pyにapp_name
を追加 - プロジェクトのurls.pyのinclude()の引数として、
(アプリのurls.py, 名前空間)
となるタプルを渡す - プロジェクトのurls.pyのinclude()に
namespace
引数を使用し、アプリのurls.pyでもapp_name
を追加
どの方法が良いのかと思い、Djangoの公式チュートリアルを見たところ、アプリのurls.pyに app_name
を追加する書き方をしていました。
https://docs.djangoproject.com/ja/2.0/intro/tutorial03/#namespacing-url-names
そのため、特に制限がなければ、アプリのurls.pyに app_name
を書くのが良いのかなと思いました。
動作確認
今回は、url テンプレートタグを使った時にいずれの方法でもURLリバースできるか
にて確認してみます。
実装
まず、プロジェクトのurls.pyは
urlpatterns = [ # アプリのurls.pyに app_name 設定があるパターン path('with/', include('myapp.urls_with_app_name')), # アプリのurls.pyには app_name 設定がなく、includeの引数をタプルにして名前空間を渡すパターン # (urlpatternsのあるモジュール, 名前空間) path('without/', include(('myapp.urls_without_app_name', 'without'))), # アプリのurls.pyに app_name 設定があるが、名前空間を別に用意するパターン path('over/', include('myapp.urls_overwrite_app_name', namespace='replaced')), # アプリのurls.pyの app_name 設定をそのまま使うパターン(再掲) path('not_over/', include('myapp.urls_overwrite_app_name')), ]
とします。
次に、app_nameのあるアプリのurls.pyはそれぞれ
myapp.urls_with_app_name.py
app_name = 'with' # urlpatternsは、他も同じなので、以下省略 urlpatterns = [ path('namespace/', TemplateView.as_view(template_name='myapp/url_with_include.html'), name='with_app'), ]
myapp.urls_overwrite_app_name.py
app_name = 'overwrite'
とします。
あとは、テンプレートを
<ul> <li><a href="{% url 'with:with_app' %}">app_name がある場合</a></li> <li><a href="{% url 'without:without_app' %}">app_name がない場合</a></li> <li><a href="{% url 'replaced:over_app' %}">app_name が上書きされた場合</a></li> <li><a href="{% url 'overwrite:over_app' %}">app_name がそのままの場合</a></li> </ul>
として用意します。
動作確認
開発サーバを起動し、curlでアクセスしてみると、いずれの方法でもURLリバースできていました。
$ curl -L http://localhost:8000/with/namespace <!DOCTYPE html> ... <ul> <li><a href="/with/namespace/">app_name がある場合</a></li> <li><a href="/without/namespace/">app_name がない場合</a></li> <li><a href="/over/namespace/">app_name が上書きされた場合</a></li> <li><a href="/not_over/namespace/">app_name がそのままの場合</a></li> </ul>
ソースコード
GitHubに上げました。
https://github.com/thinkAmi-sandbox/Django20-sample