Skip to main content
Version: 3.1

Favorites

UserResourceFavorites lets a user pin resource IDs into named lists so the owning resource's list endpoint can surface them first. It's a Care-internal personalization feature, not a FHIR resource — you touch it when you add favoriting to a resource's UI.

Each list is scoped by a resource_type and, optionally, a facility. There's no EMRResource serialize/de_serialize spec here. The moving parts are an enum (FavoriteResourceChoices), a default-list constant, a DRF filter backend (FavoritesFilter), and a viewset mixin (EMRFavoritesMixin) that wires up the read/write endpoints.

Source:

Models

ModelPurpose
UserResourceFavoritesStores a per-user, named list of favorited resource integer PKs, optionally scoped to a facility and constrained to one resource type

UserResourceFavorites extends EMRBaseModel, which supplies external_id, the audit fields created_by / updated_by / created_date / modified_date, and soft-delete semantics.

UserResourceFavorites fields

FieldTypeRequiredDefaultNotes
userFK → UseroptionalOwner of the list. CASCADE, nullable
favoritesArrayField(IntegerField)yes[]Favorited resource primary-key integers, not external_ids. Newest-first; capped at settings.MAX_FAVORITES_PER_LIST (default 50)
favorite_listCharField(255)yesList name. A user may keep multiple named lists per (resource_type, facility). Endpoints default it to "default" (DEFAULT_FAVORITE_LIST)
resource_typeCharField(255)yesThe kind of resource favorited. Always one of the FavoriteResourceChoices values, set server-side from the viewset's FAVORITE_RESOURCE — never accepted from the client
facilityFK → FacilityoptionalCASCADE, nullable. null means an instance-wide (cross-facility) list

The tuple (user, resource_type, facility, favorite_list) addresses a single row: it's what the cache keys and the get_or_create in add_favorite are built from.

resource_type values — FavoriteResourceChoices

The API constrains resource_type to the string values of FavoriteResourceChoices (care/emr/resources/favorites/spec.py). Each viewset binds to exactly one value through its FAVORITE_RESOURCE attribute.

ValueBound resourceWired viewset
activity_definitionActivity definitionActivityDefinitionViewSet
charge_item_definitionCharge item definitionChargeItemDefinitionViewSet
product_knowledgeProduct knowledgeProductKnowledgeViewSet
observation_definitionObservation definition(enum value defined; no viewset wires it yet)
questionnaireQuestionnaireQuestionnaireViewSet
facility_organizationOrganization (facility org)FacilityOrganizationViewSet

Resource specs (API schema)

Favorites skip the EMRResource Create/Update/List/Retrieve pattern. The spec surface is just:

SymbolKindRole
FavoriteResourceChoicesstr, Enum (spec.py)Allowed resource_type values (table above). A viewset selects one via FAVORITE_RESOURCE = FavoriteResourceChoices.<x>.value
DEFAULT_FAVORITE_LISTconstant (spec.py)"default" — the list name used when the request omits favorite_list
FavoriteRequestpydantic.BaseModel (viewset)Request body for write actions. One field: favorite_list: str = DEFAULT_FAVORITE_LIST
FavoritesFilterDRF BaseFilterBackend (filters.py)Read path. Adds the favorite_list query param to the resource's list endpoint and restricts/orders results to that user's favorites
EMRFavoritesMixinviewset mixin (api/viewsets/favorites.py)Adds the favorite_lists, add_favorite, remove_favorite actions

FavoriteRequest

FieldTypeRequiredDefault
favorite_liststroptional"default" (DEFAULT_FAVORITE_LIST)

Favorited objects have no read schema of their own. They come back through the owning resource's list endpoint — for example, the questionnaire list serializer — filtered and ordered by FavoritesFilter. The favorite_lists action returns only {"lists": [<list name>, ...]}.

  • User — owner of every favorites row (user FK)
  • Facility — optional scope (facility FK); null = instance-wide
  • Each resource_type binds to one favoritable resource — see the FavoriteResourceChoices table

Methods & save behaviour

Every row is mirrored into a Django cache, kept in sync by save() and refresh_cache().

Cache keys

Two module-level helpers build the keys, encoding a missing facility as -:

favorite_lists_cache_key(user, resource_type, facility)
→ "user_favorites_lists:{user.id}:{resource_type}:{facility.id|-}"

favorite_list_object_cache_key(user, resource_type, facility, favorite_list)
→ "user_favorites_list_object:{user.id}:{resource_type}:{facility.id|-}:{favorite_list}"
  • The lists key holds the ordered, deduplicated set of favorite_list names a user has for a (resource_type, facility).
  • The list-object key holds the favorites integer array for one named list.

refresh_cache(refresh_list=False)

  • Always writes the current favorites array under the list-object key.
  • With refresh_list=True, also recomputes the deduplicated, modified_date-descending set of favorite_list names for the (user, resource_type, facility) and writes it under the lists key.

save() side effects

save() sets refresh_list = not self.pk (true only on insert), calls super().save(), then refresh_cache(refresh_list=refresh_list). So:

  1. Creating a new list refreshes both the lists cache and the list-object cache.
  2. Updating an existing list refreshes only the list-object cache — the set of list names hasn't changed.

Treat these keys as platform-maintained. Write through the model or the endpoints so the cache stays consistent.

API integration notes

There's no standalone favorites endpoint. Favorites ride on the owning resource's viewset through EMRFavoritesMixin. A viewset opts in by mixing in EMRFavoritesMixin, setting FAVORITE_RESOURCE, and adding FavoritesFilter to filter_backends:

class QuestionnaireViewSet(EMRModelViewSet, EMRFavoritesMixin):
filter_backends = [filters.DjangoFilterBackend, FavoritesFilter]
FAVORITE_RESOURCE = FavoriteResourceChoices.questionnaire.value

Endpoints (relative to the owning resource)

ActionMethod · routeBodyBehaviour
add_favoritePOST {resource}/{id}/add_favorite/FavoriteRequestget_or_create the (user, favorite_list, resource_type, facility) row, insert(0, obj.id) (newest-first), dedupe, trim to MAX_FAVORITES_PER_LIST, save. Returns {}
remove_favoritePOST {resource}/{id}/remove_favorite/FavoriteRequestPops obj.id from the list. Emptying the list deletes the row and both cache keys. 404/validation error if the list does not exist. Returns {}
favorite_listsGET {resource}/favorite_lists/Returns {"lists": [<name>, ...]} (cache-first, falls back to DB). Resolves facility from the facility_external_id kwarg or ?facility= query param
list (filtered)GET {resource}/?favorite_list=<name>FavoritesFilter restricts the resource list to id__in=favorites and orders by favorite position (sort_index). Empty/unknown list → empty queryset

What's server-maintained or validated:

  • resource_type is never client-supplied — each viewset fixes it via FAVORITE_RESOURCE. The client only sends favorite_list.
  • facility is derived server-side via retrieve_facility_obj(obj) (defaulting to obj.facility), so favorites inherit the favorited object's facility scope. A null facility is an instance-wide list; a set one is facility-scoped.
  • favorites holds integer primary keys, not external_ids. Order is newest-first; add_favorite re-inserts at position 0 and dedupes.
  • The list is capped at settings.MAX_FAVORITES_PER_LIST (env MAX_FAVORITES_PER_LIST, default 50). add_favorite drops the oldest entries beyond the cap.
  • Never set the user_favorites_lists:* or user_favorites_list_object:* cache keys from a client. save() / refresh_cache() maintain them, and remove_favorite tears them down when a list empties.