Using the contrib.categories module
===================================
Flexible category system for organizing content, used as a target of a
:class:`~djangocms_custom_content.relations.RelationField`.
Installation
------------
.. code-block:: python
INSTALLED_APPS = [
"djangocms_custom_content",
"djangocms_custom_content.contrib.categories",
]
.. code-block:: bash
python manage.py migrate
Models
------
**FlatCategory** - A simple category model (inherits from ``AbstractCustomContent``)
Fields: ``title``, ``slug``, ``is_featured``
``FlatCategory`` itself declares no relations. Instead, ``BlogPost`` (the
grouper) opts in by declaring a ``RelationField`` that targets ``FlatCategory``
and invites a reverse ``blog_posts`` accessor onto it (see
``contrib/blog/models.py``):
.. code-block:: python
class BlogPost(AbstractCustomGrouper):
categories = RelationField(
"djangocms_custom_content_categories.FlatCategory",
related_name="blog_posts",
)
Usage
-----
Create a category:
.. code-block:: python
from djangocms_custom_content.contrib.categories.models import FlatCategory
category = FlatCategory.objects.create(
title="Technology",
slug="technology",
is_featured=True,
)
Link categories to blog posts:
.. code-block:: python
from djangocms_custom_content.contrib.blog.models import BlogPost
blog_post = BlogPost.objects.first()
# Add a category
blog_post.categories.add(category)
# Get all categories
categories = blog_post.categories.all()
# Remove a category
blog_post.categories.remove(category)
# Clear all categories
blog_post.categories.clear()
The reverse accessor invited by ``related_name="blog_posts"`` lets you go the
other way without ``FlatCategory`` declaring anything:
.. code-block:: python
category = FlatCategory.objects.first()
category.blog_posts.all() # BlogPost groupers in this category
Plugins
-------
- ``CategoryListPlugin`` ("Category list", model ``FlatCategoryList``) -
Display categories, optionally featured-only and limited
Admin
-----
``FlatCategory`` has **no grouper**, so it is registered with a plain
``admin.ModelAdmin`` (search by title and slug) — not the grouper admin.
What this app demonstrates
--------------------------
``FlatCategory`` is a **grouper-less content model used as a relation target**:
it opts out of versioning and app hooks, yet ``BlogPost`` can still relate to it
and gets a ``blog_posts`` reverse accessor on it for free.
In Templates
~~~~~~~~~~~~
.. code-block:: django
{% for category in blog_post.categories.all %}
{{ category.title }}
{% endfor %}
{% for category in blog_post.categories.all %}
{% if category.is_featured %}
{{ category.title }}
{% endif %}
{% endfor %}
See Also
--------
- :doc:`../how-to/m2m_relations` - Declaring ``RelationField`` relations
- :doc:`../explanation/relationships` - How grouper relations work