有没有办法重写这个看似简单的Django片段,以便它不会如此多地访问数据库?

时间:2013-11-06 19:17:50

标签: django performance generic-foreign-key

我有一个名为ToggleProperty的班级。我用它来存储有关用户是否在对象上切换某些属性的信息。属性的示例是“like”,“bookmark”和“follow”。

class ToggleProperty(models.Model):
    # "like", "bookmark", "follow" etc
    property_type = CharField()

    # The user who toggled the property
    user = ForeignKey(User)

    # The object upon which the user is toggling the property, e.g. "user likes image"
    object_id = models.TextField()
    content_type = models.ForeignKey(ContentType)
    content_object = generic.GenericForeignKey('content_type', 'object_id')

现在,我想获得一个其他用户所遵循的用户列表,让我们称他为Tom。

我不能只查询ToggleProperty,因为那会给我ToggleProperties,而不是用户!

所以我这样做:

# First get the ContentType for user, we'll need it
user_ct = ContentType.objects.get_for_model(User)

# Now get the users that Tom follows
followed_by_tom = [
    user_ct.get_object_for_this_type(id = x.object_id) for x in   
    ToggleProperty.objects.filter(
        property_type = "follow",
        user = tom,
        content_type = ContentType.objects.get_for_model(User))
]

问题在于它在我的视图中命中数据库,我不喜欢它。

如果这不够丑陋,请听我说。我实际上对Tom所关注的用户上传的图像感兴趣,所以我可以通过他所关注的人向Tom展示所有图像。

所以对于上面的代码,我添加了这个:

images = Image.objects.filter(user__in = followed_by_tom)

这最终会执行超过400个查询,并且需要花费一秒钟来处理。必须有更好的方法,请你告诉我路径?

1 个答案:

答案 0 :(得分:2)

这篇文章:

followed_by_tom = [
user_ct.get_object_for_this_type(id = x.object_id) for x in   
ToggleProperty.objects.filter(
    property_type = "follow",
    user = tom,
    content_type = ContentType.objects.get_for_model(User))
]

获取在User个查询中可以在单个查询中完成的所有N个实例。实际上,您不需要实例本身只需要id来获取Images IN查询。因此,您可以通过以下方式删除循环中的额外查询:

followed_by_tom = ToggleProperty.objects.filter(
    property_type="follow",
    user=tom,
    content_type=ContentType.objects.get_for_model(User)
).values_list('object_id', flat=True)

images = Image.objects.filter(user__in=followed_by_tom)

由于永远不会评估followed_by_tom,因此ORM应将其作为带有子选择的单个查询执行。