我们应该如何在Django ORM中为自引用的子对象做order_by?

uz75evzq  于 2023-05-01  发布在  Go
关注(0)|答案(3)|浏览(141)

Django ORM中的排序/排序有点问题。我有一个FK到self的Venue,因此可能有子场馆:

# 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)

这是我们构建的自定义 Jmeter 板的外观。我们正在做类似于: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")将“新加坡”放在“赫罗纳”之前,这将是错误的。
蒂亚

e1xvtsh3

e1xvtsh31#

您可以在 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

to94eoyn

to94eoyn2#

最后,我们决定采用一个只使用Python的解决方案;节省了我们的数据库查找,这可以使事情慢到爬行。
这就是我们如何更改ModelViewSet的list方法:

# 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)

通过添加额外的缓存,响应时间非常快。

axr492tv

axr492tv3#

你也可以像这样使用-ve排序
Venue.objects.filter(parent__isnull=True)。order_by(“-ordering”)

相关问题