2017-07-11 30 views
0

如果我下面做的一切,而不自定义的经理,这一切都按预期工作:定制经理在Django销毁缓存prefetch_related

class Content(models.Model): 
    name = models.CharField(max_length=200) 
    def __str__(self): 
     return self.name 
    class Meta: 
     app_label = 'game' 

class Requirement(models.Model): 
    content = models.ForeignKey(Content, on_delete=models.CASCADE, related_name = 'requirements') 
    value = models.IntegerField(default=0) 
    def __str__(self): 
     return "{} requires value {}".format(self.content,self.value) 
    class Meta: 
     app_label = 'game' 

def testPrefetchOrig(): 
    contents = Content.objects.filter(name__startswith = 'a').prefetch_related('requirements') 
    for c in contents: 
     for r in c.requirements.all(): 
     print r 
     logging.warning(r) 
    logging.warning("Done with query") 

这一次,再也没有预取数据:

DEBUG:django.db.backends:(0.001) SELECT "game_content"."id", "game_content"."name", "game_content"."deleted" FROM "game_content" WHERE "game_content"."name"::text LIKE 'a%'; args=(u'a%',) 
DEBUG:django.db.backends:(0.001) SELECT "game_requirement"."id", "game_requirement"."content_id", "game_requirement"."value", "game_requirement"."deleted" FROM "game_requirement" WHERE "game_requirement"."content_id" IN (5, 6); args=(5, 6) 
alphabet requires value 5 
WARNING:root:alphabet requires value 5 
alphabet requires value 3 
WARNING:root:alphabet requires value 3 
albatross requires value 1 
WARNING:root:albatross requires value 1 
albatross requires value 0 
WARNING:root:albatross requires value 0 
WARNING:root:Done with query 

但是,我想使用自定义管理器来处理通过设置已删除标志来“删除”的条目的过滤。

class DeletedItemsQuerySet(models.query.QuerySet): 
    def get(self, *args, **kwargs): 
     kwargs['deleted']=False 
     return models.query.QuerySet.get(self, *args, **kwargs) 
    def all(self): 
     return self.filterNoDeleted() 
    def filterNoDeleted(self, *args, **kwargs): 
     kwargs['deleted']=False 
     return models.query.QuerySet.filter(self, *args, **kwargs) 
    def getDeleted(self, *args, **kwargs): 
     return models.query.QuerySet.get(self, *args, **kwargs) 
    def filterDeleted(self, *args, **kwargs): 
     return models.query.QuerySet.filter(self, *args, **kwargs) 

class DeletedItemsManager(models.Manager.from_queryset(DeletedItemsQuerySet)): 
    def all(self): 
     return super(models.Manager,self).all().filterNoDeleted() 

然后我们修改我们的模型来使用:

class Content(models.Model): 
    name = models.CharField(max_length=200) 
    objects = DeletedItemsManager() 
    deleted = models.BooleanField(default=False) 
    def __str__(self): 
     return self.name 
    class Meta: 
     app_label = 'game' 

class Requirement(models.Model): 
    content = models.ForeignKey(Content, on_delete=models.CASCADE, related_name = 'requirements') 
    value = models.IntegerField(default=0) 
    deleted = models.BooleanField(default=False) 
    objects = DeletedItemsManager() 
    def __str__(self): 
     return "{} requires value {}".format(self.content,self.value) 
    class Meta: 
     app_label = 'game' 

def testPrefetchOrig(): 
    contents = Content.objects.filter(name__startswith = 'a').prefetch_related('requirements') 
    for c in contents: 
     for r in c.requirements.all(): 
     print r 
     logging.warning(r) 
    logging.warning("Done with query") 

该预取数据,但还是查询它:

DEBUG:django.db.backends:(0.001) SELECT "game_content"."id", "game_content"."name", "game_content"."deleted" FROM "game_content" WHERE "game_content"."name"::text LIKE 'a%'; args=(u'a%',) 
DEBUG:django.db.backends:(0.001) SELECT "game_requirement"."id", "game_requirement"."content_id", "game_requirement"."value", "game_requirement"."deleted" FROM "game_requirement" WHERE "game_requirement"."content_id" IN (5, 6); args=(5, 6) 
DEBUG:django.db.backends:(0.000) SELECT "game_requirement"."id", "game_requirement"."content_id", "game_requirement"."value", "game_requirement"."deleted" FROM "game_requirement" WHERE ("game_requirement"."content_id" = 5 AND "game_requirement"."deleted" = false); args=(5, False) 
alphabet requires value 5 
WARNING:root:alphabet requires value 5 
alphabet requires value 3 
WARNING:root:alphabet requires value 3 
DEBUG:django.db.backends:(0.001) SELECT "game_requirement"."id", "game_requirement"."content_id", "game_requirement"."value", "game_requirement"."deleted" FROM "game_requirement" WHERE ("game_requirement"."content_id" = 6 AND "game_requirement"."deleted" = false); args=(6, False) 
albatross requires value 1 
WARNING:root:albatross requires value 1 
albatross requires value 0 
WARNING:root:albatross requires value 0 
WARNING:root:Done with query 

我使用自定义的经理怎么还要有prefetch_related工作?

+0

等等。我排序发生了什么事情。管理器的all()方法包含一个过滤器,用于过滤掉已删除的对象,但该过滤器将销毁高速缓存。 – user8168634

回答

0

好的。 DeletedItemsManager为每个all()方法调用添加一个额外的filterNoDeleted()方法调用。该过滤器调用会破坏缓存,按照https://docs.djangoproject.com/en/1.11/ref/models/querysets/中的注释

但是,我确实需要过滤掉已删除的相关对象。

我应该可以在Requirement表中选择所需的所有行,但我不确定如何在Queryset中设置正确的字段来反映这些结果。但是,如果我只想读取结果,我可以将其转换为字典。

有没有人有更优雅的解决方案?

注意:对Django中的filtered_relation添加了一个pull请求,但该请求尚未与prefetch_related配合使用。也许在未来,Django会支持这个用例。

0
def testPrefetchOrig(): 
contents = Content.objects.filter(name__startswith = 'a').prefetch_related(Prefetch('requirements', queryset=Requirement.objects.filterNoDeleted(),to_attr='undeletedRequirements')) 
for c in contents: 
    for r in c.undeletedRequirements: 
     print r 
print "Done with query" 
+0

我想在DeletedItemsManager中自动执行此过程,但如果我理解正确,我不能只是将一个方法添加到DeletedItemsManager,因为我会调用Content的管理器的prefetch_related,在本例中它也使用DeletedItemsManager,但总的来说,它可能是一个不同的经理。 最好不要自动化。 – user8168634