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.
The simplest way to filter the queryset of any view that subclasses GenericAPIView is to override the . get_queryset() method. Overriding this method allows you to customize the queryset returned by the view in a number of different ways.
The DjangoFilterBackend class is used to filter the queryset based on a specified set of fields. This backend class automatically creates a FilterSet (django_filters. rest_framework. FilterSet) class for the given fields. We can also create our own FilterSet class with customized settings.
query_params is a more correctly named synonym for request. GET . For clarity inside your code, we recommend using request. query_params instead of the Django's standard request.
queryset - The queryset used for model instance lookups when validating the field input. Relationships must either set a queryset explicitly, or set read_only=True . many - If applied to a to-many relationship, you should set this argument to True .
I have very simple POC, that should allow you to implement more sophisticated solution.
views.py
:
from rest_framework import viewsets
from ordering_test.models import Test
from ordering_test.ordering import MyCustomOrdering
from ordering_test.serializers import TestSerializer
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
filter_backends = (MyCustomOrdering,)
ordering.py
:
from rest_framework.filters import OrderingFilter
class MyCustomOrdering(OrderingFilter):
allowed_custom_filters = ['testMethod']
def get_ordering(self, request, queryset, view):
"""
Ordering is set by a comma delimited ?ordering=... query parameter.
The `ordering` query parameter can be overridden by setting
the `ordering_param` value on the OrderingFilter or by
specifying an `ORDERING_PARAM` value in the API settings.
"""
params = request.query_params.get(self.ordering_param)
if params:
fields = [param.strip() for param in params.split(',')]
# care with that - this will alow only custom ordering!
ordering = [f for f in fields if f in self.allowed_custom_filters]
if ordering:
return ordering
# No ordering was included, or all the ordering fields were invalid
return self.get_default_ordering(view)
def filter_queryset(self, request, queryset, view):
ordering = self.get_ordering(request, queryset, view)
if ordering:
# implement a custom ordering here
ordering = ['-id']
if ordering:
return queryset.order_by(*ordering)
return queryset
The models.py
and serializers.py
are straightforward, but I will still include them here:
models.py
:
from django.db import models
class Test(models.Model):
test = models.CharField(max_length=120)
test1 = models.CharField(max_length=120)
serializers.py
:
from rest_framework import serializers
from ordering_test.models import Test
class TestSerializer(serializers.ModelSerializer):
class Meta:
model = Test
fields = ('test', 'test1')
Happy coding!
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With