Utility functions#

Utility functions provide functionality that is regularly used within the django CMS core and are also available to third party packages.

Model admin#

Action buttons#

class cms.admin.utils.ChangeListActionsMixin#

Bases: object

ChangeListActionsMixin is a mixin for the ModelAdmin class. It adds the ability to have action buttons and a burger menu in the admin’s change list view. Unlike actions that affect multiple listed items the list action buttons only affect one item at a time.

Use get_action_list() to register actions and admin_action_button() to define the button behavior.

To activate the actions make sure "admin_list_actions" is in the admin classes :prop:`~django.contrib.admin.ModelAdmin.list_display` property.

static admin_action_button(url: str, icon: str, title: str, burger_menu: bool = False, action: str = 'get', disabled: bool = False, keepsideframe: bool = True, name: str = '') str#

Returns a generic button supported by the ChangeListActionsMixin.

Parameters:
  • url (str) – Url of the action as string, typically generated by :func:`~cms.utils.urlutils.admin_reverse`_

  • icon (str) – Name of the icon shown in the button or before the title in the burger menu.

  • title (str) – Human-readable string describing the action.

  • burger_menu (bool) – If True the action item will be part of a burger menu right og all buttons.

  • action (str) – Either "get" or "post" defining the html method used for the url. Some urls require a post method.

  • disabled (bool) – If True the item is grayed out and cannot be selected.

  • keepsideframe (bool) – If False the side frame (if open) will be closed before executing the action.

  • name (str) – A string that will be added to the class list of the button/menu item: cms-action-{{ name }}

To add an action button to the change list use the following pattern in your admin class:

def my_custom_button(self, obj, request, disabled=False):
   # do preparations, e.g., check permissions, get url, ...
   url = admin_reverse("...", args=[obj.pk])
   if permissions_ok:
       return self.admin_action_button(url, "info",  _("View usage"), disabled=disabled)
   return ""  # No button
get_actions_list() List[Callable[[Model, HttpRequest], str]]#

Collect list actions from implemented methods and return as list. Make sure to call it’s super() instance when overwriting:

class MyModelAdmin(admin.ModelAdmin):
    ...

    def get_actions_list(self):
        return super().get_actions_list() + [self.my_first_action, self.my_second_action]
get_admin_list_actions(request: HttpRequest) Callable[[Model], str]#

Method to register the admin action menu with the admin’s list display

Usage (in your model admin):

class MyModelAdmin(AdminActionsMixin, admin.ModelAdmin):
    ...
    list_display = ('name', ..., 'admin_list_actions')

Grouper admin#

class cms.admin.utils.GrouperModelAdmin(model, admin_site)#

Bases: ChangeListActionsMixin, ModelAdmin

Easy-to-use ModelAdmin for grouper models. Usage example:

class MyGrouperAdmin(GrouperModelAdmin):
    # Add language tabs to change and add views
    extra_grouping_fields = ("language",)
    # Add grouper and content fields to change list view
    # Add preview and settings action to change list view
    list_display = ("field_in_grouper_model", "content__field_in_content_model", "admin_list_actions")

    # Automatically add content fields to change form (either the standard form or any form given
    form = MyChangeForm

    ...

Using GrouperModelAdmin instead of ModelAdmin adds a view standard functions to your admin class to make it more easily and more consistently customizable.

  1. By adding "admin_list_actions" to the admin’s list_display

    attribute the change list view gets an action column as described by ChangeListActionsMixin.

  2. The admin class automatically creates a method for each field of the content model form (default: all fields)

    named content__{content_model_field_name}. Those fields can be used in list_display just as grouper model fields. Currently, they are not sortable, however.

  3. The change form is amended with exactly those content fields also named content__{content_model_field_name}.

    As a result, the change form can (but does not have to) contain both grouper model fields and content model fields. The admin takes care of creating the necessary model instances.

changeform_view(request: HttpRequest, object_id: Optional[str] = None, form_url: str = '', extra_context: dict = None) HttpResponse#

Update grouping field properties for both add and change views

clear_content_cache() None#

Clear cache, e.g., for a new request

delete_view(request: HttpRequest, object_id: str, extra_context: Optional[dict] = None) HttpResponse#

Update grouping field properties for delete view

get_actions_list() list#

Collect list actions from implemented methods and return as list. Make sure to call it’s super() instance when overwriting:

class MyModelAdmin(admin.ModelAdmin):
    ...

    def get_actions_list(self):
        return super().get_actions_list() + [self.my_first_action, self.my_second_action]
