77from django .db .models import Q , Sum
88from django .db .models .aggregates import Count
99from kolibri .content import models , serializers
10- from kolibri .content .content_db_router import get_active_content_database
10+ from kolibri .content .content_db_router import get_active_content_database , using_content_database
1111from kolibri .logger .models import ContentSessionLog , ContentSummaryLog
1212from le_utils .constants import content_kinds
1313from rest_framework import filters , pagination , viewsets
1414from rest_framework .decorators import detail_route , list_route
15+ from rest_framework .generics import get_object_or_404
1516from rest_framework .response import Response
1617from six .moves .urllib .parse import parse_qs , urlparse
1718
@@ -250,15 +251,48 @@ class ContentNodeViewset(viewsets.ModelViewSet):
250251 filter_class = ContentNodeFilter
251252 pagination_class = OptionalPageNumberPagination
252253
253- def get_queryset (self ):
254- return models . ContentNode . objects . all () .prefetch_related (
254+ def prefetch_related (self , queryset ):
255+ return queryset .prefetch_related (
255256 'assessmentmetadata' ,
256257 'files' ,
257258 ).select_related ('license' )
258259
260+ def get_queryset (self , prefetch = True ):
261+ queryset = models .ContentNode .objects .all ()
262+ if prefetch :
263+ return self .prefetch_related (queryset )
264+ return queryset
265+
266+ def get_object (self , prefetch = True ):
267+ """
268+ Returns the object the view is displaying.
269+ You may want to override this if you need to provide non-standard
270+ queryset lookups. Eg if objects are referenced using multiple
271+ keyword arguments in the url conf.
272+ """
273+ queryset = self .filter_queryset (self .get_queryset (prefetch = prefetch ))
274+
275+ # Perform the lookup filtering.
276+ lookup_url_kwarg = self .lookup_url_kwarg or self .lookup_field
277+
278+ assert lookup_url_kwarg in self .kwargs , (
279+ 'Expected view %s to be called with a URL keyword argument '
280+ 'named "%s". Fix your URL conf, or set the `.lookup_field` '
281+ 'attribute on the view correctly.' %
282+ (self .__class__ .__name__ , lookup_url_kwarg )
283+ )
284+
285+ filter_kwargs = {self .lookup_field : self .kwargs [lookup_url_kwarg ]}
286+ obj = get_object_or_404 (queryset , ** filter_kwargs )
287+
288+ # May raise a permission denied
289+ self .check_object_permissions (self .request , obj )
290+
291+ return obj
292+
259293 @detail_route (methods = ['get' ])
260294 def descendants (self , request , ** kwargs ):
261- node = self .get_object ()
295+ node = self .get_object (prefetch = False )
262296 kind = self .request .query_params .get ('descendant_kind' , None )
263297 descendants = node .get_descendants ()
264298 if kind :
@@ -269,7 +303,16 @@ def descendants(self, request, **kwargs):
269303
270304 @detail_route (methods = ['get' ])
271305 def ancestors (self , request , ** kwargs ):
272- return Response (self .get_object ().get_ancestors ().values ('pk' , 'title' ))
306+ cache_key = 'contentnode_ancestors_{db}_{pk}' .format (db = get_active_content_database (), pk = kwargs .get ('pk' ))
307+
308+ if cache .get (cache_key ) is not None :
309+ return Response (cache .get (cache_key ))
310+
311+ ancestors = list (self .get_object (prefetch = False ).get_ancestors ().values ('pk' , 'title' ))
312+
313+ cache .set (cache_key , ancestors , 60 * 10 )
314+
315+ return Response (ancestors )
273316
274317 @detail_route (methods = ['get' ])
275318 def next_content (self , request , ** kwargs ):
@@ -316,10 +359,11 @@ def get_queryset(self):
316359
317360class ChannelFileSummaryViewSet (viewsets .ViewSet ):
318361 def list (self , request , ** kwargs ):
319- file_summary = models .File .objects .aggregate (
320- total_files = Count ('pk' ),
321- total_file_size = Sum ('file_size' )
322- )
323- file_summary ['channel_id' ] = get_active_content_database ()
324- # Need to wrap in an array to be fetchable as a Collection on client
325- return Response ([file_summary ])
362+ with using_content_database (kwargs ['channel_id' ]):
363+ file_summary = models .File .objects .aggregate (
364+ total_files = Count ('pk' ),
365+ total_file_size = Sum ('file_size' )
366+ )
367+ file_summary ['channel_id' ] = get_active_content_database ()
368+ # Need to wrap in an array to be fetchable as a Collection on client
369+ return Response ([file_summary ])
0 commit comments