首页 > 解决方案 > Django 根据查询集中的顺序更新行

问题描述

我有一个简单的 django 模型

class Item(Model):
  name = CharField()
  rank = PositiveIntegerField()
  created_at = DateField(auto_now_add=True)

我想在按字段(名称或created_at)排序时根据它们的顺序更新对象等级,例如按名称排序时

[("Pen", 0, "2021-05-04"), ("Ball", 0, "2021-05-04")] => [("Pen", 1, "2021-05-04"), (Ball, 0, "2021-05-04")] 

我已经知道我可以使用 bulk_update 来做到这一点,但这意味着我必须获取内存中的对象

items = Items.objects.order_by("name")
for i, item in enumerate(items):
  item.rank = i
Item.objects.bulk_update(items, ["rank"])

我想知道是否有一种方法可以直接在数据库中进行 1 个查询,而无需获取数据

标签: djangodjango-queryset

解决方案


CREATE TABLE items (
    id serial PRIMARY KEY,
    name VARCHAR ( 50 ) UNIQUE NOT NULL,
    rank smallint
);

insert into items(id, name, rank) values (1, 'A', 0);
insert into items(id, name, rank) values (2, 'B', 0);
insert into items(id, name, rank) values (3, 'C', 0);
select * from items;
ID 姓名
1 一个 0
2 0
3 C 0
UPDATE items
SET rank=calculated.calc_rank
FROM
  (SELECT id AS calc_id,
          (ROW_NUMBER() OVER (
                              ORDER BY name ASC)) AS calc_rank
   FROM items) AS calculated
WHERE items.id = calculated.calc_id;
select * from items;
ID 姓名
1 一个 1
2 2
3 C 3

为 Django 执行原始 SQL

sql = '''
UPDATE items
SET rank=calculated.calc_rank
FROM
  (SELECT id AS calc_id,
          (ROW_NUMBER() OVER (
                              ORDER BY name ASC)) AS calc_rank
   FROM items) AS calculated
WHERE items.id = calculated.calc_id
'''

with connection.cursor() as cursor:
  cursor.execute(sql)

推荐阅读