How can I use OrderingFilter without exposing field names in the database

I have a model that uses OrderingFilterBackend. Right now only field ordering is allowed id.

I want to provide the opportunity to order other fields, but without the need to specify the field names in my database. Is there any way to do this?

+4
source share
3 answers

I created an AliasedOrderingFilter that should fit this need pretty well. It extends the ordering_fields attribute to allow tuples for fields as well as strings. For example, you can set orderings_views fields of the form:

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

Using this class in a query with ordering=alias1,-alias2,field3will result in:

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

Grade:

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
+3
source
pip install django-filter

In your opinion:

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 can do something like this:

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

django-filter docs: http://django-filter.readthedocs.io/en/develop/ref/filters.html#orderingfilter

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

+5
source

, , ordering_fields , :

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

In ordering_fieldsyou specify all the fields for which you want to allow ordering.

Here field1and field2do not necessarily present in the serializer attribute fields, and so they are not responsible API.

Update: Your request URL should look like this:

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

ordering- request parameter. You need to set orderingwith the name of the field with which you want to order your request.

you can find out more here

0
source

Source: https://habr.com/ru/post/1615747/


All Articles