Users - Serializers
Location: api/nextango/apps/users/serializers.py
Last Updated: 2026-07-08
Overview
Two families of serializer cover the Users domain: nested role serializers used inside UserSerializer for a single-request view of a user and their roles, and standalone role serializers used for direct CRUD against a role model. Both read and write the same underlying Employee/Customer/Manager/Influencer/Vip models; the standalone versions additionally surface user_name and user_email so a role list view does not need a second lookup.
UserSerializer
Exposes User model fields plus four computed fields sourced from user_roles_data (primary_role, all_roles, roles_count, primary_role_data) and five computed fields sourced from the role models (employee_roles, customer_roles, manager_roles, influencer_roles, vip_roles), each rendered through the matching nested role serializer below.
Read-only fields: id, created_date, is_staff, is_superuser. The last two are set only through a manager role's adminAccess data, never directly by an API client.
Writing user_roles_data validates that the value is an array of objects, each carrying a _type from the five valid Sanity role types (roleEmployee, roleCustomer, roleManager, roleInfluencer, roleVip). A successful update that includes user_roles_data calls sync_roles_to_model_records() afterward so the role models stay current.
Nested Role Serializers
Used inside UserSerializer's employee_roles / customer_roles / etc. fields. Each resolves Sanity ID references embedded in its model's JSONFields to Django IDs, in a single batched query rather than one lookup per reference, logging unresolved IDs as a warning (they indicate a record that has not synced yet, not necessarily an error).
| Serializer | Model | Key fields | Notes |
|---|---|---|---|
EmployeeRoleSerializer | Employee | employee_id, pin_code (write-only), hourly_rate, commission_rate, store_assignments, hire_date | store_assignments resolves Sanity store references to Django store UUIDs |
CustomerRoleSerializer | Customer | loyalty_points, store_credit (read-only), wishlist, marketing_opt_in, preferred_store, order_history (read-only) | preferred_store and wishlist resolve to Django UUIDs |
ManagerRoleSerializer | Manager | employee_id, pin_code (write-only), hourly_rate, department, reporting_level, override_permissions, reports_to, store_assignments | override_permissions is validated against a fixed whitelist (canOverridePrice, canIssueRefund, canOverrideDiscount) |
InfluencerRoleSerializer | Influencer | social_handle, commission_rate, follower_count, discount_code | No custom validation |
VipRoleSerializer | Vip | vip_tier, personal_shopper_id, exclusive_access, special_discount_rate | exclusive_access is validated against a fixed whitelist (earlyProductAccess, privateSales, vipEvents, conciergeService) |
store_credit and order_history on CustomerRoleSerializer are read-only because they are computed (store_credit from Customer.recalculate_store_credit()) or transaction-managed rather than directly editable.
Standalone Role Serializers
EmployeeSerializer, CustomerSerializer, ManagerSerializer, InfluencerSerializer, VipSerializer back the deprecated role-specific ViewSets (see Views). Each includes the same fields as its nested counterpart plus user, user_name, and user_email. Validation on store_assignments and override_permissions in the standalone versions checks only that the value is an array; the nested versions' Sanity-reference-shape and whitelist checks are not repeated here, so a write through a standalone serializer accepts a looser payload than the same write through UserSerializer.
Writing a standalone role serializer updates the role model directly; it does not currently propagate the change back into the parent user's user_roles_data, so a client that needs both in sync should write through UserSerializer instead.
UserProductCollectionSerializer
Exposes UserProductCollection with computed owner_name, owner_email, item_count, and a capped items_details (first 50 items, rendered via InventoryLevelSerializer). Cross-field validation enforces that a curation-type collection must be public, and that only curation or staff-pick collections can be is_featured. create() and update() set the M2M items field separately from the scalar fields (a Django M2M requirement) and refresh statistics afterward via update_statistics().
UserProductCollectionWebhookSerializer
A separate serializer for the Sanity webhook path into UserProductCollection, accepting the Sanity camelCase field names (collectionType, isPublic, shareToken, and so on) directly rather than relying on DRF's Meta-model field mapping. Resolves the owner and items Sanity references to Django User and InventoryLevel instances during validation, and exposes to_django_data() to produce a plain dict of snake_case fields ready for objects.create() or an instance update.