Demo project
demo/ in the repository is a full, runnable Django project proving every
feature end to end — not a toy. Four Resources in one app:
The four demo Resources
| Resource | Demonstrates |
|---|---|
Product | RBAC (mixed public/codename rules), tenancy, SoftDeleteViewSetMixin, TimestampedSerializerMixin |
Coupon | RBAC (all codenames, no public), tenancy, audit history |
Review | OwnerScopedViewSetMixin, the "authenticated" permission rule, deliberately not tenant-scoped |
Tag | Added live via anvil ai --apply during development — proof the AI engine's propose→approve→write loop produces a genuinely working Resource, not just a plausible-looking diff |
Run it yourself
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
cd demo
python manage.py migrate
python -m pytest products/ -v # 49 tests
python manage.py anvil doctor
seed_demo
Populates organizations, roles, users, and sample data — safe to re-run (everything is
get_or_create'd):
python manage.py seed_demo
python manage.py runserver
Demo accounts
| Account | What it can do |
|---|---|
admin | Superuser — full Django admin access |
alice | Acme Manager — sees only Acme's Products/Coupons/Tags, full CRUD |
bob | Globex Manager — sees only Globex's, full CRUD |
carol | Acme Staff — view-only; create/update/delete all 403 |
dave | No organization — sees nothing (tenancy fails closed) |
Password for all: demo-pass-1234. seed_demo prints this list
itself, so you never need to remember it.
Try it
curl http://127.0.0.1:8000/api/products/
# [] -- anonymous, no organization context
curl -u alice:demo-pass-1234 http://127.0.0.1:8000/api/products/
# Acme's products only
curl -u carol:demo-pass-1234 -X POST http://127.0.0.1:8000/api/products/ \
-d 'name=Test&price=1&stock=1&is_active=true'
# 403 -- Staff, view-only
Or open http://127.0.0.1:8000/admin/ to browse Roles, Organizations,
Products, Coupons (with a "History" button from audit logging), Reviews, and Tags directly.
Every one of these behaviors was verified against a live running server with real curl
requests and a real browser session — not just pytest — while the demo was
being built. Several real bugs (a crash on anonymous ownership-scoped requests, a script
that fell into its own tenancy footgun, a missing integer field type) were caught exactly
this way.