使用Django Queryset跨表检查ID
我现在正在尝试统计用户的活动,但只会在他们注册后的两天才开始计算。因为我会对数据进行后续处理,所以我希望返回一个Django的查询集。
相关的字段只有以下这些:
User
user_id, install_time
Activity
user_id, date, [list of activities]
虽然我已经拼凑出一个解决方案,但效率非常低(O(n²)),因为我必须在连续的for
循环中逐个匹配user_id
。
目前来看,ForeignKey
字段不适合我的模型。我找到了一些资料,这个链接;但是,我找不到一种方法,只返回那些user_id
匹配且date > install_time + datetime.timedelta(2)
的查询集。
有什么想法吗?
编辑:这是我的模型:
class User(models.Model):
user_id = models.CharField(max_length=36)
install_time = models.DateTimeField()
class Activity(models.Model):
user_id = models.CharField(max_length=36)
date = models.DateTimeField()
# Misc activity-related fields
2 个回答
在没有看到你的模型的情况下,很难给你明确的建议,不过Django的查询API支持一种遍历的方式。
你可以尝试做类似这样的事情:
from django.db.models import F
Activity.objects.filter(date__gt=F('user__install_time')+timedelta(2))
更新(基于编辑)
你设置的模型效率不高,无法利用上面的代码。特别是,你应该在Activity
中添加一个指向User
的外键。
class Activity(models.Model):
user = models.ForeignKey(User)
date = models.DateTimeField()
...
在User
中使用user_id
可能也不是最佳选择。虽然你没有直接指定它(或者说,正是因为你没有直接指定),Django会自动为你的模型添加一个id
字段,这个字段是你表的主键。所以你的模型实际上是:
class User(models.Model):
id = models.AutoField()
user_id = models.CharField(max_length=36)
install_time = models.DateTimeField()
在你的模型中同时有id
和user_id
会让人困惑。如果user_id
是用来表示用户名的,最好把它改成username
。否则,你可能根本不需要这个字段。
最后,你在一开始就定义一个User
类其实是有点奇怪的。如果你打算使用Django的auth
包来进行身份验证和用户管理,你会遇到很大的问题(这意味着你真的不应该自己定义一个User
类,以防万一)。如果你需要在内置的User
中添加额外的字段,可以通过一个个人资料模型来实现。详情请见:https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
因为你的模型没有使用明确的连接,所以你不能像Chris Pratt建议的那样使用字段F
对象。
如果你不能修改你的数据库结构,我可以想到另外两个选项,这些选项仍然可以利用一些django的ORM功能,但需要你自己写SQL查询或片段。
我假设你使用的是postgresql,并且完全是在猜测django可能会从你提到的那些命名不正确的模型中生成的数据库结构。
QuerySet.extra
,例如:
activity_set = Activity.objects.extra(
tables=['myapp_user'],
where=[
"myapp_activity.user_id=myapp_user.user_id",
"myapp_activity.date - myapp_user.install_time > '2 days'"
])
使用extra
可能会有点麻烦,你可以通过str(activity_set.query)
来查看它生成的SQL。
另外,你也可以使用Manager.raw
,完全手动编写查询。例如:
activity_set = Activity.objects.raw("""
SELECT
myapp_activity.* from myapp_activity, myapp_user
WHERE
myapp_activity.user_id=myapp_user.user_id
and myapp_activity.date - myapp_user.install_time > '2 days'
""")