Skip to content

Commit

Permalink
Add nested ordering filter
Browse files Browse the repository at this point in the history
Add nested ordering filter to instead original one
in rest framework.
Modified the comment of class RelatedNestedOrderingFilter.
Add the underscore comment on ordering doc.
Change the DEFAULT_FILTER_BACKENDS in settings_local.py.
Skip nested ordering check here and check in is_valid_field.
Add test cases on new codes.

JIRA: PDC-1746
  • Loading branch information
chcao55 committed Jul 18, 2017
1 parent 1ba1b2a commit 4464f04
Show file tree
Hide file tree
Showing 11 changed files with 379 additions and 4 deletions.
11 changes: 11 additions & 0 deletions .idea/cc-temp-git.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

283 changes: 283 additions & 0 deletions .idea/workspace.xml

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pdc/apps/common/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@

URL_SPEC_RE = re.compile(r'\$(?P<type>URL|LINK):(?P<details>[^$]+)\$')
ORDERING_STRING = "\n * `ordering` is used to override the ordering of the results, the value could be: "
ORDERING_STRING_NESTED = "The nested ordering is supported by using double underscore \_\_ ."


class ReadOnlyBrowsableAPIRenderer(BrowsableAPIRenderer):
Expand Down Expand Up @@ -144,7 +145,7 @@ def format_docstring(self, view, method, docstring):
if 'list' == method:
ordering_field = get_ordering_field(view, method)
if ordering_field:
ordering_string = ORDERING_STRING + " %s ." % ordering_field
ordering_string = ORDERING_STRING + " %s . " % ordering_field + ORDERING_STRING_NESTED
macros['FILTERS'] += ordering_string
if '%(SERIALIZER)s' in docstring:
macros['SERIALIZER'] = get_serializer(view, include_read_only=True)
Expand Down
3 changes: 2 additions & 1 deletion pdc/apps/common/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ def _check_ordering_keys(self, request):
serializer_fields = self._get_fields_from_serializer_class()
valid_fields = list(set(model_fields).intersection(set(serializer_fields)))
valid_fields += self.queryset.query.aggregates.keys()
tmp_list = [param.strip().lstrip('-') for param in ordering_keys.split(',')]
# If there is a netsted ordering with '__', check if it is valid in the RelatedNestedOrderingFilter
tmp_list = [param.strip().lstrip('-') for param in ordering_keys.split(',') if '__' not in param]
invalid_fields = set(tmp_list) - set(valid_fields)
if invalid_fields:
raise FieldError('Unknown query key: %s not in fields: %s' %
Expand Down
19 changes: 19 additions & 0 deletions pdc/apps/component/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1866,6 +1866,25 @@ def test_delete_group(self):
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

def test_list_relationship_by_nested(self):

def f(ordering_value):
response = self.client.get(reverse("rcrelationship-list"), {'ordering': ordering_value}, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 2)
data = response.data.get('results')
if ordering_value == "to_component__name":
self.assertTrue(self, data[0].get('to_component')["name"] <= data[1].get('to_component')["name"])
elif ordering_value == "-to_component__name":
self.assertTrue(self, data[0].get('to_component')["name"] >= data[1].get('to_component')["name"])
elif ordering_value == "to_component__release":
self.assertTrue(self, data[0].get('to_component')["release"] <= data[1].get('to_component')["release"])
else:
self.assertTrue(self, data[0].get('to_component')["release"] >= data[1].get('to_component')["release"])

for v in ["to_component__name", "-to_component__name", "to_component__release", "-to_component__release"]:
f(v)


class ComponentRelationshipTypeRESTTestCase(TestCaseWithChangeSetMixin, APITestCase):
"""
Expand Down
42 changes: 42 additions & 0 deletions pdc/apps/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models.signals import pre_save
from django.forms.models import model_to_dict
from rest_framework.filters import OrderingFilter
from django.core.exceptions import FieldError


def connect_app_models_pre_save_signal(app, models=None):
Expand Down Expand Up @@ -59,3 +61,43 @@ def read_permission_for_all():

def get_model_name_from_obj_or_cls(obj_or_cls):
return ContentType.objects.get_for_model(obj_or_cls).model


class RelatedNestedOrderingFilter(OrderingFilter):
"""
Extends OrderingFilter to support ordering by fields in related models
using Django orm '__'.
This class is copied and changed from the patch of the github issue.
https://github.com/encode/django-rest-framework/issues/1005
This is based on rest_framework 3.2.5 because pdc-server is using
this version.
The patch has not been merged in the latest version yet. If pdc-server
upgrades the rest_framework to the version which includes this patch in
the future, this class should obsolete.
"""
def is_valid_field(self, model, field):
"""
Return true if the field exists within the model or there is a '__' here.
"""
components = field.split('__', 1)
try:
field, parent_model, direct, m2m = \
model._meta.get_field_by_name(components[0])

# Check if foreign key value exists
if field.rel and len(components) == 2:
return self.is_valid_field(field.rel.to, components[1])
return True
except Exception as e:
# There is no such field
raise FieldError("Unknown query key: %s" % e)

def remove_invalid_fields(self, queryset, ordering_files, view):
"""
Rewrite the remove_invalid_fields methods and add the nested ordering
"""
return [term for term in ordering_files
if self.is_valid_field(queryset.model, term.lstrip('-'))]
2 changes: 1 addition & 1 deletion pdc/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
'DEFAULT_METADATA_CLASS': 'contrib.bulk_operations.metadata.BulkMetadata',

'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',
'rest_framework.filters.OrderingFilter'),
'pdc.apps.utils.utils.RelatedNestedOrderingFilter'),

'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
Expand Down
2 changes: 1 addition & 1 deletion pdc/settings_local.py.dist
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
# # ],
#
# 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',
# 'rest_framework.filters.OrderingFilter',),
# 'pdc.apps.utils.utils.RelatedNestedOrderingFilter'),
#
# 'DEFAULT_METADATA_CLASS': 'contrib.bulk_operations.metadata.BulkMetadata',
#
Expand Down

0 comments on commit 4464f04

Please sign in to comment.