A modern, role-based Django CMS registry with rich media previews
Project description
django-var-cms
A modern, fully-customisable CMS registry for Django.
Think django-admin — but yours to control.
Quick Start
uv add django pillow whitenoise
uv run python manage.py migrate
uv run python manage.py seed_demo
uv run python manage.py runserver
# → http://127.0.0.1:8000/var-cms/
Demo accounts
| Username | Password | Role | Permissions |
|---|---|---|---|
| admin | admin | superuser | Full access |
| editor | editor | editor | Add + Edit (no delete) |
| author | author | author | Add + limited field edits |
| viewer | viewer | viewer | List + View only |
| alice | alice | viewer | + delete override via UserPermission |
Registration
Create var_cms_admin.py in any Django app — auto-discovered on startup.
from var_cms.registry import var_cms_site, VarCMSModelAdmin
from var_cms.permissions import RolePermission, UserPermission
class ArticleAdmin(VarCMSModelAdmin):
# ── List view ─────────────────────────────────────────────────────
list_display = ["title", "category__name", "author", "status", "created_at"]
list_filter = ["status", "category", "is_featured"]
search_fields = ["title", "body", "author"]
list_per_page = 25
ordering = ["-created_at"]
# ── Form ──────────────────────────────────────────────────────────
readonly_fields = ["created_at", "updated_at", "view_count"]
exclude_fields = ["internal_notes"]
# ── Role permissions ──────────────────────────────────────────────
permissions = [
RolePermission("superuser", add=True, list=True, view=True, edit=True, delete=True),
RolePermission("editor", add=True, list=True, view=True, edit=True, delete=False),
RolePermission("author", add=True, list=True, view=True, edit=True, delete=False),
RolePermission("viewer", add=False, list=True, view=True, edit=False, delete=False),
UserPermission("alice", add=True, list=True, view=True, edit=True, delete=True),
]
# ── Per-role editable fields ──────────────────────────────────────
role_editable_fields = {
"superuser": "__all__", # everything
"editor": ["title", "body", "status", "category"],
"author": ["title", "body", "status"], # can't touch slug/category
"*": [], # all others: read-only
}
var_cms_site.register(Article, ArticleAdmin)
Permissions
RolePermission — matched by Django group name or "superuser"
RolePermission("editor", add=True, list=True, view=True, edit=True, delete=False)
UserPermission — highest priority, matched by username
UserPermission("alice", add=True, list=True, view=True, edit=True, delete=True)
Overriding permission logic
class ArticleAdmin(VarCMSModelAdmin):
def has_permission(self, request, action, obj=None):
if action == "delete" and obj and obj.is_published:
return False # nobody can delete published articles
return super().has_permission(request, action, obj)
Media Features
Image preview + Cropper
- Click any image thumbnail to open the preview modal
- Crop with free/fixed aspect ratio (1:1, 16:9, 4:3, etc.)
- Rotate and flip
- Export as JPEG, PNG, or WebP
- Cropped file saved to
MEDIA_ROOT/crops/
Video / Audio player
- Native
<video>and<audio>controls in the modal
PDF viewer
- Inline
<iframe>PDF preview
File conversion (API)
- Images: JPEG ↔ PNG ↔ WebP ↔ BMP ↔ TIFF
- Audio: MP3 ↔ WAV ↔ OGG ↔ FLAC ↔ AAC (requires
ffmpeg) - Video: MP4 ↔ WebM ↔ AVI ↔ MOV (requires
ffmpeg) - PDF→PNG: page-by-page (requires
uv add pdf2image)
# Install ffmpeg (Linux)
sudo apt install ffmpeg
# PDF support
uv add pdf2image
Hooks
class ArticleAdmin(VarCMSModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).filter(site=request.site)
def save_model(self, request, obj, form, change):
if not change:
obj.created_by = request.user
obj.save()
def delete_model(self, request, obj):
obj.is_deleted = True # soft delete
obj.save()
Supported Field Types
| Type | List | Filter | Form |
|---|---|---|---|
| CharField / SlugField | ✓ | text | text input |
| TextField | ✓ truncated | text | textarea |
| IntegerField / Decimal | ✓ | min/max | number |
| BooleanField | ✓ icon | yes/no | checkbox |
| DateField / DateTimeField | ✓ | date range | date picker |
| ForeignKey | ✓ | select | select |
| choices | ✓ | select | select |
| ImageField | ✓ thumbnail + crop modal | — | file |
| FileField | ✓ preview link + modal | — | file |
| PointField / PolygonField (GIS) | ✓ geo badge | — | WKT input |
URL Structure
/var-cms/ → Dashboard
/var-cms/{app}/{model}/ → List
/var-cms/{app}/{model}/add/ → Add form
/var-cms/{app}/{model}/{pk}/ → Edit form
/var-cms/{app}/{model}/{pk}/view/ → Detail view (read-only)
/var-cms/{app}/{model}/{pk}/delete/→ Delete confirm
/var-cms/api/media/crop/ → POST: crop image
/var-cms/api/media/convert/ → POST: convert file format
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
django_var_cms-1.0.1.tar.gz
(745.5 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_var_cms-1.0.1.tar.gz.
File metadata
- Download URL: django_var_cms-1.0.1.tar.gz
- Upload date:
- Size: 745.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82fe4be9404320a5875edcdf278fe48d1676d3fff706dfc1d830c974143f1f1f
|
|
| MD5 |
c8ec6374516b888f6912ea5e2bf948ad
|
|
| BLAKE2b-256 |
e3b2fdd65d732a3cf1b31db1f1cba8f698d531b39f77d0f2ce2bc75f8e66607a
|
File details
Details for the file django_var_cms-1.0.1-py3-none-any.whl.
File metadata
- Download URL: django_var_cms-1.0.1-py3-none-any.whl
- Upload date:
- Size: 757.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c11b1c425820e39661236ee107417a0543eb40d90024e6a940a206e4cf49da06
|
|
| MD5 |
e0c5994018180f2f425910118affe2f4
|
|
| BLAKE2b-256 |
bebd13c3dc8c73772d37ccb21c3dc78d262378492864231605de519d38acfa50
|