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_typefield containing the selected page type, and anapp_namespacefield 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 theLanguageMixinis a required dependency offeincms3.applications.TYPEScontains a list of page type instances, eitherTemplateTypeorApplicationTypeand maybe others in the future. The configuration values are specific to each type, common to all of them are a key (stored in thepage_typefield) and a user-visible title.Template types additionally require a
template_nameand aregionsvalue.Application types require a
urlconfvalue and support the following options:urlconf: The path to the URLconf module for the application. Besides theurlpatternslist 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_typefrom theTYPESclass variable. This method is a receiver of Django’sclass_preparedsignal.
- property type¶
Returns the appropriate page type instance, either the selected type or the first type in the list of
TYPESif 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
urlpatternsfromROOT_URLCONFat the end. Returns the value ofROOT_URLCONFdirectly 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
appsby default. This value can be overridden by setting theLANGUAGE_CODES_NAMESPACEclass attribute of the page class to a different value. The instance namespaces consist of theLANGUAGE_CODES_NAMESPACEvalue with a language added at the end. As long as you’re always usingreverse_appyou 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_namein 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
NoReverseMatchexception is raised. Returnfallbackif 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_appfirst 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-detailapps-fr.articles.article-detailapps-de.publications.article-detailapps-de.articles.article-detailapps-en.publications.article-detailapps-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
fallbackkeyword 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), orfallbackif the former raises aNoReverseMatchexception. 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_appsupports 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='/'] %}
namespacescan either be a list or a comma-separated list of namespaces.NoReverseMatchexceptions can be avoided by providing afallbackas 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 %}