Skip to main content

Admin Fields

You can define admin fields that will be used to generate the DRF serializer. These fields will be used as the default view fields too:

from habarico import ModelAdmin

from .models import Category


class CategoryAdmin(ModelAdmin):
model = Category
read_only_fields = ["id"] # Fields that are read only
fields = ["id", "name", "email", "description", "is_active"] # Fields to show in the admin interface

You can change fields according to the action and request with getter methods:

from habarico import ModelAdmin

from .models import Product


class ProductAdmin(ModelAdmin):
model = Product

def get_fields(self, action=None, request=None):
if action == "create":
return ["name", "sku", "description", "category", "brand"]
return ["name", "sku", "description", "short_description", "status", "category", "brand", "created_at", "updated_at"]

def get_read_only_fields(self, action=None, request=None):
if action == "partial_update": # Fields that are read only in the partial update action
return ["sku", "category", "created_at", "updated_at"]
return ["created_at", "updated_at"]

The admin fields cannot be set if you define a custom serializer in the viewset or in your administration:

from rest_framework import serializers
from habarico import ModelAdmin
from habarico.serializers import AdminModelSerializer

from .models import Product


class ProductSerializer(AdminModelSerializer):
formatted_price = serializers.SerializerMethodField()

class Meta:
model = Product
fields = ["id", "name", "price", "formatted_price", "stock", "is_available"]

def get_formatted_price(self, obj):
return f"${obj.price:.2f}"


class ProductAdmin(ModelAdmin):
model = Product
base_serializer_class = ProductSerializer

Validation rules in the schema​

The constraints a field already carries are exported into its schema, so the frontend can reject a value before the request is sent. Nothing has to be declared for this: the generator reads max_length, min_length, max_value and min_value off the DRF field and takes the first RegexValidator in field.validators.

{"name": "sku", "type": "string", "options": {"validators": [{"type": "max_length", "value": 16}]}}

Auto-extracted rules are emitted without a message on purpose. DRF messages are translated on the backend into the locale of the request that happened to fetch the schema; the frontend renders its own translated default instead, which follows the user's language.

Declaring rules explicitly​

Pass validators on the field descriptor, or on a FieldOptions object, when a rule has no counterpart on the model field or needs its own message:

from habarico.schema import Field, MaxLengthValidatorRule, RegexValidatorRule

Field(
name="sku",
validators=[
MaxLengthValidatorRule(16),
RegexValidatorRule(r"^[A-Z]{3}-\d{4}$", "Format: ABC-1234"),
],
)

habarico.schema exports MaxLengthValidatorRule, MinLengthValidatorRule, MaxValueValidatorRule, MinValueValidatorRule and RegexValidatorRule. For a rule type of your own, subclass ValidatorRule and set type, or pass type= to it directly.

Rules from the three sources are merged per rule type, not one list replacing another:

auto-extracted < serializer field options < field descriptor

So a descriptor that sets only max_length keeps the regex rule extracted from the model. A rule whose value is None is dropped, so a message without a value never produces a dangling rule.

caution

URLValidator is skipped — its pattern is not JavaScript-compatible. URL fields are validated on the frontend through their format instead. The same applies to any pattern only Python understands: write patterns both languages accept, or the rule is silently left to the backend.

Frontend validation never replaces the backend one. Every rule is still enforced on save — the schema copy only moves the first "no" closer to the user.

File size limits​

Django and DRF ship no file-size validator. habarico.validators.MaxFileSizeValidator fills that gap for a file/image serializer field:

from habarico.validators import MaxFileSizeValidator

class ProductSerializer(AdminModelSerializer):
datasheet = AdminFileSerializer(validators=[MaxFileSizeValidator(5 * 1024 * 1024)])

It runs where every other DRF field validator runs (Field.run_validators()), so an oversized file is always refused on save.

AdminFileSerializer / AdminImageFileSerializer also expose it to the frontend automatically, as a dedicated options.max_size - not through the generic options.validators array above, since that array only understands {type, value, message} rules and there is no max_file_size type for the frontend's validator provider to render. A field carrying FileExtensionValidator (or, for AdminImageFileSerializer, Django's own validate_image_file_extension) gets the same treatment as options.accept - concrete content types guessed from the allowed extensions, or image/* for the image case. Neither key is emitted when it cannot be derived, and a value already set through options={"max_size": ...} on the field always wins over the derived one.

{"name": "datasheet", "options": {"max_size": 5242880, "accept": ["application/pdf"]}}

This is a courtesy, not the enforcement — the backend always validates on save regardless. See habarico_enterprise.uploads for what a chunked-upload install adds on top: the project-wide MAX_UPLOAD_SIZE ceiling folded into max_size for a field a provider actually serves, and the hard content-type blocklist that guards what its initiate endpoint actually accepts - options.accept reflects a field's own declared types either way, but says nothing about that floor.

See also​