2017-07-11 1 views
0

Wenn ich alles tun, unten ohne benutzerdefinierten Manager, alles funktioniert wie erwartet:Custom Manager in Django Zerstört Caching für 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") 

Diese Prefetch-Vorgänge für die Daten einmal und nie wieder:

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 

Ich möchte jedoch einen benutzerdefinierten Manager verwenden, um das Filtern von "gelöschten" Einträgen zu handhaben, indem ein gelöschtes Flag gesetzt wird.

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() 

Und dann ändern wir unsere Modelle diese verwenden:

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") 

Diese Prefetch-Vorgänge für die Daten, aber immer noch fragt er:

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 

Wie ich den benutzerdefinierten Manager verwenden Sie und noch prefetch_related Arbeit haben?

+0

Warten. Ich verstehe, was passiert. Die all() -Methode des Managers enthält einen Filter, der gelöschte Objekte herausfiltert, aber dieser Filter zerstört den Cache. – user8168634

Antwort

0

Okay. Der DeletedItemsManager fügt einen zusätzlichen FilterNoDeleted() -Methodenaufruf für jeden Aufruf der Methode all() hinzu. Dieser Filteraufruf zerstört den Cache, wie in der Notiz in https://docs.djangoproject.com/en/1.11/ref/models/querysets/

Ich muss jedoch wirklich die gelöschten verwandten Objekte herausfiltern.

Ich sollte alle erforderlichen Zeilen in der Anforderungs-Tabelle auswählen können, obwohl ich nicht sicher bin, wie die richtigen Felder im Queryset festgelegt werden, um diese Ergebnisse widerzuspiegeln. Wenn ich jedoch nur die Ergebnisse lesen möchte, kann ich das wahrscheinlich in ein Wörterbuch umwandeln.

Hat jemand eine elegantere Lösung?

Hinweis: Es gibt eine Pull-Anforderung für einen Zusatz "filtered_relation" zu Django, der jedoch noch nicht mit prefetch_related funktioniert. Vielleicht wird Django in Zukunft diesen Anwendungsfall unterstützen.

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

Ich möchte diesen Prozess im DeletedItemsManager automatisieren, aber wenn ich die Dinge richtig verstehe, kann ich DeletedItemsManager nicht einfach eine Methode hinzufügen, da ich prefetch_related des Content-Managers aufrufen würde, der in diesem Beispiel auch DeletedItemsManager verwendet. aber im Allgemeinen könnte es ein anderer Manager sein. Es ist vielleicht am besten, dies nicht zu automatisieren. – user8168634

Verwandte Themen