Commit 2fd862f7 authored by Tanner Prestegard's avatar Tanner Prestegard Committed by GraceDB

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
......@@ -55,3 +55,33 @@ class SafeCreateMixin(mixins.CreateModelMixin):
return Response(serializer.data, status=status.HTTP_201_CREATED,
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):
return Response(serializer.data)
class SupereventEventViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
SafeDestroyMixin,
class SupereventEventViewSet(SafeDestroyMixin,
SupereventNestedViewSet):
"""View for events attached to a superevent"""
serializer_class = SupereventEventSerializer
......@@ -120,12 +117,7 @@ class SupereventEventViewSet(mixins.ListModelMixin,
lookup_url_kwarg = 'graceid'
destroy_error_classes = (Superevent.PreferredEventRemovalError,)
destroy_error_response_status = status.HTTP_400_BAD_REQUEST
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.events.all()
# TODO: filter events for user (?)
return queryset
# TODO: do we need to filter events by user?
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
......@@ -144,8 +136,7 @@ class SupereventEventViewSet(mixins.ListModelMixin,
add_event_log=True, issue_alert=True)
class SupereventLabelViewSet(viewsets.ModelViewSet,
SupereventNestedViewSet):
class SupereventLabelViewSet(SupereventNestedViewSet):
"""Superevent labels"""
serializer_class = SupereventLabelSerializer
pagination_class = CustomLabelPagination
......@@ -153,20 +144,14 @@ class SupereventLabelViewSet(viewsets.ModelViewSet,
SupereventLabellingModelPermissions,)
lookup_url_kwarg = 'label_name'
lookup_field = 'label__name'
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.labelling_set.all().order_by('label__name')
return queryset
list_view_order_by = ('label__name',)
def perform_destroy(self, instance):
remove_label_from_superevent(instance, self.request.user,
add_log_message=True, issue_alert=True)
class SupereventLogViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
class SupereventLogViewSet(SafeCreateMixin,
SupereventNestedViewSet):
"""
View for log messages attached to a superevent.
......@@ -179,20 +164,10 @@ class SupereventLogViewSet(mixins.ListModelMixin,
SupereventLogModelPermissions, ParentSupereventAnnotatePermissions,)
lookup_url_kwarg = 'N'
lookup_field = '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
list_view_order_by = ('N',)
class SupereventLogTagViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
class SupereventLogTagViewSet(SafeCreateMixin,
SafeDestroyMixin,
SupereventNestedViewSet):
"""
......@@ -204,6 +179,7 @@ class SupereventLogTagViewSet(mixins.ListModelMixin,
SupereventLogTagModelPermissions, SupereventLogTagObjectPermissions,)
lookup_url_kwarg = 'tag_name'
lookup_field = 'name'
list_view_order_by = ('name',)
def _set_parent_log(self):
"""Gets and caches parent log object"""
......@@ -227,7 +203,7 @@ class SupereventLogTagViewSet(mixins.ListModelMixin,
def get_queryset(self):
parent_log = self.get_parent_log()
return parent_log.tags.all().order_by('name')
return parent_log.tags.all()
def perform_destroy(self, instance):
parent_log = self.get_parent_log()
......@@ -299,9 +275,7 @@ class SupereventFileViewSet(SupereventNestedViewSet):
return check_and_serve_file(request, file_path, ResponseClass=Response)
class SupereventVOEventViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
class SupereventVOEventViewSet(SafeCreateMixin,
SupereventNestedViewSet):
"""
View for VOEvents attached to a superevent.
......@@ -314,15 +288,8 @@ class SupereventVOEventViewSet(mixins.ListModelMixin,
lookup_url_kwarg = 'N'
lookup_field = 'N'
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.voevent_set.all()
return queryset
class SupereventEMObservationViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
SafeCreateMixin,
class SupereventEMObservationViewSet(SafeCreateMixin,
SupereventNestedViewSet):
"""
View for EMObservations attached to a superevent.
......@@ -334,14 +301,8 @@ class SupereventEMObservationViewSet(mixins.ListModelMixin,
lookup_url_kwarg = 'N'
lookup_field = 'N'
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.emobservation_set.all()
return queryset
class SupereventSignoffViewSet(viewsets.ModelViewSet,
SafeCreateMixin,
class SupereventSignoffViewSet(SafeCreateMixin,
SupereventNestedViewSet):
"""
View for signoffs associated with a superevent.
......@@ -355,12 +316,7 @@ class SupereventSignoffViewSet(viewsets.ModelViewSet,
SupereventSignoffTypeModelPermissions,
SupereventSignoffTypeObjectPermissions,)
lookup_url_kwarg = 'typeinst' # signoff_type + instrument
def get_queryset(self):
superevent = self.get_parent_object()
queryset = superevent.signoff_set.all().order_by('signoff_type',
'instrument')
return queryset
list_view_order_by = ('signoff_type', 'instrument',)
def get_object(self):
queryset = self.filter_queryset(self.get_queryset())
......@@ -394,8 +350,7 @@ class SupereventSignoffViewSet(viewsets.ModelViewSet,
issue_alert=True)
class SupereventGroupObjectPermissionViewSet(viewsets.ModelViewSet,
SafeCreateMixin,
class SupereventGroupObjectPermissionViewSet(SafeCreateMixin,
SafeDestroyMixin,
SupereventNestedViewSet):
"""
......@@ -407,10 +362,6 @@ class SupereventGroupObjectPermissionViewSet(viewsets.ModelViewSet,
SupereventGroupObjectPermissionPermissions,)
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)
def modify(self, request, superevent_id):
"""
......
......@@ -3,13 +3,14 @@ import logging
from superevents.models import Superevent
from superevents.utils import get_superevent_by_date_id_or_404
from .settings import SUPEREVENT_LOOKUP_URL_KWARG
from ..viewsets import NestedViewSet
from ..mixins import OrderedListModelMixin
from ..viewsets import NestedModelViewSet
# Set up logger
logger = logging.getLogger(__name__)
class SupereventNestedViewSet(NestedViewSet):
class SupereventNestedViewSet(OrderedListModelMixin, NestedModelViewSet):
"""
Gets a parent superevent object for a nested object by using the
URL kwargs. Also does a check on object permissions for the superevent,
......
......@@ -131,3 +131,26 @@ class NestedViewSet(viewsets.GenericViewSet):
parent = get_object_or_404(parent_queryset,
**{self.parent_lookup_field: parent_lookup_value})
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)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment