Apps and instances¶
Applications can be added to the tree several times. Django itself supports this through the differentiation between application namespaces and instance namespaces. feincms3 builds on this functionality.
The feincms3.applications.apps_urlconf()
function generates a dynamic
URLconf Python module including all applications in their assigned place
and adding the urlpatterns
from ROOT_URLCONF
at the end (or
returns the value of ROOT_URLCONF
directly if there are no active
applications).
Application namespaces and instance namespace¶
The application URLconfs are included using nested namespaces:
The outer application namespace is
"apps"
by default.The outer instance namespace is
"apps-" + LANGUAGE_CODE
.The inner namespace is the app namespace, specified by the value of
app_name
in the apps’ URLconf module. The string must correspond with the key of theApplicationType
instance in theTYPES
list on the page model.The inner instance namespace is the same as the app namespace, except if you return a different value from the application type’s
app_namespace
function.
Apps are contained in nested URLconf namespaces which
allows for URL reversing using Django’s reverse()
mechanism. The
inner namespace is the app itself, the outer namespace the language.
(Currently the apps code depends on
LanguageMixin
and cannot be used without it.)
reverse_app()
hides a good part of the complexity
of finding the best match for a given view name since apps will often be
added several times in different parts of the tree, especially on sites
with more than one language.
Reversing application URLs¶
The best way for reversing app URLs is by using
feincms3.applications.reverse_app()
. The method expects at least two
arguments, a namespace and a viewname. The namespace argument also
supports passing a list of namespaces which is useful in conjunction
with the app_namespace
option of applications.
reverse_app()
first generates a list of viewnames
and passes them on to feincms3.applications.reverse_any()
(which returns
the first viewname that can be reversed to a URL).
For the sake of an example let’s assume that our site is configured with
english, german and french as available languages and that we’re trying to
reverse the article list page, and that the current language (as returned by
get_language
) is german:
from feincms3.applications import reverse_app
# Inside a view or middleware:
articles_list_url = reverse_app("articles", "article-list")
...
The list of viewnames reversed is in order:
apps-de.articles.article-list
apps-en.articles.article-list
apps-fr.articles.article-list
The german apps namespace comes first in the list. If the german part of the site does not contain an articles app, the reversing continues in all other languages.
If the namespace argument to reverse_app()
was a
list (or tuple), the list is even longer. Suppose that variants of the
articles app may be added to the tree where only a single category is
shown:
from feincms3.pages import AbstractPage
from feincms3.applications import PageTypeMixin
from feincms3.applications import ApplicationType, TemplateType
class Page(AbstractPage, PageTypeMixin, LanguageMixin, ...):
TYPES = [
TemplateType(
key="standard",
title="...",
regions=[Region(key="main", title="...")],
# Available as page.type.template_name
template_name="pages/standard.html",
),
ApplicationType(
key="articles",
title=_("Articles"),
urlconf="app.articles.urls",
app_namespace=lambda page: f"{page.page_type}-{page.category_id or 'all'}",
),
...
]
category = models.ForeignKey(
"articles.Category",
blank=True,
null=True,
...
)
In this case we might prefer the URL of a specific categories’ articles app, but also be content with an articles app without a specific category:
reverse_app(
[f"articles-{category.pk}", "articles"],
"article-list",
)
The list of viewnames in this case is (assuming that the category has a
pk
value of 42):
apps-de.articles-42.article-list
apps-de.articles.article-list
apps-en.articles-42.article-list
apps-en.articles.article-list
apps-fr.articles-42.article-list
apps-fr.articles.article-list
As you can see reverse_app
prefers apps in the current language to
apps with the closer matching instance namespace.
Note
Some of the time Django’s stock reverse()
function works as well
for reversing app URLs, e.g:
from django.urls import reverse
reverse("apps:articles:article-list")
However, it’s still recommended to use reverse_app
. reverse
may not find apps because Django is content with the first match when
searching for matching namespaces. Also, reverse
may not find the
best match in the presence of several app instances, be it because of
several languages on the site or because of other factors.
Reversing URLs while preferring a specific language¶
Suppose that articles are written in a language, and get_absolute_url
should prefer an app in this language to the same app in other languages. Since
reverse_app
automatically prefers the currently active language we use
override
to activate this language for the duration of the reverse_app
call:
from django.utils.translation import override
from feincms3.applications import reverse_app
from feincms3.mixins import LanguageMixin
class Article(LanguageMixin):
# ...
def get_absolute_url(self):
with override(self.language_code):
return reverse_app(
"articles",
"article-detail",
kwargs={"slug": self.slug},
# Pass a fallback value if you do not want to crash with a
# NoReverseMatch exception if reversing fails. Maybe use a
# better fallback value.though.
fallback="/",
)
Note
Previous versions recommended the use of
reverse_fallback()
. It’s still available but
not recommended anymore.
Reversing URLs outside the request-response cycle¶
Outside the request-response cycle, respectively outside
feincms3.applications.apps_middleware()
’s request.urlconf
assignment,
the reversing functions only use the URLconf module configured using the
ROOT_URLCONF
setting. In this case applications are impossible to
find. However, all reversing functions support specifying the root URLconf
module used for reversing:
from feincms3.applications import apps_urlconf, reverse_app
reverse_app("articles", "article-list", urlconf=apps_urlconf())