Register the grouper admin

Content is edited through the grouper admin, not through a separate admin for the content model. Registering the grouper with django CMS’s GrouperModelAdmin plus CustomGrouperAdminMixin is what wires up versioning, frontend editing, the toolbar entries, and the relation autocomplete widgets. A plain admin.ModelAdmin gives you none of that.

The pattern

from cms.admin.placeholderadmin import FrontendEditableAdminMixin
from cms.admin.utils import GrouperModelAdmin
from django.contrib import admin

from djangocms_custom_content.admin import CustomGrouperAdminMixin
from .models import Article

@admin.register(Article)
class ArticleAdmin(CustomGrouperAdminMixin, FrontendEditableAdminMixin, GrouperModelAdmin):
    grouper_field_name = "article"   # the FK on ArticleContent pointing at Article

    list_display = ("content__title", "content__slug")
    search_fields = ("content__title", "content__body")
    prepopulated_fields = {"content__slug": ("content__title",)}

Key points:

  • Register only the grouper. You do not register the content model yourself — versioning and the admin handle the content edit views for you.

  • Reach content fields with the content__ prefix in list_display, search_fields, prepopulated_fields and fieldsets. The current language/version content is resolved behind that prefix.

  • Tell the admin where the content is with either grouper_field_name (the name of the foreign key on the content model, as above) or content_model = ArticleContent.

  • Add FrontendEditableAdminMixin only if the content model enables enable_frontend_editing.

  • If the content model has a language field, the language grouping column is added to the admin automatically — you do not set extra_grouping_fields.

Relation fields in the admin

Any RelationField on the grouper is rendered automatically as an autocomplete multi-select, drag-sortable when the relation is declared ordered=True (see RelationAdminMixin, which CustomGrouperAdminMixin already includes). Two things are required for it to work:

  • The target model must have a registered admin that defines search_fields — the autocomplete searches the target through those fields (content__ prefixes are routed to the target’s content model).

  • The owning grouper admin must be a GrouperModelAdmin (so the autocomplete endpoint is served).

Opt out per admin with relation_autocomplete = False.

Plain models

A content model that has no grouper (for example a simple taxonomy like FlatCategory) is just an ordinary model — register it with a normal admin.ModelAdmin. The grouper admin pattern only applies to grouper/content pairs.

See Also