在Django中递归ManyToMany关系的'through'模型中的字段上排序

Ale*_*ias 1 django recursion many-to-many sql-order-by

假设以下型号:

class Category(models.Model):
    related = models.ManyToManyField('self', symmetrical = False, through = 'CategoryRelation', null = True, blank = True)
Run Code Online (Sandbox Code Playgroud)

假设以下中间'通过'关系:

class CategoryRelation(models.Model):
    source = models.ForeignKey('Category', null = False, blank = False, verbose_name = _('source'), related_name = 'relation_source')

    target = models.ForeignKey('Category', null = False, blank = False, verbose_name = _('target'), related_name = 'relation_target')

    order = models.PositiveIntegerField(_('order'), null = False, blank = True, default = 0)

    class Meta:
        ordering = ( 'order', )
Run Code Online (Sandbox Code Playgroud)

如何在保留排序的同时获取与给定类别相关的Category对象?以下代码将生成正确的Category对象,而不是正确的顺序,并且即使在使用时,也会在查询集中包含重复的条目.distinct():

relations = CategoryRelation.objects.filter(source = self)

Category.objects.filter(relation_target__in = relations).order_by('related')
Run Code Online (Sandbox Code Playgroud)

Ale*_*ias 5

以下工作正确,但不会遗漏重复的条目:

relations = CategoryRelation.objects.filter(source = self)

Category.objects.filter(relation_target__in = relations).order_by('relation_target')
Run Code Online (Sandbox Code Playgroud)

调用.distinct()不会产生任何影响,因为之后会应用.order_by()逻辑.但是,在这种情况下,可以利用订单是正整数字段的事实,并使用relation_target字段的order字段的Min值注释每个Category,并使用此新注释字段进行排序:

return Category.objects.filter(relation_target__in = relations).annotate(relation_target_order = models.Min('relation_target__order')).order_by('relation_target_order')
Run Code Online (Sandbox Code Playgroud)

这几乎是完整的,但由于此查询的语义本质上使其唯一,因此调用.distinct()以确保distinct标志为True以便以后与其他不同查询的组合可以发生是明智的:

return Category.objects.filter(relation_target__in = relations).annotate(relation_target_order = models.Min('relation_target__order')).order_by('relation_target_order').distinct()
Run Code Online (Sandbox Code Playgroud)

在这种情况下.distinct()不会影响查询,但确保db/models/sql/query.py方法组合(self,rhs,connector)传递其断言:

assert self.distinct == rhs.distinct, \ ...
Run Code Online (Sandbox Code Playgroud)