如何在Django查询中获取外键第一次出现的记录?
基本上,我有一个表格,里面有很多外键,我想根据“创建时间”字段查询某个特定键的第一次出现。以博客/条目的例子来说,如果条目模型有一个指向博客的外键和一个指向用户的外键,那么我该如何构建一个查询,来选择某个特定用户为不同博客写的第一条条目呢?
class Blog(models.Model):
...
class User(models.Model):
...
class Entry(models.Model):
blog = models.Foreignkey(Blog)
user = models.Foreignkey(User)
我想我可能错过了一些技巧,来选择一个博客的第一条条目,然后可以通过添加条件进一步筛选到特定用户:
query = Entry.objects.magicquery.filter(user=user)
不过,也许还有其他更有效的方法。谢谢!
3 个回答
现在没法测试这个
Entry.objects.filter(user=user).distinct("blog").order_by("id")
这是个老问题,我知道 - @zalew的回答接近正确,但可能会导致以下错误:
编程错误:SELECT DISTINCT ON 表达式必须与初始的 ORDER BY 表达式匹配
要解决这个问题,可以尝试让查询中的 ordering
和 distinct
部分对齐:
Entry.objects.filter(user=user).distinct("blog").order_by("blog", "created")
另外,如果有多个条目在完全相同的时间被创建(虽然不太可能,但谁知道呢!),你可以通过在 order_by
中加入 id
来增加确定性:
要解决这个问题,可以尝试让查询中的 ordering
和 distinct
部分对齐:
Entry.objects.filter(user=user).distinct("blog").order_by("blog", "created", "id")
query = Entry.objects.filter(user=user).order_by('id')[0]
这段代码的意思是:先根据用户筛选出相关的记录,然后按照ID从小到大排序,最后只取出第一个结果。
我现在没有安装Django,所以不能测试这行代码。如果你想确认一下我的写法有没有问题,可以查看官方文档:
顺便提一下,关于“限制查询结果”的手册部分有个有趣的说明:
如果你想获取单个对象,而不是一个列表(比如说:SELECT foo FROM bar LIMIT 1),可以用简单的索引来代替切片。举个例子,这段代码会返回数据库中按标题字母顺序排列后的第一个Entry:
Entry.objects.order_by('headline')[0]
编辑:好的,这是我目前能想到的最好方法(根据你和我的评论)。这段代码返回的不是Entry对象,而是它们的ID,命名为entry_id
。
query = Entry.objects.values('blog').filter(user=user).annotate(Count('blog')).annotate(entry_id=Min('id'))
我会继续寻找更好的方法。