The Resource class
A Resource is the single declarative description of everything Anvil generates
for a model. Define it once in <app>/resources.py; it registers itself
automatically (the same way Django's own admin.py autodiscovery works) —
no manual registration call needed.
from django_anvil.core.resource import Resource
from django_anvil.mixins import SoftDeleteViewSetMixin, TimestampedSerializerMixin
from .models import Product
class ProductResource(Resource):
model = Product
fields = ["id", "name", "price", "stock", "is_active", "created_at"]
read_only_fields = ["id", "created_at"]
searchable = ["name"]
filters = ["is_active"]
sortable = ["price", "created_at"]
mixins = [SoftDeleteViewSetMixin, TimestampedSerializerMixin]
# optional -- see their own pages
permissions = {}
tenant_scoped = False
audited = False
Field declarations
| Attribute | Controls |
|---|---|
fields | Every field exposed on the serializer (readable + writable, unless also listed in read_only_fields) |
read_only_fields | Visible in responses, rejected in request bodies |
searchable | Wired into DRF's SearchFilter (?search=) |
filters | Wired into django-filter's DjangoFilterBackend (?field=value) |
sortable | Wired into DRF's OrderingFilter (?ordering=-field) |
These wrap django-filter and DRF's own filter backends directly — Anvil doesn't
reimplement filtering, it just wires the declaration through.
What gets generated
One anvil resource Product call produces:
| File | Content |
|---|---|
api/serializers.py | ProductSerializer(ModelSerializer) |
api/views.py | ProductViewSet(ModelViewSet) — full CRUD, filters wired |
api/urls.py | Router registration for the viewset |
admin.py | ProductAdmin registration, merged into the file if it already has content |
tests/test_product_api.py | List/create/retrieve/delete tests against a real running API, using the DRF test client |
Every generated file is normal, readable Django/DRF code — no runtime magic, no base classes you can't read the source of. Edit it by hand afterward like anything else in your project; that's the point.
Mixins
django_anvil.mixins ships real, reusable behavior — not codegen templates. A
Resource opts in by listing them in mixins; the generator wires the matching one
into the serializer or viewset base classes based on its name
(...SerializerMixin vs ...ViewSetMixin).
SoftDeleteViewSetMixin
Requires an is_deleted boolean field. Excludes soft-deleted rows from every
query and turns DELETE into setting the flag instead of removing the row.
class SoftDeleteViewSetMixin:
def get_queryset(self):
return super().get_queryset().filter(is_deleted=False)
def perform_destroy(self, instance):
instance.is_deleted = True
instance.save(update_fields=["is_deleted"])
OwnerScopedViewSetMixin
Requires an owner FK to the user model. Scopes every action —
including list/retrieve — to request.user's own rows, and auto-assigns the owner
on create. Fails closed (empty, not a crash) for anonymous requests.
This means "your own stuff only", for every action — like a private notes or wishlist
feature. Don't pair it with permissions = {"view": "public"}" expecting a
public-reads/private-writes feed (e.g. product reviews visible to everyone); that needs a
different get_queryset than this mixin provides out of the box.
TimestampedSerializerMixin
Marks created_at/updated_at read-only on the serializer if
present, so callers can't set timestamps through the API even if you forgot to list them in
read_only_fields.
Write your own the same way: a plain class following the naming convention, mixed into the
generated ModelViewSet/ModelSerializer. Multiple mixins compose via
normal Python MRO — put the one that should run last-in-chain closest to the base class.
Multiple Resources in one app
serializers.py, views.py, urls.py, and
admin.py are shared, per-app files. Generating a second Resource
(Coupon, say) into an app that already has Product adds its classes
alongside — merges the router registration, folds new imports into existing
from ..models import ... lines (sorted, deduplicated), and never disturbs
Product's own generated code.
python manage.py anvil resource Product
python manage.py anvil resource Coupon # adds CouponSerializer/ViewSet alongside Product's