Applications (feincms3.applications
)¶
- class feincms3.applications.ApplicationType(**kwargs)[source]¶
Application page type
Example usage:
from feincms3.applications import PageTypeMixin, TemplateType from feincms3.pages import AbstractPage from content_editor.models import Region class Page(AbstractPage, PageTypeMixin) TYPES = [ TemplateType( # Required arguments key="standard", title="Standard page", urlconf="path.to.urlconf.module", # Optional arguments template_name="pages/standard.html", regions=[ Region(key="main", title="Main"), ], app_namespace=lambda page: ..., # You may pass other arguments here, they will be available # on ``page.type`` as-is. ), ]
- class feincms3.applications.PageTypeMixin(*args, **kwargs)[source]¶
The page class should inherit this mixin. It adds a
page_type
field containing the selected page type, and anapp_namespace
field which contains the instance namespace of the application, if the type of the page is an application type. The field is empty e.g. for template page types. Note that currently theLanguageMixin
is a required dependency offeincms3.applications
.TYPES
contains a list of page type instances, eitherTemplateType
orApplicationType
and maybe others in the future. The configuration values are specific to each type, common to all of them are a key (stored in thepage_type
field) and a user-visible title.Template types additionally require a
template_name
and aregions
value.Application types require a
urlconf
value and support the following options:urlconf
: The path to the URLconf module for the application. Besides theurlpatterns
list the module should probably also specify aapp_name
.required_fields
: A list of page class fields which must be non-empty for the application to work. The values are checked inPageTypeMixin.clean_fields
.app_namespace
: A callable which receives the page instance as its only argument and returns a string suitable for use as an instance namespace.
Usage:
from content_editor.models import Region from django.utils.translation import gettext_lazy as _ from feincms3.applications import PageTypeMixin from feincms3.mixins import LanguageMixin from feincms3.pages import AbstractPage class Page(AbstractPage, PageTypeMixin, LanguageMixin): TYPES = [ # It is recommended to always put a TemplateType type first # because it will be the default type: TemplateType( key="standard", title=_("Standard"), template_name="pages/standard.html", regions=[Region(key="main", title=_("Main"))], ), ApplicationType( key="publications", title=_("publications"), urlconf="app.articles.urls", ), ApplicationType( key="blog", title=_("blog"), urlconf="app.articles.urls", ), ApplicationType( key="contact", title=_("contact form"), urlconf="app.forms.contact_urls", ), ApplicationType( key="teams", title=_("teams"), urlconf="app.teams.urls", app_namespace=lambda page: f"{page.page_type}-{page.team_id}", required_fields=["team"], ), ]
- LANGUAGE_CODES_NAMESPACE = 'apps'¶
Override this to set a different name for the outer namespace.
- clean_fields(exclude=None)[source]¶
Checks that required fields are given and that an app namespace only exists once per site and language.
- static fill_page_type_choices(sender, **kwargs)[source]¶
Fills in the choices for
page_type
from theTYPES
class variable. This method is a receiver of Django’sclass_prepared
signal.
- property type¶
Returns the appropriate page type instance, either the selected type or the first type in the list of
TYPES
if no type is selected or if the type does not exist anymore.
- class feincms3.applications.TemplateType(**kwargs)[source]¶
Template page type
Example usage:
from feincms3.applications import PageTypeMixin, TemplateType from feincms3.pages import AbstractPage from content_editor.models import Region class Page(AbstractPage, PageTypeMixin) TYPES = [ TemplateType( # Required arguments key="standard", title="Standard page", template_name="pages/standard.html", regions=[ Region(key="main", title="Main"), ], # You may pass other arguments here, they will be available # on ``page.type`` as-is. ), ]
- feincms3.applications.apps_middleware(get_response)[source]¶
This middleware must be put in
MIDDLEWARE
; it simply assigns the return value ofapps_urlconf()
torequest.urlconf
. This middleware should probably be one of the first since it has to run before any resolving happens.
- feincms3.applications.apps_urlconf(*, apps=None)[source]¶
Generates a dynamic URLconf Python module including all application page types in their assigned place and adding the
urlpatterns
fromROOT_URLCONF
at the end. Returns the value ofROOT_URLCONF
directly if there are no active application page types.Since Django uses an LRU cache for URL resolvers, we try hard to only generate a changed URLconf when application URLs actually change.
The application URLconfs are put in nested namespaces:
The outer application namespace is
apps
by default. This value can be overridden by setting theLANGUAGE_CODES_NAMESPACE
class attribute of the page class to a different value. The instance namespaces consist of theLANGUAGE_CODES_NAMESPACE
value with a language added at the end. As long as you’re always usingreverse_app
you do not have to know the specifics.The inner namespace is the app namespace, where the application namespace is defined by the app itself (assign
app_name
in the same module asurlpatterns
) and the instance namespace is defined by the application name (fromTYPES
).
Modules stay around as long as the Python (most of the time WSGI) process lives. Unloading modules is tricky and probably not worth it since the URLconf modules shouldn’t gobble up much memory.
The set of applications can be overridden by passing a list of
(path, page_type, app_namespace, language_code)
tuples.
- feincms3.applications.page_for_app_request(request, *, queryset=None)[source]¶
Returns the current page if we’re inside an app. Should only be called while processing app views. Will pass along exceptions caused by non-existing or duplicated apps (this should never happen inside an app because
apps_urlconf()
wouldn’t have added the app in the first place if a matching page wouldn’t exist, but still.)Example:
def article_detail(request, slug): page = page_for_app_request(request) page.activate_language(request) instance = get_object_or_404(Article, slug=slug) return render( request, "articles/article_detail.html", {"article": article, "page": page}, )
It is possible to override the queryset used to fetch a page instance. The default implementation simply uses the first concrete subclass of
PageTypeMixin
.
- feincms3.applications.reverse_any(viewnames, urlconf=None, args=None, kwargs=None, fallback=<object object>, *fargs, **fkwargs)[source]¶
Try reversing a list of viewnames with the same arguments, and returns the first result where no
NoReverseMatch
exception is raised. Returnfallback
if it is provided and all viewnames fail to be reversed.Usage:
url = reverse_any( ("blog:article-detail", "articles:article-detail"), kwargs={"slug": "article-slug"}, )
- feincms3.applications.reverse_app(namespaces, viewname, *args, languages=None, **kwargs)[source]¶
Reverse app URLs, preferring the active language.
reverse_app
first generates a list of viewnames and passes them on toreverse_any
.Assuming that we’re trying to reverse the URL of an article detail view, that the project is configured with german, english and french as available languages, french as active language and that the current article is a publication, the viewnames are:
apps-fr.publications.article-detail
apps-fr.articles.article-detail
apps-de.publications.article-detail
apps-de.articles.article-detail
apps-en.publications.article-detail
apps-en.articles.article-detail
reverse_app tries harder returning an URL in the correct language than returning an URL for the correct instance namespace. The
fallback
keyword argument is supported too.Example:
url = reverse_app( ("category-1", "blog"), "post-detail", kwargs={"year": 2016, "slug": "my-cat"}, )
- feincms3.applications.reverse_fallback(fallback, fn, *args, **kwargs)[source]¶
Returns the result of
fn(*args, **kwargs)
, orfallback
if the former raises aNoReverseMatch
exception. This is especially useful for reversing app URLs from outside the app and you do not want crashes if the app isn’t available anywhere.The following two examples are equivalent, choose whichever you like best:
reverse_fallback( "/", lambda: reverse_app( ("articles",), "article-detail", kwargs={"slug": self.slug}, ), ) reverse_fallback( "/", reverse_app ("articles",), "article-detail", kwargs={"slug": self.slug}, )
Note though that
reverse_app
supports directly specifying the fallback since 3.1.1:reverse_app( ("articles",), "article-detail", kwargs={"slug": self.slug}, fallback="/", )
- feincms3.templatetags.feincms3.reverse_app(parser, token)[source]¶
Reverse app URLs, preferring the active language.
Usage:
{% load feincms3 %} {% reverse_app 'blog' 'detail' [args] [kw=args] [fallback='/'] %}
namespaces
can either be a list or a comma-separated list of namespaces.NoReverseMatch
exceptions can be avoided by providing afallback
as a keyword argument or by saving the result in a variable, similar to{% url 'view' as url %}
does:{% reverse_app 'newsletter' 'subscribe-form' fallback='/newsletter/' %}
Or:
{% reverse_app 'extranet' 'login' as login_url %}