Django ORM 中的排序/排序有点问题。我拥有的是一个 FK 到
self
的场地,因此子场地的潜力:
# models.py
class Venue(models.Model):
parent = models.ForeignKey(
to="self", related_name="children", on_delete=models.CASCADE, null=True, blank=True
)
ordering = models.PositiveSmallIntegerField(default=0)
class Meta:
verbose_name = "Venue"
verbose_name_plural = "Venues"
ordering = ["ordering"]
让我们填充一些数据:
top = Venue.objects.create(name="Spain", ordering=0, parent=None)
Venue.objects.create(name="Barcelona", ordering=0, parent=top)
Venue.objects.create(name="Castelldefels", ordering=1, parent=top)
Venue.objects.create(name="Girona", ordering=2, parent=top)
top = Venue.objects.create(name="Singapore", ordering=1, parent=None)
这就是我们构建的自定义仪表板中的样子。我们正在做类似于:
Venue.objects.filter(parent__isnull=True)
然后,对于每个场地,我们正在做obj.children.all()
,从而创建这个布局:
# find all parent=None, then loop over all obj.children.all()
Spain (ordering=0, parent=None)
- Barcelona (ordering=0, parent="Spain")
- Castelldefels (ordering=1, parent="Spain")
- Girona (ordering=2, parent="Spain")
Singapore (ordering=1, parent=None)
这是我们在请求 API 端点时希望看到的内容:
// json response
[
{"venue": "Spain", "ordering": 0},
{"venue": "Barcelona", "ordering": 0},
{"venue": "Castelldefels", "ordering": 1},
{"venue": "Girona", "ordering": 2},
{"venue": "Singapore", "ordering": 1}
]
问题是当我们调用 API 时,我们也在 ModelViewSet 中使用了
django-filters
因此我们无法过滤 parent__isnull=True
或者我们不会在 API 响应中看到孩子。在将查询集返回给序列化程序之前,我们仍然可以在 def list
方法中访问查询集。
问题是:我们如何设置正确的
.order_by()
?一个简单的.order_by("ordering")
会将“新加坡”放在“Girona”之前,这是错误的。
TYIA.
您可以在Meta
中设置默认排序class Venue(models.Model):
company = models.ForeignKey(to="Company", related_name="venues", on_delete=models.CASCADE)
parent = models.ForeignKey(
to="self", related_name="children", on_delete=models.CASCADE, null=True, blank=True
)
ordering = models.PositiveSmallIntegerField(default=0)
# ...
class Meta:
ordering = ('ordering',)
order_with_respect_to
也很有用,请参阅doc
最后我们决定采用只使用 Python 的解决方案;为我们节省了数据库查找,这会使事情变慢。
这就是我们如何更改 ModelViewSet 的列表方法:
# overload modelviewset.list
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
all_venues = list(queryset)
root_venues = []
for venue in all_venues:
if not venue.parent_id:
root_venues.append(venue)
venue_list = []
for root_venue in root_venues:
venue_list.append(root_venue)
for venue in all_venues:
if venue.parent_id == root_venue.id:
venue_list.append(venue)
serializer_kwargs = {
"many": True,
"context": {"request": self.request},
}
serializer = self.get_serializer(venue_list, **serializer_kwargs)
return Response(serializer.data)
通过添加额外的缓存,响应时间非常快。