<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=References%2FFavorites</id>
	<title>References/Favorites - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://ohcnwiki.tellmey.fyi/index.php?action=history&amp;feed=atom&amp;title=References%2FFavorites"/>
	<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Favorites&amp;action=history"/>
	<updated>2026-07-06T05:22:18Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.4</generator>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=References/Favorites&amp;diff=475&amp;oldid=prev</id>
		<title>Admin: Add Navbox platform (via update-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Favorites&amp;diff=475&amp;oldid=prev"/>
		<updated>2026-07-06T04:25:28Z</updated>

		<summary type="html">&lt;p&gt;Add Navbox platform (via update-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;col class=&quot;diff-marker&quot; /&gt;
				&lt;col class=&quot;diff-content&quot; /&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;2&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 09:55, 6 July 2026&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 214:&lt;/td&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 214:&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* The list is capped at &amp;lt;code&amp;gt;settings.MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt; (env &amp;lt;code&amp;gt;MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt;, default &amp;lt;code&amp;gt;50&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt; drops the oldest entries beyond the cap.&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* The list is capped at &amp;lt;code&amp;gt;settings.MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt; (env &amp;lt;code&amp;gt;MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt;, default &amp;lt;code&amp;gt;50&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt; drops the oldest entries beyond the cap.&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Never set the &amp;lt;code&amp;gt;user_favorites_lists:*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;user_favorites_list_object:*&amp;lt;/code&amp;gt; cache keys from a client. &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;refresh_cache()&amp;lt;/code&amp;gt; maintain them, and &amp;lt;code&amp;gt;remove_favorite&amp;lt;/code&amp;gt; tears them down when a list empties.&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;* Never set the &amp;lt;code&amp;gt;user_favorites_lists:*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;user_favorites_list_object:*&amp;lt;/code&amp;gt; cache keys from a client. &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;refresh_cache()&amp;lt;/code&amp;gt; maintain them, and &amp;lt;code&amp;gt;remove_favorite&amp;lt;/code&amp;gt; tears them down when a list empties.&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td colspan=&quot;2&quot; class=&quot;diff-empty diff-side-deleted&quot;&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot; data-marker=&quot;+&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #a3d3ff; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Navbox platform}}&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;br /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Related}}&lt;/div&gt;&lt;/td&gt;
  &lt;td class=&quot;diff-marker&quot;&gt;&lt;/td&gt;
  &lt;td style=&quot;background-color: #f8f9fa; color: #202122; font-size: 88%; border-style: solid; border-width: 1px 1px 1px 4px; border-radius: 0.33em; border-color: #eaecf0; vertical-align: top; white-space: pre-wrap;&quot;&gt;&lt;div&gt;{{Related}}&lt;/div&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://ohcnwiki.tellmey.fyi/index.php?title=References/Favorites&amp;diff=167&amp;oldid=prev</id>
		<title>Admin: OHC identity seed</title>
		<link rel="alternate" type="text/html" href="https://ohcnwiki.tellmey.fyi/index.php?title=References/Favorites&amp;diff=167&amp;oldid=prev"/>
		<updated>2026-07-04T22:45:27Z</updated>

		<summary type="html">&lt;p&gt;OHC identity seed&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Doc header&lt;br /&gt;
|type=reference&lt;br /&gt;
|domain=platform&lt;br /&gt;
|title=Favorites&lt;br /&gt;
|order=7&lt;br /&gt;
|introduced=3.0&lt;br /&gt;
|model=Favorites&lt;br /&gt;
|reference=References/Organization&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;UserResourceFavorites&amp;lt;/code&amp;gt; lets a user pin resource IDs into named lists so the owning resource&amp;#039;s list endpoint can surface them first. It&amp;#039;s a Care-internal personalization feature, not a FHIR resource — you touch it when you add favoriting to a resource&amp;#039;s UI.&lt;br /&gt;
&lt;br /&gt;
Each list is scoped by a &amp;lt;code&amp;gt;resource_type&amp;lt;/code&amp;gt; and, optionally, a &amp;lt;code&amp;gt;facility&amp;lt;/code&amp;gt;. There&amp;#039;s no &amp;lt;code&amp;gt;EMRResource&amp;lt;/code&amp;gt; serialize/de_serialize spec here. The moving parts are an enum (&amp;lt;code&amp;gt;FavoriteResourceChoices&amp;lt;/code&amp;gt;), a default-list constant, a DRF filter backend (&amp;lt;code&amp;gt;FavoritesFilter&amp;lt;/code&amp;gt;), and a viewset mixin (&amp;lt;code&amp;gt;EMRFavoritesMixin&amp;lt;/code&amp;gt;) that wires up the read/write endpoints.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Source:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
* Model: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/models/favorites.py &amp;lt;code&amp;gt;care/emr/models/favorites.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Spec (enum + constant): [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/favorites/spec.py &amp;lt;code&amp;gt;care/emr/resources/favorites/spec.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Filter backend: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/resources/favorites/filters.py &amp;lt;code&amp;gt;care/emr/resources/favorites/filters.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
* Viewset mixin: [&amp;lt;nowiki/&amp;gt;https://github.com/ohcnetwork/care/blob/develop/care/emr/api/viewsets/favorites.py &amp;lt;code&amp;gt;care/emr/api/viewsets/favorites.py&amp;lt;/code&amp;gt;]&lt;br /&gt;
&lt;br /&gt;
== Models ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Model&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;UserResourceFavorites&amp;lt;/code&amp;gt;&lt;br /&gt;
| Stores a per-user, named list of favorited resource integer PKs, optionally scoped to a facility and constrained to one resource type&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;UserResourceFavorites&amp;lt;/code&amp;gt; extends [[References/Base models &amp;amp;amp; conventions|&amp;lt;code&amp;gt;EMRBaseModel&amp;lt;/code&amp;gt;]], which supplies &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt;, the audit fields &amp;lt;code&amp;gt;created_by&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;updated_by&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;created_date&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;modified_date&amp;lt;/code&amp;gt;, and soft-delete semantics.&lt;br /&gt;
&lt;br /&gt;
== &amp;lt;code&amp;gt;UserResourceFavorites&amp;lt;/code&amp;gt; fields ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Required&lt;br /&gt;
! Default&lt;br /&gt;
! Notes&lt;br /&gt;
{{Field|model=Favorites|section=UserResourceFavorites fields|name=user|type=FK → User|notes=optional}}&lt;br /&gt;
{{Field|model=Favorites|section=UserResourceFavorites fields|name=favorites|type=ArrayField(IntegerField)|notes=yes}}&lt;br /&gt;
{{Field|model=Favorites|section=UserResourceFavorites fields|name=favorite_list|type=CharField(255)|notes=yes}}&lt;br /&gt;
{{Field|model=Favorites|section=UserResourceFavorites fields|name=resource_type|type=CharField(255)|notes=yes}}&lt;br /&gt;
{{Field|model=Favorites|section=UserResourceFavorites fields|name=facility|type=FK → Facility|notes=optional}}&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The tuple &amp;lt;code&amp;gt;(user, resource_type, facility, favorite_list)&amp;lt;/code&amp;gt; addresses a single row: it&amp;#039;s what the cache keys and the &amp;lt;code&amp;gt;get_or_create&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt; are built from.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;resource_type&amp;lt;/code&amp;gt; values — &amp;lt;code&amp;gt;FavoriteResourceChoices&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
The API constrains &amp;lt;code&amp;gt;resource_type&amp;lt;/code&amp;gt; to the string values of &amp;lt;code&amp;gt;FavoriteResourceChoices&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;care/emr/resources/favorites/spec.py&amp;lt;/code&amp;gt;). Each viewset binds to exactly one value through its &amp;lt;code&amp;gt;FAVORITE_RESOURCE&amp;lt;/code&amp;gt; attribute.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Bound resource&lt;br /&gt;
! Wired viewset&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_definition&amp;lt;/code&amp;gt;&lt;br /&gt;
| [[References/Activity Definition&lt;br /&gt;
| Activity definition]]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;charge_item_definition&amp;lt;/code&amp;gt;&lt;br /&gt;
| [[References/Charge Item Definition&lt;br /&gt;
| Charge item definition]]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;product_knowledge&amp;lt;/code&amp;gt;&lt;br /&gt;
| [[References/Product Knowledge&lt;br /&gt;
| Product knowledge]]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;observation_definition&amp;lt;/code&amp;gt;&lt;br /&gt;
| [[References/Observation Definition&lt;br /&gt;
| Observation definition]]&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;questionnaire&amp;lt;/code&amp;gt;&lt;br /&gt;
| [[References/Questionnaire]]&lt;br /&gt;
| &amp;lt;code&amp;gt;QuestionnaireViewSet&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;facility_organization&amp;lt;/code&amp;gt;&lt;br /&gt;
| [[References/Organization]] (facility org)&lt;br /&gt;
| &amp;lt;code&amp;gt;FacilityOrganizationViewSet&amp;lt;/code&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Resource specs (API schema) ==&lt;br /&gt;
&lt;br /&gt;
Favorites skip the &amp;lt;code&amp;gt;EMRResource&amp;lt;/code&amp;gt; Create/Update/List/Retrieve pattern. The spec surface is just:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Symbol&lt;br /&gt;
! Kind&lt;br /&gt;
! Role&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FavoriteResourceChoices&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;str, Enum&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;spec.py&amp;lt;/code&amp;gt;)&lt;br /&gt;
| Allowed &amp;lt;code&amp;gt;resource_type&amp;lt;/code&amp;gt; values (table above). A viewset selects one via &amp;lt;code&amp;gt;FAVORITE_RESOURCE = FavoriteResourceChoices.&amp;amp;lt;x&amp;amp;gt;.value&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;DEFAULT_FAVORITE_LIST&amp;lt;/code&amp;gt;&lt;br /&gt;
| constant (&amp;lt;code&amp;gt;spec.py&amp;lt;/code&amp;gt;)&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;quot;default&amp;amp;quot;&amp;lt;/code&amp;gt; — the list name used when the request omits &amp;lt;code&amp;gt;favorite_list&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FavoriteRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;pydantic.BaseModel&amp;lt;/code&amp;gt; (viewset)&lt;br /&gt;
| Request body for write actions. One field: &amp;lt;code&amp;gt;favorite_list: str = DEFAULT_FAVORITE_LIST&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;FavoritesFilter&amp;lt;/code&amp;gt;&lt;br /&gt;
| DRF &amp;lt;code&amp;gt;BaseFilterBackend&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;filters.py&amp;lt;/code&amp;gt;)&lt;br /&gt;
| Read path. Adds the &amp;lt;code&amp;gt;favorite_list&amp;lt;/code&amp;gt; query param to the resource&amp;#039;s list endpoint and restricts/orders results to that user&amp;#039;s favorites&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;EMRFavoritesMixin&amp;lt;/code&amp;gt;&lt;br /&gt;
| viewset mixin (&amp;lt;code&amp;gt;api/viewsets/favorites.py&amp;lt;/code&amp;gt;)&lt;br /&gt;
| Adds the &amp;lt;code&amp;gt;favorite_lists&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;remove_favorite&amp;lt;/code&amp;gt; actions&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;FavoriteRequest&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Field&lt;br /&gt;
! Type&lt;br /&gt;
! Required&lt;br /&gt;
! Default&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;favorite_list&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;str&amp;lt;/code&amp;gt;&lt;br /&gt;
| optional&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;amp;quot;default&amp;amp;quot;&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;DEFAULT_FAVORITE_LIST&amp;lt;/code&amp;gt;)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Favorited objects have no read schema of their own. They come back through the owning resource&amp;#039;s list endpoint — for example, the questionnaire list serializer — filtered and ordered by &amp;lt;code&amp;gt;FavoritesFilter&amp;lt;/code&amp;gt;. The &amp;lt;code&amp;gt;favorite_lists&amp;lt;/code&amp;gt; action returns only &amp;lt;code&amp;gt;{&amp;amp;quot;lists&amp;amp;quot;: [&amp;amp;lt;list name&amp;amp;gt;, ...]}&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Related models ==&lt;br /&gt;
&lt;br /&gt;
* [[References/User &amp;amp;amp; Skills|&amp;lt;code&amp;gt;User&amp;lt;/code&amp;gt;]] — owner of every favorites row (&amp;lt;code&amp;gt;user&amp;lt;/code&amp;gt; FK)&lt;br /&gt;
* [[References/Facility|&amp;lt;code&amp;gt;Facility&amp;lt;/code&amp;gt;]] — optional scope (&amp;lt;code&amp;gt;facility&amp;lt;/code&amp;gt; FK); &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; = instance-wide&lt;br /&gt;
* Each &amp;lt;code&amp;gt;resource_type&amp;lt;/code&amp;gt; binds to one favoritable resource — see the [[#resource_type-values--favoriteresourcechoices|&amp;lt;code&amp;gt;FavoriteResourceChoices&amp;lt;/code&amp;gt; table]]&lt;br /&gt;
&lt;br /&gt;
== Methods &amp;amp;amp; save behaviour ==&lt;br /&gt;
&lt;br /&gt;
Every row is mirrored into a Django cache, kept in sync by &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;refresh_cache()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Cache keys ===&lt;br /&gt;
&lt;br /&gt;
Two module-level helpers build the keys, encoding a missing facility as &amp;lt;code&amp;gt;-&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;text&amp;quot;&amp;gt;favorite_lists_cache_key(user, resource_type, facility)&lt;br /&gt;
  → &amp;quot;user_favorites_lists:{user.id}:{resource_type}:{facility.id|-}&amp;quot;&lt;br /&gt;
&lt;br /&gt;
favorite_list_object_cache_key(user, resource_type, facility, favorite_list)&lt;br /&gt;
  → &amp;quot;user_favorites_list_object:{user.id}:{resource_type}:{facility.id|-}:{favorite_list}&amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
* The lists key holds the ordered, deduplicated set of &amp;lt;code&amp;gt;favorite_list&amp;lt;/code&amp;gt; names a user has for a &amp;lt;code&amp;gt;(resource_type, facility)&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The list-object key holds the &amp;lt;code&amp;gt;favorites&amp;lt;/code&amp;gt; integer array for one named list.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;refresh_cache(refresh_list=False)&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
* Always writes the current &amp;lt;code&amp;gt;favorites&amp;lt;/code&amp;gt; array under the list-object key.&lt;br /&gt;
* With &amp;lt;code&amp;gt;refresh_list=True&amp;lt;/code&amp;gt;, also recomputes the deduplicated, &amp;lt;code&amp;gt;modified_date&amp;lt;/code&amp;gt;-descending set of &amp;lt;code&amp;gt;favorite_list&amp;lt;/code&amp;gt; names for the &amp;lt;code&amp;gt;(user, resource_type, facility)&amp;lt;/code&amp;gt; and writes it under the lists key.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt; side effects ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt; sets &amp;lt;code&amp;gt;refresh_list = not self.pk&amp;lt;/code&amp;gt; (true only on insert), calls &amp;lt;code&amp;gt;super().save()&amp;lt;/code&amp;gt;, then &amp;lt;code&amp;gt;refresh_cache(refresh_list=refresh_list)&amp;lt;/code&amp;gt;. So:&lt;br /&gt;
&lt;br /&gt;
# Creating a new list refreshes both the lists cache and the list-object cache.&lt;br /&gt;
# Updating an existing list refreshes only the list-object cache — the set of list names hasn&amp;#039;t changed.&lt;br /&gt;
&lt;br /&gt;
Treat these keys as platform-maintained. Write through the model or the endpoints so the cache stays consistent.&lt;br /&gt;
&lt;br /&gt;
== API integration notes ==&lt;br /&gt;
&lt;br /&gt;
There&amp;#039;s no standalone favorites endpoint. Favorites ride on the owning resource&amp;#039;s viewset through &amp;lt;code&amp;gt;EMRFavoritesMixin&amp;lt;/code&amp;gt;. A viewset opts in by mixing in &amp;lt;code&amp;gt;EMRFavoritesMixin&amp;lt;/code&amp;gt;, setting &amp;lt;code&amp;gt;FAVORITE_RESOURCE&amp;lt;/code&amp;gt;, and adding &amp;lt;code&amp;gt;FavoritesFilter&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;filter_backends&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;python&amp;quot;&amp;gt;class QuestionnaireViewSet(EMRModelViewSet, EMRFavoritesMixin):&lt;br /&gt;
    filter_backends = [filters.DjangoFilterBackend, FavoritesFilter]&lt;br /&gt;
    FAVORITE_RESOURCE = FavoriteResourceChoices.questionnaire.value&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Endpoints (relative to the owning resource) ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Action&lt;br /&gt;
! Method · route&lt;br /&gt;
! Body&lt;br /&gt;
! Behaviour&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;POST {resource}/{id}/add_favorite/&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FavoriteRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;get_or_create&amp;lt;/code&amp;gt; the &amp;lt;code&amp;gt;(user, favorite_list, resource_type, facility)&amp;lt;/code&amp;gt; row, &amp;lt;code&amp;gt;insert(0, obj.id)&amp;lt;/code&amp;gt; (newest-first), dedupe, trim to &amp;lt;code&amp;gt;MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt;, save. Returns &amp;lt;code&amp;gt;{}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;remove_favorite&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;POST {resource}/{id}/remove_favorite/&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;FavoriteRequest&amp;lt;/code&amp;gt;&lt;br /&gt;
| Pops &amp;lt;code&amp;gt;obj.id&amp;lt;/code&amp;gt; from the list. Emptying the list deletes the row and both cache keys. 404/validation error if the list does not exist. Returns &amp;lt;code&amp;gt;{}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;favorite_lists&amp;lt;/code&amp;gt;&lt;br /&gt;
| &amp;lt;code&amp;gt;GET {resource}/favorite_lists/&amp;lt;/code&amp;gt;&lt;br /&gt;
| —&lt;br /&gt;
| Returns &amp;lt;code&amp;gt;{&amp;amp;quot;lists&amp;amp;quot;: [&amp;amp;lt;name&amp;amp;gt;, ...]}&amp;lt;/code&amp;gt; (cache-first, falls back to DB). Resolves &amp;lt;code&amp;gt;facility&amp;lt;/code&amp;gt; from the &amp;lt;code&amp;gt;facility_external_id&amp;lt;/code&amp;gt; kwarg or &amp;lt;code&amp;gt;?facility=&amp;lt;/code&amp;gt; query param&lt;br /&gt;
|-&lt;br /&gt;
| list (filtered)&lt;br /&gt;
| &amp;lt;code&amp;gt;GET {resource}/?favorite_list=&amp;amp;lt;name&amp;amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| —&lt;br /&gt;
| &amp;lt;code&amp;gt;FavoritesFilter&amp;lt;/code&amp;gt; restricts the resource list to &amp;lt;code&amp;gt;id__in=favorites&amp;lt;/code&amp;gt; and orders by favorite position (&amp;lt;code&amp;gt;sort_index&amp;lt;/code&amp;gt;). Empty/unknown list → empty queryset&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
What&amp;#039;s server-maintained or validated:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;resource_type&amp;lt;/code&amp;gt; is never client-supplied — each viewset fixes it via &amp;lt;code&amp;gt;FAVORITE_RESOURCE&amp;lt;/code&amp;gt;. The client only sends &amp;lt;code&amp;gt;favorite_list&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;lt;code&amp;gt;facility&amp;lt;/code&amp;gt; is derived server-side via &amp;lt;code&amp;gt;retrieve_facility_obj(obj)&amp;lt;/code&amp;gt; (defaulting to &amp;lt;code&amp;gt;obj.facility&amp;lt;/code&amp;gt;), so favorites inherit the favorited object&amp;#039;s facility scope. A &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;facility&amp;lt;/code&amp;gt; is an instance-wide list; a set one is facility-scoped.&lt;br /&gt;
* &amp;lt;code&amp;gt;favorites&amp;lt;/code&amp;gt; holds integer primary keys, not &amp;lt;code&amp;gt;external_id&amp;lt;/code&amp;gt;s. Order is newest-first; &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt; re-inserts at position 0 and dedupes.&lt;br /&gt;
* The list is capped at &amp;lt;code&amp;gt;settings.MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt; (env &amp;lt;code&amp;gt;MAX_FAVORITES_PER_LIST&amp;lt;/code&amp;gt;, default &amp;lt;code&amp;gt;50&amp;lt;/code&amp;gt;). &amp;lt;code&amp;gt;add_favorite&amp;lt;/code&amp;gt; drops the oldest entries beyond the cap.&lt;br /&gt;
* Never set the &amp;lt;code&amp;gt;user_favorites_lists:*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;user_favorites_list_object:*&amp;lt;/code&amp;gt; cache keys from a client. &amp;lt;code&amp;gt;save()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;refresh_cache()&amp;lt;/code&amp;gt; maintain them, and &amp;lt;code&amp;gt;remove_favorite&amp;lt;/code&amp;gt; tears them down when a list empties.&lt;br /&gt;
&lt;br /&gt;
{{Related}}&lt;/div&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
</feed>