skip to content

How do you mount one reusable Django comments app at both /news/comments/ and /support/comments/ without its URL names clashing?

level: middleimportance: should knowfreq 38%

answer

  1. one app name, two instances
  2. the namespace argument per mount
  3. which instance a lookup picks
  4. current, then default, then last
  5. templates versus Python code

basics

~10 s

Keep app_name = 'comments' in the app and include it twice with different instance namespaces, such as namespace='news-comments' and namespace='support-comments'. The app keeps reversing 'comments:...' and Django picks the instance from the current request.

solid answer

~40 s

Include the same URLconf twice with distinct `namespace=` values; both mounts share the application namespace `comments`. A lookup of `'comments:list'` then resolves in order: the **current app** if one is given (the `current_app` argument, or for `{% url %}` the request's `current_app` or `resolver_match.namespace`), otherwise the **default instance** whose instance namespace is `comments`, otherwise the **last deployed** instance. So templates rendered with the request link within their own section automatically, while `'news-comments:list'` always targets one mount. The app's own code must stick to `'comments:...'`; Python code that reverses must pass `current_app=request.resolver_match.namespace`, because nothing fills it in for `reverse()`. Giving both mounts the same namespace triggers `urls.W005`, and URLs in one of them become unreachable by name.

code

python · 9 lines
python
from django.shortcuts import render

from .models import Comment


def comment_list(request):
    section = request.resolver_match.namespace  # 'news-comments' or 'support-comments'
    comments = Comment.objects.filter(section=section)
    return render(request, 'comments/list.html', {'comments': comments})

go deeper

for a junior

Recall that the same app can be included twice if each include() gets its own namespace=, while the app keeps one app_name.

for a middle

Explain the lookup order, current app then default instance then last deployed, and why templates rendered with the request link correctly without extra code.

for a senior

Make shared views section-aware through resolver_match.namespace, pass current_app wherever Python builds URLs, and choose a default instance so fallbacks are predictable.

for a principal

Decide whether a second mount should be an instance of one app or a separate app, weighing shared code and data against URL and permission isolation.

## The setup A project wants the same comments app in two places: under news articles and under support tickets. Both mounts use the app's single URLconf, and each gets its own **instance namespace**: ```python # mysite/urls.py from django.urls import include, path urlpatterns = [ path('news/comments/', include('comments.urls', namespace='news-comments')), path('support/comments/', include('comments.urls', namespace='support-comments')), ] ``` `comments/urls.py` keeps `app_name = 'comments'`, so both mounts share the **application namespace** `comments`. Resolution is unaffected by namespaces: `/news/comments/42/` and `/support/comments/42/` both reach `comment_detail(request, pk=42)`. Namespaces matter when URLs are built from names. ## How Django picks an instance For a lookup like `'comments:list'`, Django's documented strategy is: 1. Treat `comments` as an **application namespace** and collect its instances: `news-comments` and `support-comments`. 2. If a **current app** is known and is one of those instances, use it. It comes from the `current_app` argument of `reverse()`. The `{% url %}` tag fills it in from `request.current_app` if a view set it, otherwise from `request.resolver_match.namespace`. 3. Otherwise use the **default instance**, the one whose instance namespace equals the app name. 4. Otherwise use the **last deployed** instance, the one declared last in `urlpatterns`. 5. If `comments` were not an application namespace at all, Django would try it as an instance namespace directly. ## What each lookup returns here | Where the lookup runs | `'comments:list'` | `'news-comments:list'` | |---|---|---| | template rendered for `/news/comments/` with the request | `/news/comments/` | `/news/comments/` | | template rendered for `/support/comments/` with the request | `/support/comments/` | `/news/comments/` | | `reverse()` in Python, no `current_app` | `/support/comments/` (last deployed) | `/news/comments/` | | `reverse(..., current_app='news-comments')` | `/news/comments/` | `/news/comments/` | The third row is the surprise: without a current app and without a default instance, Python code silently lands in whichever section was included last. ## Making one mount the default If one section is the natural home, give it the instance namespace `comments`, for example `include('comments.urls')` for news with no `namespace=` at all. Lookups with no current app then go to news instead of whichever mount is last, which is much easier to reason about than list order. ## Rules for the reusable app's own code - **Reverse through the application namespace.** Write `'comments:detail'`, never `'news-comments:detail'`; the app must not know how the project mounted it. - **Pass the request to templates.** `render(request, ...)` gives `{% url %}` access to the request and therefore to the current namespace. A template rendered without a request, such as an email body built with `render_to_string()` and no request, has no current app. - **Pass `current_app` in Python.** `reverse('comments:list', current_app=request.resolver_match.namespace)` keeps redirects and API links inside the section that served the request. - **Read the section from the request.** `request.resolver_match.namespace` tells a shared view which mount it serves, for example to filter comments to news or support. ## When not to share one app Mounting one app twice is the right tool when both sections genuinely run the same feature. It is the wrong tool when they only look similar: - **Different rules per section.** If support comments need moderation and news comments do not, branching on `resolver_match.namespace` in every view soon costs more than two small apps or a setting per section. - **Different permissions.** Namespaces separate URLs, not access; each shared view must still check who may post where. - **Different data.** The views must filter by section explicitly, since both mounts query the same models. When those differences are small and stable, one app with two instance namespaces keeps a single codebase and a single set of templates. ## Checks and failure modes - Including the URLconf twice **without** `namespace=` gives both mounts the instance namespace `comments`; system check `urls.W005` warns that the namespace is not unique and some URLs cannot be reversed. - Including it without `app_name` and without namespaces puts `list` and `detail` in the global pool twice; the last pattern in `urlpatterns` wins every lookup. - Django's own admin follows the same model: every `AdminSite` shares the application namespace `admin`, and the admin's code reverses with `current_app` set to its site's name.

  • In Django, how can a view make {% url %} use a different instance than the one that served the request?
    Set `request.current_app` to the instance namespace before rendering, for example `request.current_app = 'support-comments'`. The `{% url %}` tag checks that attribute before falling back to `request.resolver_match.namespace`. It affects only the template tag; `reverse()` calls in Python still need `current_app` passed explicitly.
  • What changes if one of the two Django comments mounts uses the instance namespace 'comments'?
    That mount becomes the default instance. Lookups of `'comments:...'` that have no current app, such as `reverse()` without `current_app` or a template rendered without a request, now resolve to it instead of to whichever instance appears last in `urlpatterns`.

It works like a chain store: asking for the brand sends you to the branch you are standing in; if you are not in any branch you go to the flagship, and if there is no flagship, to the most recently opened branch.

saying these in an interview costs you the question

  • With no current app, 'comments:list' resolves to the first instance included.
  • reverse() in a view automatically uses the namespace of the current request.
  • Two mounts of one URLconf need two different app_name values.
  • A reusable app should hardcode the instance namespaces it expects projects to use.
  • Including the same URLconf twice without namespace= raises an error at startup.