首页 文章

在不相关的Django模型上执行SQL JOIN?

提问于
浏览
3

我有2个模型,用户(django.contrib.auth.models.User)和一个名为Log的模型 . 两者都包含“电子邮件”字段 . Log没有指向User模型的ForeignKey . 我试图找出如何使用电子邮件字段作为通用性来对这两个表执行JOIN .

我希望能够执行基本上2个查询 . 过滤的基本联接

#Get all the User objects that have related Log objects with the level parameter set to 3.
User.objects.filter(log__level=3)

我也想做一些聚合 .

User.objects.all().anotate(Count('log'))

当然,能够反过来也是很好的 .

log = Log.objects.get(pk=3)
log.user...

有没有办法用ORM做到这一点?也许我可以添加到模型的Meta类来“激活”关系?

谢谢!

3 回答

  • 0

    您可以使用MonkeyPatching / DuckPunching在User类中添加额外的方法:

    def logs(user):
        return Log.objects.filter(email=user.email)
    
    from django.contrib.auth.models import User
    User.logs = property(logs)
    

    现在,您可以查询用户,并询问附加的日志(例如,在视图中):

    user = request.user
    logs = user.logs
    

    这种类型的进程在Ruby世界中很常见,但似乎在Python中不受欢迎 .

    (前几天我遇到了DuckPunching这个术语 . 它基于Duck Typing,当你打它时我们不会嘎嘎叫,继续打击直到它嘎嘎叫) .

  • 3

    为什么不用extra()

    示例(未经测试):

    User.objects.extra(
        select={
            'log_count': 'SELECT COUNT(*) FROM myapp_log WHERE myapp_log.email = auth_user.email'
        },
    )
    

    对于 User.objects.filter(log__level=3) 部分,此处与额外(未经测试)相当:

    User.objects.extra(
        select={
            'log_level_3_count': 'SELECT COUNT(*) FROM myapp_log WHERE (myapp_log.email = auth_user.email) AND (myapp_log.level=3)'
        },
    ).filter(log_level_3_count__gt=0)
    
  • 2

    Log.email值是否始终对应于用户?如果是这样,那么只需将一个ForeignKey(User)添加到Log对象中呢?

    class Log(models.Model):
        # ...
        user = models.ForeignKey(User)
    

    使用FK to User,找到你想要的东西变得相当简单:

    User.objects.filter(log__level=3)
    User.objects.all().anotate(Count('log'))
    
    user.log_set.all()
    user.log_set.count()
    
    log.user
    

    如果Log.email值不必属于用户,则可以尝试将方法添加到model manager .

    class LogManager(models.Manager):
        def for_user(self, user):
            return super(LobManager, self).get_query_set().filter(email=user.email)
    
    class Log(models.Model):
        # ...
        objects = LogManager()
    

    然后像这样使用它:

    user = User.objects.get(pk=1)
    logs_for_user = Log.objects.for_user(user)
    

相关问题