get_changelist(request: HttpRequest, **kwargs) type#

Allow for extra grouping fields as a non-filter parameter

get_changelist_instance(request: HttpRequest) GrouperChangeListBase#

Update grouping field properties and get changelist instance

get_content_field(obj: Model, field_name: str, request: Optional[HttpRequest] = None) Any#

Retrieves the content of a field stored in the content model. If request is given extra grouping fields are processed before.

get_extra_context(request: HttpRequest, object_id: Optional[str] = None) Dict[str, Any]#

Provide the grouping fields to the change view.

get_form(request: HttpRequest, obj: Optional[Model] = None, **kwargs) type#

Adds the language from the request to the form class

get_grouper_obj(obj: Model) Model#

Get the admin object. If obj is a content object assume that the admin object resides in the field named after the admin model. The admin model name must be the same as the content model name minus “Content” at the end.

get_grouping_from_request(request: HttpRequest) None#

Retrieves the current grouping selectors from the request

get_language() str#

Hook on how to get the current language. By default, Django provides it.

get_language_from_request(request: HttpRequest) str#

Hook for get_language_from_request which by default uses the cms utility

get_language_tuple() Tuple[Tuple[str, str], ...]#

Hook on how to get all available languages for the language selector.

get_preserved_filters(request: HttpRequest) str#

Always preserve grouping get parameters! Also, add them to changelist filters: * Save and continue will keep the grouping parameters * Save and returning to changelist will keep the grouping parameters

get_queryset(request: HttpRequest) QuerySet#

Annotates content fields with the name “content__{field_name}” to the grouper queryset if for all content fields that appear in the

get_readonly_fields(request: HttpRequest, obj: Optional[Model] = None)#

Allow access to content fields to be controlled by a method “can_change_content”: This allows versioned content to be protected if needed

history_view(request: HttpRequest, object_id: str, extra_context: Optional[dict] = None) HttpResponse#

Update grouping field properties for history view

save_model(request: HttpRequest, obj: Model, form: Form, change: bool) None#

Save/create both grouper and content object

view_on_site(obj: Model) Optional[str]#

bool(x) -> bool

Returns True when the argument x is true, False otherwise. The builtins True and False are the only two instances of the class bool. The class bool is a subclass of the class int, and cannot be subclassed.

content_model: Optional[Model] = None#

The content model class to be used. Defaults to the model class named like the grouper model class plus "Content" at the end from the same app as the grouper model class, e.g., BlogPostContent if the grouper is BlogPost.

Name of the inverse relation field giving the set of content models belonging to a grouper model. Defaults to the first field found as an inverse relation. If you have more than one inverse relation please make sure to specify this field. An example would be if the blog post content model contained a many-to-many relationship to the grouper model for, say, related blog posts.

property current_content_filters: Dict[str, Any]#

Filters needed to get the correct content model instance

extra_grouping_fields: Tuple[str, ...] = ()#

Indicates additional grouping fields such as "language" for example. Additional grouping fields create tabs in the change form and a dropdown menu in the change list view.

Note

All fields serving as extra grouping fields must be part of the admin’s fieldsets setting for GrouperModelAdmin to work properly. In the change form the fields will be invisible.

grouper_field_name: Optional[str] = None#

The name of the ForeignKey in the content model that points to the grouper instance. If not given it is assumed to be the snake case name of the grouper model class, e.g. "blog_post" for the "BlogPost" model.

Placeholders#

cms.utils.placeholder.get_placeholder_from_slot(placeholder_relation: Manager, slot: str, template_obj=None) Placeholder#

Retrieves the placeholder instance for a PlaceholderRelationField either by scanning the template of the template_obj (if given) or by creating or getting a Placeholder in the database

cms.utils.placeholder.get_declared_placeholders_for_obj(obj)#

Returns declared placeholders for an object. The object is supposed to have a method get_template which returns the template path as a string that renders the object. get_declared_placeholders returns a list of placeholders used in the template by the {% placeholder %} template tag.

Plugins#

cms.utils.plugins.get_plugins(request, placeholder, template, lang=None)#

Get a list of plugins for a placeholder in a specified template. Respects the placeholder’s cache.

Parameters:
  • request – (HttpRequest) The HTTP request object.

  • placeholder – (Placeholder) The placeholder object for which to retrieve plugins.

  • template – (Template) The template object in which the placeholder resides (not used).

  • lang – (str, optional) The language code for localization. Defaults to None.

Returns:

