在 DRF 中订购注释字段的有效方法

问题描述 投票:0回答:1

我有 3 个模型

models.py
,

class AAA(models.Model):
    name = models.CharField(max_length=300, null=False, blank=False)

class BBB(models.Model):
    aaa = models.ForeignKey(AAA, on_delete=models.CASCADE, null=False, blank=False)
    content = models.CharField(max_length=300, null=False, blank=False)

class CCC(models.Model):
    aaa = models.ForeignKey(AAA, on_delete=models.CASCADE, null=False, blank=False)
    name = models.CharField(max_length=300, null=False, blank=False)

我想在

views.py
订购,

class AAAListCreateView(generics.ListAPIView):
    class AAAPagination(CursorPagination):
        page_size = 2

    queryset = AAA.objects.all().annotate(
        b_count=Count("bbb", distinct=True),
        c_count=Count("ccc", distinct=True),
        total=F('b_count')+('c_count')
    )
    serializer_class = AAAListSerializer
    pagination_class = AAAPagination
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_class = AAAListFilterSet
    ordering_fields = ['b_count', 'c_count', 'total']
    ordering = 'b_count'

我想问的问题是...

  1. 我想对我的所有 ordering_fields 进行降序排列。 降序排列,不指定
    -
  2. views.py
    中,我注释了所有 ordering_fields。但我认为这不是有效的方法,因为它获取所有排序注释字段

我该怎么办?

django django-rest-framework
1个回答
0
投票

因此,因为您正在使用 django-filter 来进行排序,所以移动可能是覆盖该 OrderingFilter 类..主要查看 filter() 方法

这就是我想出来的。如果这对您来说是一个常见问题,则需要做更多的工作才能使其更加动态;但对于 1-2 种情况,理论上它应该有效:

自定义订购过滤器

  • {project_root}/utils/django_filters_overrides.py
  • 注意:我通过ruff运行了这个,但还没有测试过
  • TODO:
    def filter
  • 中的实际注释
from django.db.models import Count, F
from django.forms.utils import pretty_name
from django.utils.translation import gettext_lazy as _

from django_filters import OrderingFilter
from django_filters.constants import EMPTY_VALUES

class FancyOrderingFilter(OrderingFilter):
    """OrderingFilter Override, always descending order + dynamic annotate"""

    def get_ordering_value(self, param):
        """Override order, always descend"""

        # Keep all this so actually putting "-" still works
        descending = param.startswith("-")
        param = param[1:] if descending else param
        field_name = self.param_map.get(param, param)

        # return "-%s" % field_name if descending else field_name
        return "-%s" % field_name # always descend

    def filter(self, qs, value):
        """Override filter, dynamically annotate"""
        if value in EMPTY_VALUES:
            return qs

        ordering = [
            self.get_ordering_value(param)  # <- call override
            for param in value
            if param not in EMPTY_VALUES
        ]

        # manually annotate (sucks, but you've only got to do it once!)
        annotate_dict = {}
        if "-total" in ordering:
            annotate_dict["b_count"] = Count("bbb", distinct=True)
            annotate_dict["c_count"] = Count("ccc", distinct=True)
            annotate_dict["total"] = F("b_count")+("c_count")
        if "-b_count" in ordering and "-b_count" not in annotate_dict:
            annotate_dict["b_count"] = Count("bbb", distinct=True)
        if "-c_count" in ordering and "-c_count" not in annotate_dict:
            annotate_dict["c_count"] = Count("ccc", distinct=True)

        # NOTE: `**` on a dict turns them into keyword args
        #   I personally like this trick as it's only a single `.filter()` or
        #   `.annotate()` call
        qs = qs.annotate(**annotate_dict)

        # Normal Django ordering
        return qs.order_by(*ordering)

    def build_choices(self, fields, labels):
        """
        Override build choices, so labels are correct.
        See source, this no_dash label.get() is iffy.
        Not sure if this method is important :shrugs:
        """
        no_dash = [
            (param, labels.get("%s" % param, self.descending_fmt % labels.get(
                field, _(pretty_name(param))
            )))
            for field, param in fields.items()
        ]
        with_dash = [
            ("-%s" % param, labels.get("-%s" % param, self.descending_fmt % label))
            for param, label in no_dash
        ]

        # interleave the no dash and with dash choices
        return [val for pair in zip(no_dash, with_dash) for val in pair]

新视图集

from utils.django_filters_overrides import FancyOrderingFilter

class AAAListCreateView(generics.ListAPIView):
    class AAAPagination(CursorPagination):
        page_size = 2

    queryset = AAA.objects.all()  # <- change
    serializer_class = AAAListSerializer
    pagination_class = AAAPagination
    filter_backends = [DjangoFilterBackend, FancyOrderingFilter]  # <- change
    filterset_class = AAAListFilterSet
    ordering_fields = ['b_count', 'c_count', 'total']
    ordering = 'b_count'

.all()
注释问题

现在

filter_backends = [DjangoFilterBackend, FancyOrderingFilter]
让我觉得它确实在运行
base_qs -> DjangoFilterBackend -> FancyOrderingFilter
。因此,如果您可以在该
print(qs.count())
方法中执行
FancyOrderingFilter.filter
并针对
AAA.objects.all().count()
进行验证,那就太酷了。我希望如此!-因为这意味着我们已经完成了,减去正常的调试。

© www.soinside.com 2019 - 2024. All rights reserved.