Django ORM中自引用子对象的order_by应该怎么做?

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

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.

python django django-models django-rest-framework django-orm
2个回答
0
投票

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


0
投票

最后我们决定采用只使用 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)

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

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