使用Django Queryset跨表检查ID

0 投票
2 回答
1118 浏览
提问于 2025-04-17 08:26

我现在正在尝试统计用户的活动,但只会在他们注册后的两天才开始计算。因为我会对数据进行后续处理,所以我希望返回一个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 个回答

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()

在你的模型中同时有iduser_id会让人困惑。如果user_id是用来表示用户名的,最好把它改成username。否则,你可能根本不需要这个字段。

最后,你在一开始就定义一个User类其实是有点奇怪的。如果你打算使用Django的auth包来进行身份验证和用户管理,你会遇到很大的问题(这意味着你真的不应该自己定义一个User类,以防万一)。如果你需要在内置的User中添加额外的字段,可以通过一个个人资料模型来实现。详情请见:https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users

3

因为你的模型没有使用明确的连接,所以你不能像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'
""")

撰写回答