简体   繁体   English

Django Rest 订购定制

[英]Django Rest Ordering custom

I'm using django rest framework and I would like to order with a custom method.我正在使用 django rest 框架,我想使用自定义方法进行订购。

So, when I call this url for example: http://127.0.0.1:8000/api/sets/?ordering=partMissing所以,当我调用这个 url 例如: 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.我计算了多对多字段中部分的总和。

I have very simple POC, that should allow you to implement more sophisticated solution.我有非常简单的 POC,它应该允许您实施更复杂的解决方案。

views.py : 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 : 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.pyserializers.py很简单,但我仍将在此处包含它们:

models.py : 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 : 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:我认为 opalczynski 解决方案的一个更简单的方法是为自定义排序引入一个新字段:

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 .然后您可以轻松地按您想要的任何字段进行排序: 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 .在我的例子,我用了一个固定的模式场,但你可以改变你的订购方式filter_order_by的方法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.只需将其更改为您想要的逻辑,但请确保使用.filter(=queryset.values_list('id', flat=True))以确保正在使用设置的其他过滤器。

You can use FilterSet to annotate and then OrderingFilter to order accordingly.您可以使用FilterSet进行注释,然后使用OrderingFilter进行相应的排序。 As an advantage you may use the OrderingField syntax and can still order by multiple fields ad the same time.作为一个优势,您可以使用OrderingField语法,并且仍然可以同时按多个字段进行排序。

  • /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
    ]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM