Skip to content

Teams — serializers

Responsibilities

The serializers in the Teams app are responsible for: - Converting Team and TeamMembership data into API responses - Providing lightweight, frontend-friendly representations - Enriching data with computed fields (e.g. roles, labels, membership context) - Supporting structured write operations for team and membership management - Normalizing how users are displayed across team-related endpoints

They are intentionally thin, with all business logic delegated to the service layer.


Main serializers

TeamListSerializer

Represents a team in list responses (e.g. “my teams”).

Fields - id - name - org - role — Current user’s role in this team (computed) - membership_id — Current user’s membership ID (computed) - is_primary — Whether this is the user’s primary team (computed) - is_active - created_at

Behavior - Uses request.user from context - Resolves membership via TeamMembership - Enriches response with user-specific data

Relationships - Reads from Team - Queries TeamMembership


TeamDetailSerializer

Represents a team with aggregated data.

Fields - id - name - org - is_active - created_at - member_count — Active member count (computed)

Behavior - Counts active memberships (TeamMembership) - Used for detail views or admin-style endpoints

Relationships - Reads from Team - Aggregates TeamMembership


TeamCreateSerializer

Used for creating new teams.

Fields - name - is_active (optional, default: true)

Behavior - Validates name presence and trimming - No DB logic (handled in services)


TeamUpdateSerializer

Used for partial updates to teams.

Fields - name (optional) - is_active (optional)

Behavior - Requires at least one field - Validates name if provided


TeamMembershipPairSerializer

Lightweight serializer for mapping relationships.

Fields - team_id - user_id

Use cases - Efficient graph-like responses - Frontend joins / mapping structures - Avoids heavy nested serialization

Relationships - Reads from TeamMembership - Does not include nested objects


TeamMemberOptionSerializer

Represents a user as a selectable option in a team context.

Fields - id — User ID - label — Display name (computed) - email - role — Org-level role (from OrgMembership)

Behavior - Generates a human-friendly label: 1. profile.display_name 2. full_name 3. email 4. fallback to username / ID

Relationships - Reads from OrgMembership - Accesses related User


TeamMemberSerializer

Represents a team member (replaces inline payloads).

Fields - id - user_id - user_email - user_username - user_label — Computed display label - role - is_primary - is_active - joined_at

Behavior - Normalizes user display logic - Used in team member listing endpoints

Relationships - Reads from TeamMembership - Accesses related User


TeamMembershipListSerializer

Represents full membership rows with team + user context.

Fields - id - team_id - team_name - user_id - user_email - user_username - user_label - role - is_primary - is_active - joined_at

Use cases - Admin views - Debugging / analytics endpoints - Rich membership listings


TeamMembershipCreateSerializer

Used for adding a user to a team.

Fields - user_id - role (optional, default: member) - is_primary (optional, default: false) - is_active (optional, default: true)

Behavior - Provides sensible defaults - Input validation only (no business logic)


TeamMembershipWriteSerializer

Generic update serializer for memberships.

Fields - user_id - role (optional) - is_primary (optional) - is_active (optional)

Behavior - Requires at least one field - Used for flexible updates


TeamMembershipRoleUpdateSerializer

Used for updating membership roles.

Fields - role

Behavior - Validates against allowed TeamMembership.Role values


TeamMembershipPrimaryUpdateSerializer

Used for setting a membership as primary.

Fields - is_primary (must be true)

Behavior - Only allows promoting a membership to primary - Clearing primary handled separately in services


TeamMembershipStatusSerializer

Used for activating/deactivating memberships.

Fields - is_active

Behavior - Boolean normalization only


Serializer relationships (diagram)

graph TD

    TeamListSerializer --> Team
    TeamListSerializer --> TeamMembership

    TeamDetailSerializer --> Team
    TeamDetailSerializer --> TeamMembership

    TeamMembershipPairSerializer --> TeamMembership

    TeamMemberSerializer --> TeamMembership
    TeamMemberSerializer --> User

    TeamMemberOptionSerializer --> OrgMembership
    TeamMemberOptionSerializer --> User

    TeamMembershipListSerializer --> TeamMembership
    TeamMembershipListSerializer --> Team
    TeamMembershipListSerializer --> User

    OrgMembership --> User
    TeamMembership --> User
    TeamMembership --> Team

Key design notes

1. Read vs write separation

  • Clear distinction between:
  • Read serializers (list/detail)
  • Write serializers (create/update)
  • Improves clarity and maintainability

2. Context-aware serialization

  • TeamListSerializer depends on:
  • request.user
  • Enables:
  • per-user role resolution
  • personalized responses

3. Lightweight vs rich responses

Two patterns are used:

Lightweight - TeamMembershipPairSerializer - Optimized for performance and mapping

Rich - TeamMemberSerializer - TeamMembershipListSerializer - UI-friendly, detailed responses


4. No business logic in serializers

Serializers: - ❌ Do not enforce permissions
- ❌ Do not modify database state
- ❌ Do not contain domain rules

They only: - validate input structure - format output - compute simple derived fields

All logic lives in: - teams.services.*


5. Label normalization pattern

Consistent user labeling logic: 1. profile.display_name 2. full_name 3. email 4. fallback to username / ID

This ensures: - stable UI rendering - predictable identity display