如何在Django中使用链表查找前后对象?
假设我们有一个问答网站。当你打开一个问题,比如在地址 /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
。