'Django Rest Ordering custom
I'm using django rest framework and I would like to order with a custom method.
So, when I call this url for example : http://127.0.0.1:8000/api/sets/?ordering=partMissing
It's possible to call a custom method because I would like to order with the number of part missing by snippet. I made count the sum of number of part in the many to many field.
Solution 1:[1]
I think a much easier approach to opalczynski's solution would be to introduce a new field for custom ordering:
from django import forms
import django_filters
from rest_framework import serializers
from .models import MyModel
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ('field1',)
class CustomOrderingFilter(django_filters.FilterSet):
order_by = django_filters.BooleanFilter(
widget=forms.HiddenInput(),
method='filter_order_by',
)
class Meta:
model = MyModel
fields = [
'order_by'
]
def filter_order_by(self, queryset, name, value):
if value:
return self.Meta.model.objects.filter(
id__in=queryset.values_list('id', flat=True)
).order_by(value)
return queryset
class TestViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
filter_class = CustomOrderingFilter
Then you can easily order by any field you want like this: example.com/api/mymodel/?order_by=partMissing.
In my example, I used a fixed model field, but you can change the way you order in the filter_order_by method on the CustomOrderingFilter. Just change it to the logic you want, but make sure to use the .filter(=queryset.values_list('id', flat=True)) to ensure that other filters that are set, are being used.
Solution 2:[2]
You can use FilterSet to annotate and then OrderingFilter to order accordingly. As an advantage you may use the OrderingField syntax and can still order by multiple fields ad the same time.
/api/?annotate_related={id}&order=subscribed/api/?annotate_related={id}&order=-subscribed/api/?annotate_related={id}&order=-subscribed,-modified
FilterSet:
class YourFilterSet(FilterSet):
annotate_related = filters.NumberFilter(method="_annotate_related")
class Meta:
model = Model
def _annotate_related(self, queryset, key, value, *args, **kwargs):
# eg. annotate if user belongs to a certain category
return queryset.annotate(is_subscribed=Case(When(annotate_related__id=value, then=1), output_field=IntegerField(), default=0))
ViewSet:
class YourViewSet(ModelViewSet):
queryset = Model.objects.all()
filterset_class = YourFilterSet
filter_backends = [OrderingFilter, DjangoFilterBackend]
ordering_fields = [
"is_subscribed", # order by annotated field
]
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Özer |
| Solution 2 |
