我有 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'
我想问的问题是...
-
。views.py
中,我注释了所有 ordering_fields。但我认为这不是有效的方法,因为它获取所有排序注释字段。我该怎么办?
因此,因为您正在使用 django-filter 来进行排序,所以移动可能是覆盖该 OrderingFilter 类..主要查看 filter() 方法
这就是我想出来的。如果这对您来说是一个常见问题,则需要做更多的工作才能使其更加动态;但对于 1-2 种情况,理论上它应该有效:
{project_root}/utils/django_filters_overrides.py
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()
进行验证,那就太酷了。我希望如此!-因为这意味着我们已经完成了,减去正常的调试。