[Fixed]-How can I use OrderingFilter without exposing the names of the fields in the database

14πŸ‘

βœ…

I created an AliasedOrderingFilter that should fit this need rather well. It extends the ordering_fields attribute to allow tuples for fields as well as strings. For example you could set the views ordering_fields to:

ordering_fields = (('alias1', 'field1'),('alias2', 'field2'), 'field3')

Using this class in a request with ordering=alias1,-alias2,field3 will result in:

qs.order_by('field1', '-field2', 'field3)

The class:

class AliasedOrderingFilter(OrderingFilter):
    ''' this allows us to "alias" fields on our model to ensure consistency at the API level
        We do so by allowing the ordering_fields attribute to accept a list of tuples.
        You can mix and match, i.e.:
        ordering_fields = (('alias1', 'field1'), 'field2', ('alias2', 'field2')) '''

    def remove_invalid_fields(self, queryset, fields, view):      
        valid_fields = getattr(view, 'ordering_fields', self.ordering_fields)
        if valid_fields is None or valid_fields == '__all__':
            return super(AliasedOrderingFilter, self).remove_invalid_fields(queryset, fields, view)

        aliased_fields = {}
        for field in valid_fields:
            if isinstance(field, basestring):
                aliased_fields[field] = field
            else:
                aliased_fields[field[0]] = field[1]

        ordering = []
        for raw_field in fields:
            invert = raw_field[0] == '-'
            field = raw_field.lstrip('-')
            if field in aliased_fields:
                if invert:
                    ordering.append('-{}'.format(aliased_fields[field]))
                else:
                    ordering.append(aliased_fields[field])
        return ordering
πŸ‘€brocksamson

17πŸ‘

pip install django-filter

In your view:

from rest_framework import viewsets, filters
from django_filters.filters import OrderingFilter
from .models import MyList
from .serializers import MyListSerializer

class MyFilter(django_filters.FilterSet):
    surname = django_filters.CharFilter(name="model_field_name_2")

    order_by_field = 'ordering'
    ordering = OrderingFilter(
        # fields(('model field name', 'parameter name'),)
        fields=(
            ('model_field_name_1', 'name'),
            ('model_field_name_2', 'surname'),
            ('model_field_name_3', 'email'),
        )
    )

    class Meta:
        model = MyList
        fields = ['model_field_name_2',]

class MyListViewSet(viewsets.ModelViewSet):
    serializer_class = MyListSerializer
    filter_backends = (filters.DjangoFilterBackend,)
    filter_class = MyFilter
    
    def get_queryset(self):
        return MyList.objects.all()

And you could do something like this:

/my-list?ordering=name,surname,email
/my-list?ordering=-email&surname=taylor

django-filter docs:
https://django-filter.readthedocs.io/en/latest/ref/filters.html#orderingfilter

drf docs:
http://www.django-rest-framework.org/api-guide/filtering/#djangofilterbackend

0πŸ‘

Yes you can do that by using the ordering_fields attribute in your view like:

class YourViewSet(viewsets.ModelViewSet):
    serializer_class = yourSerializer
    filter_backends = (filters.OrderingFilter,)
    ordering_fields = ('field1', 'field2')

In ordering_fields you mention all the fields by which you want to allow ordering.

Here field1 and field2 need not be present in the serializer fields attribute and therefore they won’t be in your API response.

Update:
Your request url should be like:

http://example.com/api/users?ordering=field1

ordering is the query parameter. You need to set ordering with the field name with which you want to order your queryset.

you can learn more here

Leave a comment