You can also create groups programmatically in a migration or management command: Group.objects.get_or_create(name="manager")
Assigning Users to Roles
Assign a user to a group in Django Admin:
Go to /admin/auth/user/ → click a user
Scroll to Groups section
Select your group (e.g. manager) and save
That user will now have the permissions you defined in RolePermission("manager", ...).
Per-user override (no group needed)
If you want to grant one specific user extra access independently of their group, use UserPermission:
from var_cms.permissions import UserPermission
permissions = [
RolePermission("viewer", add=False, list=True, view=True, edit=False, delete=False),
UserPermission("alice", add=True, list=True, view=True, edit=True, delete=True),
# alice gets full access even though she may be in the viewer group
]
Security
Authentication
All CMS views require login — unauthenticated users are redirected to the login page.
Optional OTP 2FA (email-based 6-digit code) via VAR_CMS_ENABLE_OTP = True.
Passwords are hashed using Django's standard PBKDF2 algorithm.
Authorization
Every view (add / list / view / edit / delete) checks permission before rendering.
Unauthorized access raises PermissionDenied (HTTP 403), never silently redirects.
Edit button is shown as disabled (not hidden) in the list view so users understand the restriction.
Field-level: role_editable_fields restricts which fields each role may modify on the form.
readonly_fields are always non-editable regardless of role.
CSRF & XSS
All forms include Django's CSRF token.
All output is auto-escaped by Django's template engine. Only explicitly marked safe values (file previews, icons) bypass escaping.
File Uploads
Files are validated by Django's ImageField / FileField.
Uploads go to your configured MEDIA_ROOT — never executed as code.
Image crop and convert endpoints require login and operate only on files already in MEDIA_ROOT.
Always run with DEBUG = False in production and set a strong SECRET_KEY. Use HTTPS to protect sessions.
Form Column Widths
Use form_field_widths to set how wide each field is in the 12-column grid.
Use form_field_rows to place multiple fields side-by-side in one visual row. Fields in the same row split the width equally.
class CustomerAdmin(VarCMSModelAdmin):
form_field_rows = [
["first_name", "last_name"], # 2 cols → each takes half
["mobile", "email", "date_of_birth"], # 3 cols → each takes one-third
["city", "state", "country", "pin"], # 4 cols → each takes one-fourth
]
form_field_rows takes priority over form_field_widths for listed fields.
Dropdown Widget Types
Use form_field_widgets to control the dropdown style per field.
Value
Renders as
Best for
"select"
Standard HTML select
Short choice lists (default)
"select_search"
Searchable dropdown
ForeignKey with many options
"multiselect"
Checkbox list
ManyToManyField
"multiselect_search"
Checkbox list + search
ManyToManyField with many items
class OrderAdmin(VarCMSModelAdmin):
form_field_widgets = {
"status": "select", # plain dropdown (default)
"customer": "select_search", # searchable dropdown
"tags": "multiselect_search", # checkbox list with search
}
Placeholders & Help Text
class ArticleAdmin(VarCMSModelAdmin):
form_field_placeholders = {
"title": "Enter the article headline…",
"slug": "auto-generated-from-title",
}
form_field_help_texts = {
"slug": "URL-safe identifier. Leave blank to auto-generate.",
"body": "Supports full HTML via the Quill editor.",
}
# HTML rich text editor on specific fields:
html_fields = ["body", "description"]
# Regex pattern validation:
regex_validators = {
"slug": (r"^[a-z0-9-]+$", "Only lowercase letters, numbers, hyphens."),
}
Dashboard Cards
By default, dashboard cards are hidden (dashboard_card = False). Set it to True explicitly to show them, or configure globally in settings.py.
Per model: show or hide from dashboard
# Per model: show on dashboard (default is False, so set True to show)
class InvoiceAdmin(VarCMSModelAdmin):
dashboard_card = True # will appear on dashboard
# Per model: hide from dashboard (default)
class LogAdmin(VarCMSModelAdmin):
dashboard_card = False # won't appear on dashboard
Globally in settings.py
# settings.py
# Hide specific cards:
VAR_CMS_HIDDEN_DASHBOARD_CARDS = ["logentry", "demo.category"]
# Show ONLY these cards:
VAR_CMS_SHOWN_DASHBOARD_CARDS = ["invoice", "customer"] # show ONLY these
Custom card buttons
class InvoiceAdmin(VarCMSModelAdmin):
card_buttons = [
{"label": "All Invoices", "action": "list"},
{"label": "New Invoice", "action": "add"},
{"label": "Reports", "url": "/reports/", "class": "btn-ghost"},
]
Accent color is an HSL triplet (hue, saturation%, lightness%) — e.g. purple is 250, 95%, 72%, teal is 180, 60%, 50%.
Live Accent Color Theme Preview
Pick a custom color or choose one of our harmonized presets to see the entire CMS theme adapt immediately. Once you find a look you love, copy the configuration snippet below for your settings.py.