Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/rahul-baberwal/django-var-cms/llms.txt

Use this file to discover all available pages before exploring further.

When var_cms starts up, it automatically iterates every entry in INSTALLED_APPS and attempts to import {app}.var_cms_admin for each app. This means you never need to manually wire up any import — simply create the file in your app directory and django-var-cms will discover and load it at startup, registering all of your models with the global var_cms_site registry.

Creating var_cms_admin.py

1

Create the file

Inside any Django app that you want to expose in the CMS, create a file named var_cms_admin.py at the root of that app:
myapp/
├── models.py
├── views.py
├── var_cms_admin.py   ← create this
└── ...
2

Import the registry and base class

At the top of your new file, import the global site instance and the base admin class:
# myapp/var_cms_admin.py
from var_cms.registry import var_cms_site, VarCMSModelAdmin
from .models import Article, Category
3

Define an admin class

Subclass VarCMSModelAdmin and declare your configuration as class attributes:
class ArticleAdmin(VarCMSModelAdmin):
    list_display  = ["title", "category", "author", "status", "created_at"]
    list_filter   = ["status", "category"]
    search_fields = ["title", "body", "author"]
    ordering      = ["-created_at"]
    icon          = "file-text"
4

Register with var_cms_site

Call var_cms_site.register() at module level, passing the model class and your admin class:
var_cms_site.register(Article, ArticleAdmin)

Class Attributes Reference

Every attribute on VarCMSModelAdmin is optional — sensible defaults are applied automatically. The table below covers all configuration options.
AttributeTypeDefaultDescription
list_displaylist[str]First 6 model fieldsColumn names shown in the list view table
list_filterlist[str][]Fields rendered as sidebar filter widgets
search_fieldslist[str][]Fields searched with a case-insensitive icontains lookup
orderinglist[str][]Default queryset ordering (prefix - for descending)
list_per_pageint25Number of rows per page in the list view
list_select_relatedboolFalseCall select_related() on the queryset to reduce N+1 queries
list_image_widthint38Pixel width of image thumbnails in list columns
list_image_heightint38Pixel height of image thumbnails in list columns
readonly_fieldslist[str][]Fields always displayed but never editable
exclude_fieldslist[str][]Fields hidden from the form entirely
html_fieldslist[str][]TextField names that get a Quill.js rich text editor
regex_validatorsdict[str, tuple]{}Per-field (pattern, message) tuples applied client-side and server-side
form_field_widthsdict[str, str]{}Named grid-width preset per field ("full", "half", etc.)
form_field_rowslist[list[str]][]Groups of fields sharing a single visual row with equal column widths
form_field_widgetsdict[str, str]{}Custom widget type per field ("select_search", "multiselect", etc.)
form_widget_classesdict[str, str]{}CSS classes appended to the input/select/textarea element
form_field_placeholdersdict[str, str]{}HTML placeholder attribute per field
form_field_help_textsdict[str, str]{}Help text shown beneath each field (overrides model-level help_text)
form_field_classesdict[str, str]{}CSS classes added to the field’s wrapper <div>
form_field_stylesdict[str, str]{}Inline CSS styles added to the field’s wrapper <div>
iconstr""Lucide icon name displayed beside the model in the sidebar and dashboard
dashboard_cardboolFalseWhether this model appears as a card on the dashboard
card_buttonslist[dict][]Navigation buttons on the dashboard card (see Custom Actions)
custom_object_actionslist[dict][]Per-object action buttons on list and detail views (see Custom Actions)

The icon attribute

The icon value is a Lucide icon name — the same string you would use in <i data-lucide="...">. You can browse the full icon catalogue at lucide.dev. Common examples for CMS models:
class ArticleAdmin(VarCMSModelAdmin):
    icon = "file-text"      # articles / posts

class CategoryAdmin(VarCMSModelAdmin):
    icon = "folder"         # categories / folders

class MediaAssetAdmin(VarCMSModelAdmin):
    icon = "image"          # media / images

