1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

djangoのincludeメソッドを少し深堀る

Posted at

目次

対象読者

  • includeメソッドの内部処理に興味がある方
  • startproject コマンドで生成される config/urls.py から各アプリへ URL を委譲する仕組みを理解したい方

なにはともあれincludeメソッドの中身を見ていこうではないか

まずは実際のコードを確認しておきます。Django 公式が提供している include() 関数は以下のように実装されています。

site-packages/django/urls/conf.py
def include(arg, namespace=None):
    app_name = None
    if isinstance(arg, tuple):
        # Callable returning a namespace hint.
        try:
            urlconf_module, app_name = arg
        except ValueError:
            if namespace:
                raise ImproperlyConfigured(
                    "Cannot override the namespace for a dynamic module that "
                    "provides a namespace."
                )
            raise ImproperlyConfigured(
                "Passing a %d-tuple to include() is not supported. Pass a "
                "2-tuple containing the list of patterns and app_name, and "
                "provide the namespace argument to include() instead." % len(arg)
            )
    else:
        # No namespace hint - use manually provided namespace.
        urlconf_module = arg

    if isinstance(urlconf_module, str):
        urlconf_module = import_module(urlconf_module)

    patterns = getattr(urlconf_module, "urlpatterns", urlconf_module)
    app_name = getattr(urlconf_module, "app_name", app_name)
    if namespace and not app_name:
        raise 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.",
        )
    namespace = namespace or app_name
    # Make sure the patterns can be iterated through (without this, some
    # testcases will break).
    if isinstance(patterns, (list, tuple)):
        for url_pattern in patterns:
            pattern = getattr(url_pattern, "pattern", None)
            if isinstance(pattern, LocalePrefixPattern):
                raise ImproperlyConfigured(
                    "Using i18n_patterns in an included URLconf is not allowed."
                )
    return (urlconf_module, app_name, namespace)

重要なポイントはここ

ここからは、引数 argnamespace がどのように扱われているかを順番に追いながら、内部処理を解説します。

  1. tuple かどうかの判定
    include() は第一引数に「URLconf モジュール」または「URLパターンのリスト」を受け取ります。

    • もし (urlconf_module, app_name) のような 2 要素タプルが渡されれば、2 番目の値が app_name として扱われます。
    • 要素数が 2 以外のタプルだった場合は ImproperlyConfigured 例外を投げて、正しい引数の形で呼び出すよう警告しています。
    • タプルではない場合(一般的には 文字列でモジュールパスを渡す ケース)には、そのまま urlconf_module として扱います。
  2. 文字列なら動的 import

    if isinstance(urlconf_module, str):
        urlconf_module = import_module(urlconf_module)
    

    ここが一番の肝です。例えば include('modelonetomany.urls') と書くと、Django が自動的に modelonetomany.urls というモジュールを import してくれます。これにより、config/urls.py 側で明示的な import 文を書く必要がありません。
    import_module(urlconf_module)のおかげですね。

  3. urlpatternsapp_name の取得

    patterns = getattr(urlconf_module, "urlpatterns", urlconf_module)
    app_name = getattr(urlconf_module, "app_name", app_name)
    
    • import したモジュールに urlpatterns という属性があればそれを使用します。もしリストが直接渡されていれば、そのまま URL パターンとして扱います。
    • 同様に app_name という属性が定義されていれば上書きします。これが URL 逆引き時の名前空間 に使われる値です。

補足

  • より深く理解したい場合は、以下の記事も合わせてご覧いただくと把握が進むと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?