2017-08-10 6 views
1

Ich rufe eine Funktion auf, die einen Prozess startet, der länger dauert, viele verschiedene Dinge werden getan. Diese Funktion behandelt hauptsächlich Instanzen einer bestimmten Klasse, Item. Diese Artikel sind nach verschiedenen Attributen kategorisiert: category1, category2 und category3.Stellen Sie sicher, dass Abfragegruppe zwischengespeichert wird

Jetzt gibt es ein anderes Modell, das eine Art von Regeln in diesen Kategorien gilt: Rule mit many-to-many-Attributen: categories1, categories2 und categories3. Eine Regel gilt für eine Item, wenn dieselbe Regel auf verschiedene Kategorien verweist, von denen nur eine angewendet werden sollte. Die Entscheidung, von denen eine durch eine bestimmte Logik ist definiert in Abhängigkeit verkapselt:

class Rule(models.Model): 
    warehouse = models.ForeignKey('Warehouse') 
    categories1 = models.ManyToManyField('Category1') 
    categories2 = models.ManyToManyField('Category2') 
    categories3 = models.ManyToManyField('Category3') 

    @staticmethod 
    def get_rules_that_applies(item): 
     rules = warehouse.rule_set.all() 
     if not rules.exists(): 
      return None 
     # ... determine which rule applies to the item by filtering, etc. 
     return rule 

Das Problem liegt in der get_rules_that_applies Methode. Jedes Mal, wenn wir die Regel, die für einen bestimmten Artikel gilt, bekommen müssen, und lassen Sie mich noch einmal sagen, dass viele viele Dinge in den Prozess involviert sind, über den wir reden, warehouse.rule_set.all() heißt.

Da die Regeln nicht während dieses Prozesses ändern, können wir nur alle Regeln im Warehouse zwischenspeichern, aber wie? Wie kann ich sicherstellen, dass warehouse = warehouse.rule_set.all() zwischengespeichert wird und alle Filter- und QuerySet-Operationen, die auf diese Regeln einwirken, nicht die Datenbank treffen?

+0

Was in get_rules_that_applies ist Lager? –

+0

@DimaKudosh yeah Ich hätte es erwähnen sollen: das 'warehouse' ist wie das" master/context "-Objekt, wo der ganze Prozess passiert. Alles in der App dreht sich um eine einzelne "Warehouse" -Instanz. – dabadaba

Antwort

0

I glaube, dass die Lösung, die du suchst, die ist memoization der get_rules_that_applies Methode.

Es gibt ein Werkzeug, das dafür bereit ist, genannt django-memoize und those sind seine Dokumente.

Schnellstart-Verbrauch:

  1. pip install django-memoize
  2. Platz auf Ihrem INSTALLED_APPS

    INSTALLED_APPS = [ 
        '...', 
        'memoize', 
    ] 
    
  3. In Ihrem model.py:

    from memoize import memoize 
    
    class Rule(models.Model): 
        warehouse = models.ForeignKey('Warehouse') 
        categories1 = models.ManyToManyField('Category1') 
        categories2 = models.ManyToManyField('Category2') 
        categories3 = models.ManyToManyField('Category3') 
    
        @staticmethod 
        @memoize(timeout=something_reasonable_in_seconds) 
        def get_rules_that_applies(item): 
         rules = warehouse.rule_set.all() 
         if not rules.exists(): 
          return None 
          # ... determine which rule applies to the item by filtering, etc. 
         return rules 
    

(Update) Ein Semi-DIY Ansatz:

Da meine Antwort, las ich den folgenden Beitrag: https://www.peterbe.com/plog/cache_memoize-cache-decorator-for-django, die von einem gist wie memoization selbst zu erreichen, begleitet wird.


A More DIY Ansatz:

Python 3.2 und bis:

Der @functools.lru_cache decorator worin a:

Decorator einzuwickeln eine Funktion mit einer memoizing Aufrufbar, die bis zu den letzten Aufrufen der maximalen Größe speichert. Es kann Zeit sparen, wenn eine teure oder E/A-gebundene Funktion regelmäßig mit denselben Argumenten aufgerufen wird.

Wie man es verwendet:

from functools import lru_cache 


class Rule(models.Model): 
    ... 

    @lru_cache(maxsize=a_reasonable_integer_size_of_cache) 
    def get_rules_that_applies(item): 
     rules = warehouse.rule_set.all() 
     if not rules.exists(): 
      return None 
      # ... determine which rule applies to the item by filtering, etc. 
     return rules 

maxsize: Definiert die Größe des Cache in Funktion aufruft, gespeichert werden. Es kann auf None gesetzt werden, um jeden Anruf zwischenzuspeichern.

Python < 3,2

In hier What is memoization and how can I use it in Python? existiert eine "alte Schule" -Ansatz.


Wie ein queryset mit einem der oben genannten Methoden cachen:

Warum nicht definieren eine Zwischenfunktion die queryset und Cache, die Funktionen Ergebnisse zu bilden?

@lru_cache(maxsize=None) 

or 

@memoize() 
def middle_function(): 
    return warehouse.rule_set.all() 

und dann in Ihrer get_rules_that_applies Funktion:

def get_rules_that_applies(item): 
    rules = middle_function() 
+0

Ich bin nicht wirklich hinter einem Cache mit einer Zeitüberschreitung, es kann und sollte während des gesamten Lebenszyklus der Ausführung des Prozesses (Sie kann leicht feststellen, wann es beendet ist, wenn die 'warehouse'-Instanz aus dem Speicher freigegeben wird. Auch das würde die Regel für das Objekt richtig zwischenspeichern? (da es die Funktionsergebnisse zwischenspeichert) Kann ich nicht einfach einen Abfrage-Satz zwischenspeichern? – dabadaba

+0

@dabadaba Ich habe eine Bearbeitung gemacht, wie man ein Queryset zwischenspeichert, wie ich es mir vorstelle :) Btw, für 'memoize' Ich weiß es nicht, aber der' lru_cache' scheint kein Timeout zu haben. –

+0

Ich habe gerade festgestellt, dass ich 'Django-memoize' nicht verwenden kann, weil meine Django-Version älter ist. – dabadaba

0

Sie haben 2 Option:

  1. Cache das Element in der Ansicht
  2. cahce das Element im Modell

Der Code wird das gleiche im Blick und im Modell, Import sein cahce:

from django.core.cache import cache 

Code:

if cache.get('query_result') is not None: 
    return cache.get('query_result') 
else: 
    cache.set('query_result', result, 3600) 
    #cache.set('cache_name', 'your query', 'expiry time') 
    return rule 

Ihr Modell wird:

class Rule(models.Model): 
warehouse = models.ForeignKey('Warehouse') 
categories1 = models.ManyToManyField('Category1') 
categories2 = models.ManyToManyField('Category2') 
categories3 = models.ManyToManyField('Category3') 

@staticmethod 
def get_rules_that_applies(item): 
    rules = warehouse.rule_set.all() 
    if not rules.exists(): 
     return None 
    # ... determine which rule applies to the item by filtering, etc. 
    if cache.get('query_result') is not None: 
     return cache.get('query_result') 
    else: 
     cache.set('query_result', result, 3600) 
     #cache.set('cache_name', 'your query', 'expiry time') 
     return rule 

    return rule 

Ein paar Infos zu Django Abfrage, wenn sie ausgewertet werden ?:

https://docs.djangoproject.com/en/1.11/ref/models/querysets/#when-querysets-are-evaluated

Hope this Hilfe

+0

das wäre Caching die Regel, die für das Element gilt, nicht die Quelle Abfrage von Regeln – dabadaba

Verwandte Themen