在Django模型上执行不相关的SQL JOIN?
我有两个模型,一个是用户(django.contrib.auth.models.User),另一个是日志(Log)。这两个模型都有一个“email”字段。日志模型没有指向用户模型的外键。我想知道如何通过这两个表的email字段来进行连接。
其实我想执行两个基本的查询。第一个是简单的连接查询,用来过滤数据。
#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
Log.email的值是否总是对应一个用户?如果是的话,直接在Log对象中添加一个外键(ForeignKey)指向用户(User)不就可以了吗?
class Log(models.Model):
# ...
user = models.ForeignKey(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的值不一定要属于某个用户,你可以尝试在一个模型管理器中添加一个方法。
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)
2
为什么不使用 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)
这一部分,使用 extra 的写法是这样的(未经测试):
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)
3
你可以在用户类上添加一个额外的方法,这种做法叫做猴子补丁(Monkey Patching)或鸭子打击(Duck Punching):
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中似乎不太受欢迎。
(我前几天遇到了“鸭子打击”这个词。它是基于鸭子类型的概念,我们不在乎某个东西属于哪个类:只要它听起来像只鸭子,那它就是鸭子。如果你打它的时候它不叫,那就继续打,直到它叫出来为止。)