@@ -156,6 +156,10 @@ class DataViewSet(
156156
157157 queryset = XForm .objects .filter (deleted_at__isnull = True )
158158
159+ def __init__ (self , * args , ** kwargs ):
160+ super ().__init__ (* args , ** kwargs )
161+ self ._cached_object = None
162+
159163 def get_serializer_class (self ):
160164 """Returns appropriate serializer class based on context."""
161165 pk_lookup , dataid_lookup = self .lookup_fields
@@ -187,6 +191,10 @@ def get_serializer_class(self):
187191 # pylint: disable=unused-argument
188192 def get_object (self , queryset = None ):
189193 """Returns the appropriate object based on context."""
194+ # Cache the object to avoid multiple queries in the same request
195+ if self ._cached_object is None :
196+ self ._cached_object = super ().get_object ()
197+ obj = self ._cached_object
190198 pk_lookup , dataid_lookup = self .lookup_fields
191199 form_pk = self .kwargs .get (pk_lookup )
192200 dataid = self .kwargs .get (dataid_lookup )
@@ -227,21 +235,33 @@ def _get_public_forms_queryset(self):
227235
228236 def _filtered_or_shared_queryset (self , queryset , form_pk ):
229237 filter_kwargs = {self .lookup_field : form_pk }
230- queryset = queryset .filter (** filter_kwargs ).only ("id" , "shared" )
238+ # Don't override .only() - let the queryset keep its field restrictions
239+ queryset = queryset .filter (** filter_kwargs )
231240
232- if not queryset :
241+ # Use .exists() to avoid executing the full query twice
242+ if not queryset .exists ():
233243 filter_kwargs ["shared_data" ] = True
234- queryset = XForm .objects .filter (** filter_kwargs ).only ("id" , "shared" )
244+ # Use the same field restrictions as the parent filter_queryset
245+ queryset = XForm .objects .filter (** filter_kwargs ).only (
246+ "id" , "shared" , "num_of_submissions" , "json" , "is_merged_dataset"
247+ )
235248
236- if not queryset :
249+ if not queryset . exists () :
237250 raise Http404 (_ ("No data matches with given query." ))
238251
239252 return queryset
240253
241254 # pylint: disable=unused-argument
242255 def filter_queryset (self , queryset , view = None ):
243256 """Returns and filters queryset based on context and query params."""
244- queryset = super ().filter_queryset (queryset .only ("id" , "shared" ))
257+ # Fetch only the fields we need: id, shared (for permissions),
258+ # num_of_submissions (for pagination), json (for survey structure),
259+ # and is_merged_dataset (for query building)
260+ queryset = super ().filter_queryset (
261+ queryset .only (
262+ "id" , "shared" , "num_of_submissions" , "json" , "is_merged_dataset"
263+ )
264+ )
245265 form_pk = self .kwargs .get (self .lookup_field )
246266
247267 if form_pk :
@@ -578,10 +598,12 @@ def list(self, request, *args, **kwargs):
578598 # pylint: disable=attribute-defined-outside-init
579599 self .object_list = self ._get_public_forms_queryset ()
580600 elif lookup :
581- queryset = self .filter_queryset (self .get_queryset ()).values_list (
582- "pk" , "is_merged_dataset"
583- )
584- xform_id , is_merged_dataset = queryset [0 ] if queryset else (lookup , False )
601+ # Use get_object() to leverage caching and maintain permission
602+ # checks. This replaces the separate filter_queryset() call to
603+ # avoid duplicate queries
604+ xform = self .get_object ()
605+ xform_id = xform .id
606+ is_merged_dataset = xform .is_merged_dataset
585607 pks = [xform_id ]
586608 if is_merged_dataset :
587609 merged_form = MergedXForm .objects .get (pk = xform_id )
@@ -594,9 +616,9 @@ def list(self, request, *args, **kwargs):
594616 except ValueError :
595617 pks , num_of_submissions = [], 0
596618 else :
597- num_of_submissions = XForm . objects . get ( id = xform_id ) .num_of_submissions
619+ num_of_submissions = xform .num_of_submissions
598620 # pylint: disable=attribute-defined-outside-init
599- # Include geom and xform_id fields for geojson format to avoid N+1 queries
621+ # For GeoJSON, we need id, json, xform_id( for xform relationship), and geom
600622 if export_type == "geojson" :
601623 self .object_list = Instance .objects .filter (
602624 xform_id__in = pks , deleted_at = None
0 commit comments