2010-11-26 15 views
5

Aşağıdaki örnekte, bir veritabanı-pahalı özelliği (örnekte related_spam) çağrıldığında model örneğinde bir öznitelik almak veya ayarlamak için cached_attr kullanılır. Örnekte, sorguları kaydetmek için cached_spam kullanıyorum. Ayarlarken ve değerleri alırken baskı ifadeleri koydum, böylece test edebilirim. Egg örneğini, {{ egg.cached_spam }} kullanarak görünümde ve görünümde geçirerek, cached_spam çağrılarını cached_spam'a çeviren Egg modelindeki diğer yöntemleri kullanarak test ettim. Django'nun geliştirme sunucusundaki kabuk çıktısını bitirip test ettiğimde, özellik önbelleğinin birkaç kez kaçırılmasının yanı sıra birkaç kez başarılı bir şekilde kazanıldığını gösterdi. Tutarsız görünüyor. Aynı verilerle, küçük değişiklikler yaptığımda (baskı ifadesinin dizgisini değiştirmeden) ve yenilendiğinde (aynı verilerle), farklı miktarlarda hatalar/başarılar oldu. Bu nasıl ve neden oluyor? Bu kod yanlış mı yoksa çok sorunlu mu?Python - gelecekteki hesaplamaları önlemek için bir özelliği önbelleğe alma

class Egg(models.Model): 
    ... fields 

    @property 
    def related_spam(self): 
     # Each time this property is called the database is queried (expected). 
     return Spam.objects.filter(egg=self).all() # Spam has foreign key to Egg. 

    @property 
    def cached_spam(self): 
     # This should call self.related_spam the first time, and then return 
     # cached results every time after that. 
     return self.cached_attr('related_spam') 

    def cached_attr(self, attr): 
     """This method (normally attached via an abstract base class, but put 
     directly on the model for this example) attempts to return a cached 
     version of a requested attribute, and calls the actual attribute when 
     the cached version isn't available.""" 
     try: 
      value = getattr(self, '_p_cache_{0}'.format(attr)) 
      print('GETTING - {0}'.format(value)) 
     except AttributeError: 
      value = getattr(self, attr) 
      print('SETTING - {0}'.format(value)) 
      setattr(self, '_p_cache_{0}'.format(attr), value) 
     return value 
+1

Bu sıkıntıya karşı geldim ve bu problemi çözmenin başka bir yolunun hesaplanmış bir sonuç için bir takma ad oluşturmak üzere 'with' şablon etiketini kullanmak olduğunu fark ettim. – nedned

+0

@humble - thx, aslında oldukça yararlı bir not. – orokusaki

cevap

9

Kodunuzda olduğu kadarıyla yanlış bir şey yok. Sorun muhtemelen orada değil, bu kodu nasıl kullandığınız konusunda.

Gerçekleştirilmesi gereken en önemli şey, model örneklerinin kimlik içermemesidir. Bu, bir yumurta nesnesini bir yerde ve başka bir yerde farklı bir nesneyi başlatırsanız, aynı temel veritabanı satırına başvursalar bile, iç durumu paylaşmayacakları anlamına gelir. Bu nedenle cached_attr numaralı telefonu arayarak önbelleğin diğerinde yer almasına neden olmaz.

my_first_egg = Egg.objects.get(pk=1) 
my_related_object = RelatedObject.objects.get(egg__pk=1) 
my_second_egg = my_related_object.egg 
İşte

my_first_egg ve my_second_egg hem pk 1 ile veritabanı satırı belirtirken, ancak değil aynı nesne şunlardır: Eğer Egg bir ForeignKey sahip bir relatedObject sınıf var varsayarak Örneğin

:

>>> my_first_egg.pk == my_second_egg.pk 
True 
>>> my_first_egg is my_second_egg 
False 

Yani, my_first_egg önbelleği doldurma my_second_egg üzerine doldurun etmez.

Ve tabi ki, nesneler isteklere göre devam etmeyecek (özellikle genel olarak yapılmadıkça, korkunç olan), bu nedenle önbellek de kalıcı olmayacaktır. Ölçeklendirilmiş Http sunucuları hiç bir şey paylaşılmadığında;

+0

teşekkürler. Sağlanan örnek, bence, tam olarak neler oluyor. Kodumda nerede olduğunu çok fazla örnek alıyorum. Kod incelememe dayanarak sadece 2 ayrı örneğe sahip olması gereken bir nesne ('my_related_object.egg' den biri) 4 örneğe sahiptir. Sanırım modelimin tam bir revizyon zamanı geldi; tüm 200 satır 8 ( – orokusaki

+0

Ben suçlu buldum! Ben tekrar bir nesnenin tüm almak oldu bir hileli bir yöntem vardı (editörün arama sadece sözdizimi filtreleme nedeniyle biraz farklı olduğu için bulamadı). önbellekleme (basit not alma) yöntemim ve akıllıca bir seçim ile ilgili 31 sorgularım 3'e düşürdüm. Tekrar işaretçi için teşekkürler. – orokusaki

1

Singleton olmak için hiçbir şeye güvenemezsin. Durumu paylaşmak için özel amaçlı bir servise bağlanmanız gerekir. Django'nun caching support, kullanım durumunuz için uygundur. Bu, zorunlu olarak küresel bir tekton değildir; locmem:// kullanırsanız, işlem yerel olacaktır, bu daha verimli bir seçim olabilir.

+0

sadece istek başına önbelleğe alma içindir. Kendi önbellekleme çerçevemi asla uygulamaya çalışmam. Sadece aynı istek sırasında veri tabanından nesnelerin tekrar tekrar çekilmesinden bıktım. – orokusaki

İlgili konular