list: A list of plugins for the specified placeholder in the template.

Raises:

None.

Examples:

# Get plugins for a placeholder in a template
plugins = get_plugins(request, placeholder, template)

# Get plugins for a placeholder in a template with specific language
plugins = get_plugins(request, placeholder, template, lang='en')
cms.utils.plugins.assign_plugins(request, placeholders, template=None, lang=None)#

Fetch all plugins for the given placeholders and cast them down to the concrete instances in one query per type.

Parameters:
  • request – The current request.

  • placeholders – An iterable of placeholder objects.

  • template – (optional) The template object.

  • lang – (optional) The language code.

This method assigns plugins to the given placeholders. It retrieves the plugins from the database based on the placeholders and the language. The plugins are then downcasted to their specific plugin types.

The plugins are split up by placeholder and stored in a dictionary where the key is the placeholder ID and the value is a list of plugins.

For each placeholder, if there are plugins assigned to it, the plugins are organized as a layered tree structure. Otherwise, an empty list is assigned.

The list of all plugins for each placeholder is stored in the _all_plugins_cache attribute of the placeholder, while the list of root plugins is stored in the _plugins_cache attribute

cms.utils.plugins.has_reached_plugin_limit(placeholder, plugin_type, language, template=None)#

Checks if the global maximum limit for plugins in a placeholder has been reached. If not then it checks if it has reached its maximum plugin_type limit.

Parameters: - placeholder: The placeholder object to check the limit for. - plugin_type: The type of plugin to check the limit for. - language: The language code for the plugins. - template: The template object for the placeholder. Optional.

Returns: - False if the limit has not been reached.

Raises: - PluginLimitReached: If the limit has been reached for the placeholder.

cms.utils.plugins.get_plugin_class(plugin_type: str) CMSPluginBase#

Returns the plugin class for a given plugin_type (str)

cms.utils.plugins.get_plugin_model(plugin_type: str) CMSPlugin#

Returns the plugin model class for a given plugin_type (str)

cms.utils.plugins.get_plugins_as_layered_tree(plugins)#

Given an iterable of plugins ordered by position, returns a deque of root plugins with their respective children set in the child_plugin_instances attribute.

cms.utils.plugins.copy_plugins_to_placeholder(plugins, placeholder, language=None, root_plugin=None, start_positions=None)#

Copies an iterable of plugins to a placeholder

Parameters:
  • plugins (iterable) – Plugins to be copied

  • placeholder (cms.models.pluginmodel.CMSPlugin instance) – Target placeholder

  • language (str) – target language (if no root plugin is given)

  • root_plugin

  • start_positions (int) – Cache for start positions by language

The logic of this method is the following:

  1. Get bound plugins for each source plugin

  2. Get the parent plugin (if it exists)

  3. then get a copy of the source plugin instance

  4. Set the id/pk to None to it the id of the generic plugin instance above; this will effectively change the generic plugin created above into a concrete one

  5. find the position in the new placeholder

  6. save the concrete plugin (which creates a new plugin in the database)

  7. trigger the copy relations

  8. return the plugin ids

cms.utils.plugins.downcast_plugins(plugins, placeholders=None, select_placeholder=False, request=None)#

Downcasts the given list of plugins to their respective classes. Ignores any plugins that are not available.

Parameters:
  • plugins (List[CMSPlugin]) – List of plugins to downcast.

  • placeholders (Optional[List[Placeholder]]) – List of placeholders associated with the plugins.

  • select_placeholder (bool) – If True, select_related the plugin queryset with placeholder.

  • request (Optional[HttpRequest]) – The current request.

Returns:

Generator that yields the downcasted plugins.

Return type:

Generator[CMSPlugin, None, None]

cms.utils.plugins.get_bound_plugins(plugins)#

Get the bound plugins by downcasting the plugins to their respective classes. Raises a KeyError if the plugin type is not available.

Creates a map of plugin types and their corresponding plugin IDs for later use in downcasting. Then, retrieves the plugin instances from the plugin model using the mapped plugin IDs. Finally, iterates over the plugins and yields the downcasted versions if they have a valid parent. Does not affect caching.

Parameters:

plugins (List[CMSPlugin]) – List of CMSPlugin instances.

Returns:

Generator that yields the downcasted plugins.

Return type:

Generator[CMSPlugin, None, None]

Example:

plugins = [plugin_instance1, plugin_instance2]
for bound_plugin in get_bound_plugins(plugins):
    # Do something with the bound_plugin
    pass