Approvals — Serializers¶
Overview¶
The serializers in the approvals app define the API contract between backend and frontend.
They are responsible for:
- validating incoming request payloads
- shaping approval and delegation responses
- exposing enriched metadata for frontend cards
- keeping transport concerns separate from workflow logic
The serializer layer should remain thin.
Business logic such as:
- deciding approvals
- forwarding approvals
- creating delegations
- assigning users
- applying side effects
- sending notifications
belongs in services, not in serializers.
Main serializers¶
ApprovalAssigneeOut¶
Represents a single approval assignee in API responses.
This serializer enriches the raw assignee row with frontend-friendly user metadata.
Fields¶
useruser_labeluser_metareasoncreated_atcreated_bycreated_by_labelcreated_by_meta
Purpose¶
Used to show:
- who is assigned to the approval
- why they are assigned
- who created the assignment
Notes¶
This serializer is read-only in practice and is nested inside ApprovalSerializer.
ApprovalSerializer¶
Represents the main approval payload returned by the API.
It supports both:
- creation input
- enriched read output
Write-only input fields¶
target_typetarget_id
These allow clients to create an approval for a generic target object.
Read-only target fields¶
content_typeobject_idtarget_labeltarget_meta
These help frontend clients render meaningful approval cards.
Additional enriched fields¶
assigneesdelegateddelegated_from_metadelegated_untilforwardedforwarded_to_metaforwarded_by_metaforwarded_at
Responsibilities¶
- validate org context exists
- parse generic target input
- expose generic target information
- expose delegation/forwarding metadata
- include nested assignee output
- create
Approvalrows from validated payloads
Notes¶
This serializer supports timesheet-specific target enrichment through context, but remains generic enough to support future approval targets.
ApprovalDecisionSerializer¶
Validates decision requests for existing approvals.
Fields¶
decision-
allowed values:
approvereject
-
reason - optional for approval
- required for rejection
Responsibilities¶
- validate decision choice
- enforce rejection reason requirement
Purpose¶
Used by the decision endpoint to validate user actions before calling workflow services.
ApprovalDelegationSerializer¶
Represents approval delegation rows and validates delegation creation input.
Fields¶
idorgfrom_userfrom_user_labelfrom_user_metato_userto_user_labelto_user_metakindstarts_atends_atis_activecreated_atdisabled_at
Responsibilities¶
- validate org context
- normalize delegation kind
- enforce self-service behavior for non-admin users
- validate from/to user membership in org
- validate time window consistency
- call delegation service for creation
- expose frontend-friendly labels and user metadata
Notes¶
Delegation creation is service-backed.
The serializer should validate request structure and context, then delegate workflow behavior to approvals.services.delegations.
ApprovalForwardSerializer¶
Validates forwarding requests for approvals.
Fields¶
to_usernote
Responsibilities¶
- validate recipient user reference
- validate optional note payload
Purpose¶
Used by:
- forward endpoint
- unforward endpoint
Read-model enrichment¶
The approvals serializers expose several frontend-oriented helper fields.
These are intentionally transport-layer enrichments, not business logic.
Target enrichment¶
ApprovalSerializer provides:
target_labeltarget_meta
This allows clients to render useful approval cards without re-querying related objects.
For example:
- a weekly timesheet approval can return serialized weekly timesheet metadata
- future approval types can provide their own target metadata later
Delegation enrichment¶
ApprovalSerializer provides:
delegateddelegated_from_metadelegated_until
These fields help the frontend distinguish:
- directly assigned approvals
- approvals visible via delegation
This data is provided through serializer context rather than computed inside the serializer from scratch.
Forwarding enrichment¶
ApprovalSerializer provides:
forwardedforwarded_to_metaforwarded_by_metaforwarded_at
This helps the UI show forwarded approvals clearly.
The serializer derives this from prefetched assignee rows.
Serializer context expectations¶
Some serializers depend on context data supplied by views.
ApprovalSerializer context¶
Expected optional keys include:
requestts_meta_by_iddelegation_info_by_approval_id
These are used for:
- org resolution validation
- target metadata lookup
- delegation metadata lookup
Why this matters¶
This keeps serializers efficient by avoiding unnecessary DB queries during serialization.
Responsibilities¶
Serializer layer¶
- validate request payload shape
- normalize and expose data for API consumers
- derive convenience fields for frontend usage
- convert service/model output into stable response structures
Service layer¶
- create delegations
- decide approvals
- forward/unforward approvals
- compute assignees
- apply side effects
- send notifications
View layer¶
- parse request
- call serializers
- call services
- return HTTP responses
Data flow¶
flowchart TD
A[HTTP Request] --> B[Serializer validation]
B --> C[View]
C --> D[Service layer]
D --> E[Models]
E --> F[Serializer output]
F --> G[HTTP Response]
Approval creation flow¶
flowchart TD
A[Client sends target_type + target_id] --> B[ApprovalSerializer]
B --> C[Resolve ContentType]
C --> D[Validate org context]
D --> E[Create Approval row]
E --> F[Return enriched approval payload]
Delegation creation flow¶
flowchart TD
A[Client submits delegation payload] --> B[ApprovalDelegationSerializer]
B --> C[Validate org context]
C --> D[Validate user membership and dates]
D --> E[Call delegation service]
E --> F[Create or replace active delegation]
F --> G[Return delegation payload]
What serializers do not do¶
Serializers do not:
- determine who may view an approval
- determine who may decide an approval
- compute assignees
- determine delegation visibility rules
- apply decision side effects
- send notifications directly
- enforce workflow transitions
Those responsibilities belong to:
- permissions
- services
- models
- views
Validation philosophy¶
The serializers follow a simple rule:
- structural validation stays in serializers
- workflow validation stays in services
Examples¶
Serializer-level validation¶
- request has a valid decision value
- rejection includes a reason
target_typefollowsapp_label.modelto_userexistsends_at > starts_at
Service-level validation¶
- whether an approval may be forwarded
- whether a user may decide an approval
- whether a delegation should replace an existing one
- whether notifications should be sent
- what side effects must happen after approval
Best practices¶
- keep serializers focused on transport concerns
- avoid embedding approval workflow logic in serializers
- pass precomputed context for expensive metadata
- use explicit fields rather than exposing raw model internals
- keep nested serializers shallow and UI-friendly
- treat serializer convenience fields as read-model enrichments, not workflow logic
Future extensions¶
Possible future serializer improvements include:
- dedicated list serializer vs detail serializer
- target-specific serializers per approval kind
- richer delegation status metadata
- approval history serializers
- comment / note serializers
- serializer separation into
serializers/approvals.pyandserializers/delegations.py
Summary¶
The approvals serializer layer provides the API-facing structure for:
- approval records
- assignee metadata
- decision payloads
- delegation payloads
- forwarding requests
It ensures that the API remains:
- predictable
- frontend-friendly
- efficient
- cleanly separated from business workflow logic