2011-12-29 76 views
3

我正在使用django-favorites来拉取用户存储的对象列表。该应用程序有一个模型,经理使用Django查询命中太多次的MySQL数据库

class FavoriteManager(models.Manager): 
    """ A Manager for Favorites 
    """ 
    def favorites_for_user(self, user): 
     """ Returns Favorites for a specific user 
     """ 
     return self.get_query_set().filter(user=user) 

class Favorite(models.Model): 
    user = models.ForeignKey(User) 
    content_type = models.ForeignKey(ContentType) 
    object_id = models.PositiveIntegerField() 
    content_object = generic.GenericForeignKey('content_type', 'object_id') 

    created_on = models.DateTimeField(auto_now_add=True) 

    objects = FavoriteManager() 

    class Meta: 
     verbose_name = _('favorite') 
     verbose_name_plural = _('favorites') 
     unique_together = (('user', 'content_type', 'object_id'),) 

    def __unicode__(self): 
     return "%s added %s as a favorite" % (self.user, self.content_object) 

在我看来,我拉喜爱的用户

from favorites.models import Favorite 
def myfavorites(request): 

     item = Favorite.objects.favorites_for_user(user=request.user) 

     return render_to_response('myfavorites.html', { 
      'favorites':item 
     }, 
     context_instance=RequestContext(request)) 

在我的模板,我试图让最爱的列表和它们的模型类型:

{% for fave in favorites %} 
    {{ fave.content_type }}<br> 
    {{ fave.content_object }}<br><br> 
{% endfor %} 

但使用django调试工具栏,我看到我为每个循环打了两次数据库。该代码正在查询数据库中的content_type和content_object,用于收藏夹对象中的每个fave。

如何优化代码以便数据被拉一次而不需要多次命中数据库来检索信息?

回答

9

可以几种解决方案之间做出选择:

  1. 通用关系预先撷取,您可以使用this snippet这样example app

  2. 标识映射/缓存,使用内容类型和对象ID,使键缓存模型,这里是an example你可以把里面的代码__getstate____setstate__做出一个模板标签,这是最快的解决方案,你可以找到

  3. 抓取关系直接,在某些时候,你可能需要当你与外键的工作在这个article about generic relation performance后来详细的解决方案之一。

  4. 使用JonhyCacheJohnyCache是一个查询集缓存框架与Django的缓存抽象作品。我没有亲自尝试过,因为这三种解决方案在我的项目中已经足够了。

  5. 缓存模板片段,少哈克,它看起来像official way to deal with this kind of issue一个生成大量的查询,当用户改变他的最爱你可以invalidate the cached version

注意select_related()对通用关系不起作用。这是因为相关对象可能位于任何表中,因此无法在纯SQL中进行适当的连接。泛型关系是Django的一个特性,而不是MySQL。

2

尝试使用select_related queryset方法。

像这样的东西应该做的伎俩:

item = Favorite.objects.favorites_for_user(user=request.user).select_related('content_type', 'content_object') 

编辑:JPIC正确地指出,select_related不会为“content_object”的工作,因为它是一个通用的外键。不幸的是,没有很好的方法来处理这个问题,直到Django 1.4才会出现。同时,请查看this SO question了解一些指针。 jpic在他的回答中也发布了一些很好的解决方案。

+0

不适用于泛型关系。 – jpic 2011-12-29 13:02:43

+0

你确实是对的。请参阅编辑。 – 2011-12-29 13:19:07