mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 02:53:58 +03:00
Ordering filter bug with model property serializer field (#7609)
* Add failing tests for ordering filter with model property * Fix get_default_valid_fields of OrderingFilter * Filter model properties in get_default_valid_fields of OrderingFilter
This commit is contained in:
parent
b256c46cb1
commit
ce1568322a
|
@ -226,10 +226,20 @@ class OrderingFilter(BaseFilterBackend):
|
||||||
)
|
)
|
||||||
raise ImproperlyConfigured(msg % self.__class__.__name__)
|
raise ImproperlyConfigured(msg % self.__class__.__name__)
|
||||||
|
|
||||||
|
model_class = queryset.model
|
||||||
|
model_property_names = [
|
||||||
|
# 'pk' is a property added in Django's Model class, however it is valid for ordering.
|
||||||
|
attr for attr in dir(model_class) if isinstance(getattr(model_class, attr), property) and attr != 'pk'
|
||||||
|
]
|
||||||
|
|
||||||
return [
|
return [
|
||||||
(field.source.replace('.', '__') or field_name, field.label)
|
(field.source.replace('.', '__') or field_name, field.label)
|
||||||
for field_name, field in serializer_class(context=context).fields.items()
|
for field_name, field in serializer_class(context=context).fields.items()
|
||||||
if not getattr(field, 'write_only', False) and not field.source == '*'
|
if (
|
||||||
|
not getattr(field, 'write_only', False) and
|
||||||
|
not field.source == '*' and
|
||||||
|
field.source not in model_property_names
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_valid_fields(self, queryset, view, context={}):
|
def get_valid_fields(self, queryset, view, context={}):
|
||||||
|
|
|
@ -424,6 +424,10 @@ class OrderingFilterModel(models.Model):
|
||||||
title = models.CharField(max_length=20, verbose_name='verbose title')
|
title = models.CharField(max_length=20, verbose_name='verbose title')
|
||||||
text = models.CharField(max_length=100)
|
text = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def description(self):
|
||||||
|
return self.title + ": " + self.text
|
||||||
|
|
||||||
|
|
||||||
class OrderingFilterRelatedModel(models.Model):
|
class OrderingFilterRelatedModel(models.Model):
|
||||||
related_object = models.ForeignKey(OrderingFilterModel, related_name="relateds", on_delete=models.CASCADE)
|
related_object = models.ForeignKey(OrderingFilterModel, related_name="relateds", on_delete=models.CASCADE)
|
||||||
|
@ -436,6 +440,17 @@ class OrderingFilterSerializer(serializers.ModelSerializer):
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OrderingFilterSerializerWithModelProperty(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = OrderingFilterModel
|
||||||
|
fields = (
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"text",
|
||||||
|
"description"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class OrderingDottedRelatedSerializer(serializers.ModelSerializer):
|
class OrderingDottedRelatedSerializer(serializers.ModelSerializer):
|
||||||
related_text = serializers.CharField(source='related_object.text')
|
related_text = serializers.CharField(source='related_object.text')
|
||||||
related_title = serializers.CharField(source='related_object.title')
|
related_title = serializers.CharField(source='related_object.title')
|
||||||
|
@ -551,6 +566,42 @@ class OrderingFilterTests(TestCase):
|
||||||
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
{'id': 1, 'title': 'zyx', 'text': 'abc'},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def test_ordering_without_ordering_fields(self):
|
||||||
|
class OrderingListView(generics.ListAPIView):
|
||||||
|
queryset = OrderingFilterModel.objects.all()
|
||||||
|
serializer_class = OrderingFilterSerializerWithModelProperty
|
||||||
|
filter_backends = (filters.OrderingFilter,)
|
||||||
|
ordering = ('title',)
|
||||||
|
|
||||||
|
view = OrderingListView.as_view()
|
||||||
|
|
||||||
|
# Model field ordering works fine.
|
||||||
|
request = factory.get('/', {'ordering': 'text'})
|
||||||
|
response = view(request)
|
||||||
|
assert response.data == [
|
||||||
|
{'id': 1, 'title': 'zyx', 'text': 'abc', 'description': 'zyx: abc'},
|
||||||
|
{'id': 2, 'title': 'yxw', 'text': 'bcd', 'description': 'yxw: bcd'},
|
||||||
|
{'id': 3, 'title': 'xwv', 'text': 'cde', 'description': 'xwv: cde'},
|
||||||
|
]
|
||||||
|
|
||||||
|
# `incorrectfield` ordering works fine.
|
||||||
|
request = factory.get('/', {'ordering': 'foobar'})
|
||||||
|
response = view(request)
|
||||||
|
assert response.data == [
|
||||||
|
{'id': 3, 'title': 'xwv', 'text': 'cde', 'description': 'xwv: cde'},
|
||||||
|
{'id': 2, 'title': 'yxw', 'text': 'bcd', 'description': 'yxw: bcd'},
|
||||||
|
{'id': 1, 'title': 'zyx', 'text': 'abc', 'description': 'zyx: abc'},
|
||||||
|
]
|
||||||
|
|
||||||
|
# `description` is a Model property, which should be ignored.
|
||||||
|
request = factory.get('/', {'ordering': 'description'})
|
||||||
|
response = view(request)
|
||||||
|
assert response.data == [
|
||||||
|
{'id': 3, 'title': 'xwv', 'text': 'cde', 'description': 'xwv: cde'},
|
||||||
|
{'id': 2, 'title': 'yxw', 'text': 'bcd', 'description': 'yxw: bcd'},
|
||||||
|
{'id': 1, 'title': 'zyx', 'text': 'abc', 'description': 'zyx: abc'},
|
||||||
|
]
|
||||||
|
|
||||||
def test_default_ordering(self):
|
def test_default_ordering(self):
|
||||||
class OrderingListView(generics.ListAPIView):
|
class OrderingListView(generics.ListAPIView):
|
||||||
queryset = OrderingFilterModel.objects.all()
|
queryset = OrderingFilterModel.objects.all()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user