[ django 排序 ]


django的非外键排序

两张表我需要对part里面的字段进行排序, 但是没有外键关联

class Parts(models.Model):
    ...
    ...
    ...

class LocationDrawerComponentParts(models.Model):
    location_drawer_component = models.ForeignKey(
        "cews.LocationDrawerComponent", on_delete=models.CASCADE, null=False, blank=False
    )
    stock_code = models.CharField("Stock code", max_length=255)
    serial_no = models.CharField("Serial no", max_length=255)
    location_text = models.CharField("Location text", max_length=255, default='') 
    sequence = models.IntegerField(default=1) # 抽屉里面的摆放顺序
    part_id = models.IntegerField(default=0) # 关联parts表的id

    created = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    updated = models.DateTimeField(auto_now=True, null=True, blank=True)
    deleted = models.BooleanField(default=False)
        ordering = request.query_params.get("ordering")
        if ordering in ["stock_code","-stock_code", "serial_no","-serial_no", "created","-created", "description", "-description" ]:
            if ordering in ["description", "-description"]:
                part_ids = queryset.values_list("part_id", flat=True)
                part_queryset = Parts.objects.filter(id__in=part_ids).order_by(ordering)
                ordered_ids = list(part_queryset.values_list('id', flat=True))
                preserved_order = Case(
                 *[When(part_id=id, then=pos) for pos, id in enumerate(ordered_ids)]
                 )
                queryset = queryset.order_by(preserved_order)
            else:
                queryset = queryset.order_by(ordering)
        else:
            queryset = queryset.order_by("-id")

这里使用了case来排序

                preserved_order = Case(
                 *[When(part_id=id, then=pos) for pos, id in enumerate(ordered_ids)]
                 )

在Django ORM中,Case和When是条件表达式,用于构建复杂的查询逻辑。在提供的代码中,它们的作用是​​根据关联模型(Parts)的排序结果,动态生成主查询集的排序规则​​,最终实现“通过关联模型字段排序主查询集”的效果。

代码逻辑解析 ​​当排序字段是description或-description时​​: 先获取主查询集关联的part_id列表。 根据这些part_id查询Parts表,并按description排序,得到一个有序的part对象列表。 通过Case和When,将part对象的顺序映射到主查询集的part_id上,使得主查询集按part.description的顺序排序。 ​​其他排序字段(如stock_code)​​: 直接对主查询集使用order_by(ordering)。 Case和When的作用 ​​Case​​:表示一个条件表达式块,包含多个When子句。 ​​When​​:定义条件分支,格式为When(condition, then=value)。 在代码中:

python preserved_order = Case( *[When(part_id=id, then=pos) for pos, id in enumerate(ordered_ids)] ) queryset = queryset.order_by(preserved_order) ​​生成动态排序规则​​: 遍历排序后的ordered_ids,为每个id生成一个When条件。 例如,若ordered_ids = [3, 1, 2],则生成的Case表达式为: python Case( When(part_id=3, then=0), When(part_id=1, then=1), When(part_id=2, then=2), ) 最终,主查询集会按照then的值(即0, 1, 2)升序排列,对应的记录顺序为part_id=3 → part_id=1 → part_id=2。 为什么需要这样处理? ​​间接排序​​:如果主模型(假设是Product)通过外键part_id关联到Parts,直接在主查询集用order_by("part__description")可能无法满足复杂场景(例如聚合查询或特定过滤)。 ​​控制顺序​​:通过手动指定ordered_ids的顺序,可以确保主查询集严格遵循Parts模型排序后的顺序。 类比SQL 最终的查询逻辑类似以下SQL:

SELECT * FROM main_table
ORDER BY CASE
    WHEN part_id = 3 THEN 0
    WHEN part_id = 1 THEN 1
    WHEN part_id = 2 THEN 2
END;