8000 docs: Update how-to use placeholder in custom models by fsbraun · Pull Request #7742 · django-cms/django-cms · GitHub
[go: up one dir, main page]

Skip to content

docs: Update how-to use placeholder in custom models #7742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Update how-to use placeholders within custom models
  • Loading branch information
fsbraun authored and marksweb committed Jan 10, 2024
commit 93cfe7980b5cd47b128f2947b3c09aa5278ac28e
2 changes: 1 addition & 1 deletion cms/app_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class CMSAppConfig():

The second functionality that django CMS offers is attaching Model objects to the toolbar. To use
this functionality, set list the Model classes in ``cms_toolbar_enabled_models`` and have
`cms_enabled = True```
``cms_enabled = True``
"""

def __init__(self, django_app_config):
Expand Down
12 changes: 6 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@
"sphinxext.opengraph",
]
intersphinx_mapping = {
'python': ('http://docs.python.org/3/', None),
'django': ('https://docs.djangoproject.com/en/3.2/', 'https://docs.djangoproject.com/en/2.2/_objects/'),
'classytags': ('http://readthedocs.org/docs/django-classy-tags/en/latest/', None),
'sekizai': ('http://readthedocs.org/docs/django-sekizai/en/latest/', None),
'treebeard': ('http://django-treebeard.readthedocs.io/en/latest/', None),
'python': ('https://docs.python.org/3/', None),
'django': ('https://docs.djangoproject.com/en/4.2/', 'https://docs.djangoproject.com/en/4.2/_objects/'),
'classytags': ('https://django-classy-tags.readthedocs.io/en/latest/', None),
'sekizai': ('https://django-sekizai.readthedocs.io/en/latest/', None),
'treebeard': ('https://django-treebeard.readthedocs.io/en/latest/', None),
}

autodoc_member_order = "groupwise"
Expand Down Expand Up @@ -152,7 +152,7 @@
html_theme_options = {
"navigation_with_keys": True,
}
except ImportError:
except ImportError:
html_theme = 'default'


Expand Down
231 changes: 160 additions & 71 deletions docs/how_to/01-placeholders.rst
57AE
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
.. _placeholders_outside_cms:

#######################################
How to use placeholders outside the CMS
#######################################
=======================================

Placeholders are special model fields that django CMS uses to render
user-editable content (plugins) in templates. That is, it's the place where a
user can add text, video or any other plugin to a webpage, using the same
frontend editing as the CMS pages.
Placeholders are special model fields that django CMS uses to render user-editable
content (plugins) in templates. That is, it's the place where a user can add text, video
or any other plugin to a webpage, using the same frontend editing as the CMS pages.

.. versionchanged:: 4.0

Placeholders can be viewed as containers for :class:`~cms.models.pluginmodel.CMSPlugin` instances, and
can be used outside the CMS in custom applications using the
:class:`~cms.models.fields.PlaceholderRelationField`.
Since django CMS 4.0 the toolbar offers preview and edit endpoints for Django models
which contain Placeholders.

- This allows for models (such as django CMS Alias) which do not have a user-facing
view to still contain placeholders.
- It, however, requires the registration of frontend-editable models with django
CMS.
- Also, views need to tell the toolbar if they contain a frontend-editable model.

By defining a :class:`~cms.models.fields.PlaceholderRelationField` on a
custom model you can take advantage of the full power of :class:`~cms.models.pluginmodel.CMSPlugin` in one or more placeholders.
Placeholders can be viewed as containers for :class:`~cms.models.pluginmodel.CMSPlugin`
instances, and can be used outside the CMS in custom applications using the
:class:`~cms.models.fields.PlaceholderRelationField`.

By defining a :class:`~cms.models.fields.PlaceholderRelationField` on a custom model you
can take advantage of the full power of :class:`~cms.models.pluginmodel.CMSPlugin` in
one or more placeholders.

.. warning::

Django CMS 3.x used a different way of integrating placeholders. It's ``PlaceholderField("slot_name")`` needs to be changed into a ``PlaceholderRelationField`` (available since django CMS 4.x).
Django CMS 3.x used a different way of integrating placeholders. It's
``PlaceholderField("slot_name")`` needs to be changed into a
``PlaceholderRelationField`` (available since django CMS 4.x).

***********
Get started
***********
-----------

You need to define a :class:`~cms.models.fields.PlaceholderRelationField` on the model you would like to
use::
You need to define a :class:`~cms.models.fields.PlaceholderRelationField` on the model
you would like to use:

.. code-block::

from django.db import models
from cms.models.fields import PlaceholderRelationField
Expand All @@ -44,34 +54,40 @@ use::

# your methods

The :class:`~cms.models.fields.PlaceholderRelationField` can reference more than one
field. It is customary to add (cached) properties to the model referring to specific
placeholders. The utility function
:func:`~cms.utils.placeholder.get_placeholder_from_slot` retrieves a placeholder object
based on its slot name.

The :class:`~cms.models.fields.PlaceholderRelationField` can reference more than one field. It is customary to add (cached) properties to the model referring to specific placeholders. The utility function :func:`~cms.utils.placeholder.get_placeholder_from_slot` retrieves a placeholder object based on its slot name.

The ``slot`` is used in templates, to determine where the placeholder's plugins should appear
in the page, and in the placeholder configuration :setting:`CMS_PLACEHOLDER_CONF`, which determines
which plugins may be inserted into this placeholder.
The ``slot`` is used in templates, to determine where the placeholder's plugins should
appear in the page, and in the placeholder configuration :ref:`CMS_PLACEHOLDER_CONF`,
which determines which plugins may be inserted into this placeholder.

.. note::

If you add a PlaceholderRelationField to an existing model, you'll be able to see
the placeholder in the frontend editor only after saving the relevant instance.

Admin Integration
=================
~~~~~~~~~~~~~~~~~

.. versionchanged:: 4.0

Since django CMS version 4 :class:`~cms.admin.placeholderadmin.PlaceholderAdminMixin` is not required any more. For now, it still exists as an empty mixin but will be removed in a future version.

Since django CMS version 4 :class:`~cms.admin.placeholderadmin.PlaceholderAdminMixin` is
not required any more. For now, it still exists as an empty mixin but will be removed in
a future version.

I18N Placeholders
=================
~~~~~~~~~~~~~~~~~

Placeholders and plugins within them support multiple languages out of the box.

If you need other fields translated as well, django CMS has support for `django-hvad`_. If you use
a ``TranslatableModel`` model be sure to **not** include the placeholder fields amongst the
translated fields::
If you need other fields translated as well, django CMS has support for django-hvad_. If
you use a ``TranslatableModel`` model be sure to **not** include the placeholder fields
amongst the translated fields:

.. code-block::

class MultilingualExample1(TranslatableModel):
translations = TranslatedFields(
Expand All @@ -87,85 +103,159 @@ translated fields::
def __str__(self):
return self.title


Templates
=========
~~~~~~~~~

To render the placeholder in a template you use the :ttag:`render_placeholder` tag from the
:mod:`~cms.templatetags.cms_tags` template tag library:
To render the placeholder in a template you use the :ref:`render_placeholder` tag from
the :mod:`~cms.templatetags.cms_tags` template tag library:

.. code-block:: html+django

{% load cms_tags %}

{% render_placeholder mymodel_instance.my_placeholder "640" %}

The :ttag:`render_placeholder` tag takes the following parameters:
The :ref:`render_placeholder` tag takes the following parameters:

- :class:`~cms.models.fields.PlaceholderField` instance
- ``width`` parameter for context sensitive plugins (optional)
- ``language`` keyword plus ``language-code`` string to render content in the specified
language (optional)

The view in which you render your placeholder field must return the :class:`request
<django.http.HttpRequest>` object in the context. The frontend editing and preview
endpoints require a view to render an object. This method takes the request and the
object as parameter (see example below: ``render_my_model``).

* :class:`~cms.models.fields.PlaceholderField` instance
* ``width`` parameter for context sensitive plugins (optional)
* ``language`` keyword plus ``language-code`` string to render content in the
specified language (optional)
If the object has an user-facing view it typically is identical to the preview and
editing endpoints, but has to get the object from the URL (e.g., by its primary key).
**It also needs to set the toolbar object, so that the toolbar will have Edit and
Preview buttons:**

The view in which you render your placeholder field must return the
:class:`request <django.http.HttpRequest>` object in the context. This is
typically achieved in Django applications by using :class:`~django.template.RequestContext`::
.. code-block:: python

from django.shortcuts import get_object_or_404, render


def render_my_model(request, obj):
return render(
request,
"my_model_detail.html",
{
"object": obj,
},
)


def my_model_detail(request, id):
object = get_object_or_404(MyModel, id=id)
return render(request, 'my_model_detail.html', {
'object': object,
})
obj = get_object_or_404(MyModel, id=id) # Get the object (here by id)
request.toolbar.set_object(obj) # Announce the object to the toolbar
return render_my_model(request, obj) # Same as preview rendering

.. note::

If you want to render plugins from a specific language, you can use the tag like
this:

.. code-block:: html+django

{% load cms_tags %}

If you want to render plugins from a specific language, you can use the tag
like this:
{% render_placeholder mymodel_instance.my_placeholder language 'en' %}

Adding the slots to the model
-----------------------------

To let django CMS' frontend editor know which placeholders it contains, declare them in
a second template, only needed for rendering the structure mode, called, say,
``templtes/my_app/my_model_structure.html``:

.. code-block:: html+django

{% load cms_tags %}
{% placeholder "placeholder_1" %}

The important bit is to include all slot names for the model in the structure template.
Other parts of the templte are not necessary.

Add the structure mode template to the model
--------------------------------------------

Let the model know about this template by declaring the ``get_template()`` method:

.. code-block::

class MyModel(models.Model):
...

def get_template(self):
return "my_app/my_model_structure.html"

{% render_placeholder mymodel_instance.my_placeholder language 'en' %}
...

Registering the model for frontend editing
------------------------------------------

.. versionadded:: 4.0

The final step is to register the model for frontend editing. Since django CMS 4 this is
done by adding a :class:`~cms.app_base.CMSAppConfig` class to the app's `cms_config.py` file:

.. code-block::

from cms.app_base import CMSAppConfig
from . import models, views


class MyAppConfig(CMSAppConfig):
cms_enabled = True
cms_toolbar_enabled_models = [(models.MyModel, views.render_my_model)]

*******************************
Adding content to a placeholder
*******************************
-------------------------------

Placeholders can be edited from the frontend by visiting the page displaying your model (where you
put the :ttag:`render_placeholder` tag), then appending ``?toolbar_on`` to the page's URL.
Placeholders can be edited from the frontend by visiting the page displaying your model
(where you put the :ref:`render_placeholder` tag), then appending ``?toolbar_on`` to the
page's URL.

This will make the frontend editor top banner appear (and if necessary will require you to login).
This will make the frontend editor top banner appear (and if necessary will require you
to login).

Once in frontend editing mode, the interface for your application's ``PlaceholderFields`` will work
in much the same way as it does for CMS Pages, with a switch for Structure and Content modes and so
on.
Once in frontend editing mode, the interface for your application's
``PlaceholderFields`` will work in much the same way as it does for CMS Pages, with a
switch for Structure and Content modes and so on.

.. _placeholder_object_permissions:

Permissions
===========
~~~~~~~~~~~

To be able to edit a placeholder user must be a ``staff`` member and needs either edit permissions
on the model that contains the :class:`~cms.models.fields.PlaceholderRelationField`, or permissions for
that specific instance of that model. Required permissions for edit actions are:
To be able to edit a placeholder user must be a ``staff`` member and needs either edit
permissions on the model that contains the
:class:`~cms.models.fields.PlaceholderRelationField`, or permissions for that specific
instance of that model. Required permissions for edit actions are:

* to ``add``: require ``add`` **or** ``change`` permission on related Model or instance.
* to ``change``: require ``add`` **or** ``change`` permission on related Model or instance.
* to ``delete``: require ``add`` **or** ``change`` **or** ``delete`` permission on related Model
or instance.
- to ``add``: require ``add`` **or** ``change`` permission on related Model or instance.
- to ``change``: require ``add`` **or** ``change`` permission on related Model or
instance.
- to ``delete``: require ``add`` **or** ``change`` **or** ``delete`` permission on
related Model or instance.

With this logic, an user who can ``change`` a Model's instance but can not ``add`` a new
Model's instance will be able to add some placeholders or plugins to existing Model's instances.
Model's instance will be able to add some placeholders or plugins to existing Model's
instances.

Model permissions are usually added through the default Django ``auth`` application and its admin
interface. Object-level permission can be handled by writing a custom authentication backend as
described in `django docs
Model permissions are usually added through the default Django ``auth`` application and
its admin interface. Object-level permission can be handled by writing a custom
authentication backend as described in `django docs
<https://docs.djangoproject.com/en/stable/topics/auth/customizing/#handling-object-permissions>`_

For example, if there is a ``UserProfile`` model that contains a ``PlaceholderRelationField`` then the
custom backend can refer to a ``has_perm`` method (on the model) that grants all rights to current
user only based on the user's ``UserProfile`` object::
For example, if there is a ``UserProfile`` model that contains a
``PlaceholderRelationField`` then the custom backend can refer to a ``has_perm`` method
(on the model) that grants all rights to current user only based on the user's
``UserProfile`` object:

.. code-block::

def has_perm(self, user_obj, perm, obj=None):
if not user_obj.is_staff:
Expand All @@ -175,5 +265,4 @@ user only based on the user's ``UserProfile`` object::
return True
return False


.. _django-hvad: https://github.com/kristianoellegaard/django-hvad
1 change: 1 addition & 0 deletions docs/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ django~=3.2
git+https://github.com/django-cms/django-cms@release/4.1.x
codespell
pip-tools
docstrfmt
Loading
0