class PageAdmin(VarCMSModelAdmin):
    icon = "book-open"      # pages / documents

The dashboard_card attribute

By default every registered model is hidden from the dashboard (dashboard_card = False). Set it to True to show a stats card for that model on the home screen:
class InvoiceAdmin(VarCMSModelAdmin):
    dashboard_card = True    # will appear on the dashboard

class AuditLogAdmin(VarCMSModelAdmin):
    dashboard_card = False   # hidden from dashboard (default)
You can also control visibility globally from settings.py without touching individual admin classes:
# settings.py

# Hide specific model cards by app.model or model name:
VAR_CMS_HIDDEN_DASHBOARD_CARDS = ["demo.category", "logentry"]

# Show ONLY these model cards (all others are hidden):
VAR_CMS_SHOWN_DASHBOARD_CARDS  = ["article", "invoice"]

Unregistering a Model

Call var_cms_site.unregister() to remove a previously registered model. This is useful when you want to override a registration made in a third-party app:
from var_cms.registry import var_cms_site, VarCMSModelAdmin
from third_party_app.models import SomeModel

# Remove the default registration
var_cms_site.unregister(SomeModel)

# Optionally re-register with your own admin class
class MySomeModelAdmin(VarCMSModelAdmin):
    list_display = ["name", "created_at"]

var_cms_site.register(SomeModel, MySomeModelAdmin)

save_model() and delete_model() Hooks

VarCMSModelAdmin exposes two lifecycle hooks that you can override for custom save or delete behaviour.

save_model(request, obj, form, change)

Called whenever an object is saved (both create and update). The default implementation checks whether the model has a slug field and, if it is empty, auto-generates a slug from the first non-empty value found in title, name, or headline, then calls obj.save().
ParameterDescription
requestThe current HttpRequest
objThe unsaved model instance
formThe bound ModelForm instance
changeTrue when updating an existing record; False on create
class ArticleAdmin(VarCMSModelAdmin):
    def save_model(self, request, obj, form, change):
        if not change:
            # Auto-assign the current user as author on creation
            obj.author = request.user.get_username()
        super().save_model(request, obj, form, change)

delete_model(request, obj)

Called when an object is deleted. The default implementation calls obj.delete().
class ArticleAdmin(VarCMSModelAdmin):
    def delete_model(self, request, obj):
        # Archive instead of hard-delete
        obj.status = "archived"
        obj.save()

Complete Example

The following example is taken directly from the demo app and shows CategoryAdmin and ArticleAdmin with realistic field configurations, permissions, and lifecycle hooks.
# demo/models.py
from django.db import models

class Category(models.Model):
    name        = models.CharField(max_length=100)
    slug        = models.SlugField(unique=True)
    description = models.TextField(blank=True)
    is_active   = models.BooleanField(default=True)
    created_at  = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name = "category"
        verbose_name_plural = "categories"
        ordering = ["name"]

    def __str__(self):
        return self.name


STATUS_CHOICES = [
    ("draft",     "Draft"),
    ("published", "Published"),
    ("archived",  "Archived"),
]

class Article(models.Model):
    title       = models.CharField(max_length=255)
    slug        = models.SlugField(unique=True)
    category    = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
    author      = models.CharField(max_length=120)
    body        = models.TextField()
    status      = models.CharField(max_length=20, choices=STATUS_CHOICES, default="draft")
    is_featured = models.BooleanField(default=False)
    view_count  = models.PositiveIntegerField(default=0)
    rating      = models.DecimalField(max_digits=3, decimal_places=1, null=True, blank=True)
    created_at  = models.DateTimeField(auto_now_add=True)
    updated_at  = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = "article"
        verbose_name_plural = "articles"
        ordering = ["-created_at"]

    def __str__(self):
        return self.title
If you want every model in an app to use default settings with no customisation, you can pass VarCMSModelAdmin directly as the second argument — or omit it entirely, since None defaults to the base class:
var_cms_site.register(MySimpleModel, VarCMSModelAdmin)

Build docs developers (and LLMs) love