2017-07-11 61 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