如何在Django中使用链表查找前后对象?

2 投票
2 回答
2580 浏览
提问于 2025-04-18 06:10

假设我们有一个问答网站。当你打开一个问题,比如在地址 /q/<id2> 时,页面应该同时提供链接到下一个问题 /q/<id1> 和上一个问题 /q/<id3>。这里假设问题是按照主键值或者时间戳的顺序排列的。

这个问题可以通过创建一个方法来简单解决,就像我现在使用的这样:

def next_q(self):
    # Ques model contain all questions
    # this method is a model method of Ques
    all_q = Ques.objects.all()
    q = None
    count = 1
    if count < all_q.count():
        try:
            q = all_q.get(pk=self.pk + count)
            break
        except ObjectDoesNotExist:
            count += 1
    return q

不过,我觉得这种操作成本有点高,因为每次请求一个问题 /q/id 时,它都会查询所有的问题(Ques 对象)。

我想到的一个可能方法是使用链表的概念,把下一个和上一个问题的ID存储在当前对象里。

Ques 模型中可以有两个字段,像这样:

class Ques(models.Model):
    prev_q_id = models.IntegerField()
    next_q_id = models.IntegerField()

这些字段会在创建、编辑或删除新的 Ques 对象时更新。这样可以确保我只查询一个单独的对象。不过,我在想这样做是否是个好方法。如果不是,还有什么其他的办法呢?

2 个回答

0

使用链表的方法是可行的,但你并不需要改变你的数据模型来支持你想要的功能。你只需要打开一个游标,查找所有主键大于某个值的问题,然后读取第一个。

记住,objects.all() 并不会真的读取所有对象。它只是创建了一个指向数据的游标。你可以把游标看作是数据库获取数据的“行动计划”。只有当你真正去获取数据时,它才会开始读取数据库中的记录。

你想要的基本上就是这个 SQL 语句。

select * from Question where pk > question.pk order by pk

在 Django 中,这个操作大概是这样的:

all_q = Ques.objects.all()
all_q.filter(pk__gt=question.pk)
all_q.order_by('pk')  # not really necessary as this is done implicitly
next_q = all_q.first()

对于之前的记录,你可以做同样的事情,只需使用反向顺序,或者直接调用“last”来获取最后一个。

5

在一个有序的结构中使用链表是没有意义的,因为你的数据库本身就提供了一个有序的记录列表,你可以根据需要对数据进行排序,从而轻松找到任何你想要的项目。

这里有一种方法可以帮你得到你想要的结果:

class Ques(Model):
    ....

    @classmethod
    def get_next(cls, current_id):  # current_id is the id of current record
        try:
            return cls.objects.filter(id__gt=current_id).order_by("id")[0]
       except:
            return None

    @classmethod
    def get_previous(cls, current_id):
        try:
            return cls.objects.filter(id__lt=current_id).order_by("-id")[0]
        except:
            return None

在这里使用 classmethod 会更有用,你只需获取下一个或上一个最接近的记录。如果没有记录,那么你会得到 None

撰写回答