Skip to main content

Basic usage

A resource is one ModelAdmin subclass. It names the model, lists the fields, and the library derives the REST endpoints and the schema the frontend renders from that.

# admin.py
from habarico import ModelAdmin

from .models import Product


class ProductAdmin(ModelAdmin):
model = Product
fields = ["name", "sku", "description", "status", "category", "price", "stock", "created_at"]
read_only_fields = ["created_at"]
search_fields = ["name", "description"]
filter_fields = ["status", "category", "price"]
ordering_fields = ["name", "price", "created_at"]

That is a complete admin screen: a list with filters and sorting, a detail, a create and an edit form.

Attributes at a glance​

AttributePurpose
modelThe Django model this admin exposes
fieldsFields in the generated serializer and, by default, in every view
read_only_fieldsFields shown but never written
search_fieldsFields the fulltext search looks in
filter_fieldsFields offered as filters
ordering_fieldsFields the list may be sorted by
can_create / can_read / can_update / can_deleteWhich operations are allowed at all
base_serializer_classA serializer of your own instead of the generated one
base_viewsetA viewset of your own instead of the generated one
view_schema_generatorsPer-view shaping — see View generators
action_schema_generatorsWhich actions the resource exposes — see View generators
menu_schema_generatorWhere the resource sits in the menu — see Menu customisation
label, icon, colorHow the resource is presented in the menu
nameResource name; defaults to the lower-cased model name and must be unique
abstractKeeps the class out of the registry, for a shared base admin
permission_classesDRF permissions for this resource only — see Permissions
aggregate_enabled, aggregate_fields, aggregate_max_bucketsThe /aggregate endpoint — see Aggregations
dashboard_enabledWhether dashboard widgets may use this resource — see Dashboard
auto_advisory_lock_for_update_enabled / ..._for_destroy_enabledTake a DB advisory lock around update / destroy (both default True)

Every list attribute has a matching getter for the cases where it depends on the request: get_fields(action=None, request=None), get_read_only_fields(action=None, request=None), get_search_fields(), get_filter_fields(), get_ordering_fields() and get_label().

Where to go next​

PageWhen you need it
Admin fieldsChoosing fields per action and per request
Custom administration fieldsFields that are not model fields
Custom model fieldsTeaching the generator about a field type of your own
Nested serializersWriting related records from one form
PermissionsRestricting what a user may see and do
Multiple administrations for one modelTwo screens over the same table
Locking update and deleteMaking a record read-only under a condition
View and action URLsHow the generated routes are laid out
Custom schema generatorsChanging what the schema says
Configuration validationWhy a misconfigured admin fails at startup, and how to extend the checks

Custom viewset​

When the behaviour of an operation has to change — archiving instead of deleting, say — replace the viewset rather than the schema:

from habarico import ModelAdmin
from habarico.views import AdminModelViewSet

from .models import Product


class ArchivingViewSet(AdminModelViewSet):
"""Archive products instead of deleting them."""

def perform_destroy(self, instance):
instance.status = "archived"
instance.save()


class ProductAdmin(ModelAdmin):
model = Product
base_viewset = ArchivingViewSet
fields = ["name", "sku", "description", "status", "category", "price"]
tip

The frontend needs no change for this — the delete button already calls the endpoint, and the endpoint now archives. That is the point of the schema contract: behaviour stays on the backend.

See also​