django-var-cms ships with a declarative, layered permissions system. EachDocumentation 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.
VarCMSModelAdmin class accepts a permissions list of RolePermission and UserPermission objects that control exactly which actions (add, list, view, edit, delete) a given user may perform on that model. Resolution runs in strict priority order: a matching UserPermission wins first, then a matching RolePermission, and if neither applies the request is denied. When permissions is left empty the system falls back to the legacy allow_add / allow_edit / allow_delete boolean flags.
Permission Classes
RolePermission
RolePermission maps a named role to a set of allowed actions. The role string is matched against the authenticated user in two ways:
"superuser"— matched whenrequest.user.is_superuserisTrue- Any other string — matched against
request.user.groups(Django group names)
The defaults are conservative:
list and view are True, while add, edit, and delete default to False. Always explicitly set every flag you care about to avoid relying on defaults as your permission matrix grows.| Field | Type | Default | Meaning |
|---|---|---|---|
role | str | — | Role name to match ("superuser" or a Django group name) |
add | bool | False | Create new records |
list | bool | True | View the paginated list |
view | bool | True | Open a record’s detail page |
edit | bool | False | Modify an existing record |
delete | bool | False | Delete a record |
UserPermission
UserPermission grants per-user overrides. It is matched by the user’s username (via request.user.get_username()) and takes priority over every role permission — a UserPermission match short-circuits all further checks immediately.
UserPermission carries the same five boolean fields as RolePermission. Use it to elevate or restrict a single account regardless of which groups they belong to.
GroupPermission
GroupPermission is a direct alias of RolePermission:
GroupPermission when you want to make it visually clear in code that the role string refers specifically to a Django auth group name, rather than the special "superuser" sentinel.
Resolution Algorithm
Whenhas_permission(request, action) is called, it delegates to resolve_permission():
- UserPermission match — scan for a
UserPermissionwhoseusernameequals the request user’s username. If found, return its value immediately. - RolePermission match — scan for a
RolePermissionwhoseroleequals the resolved role string. If found, return its value. - Default deny — if nothing matched, return
False.
Role Resolution: _get_user_role()
Before resolve_permission is called, the admin class resolves the user’s effective role string via _get_user_role():
- If
is_superuser→ role is"superuser", unconditionally. - If any of the user’s groups match a role explicitly declared in
permissions→ that group name is used (first match wins). - Otherwise → the first group the user belongs to, or
"anonymous"if they have no groups.
Override
_get_user_role() in your VarCMSModelAdmin subclass to implement custom role logic — for example, roles stored in a user profile model rather than Django groups.Legacy Fallback
Whenpermissions is an empty list, has_permission() falls back to three simple boolean flags:
list and view as always allowed. This mode is suitable for simple setups where any authenticated user should have full access, or for quick prototyping before a proper permission matrix is defined.
Complete Example — ArticleAdmin
The demo application’sArticleAdmin illustrates the full pattern: four RolePermission entries covering group-based access, plus one UserPermission that overrides delete for a specific user named alice.
Demo User Accounts
The demo application seeds the following test accounts to exercise every permission tier:| Username | Password | Role / Group | Permissions |
|---|---|---|---|
admin | admin | superuser | Full unrestricted access |
editor | editor | editor | Add + Edit (cannot delete) |
author | author | author | Add + Edit limited fields (cannot delete) |
viewer | viewer | viewer | List + View records only |
alice | alice | viewer | viewer role + delete override via UserPermission |
How alice's delete permission is resolved
How alice's delete permission is resolved
alice is a member of the viewer group. Without a UserPermission, her role would resolve to "viewer" and the RolePermission("viewer", delete=False) entry would deny delete. Because a UserPermission("alice", delete=True) entry appears in the permissions list, the resolver matches it in step 1 and returns True before even reaching the role check — giving alice delete access that her group alone would not grant.Putting It All Together
A minimal, production-ready permissions block for a content model with three roles looks like this:has_permission() returns False immediately when request.user.is_authenticated is False.