Skip to content
Snippets Groups Projects
Commit 2fd862f7 authored by Tanner Prestegard's avatar Tanner Prestegard Committed by GraceDB
Browse files

Generalizing nested get_queryset and ordering

Create generalized methods for nested viewsets. There is now a
default get_queryset method which finds the nested model queryset
through the parent object; the user can specify filtering parameters
as well.  We also added a order_by filter for list views; parameters
can be specified on the class.
parent 0d1a1f13
No related branches found
No related tags found
No related merge requests found
...@@ -55,3 +55,33 @@ class SafeCreateMixin(mixins.CreateModelMixin): ...@@ -55,3 +55,33 @@ class SafeCreateMixin(mixins.CreateModelMixin):
return Response(serializer.data, status=status.HTTP_201_CREATED, return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers) headers=headers)
class OrderedListModelMixin(object):
"""
Identical to ListModelMixin from DRF, but implements ordering based on
a user-provided tuple (list_view_order_by).
"""
list_view_order_by = ()
def __init__(self, **kwargs):
super(OrderedListModelMixin, self).__init__(**kwargs)
# Force list_view_order_by to be a tuple (easy for users
# to mess this up with single-item tuples)
assert isinstance(self.list_view_order_by, tuple), (
'list_view_order_by must be a tuple'
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
# Custom ordering
queryset = queryset.order_by(*self.list_view_order_by)
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
...@@ -107,10 +107,7 @@ class SupereventViewSet(SafeCreateMixin, viewsets.ModelViewSet): ...@@ -107,10 +107,7 @@ class SupereventViewSet(SafeCreateMixin, viewsets.ModelViewSet):
return Response(serializer.data) return Response(serializer.data)
class SupereventEventViewSet(mixins.ListModelMixin, class SupereventEventViewSet(SafeDestroyMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
SafeDestroyMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
"""View for events attached to a superevent""" """View for events attached to a superevent"""
serializer_class = SupereventEventSerializer serializer_class = SupereventEventSerializer
...@@ -120,12 +117,7 @@ class SupereventEventViewSet(mixins.ListModelMixin, ...@@ -120,12 +117,7 @@ class SupereventEventViewSet(mixins.ListModelMixin,
lookup_url_kwarg = 'graceid' lookup_url_kwarg = 'graceid'
destroy_error_classes = (Superevent.PreferredEventRemovalError,) destroy_error_classes = (Superevent.PreferredEventRemovalError,)
destroy_error_response_status = status.HTTP_400_BAD_REQUEST destroy_error_response_status = status.HTTP_400_BAD_REQUEST
# TODO: do we need to filter events by user?
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.events.all()
# TODO: filter events for user (?)
return queryset
def get_object(self): def get_object(self):
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
...@@ -144,8 +136,7 @@ class SupereventEventViewSet(mixins.ListModelMixin, ...@@ -144,8 +136,7 @@ class SupereventEventViewSet(mixins.ListModelMixin,
add_event_log=True, issue_alert=True) add_event_log=True, issue_alert=True)
class SupereventLabelViewSet(viewsets.ModelViewSet, class SupereventLabelViewSet(SupereventNestedViewSet):
SupereventNestedViewSet):
"""Superevent labels""" """Superevent labels"""
serializer_class = SupereventLabelSerializer serializer_class = SupereventLabelSerializer
pagination_class = CustomLabelPagination pagination_class = CustomLabelPagination
...@@ -153,20 +144,14 @@ class SupereventLabelViewSet(viewsets.ModelViewSet, ...@@ -153,20 +144,14 @@ class SupereventLabelViewSet(viewsets.ModelViewSet,
SupereventLabellingModelPermissions,) SupereventLabellingModelPermissions,)
lookup_url_kwarg = 'label_name' lookup_url_kwarg = 'label_name'
lookup_field = 'label__name' lookup_field = 'label__name'
list_view_order_by = ('label__name',)
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.labelling_set.all().order_by('label__name')
return queryset
def perform_destroy(self, instance): def perform_destroy(self, instance):
remove_label_from_superevent(instance, self.request.user, remove_label_from_superevent(instance, self.request.user,
add_log_message=True, issue_alert=True) add_log_message=True, issue_alert=True)
class SupereventLogViewSet(mixins.ListModelMixin, class SupereventLogViewSet(SafeCreateMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
""" """
View for log messages attached to a superevent. View for log messages attached to a superevent.
...@@ -179,20 +164,10 @@ class SupereventLogViewSet(mixins.ListModelMixin, ...@@ -179,20 +164,10 @@ class SupereventLogViewSet(mixins.ListModelMixin,
SupereventLogModelPermissions, ParentSupereventAnnotatePermissions,) SupereventLogModelPermissions, ParentSupereventAnnotatePermissions,)
lookup_url_kwarg = 'N' lookup_url_kwarg = 'N'
lookup_field = 'N' lookup_field = 'N'
list_view_order_by = ('N',)
def get_queryset(self):
# Get full set of logs for superevent
superevent = self.get_parent_object()
queryset = superevent.log_set.all().order_by('N')
# NOTE: filtering of logs by view permissions is handled in
# filter_queryset by the filter backends.
return queryset
class SupereventLogTagViewSet(mixins.ListModelMixin, class SupereventLogTagViewSet(SafeCreateMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
SafeDestroyMixin, SafeDestroyMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
""" """
...@@ -204,6 +179,7 @@ class SupereventLogTagViewSet(mixins.ListModelMixin, ...@@ -204,6 +179,7 @@ class SupereventLogTagViewSet(mixins.ListModelMixin,
SupereventLogTagModelPermissions, SupereventLogTagObjectPermissions,) SupereventLogTagModelPermissions, SupereventLogTagObjectPermissions,)
lookup_url_kwarg = 'tag_name' lookup_url_kwarg = 'tag_name'
lookup_field = 'name' lookup_field = 'name'
list_view_order_by = ('name',)
def _set_parent_log(self): def _set_parent_log(self):
"""Gets and caches parent log object""" """Gets and caches parent log object"""
...@@ -227,7 +203,7 @@ class SupereventLogTagViewSet(mixins.ListModelMixin, ...@@ -227,7 +203,7 @@ class SupereventLogTagViewSet(mixins.ListModelMixin,
def get_queryset(self): def get_queryset(self):
parent_log = self.get_parent_log() parent_log = self.get_parent_log()
return parent_log.tags.all().order_by('name') return parent_log.tags.all()
def perform_destroy(self, instance): def perform_destroy(self, instance):
parent_log = self.get_parent_log() parent_log = self.get_parent_log()
...@@ -299,9 +275,7 @@ class SupereventFileViewSet(SupereventNestedViewSet): ...@@ -299,9 +275,7 @@ class SupereventFileViewSet(SupereventNestedViewSet):
return check_and_serve_file(request, file_path, ResponseClass=Response) return check_and_serve_file(request, file_path, ResponseClass=Response)
class SupereventVOEventViewSet(mixins.ListModelMixin, class SupereventVOEventViewSet(SafeCreateMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
""" """
View for VOEvents attached to a superevent. View for VOEvents attached to a superevent.
...@@ -314,15 +288,8 @@ class SupereventVOEventViewSet(mixins.ListModelMixin, ...@@ -314,15 +288,8 @@ class SupereventVOEventViewSet(mixins.ListModelMixin,
lookup_url_kwarg = 'N' lookup_url_kwarg = 'N'
lookup_field = 'N' lookup_field = 'N'
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.voevent_set.all()
return queryset
class SupereventEMObservationViewSet(mixins.ListModelMixin, class SupereventEMObservationViewSet(SafeCreateMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
""" """
View for EMObservations attached to a superevent. View for EMObservations attached to a superevent.
...@@ -334,14 +301,8 @@ class SupereventEMObservationViewSet(mixins.ListModelMixin, ...@@ -334,14 +301,8 @@ class SupereventEMObservationViewSet(mixins.ListModelMixin,
lookup_url_kwarg = 'N' lookup_url_kwarg = 'N'
lookup_field = 'N' lookup_field = 'N'
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.emobservation_set.all()
return queryset
class SupereventSignoffViewSet(viewsets.ModelViewSet, class SupereventSignoffViewSet(SafeCreateMixin,
SafeCreateMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
""" """
View for signoffs associated with a superevent. View for signoffs associated with a superevent.
...@@ -355,12 +316,7 @@ class SupereventSignoffViewSet(viewsets.ModelViewSet, ...@@ -355,12 +316,7 @@ class SupereventSignoffViewSet(viewsets.ModelViewSet,
SupereventSignoffTypeModelPermissions, SupereventSignoffTypeModelPermissions,
SupereventSignoffTypeObjectPermissions,) SupereventSignoffTypeObjectPermissions,)
lookup_url_kwarg = 'typeinst' # signoff_type + instrument lookup_url_kwarg = 'typeinst' # signoff_type + instrument
list_view_order_by = ('signoff_type', 'instrument',)
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.signoff_set.all().order_by('signoff_type',
'instrument')
return queryset
def get_object(self): def get_object(self):
queryset = self.filter_queryset(self.get_queryset()) queryset = self.filter_queryset(self.get_queryset())
...@@ -394,8 +350,7 @@ class SupereventSignoffViewSet(viewsets.ModelViewSet, ...@@ -394,8 +350,7 @@ class SupereventSignoffViewSet(viewsets.ModelViewSet,
issue_alert=True) issue_alert=True)
class SupereventGroupObjectPermissionViewSet(viewsets.ModelViewSet, class SupereventGroupObjectPermissionViewSet(SafeCreateMixin,
SafeCreateMixin,
SafeDestroyMixin, SafeDestroyMixin,
SupereventNestedViewSet): SupereventNestedViewSet):
""" """
...@@ -407,10 +362,6 @@ class SupereventGroupObjectPermissionViewSet(viewsets.ModelViewSet, ...@@ -407,10 +362,6 @@ class SupereventGroupObjectPermissionViewSet(viewsets.ModelViewSet,
SupereventGroupObjectPermissionPermissions,) SupereventGroupObjectPermissionPermissions,)
pagination_class = BasePaginationFactory(results_name='permissions') pagination_class = BasePaginationFactory(results_name='permissions')
def get_queryset(self):
superevent = self.get_parent_object()
return superevent.supereventgroupobjectpermission_set.all()
@action(methods=['post'], detail=False) @action(methods=['post'], detail=False)
def modify(self, request, superevent_id): def modify(self, request, superevent_id):
""" """
......
...@@ -3,13 +3,14 @@ import logging ...@@ -3,13 +3,14 @@ import logging
from superevents.models import Superevent from superevents.models import Superevent
from superevents.utils import get_superevent_by_date_id_or_404 from superevents.utils import get_superevent_by_date_id_or_404
from .settings import SUPEREVENT_LOOKUP_URL_KWARG from .settings import SUPEREVENT_LOOKUP_URL_KWARG
from ..viewsets import NestedViewSet from ..mixins import OrderedListModelMixin
from ..viewsets import NestedModelViewSet
# Set up logger # Set up logger
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class SupereventNestedViewSet(NestedViewSet): class SupereventNestedViewSet(OrderedListModelMixin, NestedModelViewSet):
""" """
Gets a parent superevent object for a nested object by using the Gets a parent superevent object for a nested object by using the
URL kwargs. Also does a check on object permissions for the superevent, URL kwargs. Also does a check on object permissions for the superevent,
......
...@@ -131,3 +131,26 @@ class NestedViewSet(viewsets.GenericViewSet): ...@@ -131,3 +131,26 @@ class NestedViewSet(viewsets.GenericViewSet):
parent = get_object_or_404(parent_queryset, parent = get_object_or_404(parent_queryset,
**{self.parent_lookup_field: parent_lookup_value}) **{self.parent_lookup_field: parent_lookup_value})
self._parent = parent self._parent = parent
class NestedModelViewSet(NestedViewSet, viewsets.ModelViewSet):
"""
Provides a default get_queryset mechanism which filters the set of
objects which are attached to a parent object.
Set 'queryset_filter_kwargs' (dict) for database filtering on the
*queryset*. The default DRF 'lookup_field' is only used for single object
lookup.
"""
queryset_filter_kwargs = {}
def get_queryset(self):
parent = self.get_parent_object()
model = self.serializer_class.Meta.model
accessor = [f.get_accessor_name() for f in parent._meta.related_objects
if f.related_model == model][0]
# Equivalent to parent.obj_set
related_manager = getattr(parent, accessor)
return related_manager.filter(**self.queryset_filter_kwargs)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment