2011-11-04 52 views
1

由於我在Django中很新,我在這裏要求提供一些關於在Django中執行非規範化的最佳實踐的建議。按照我,我想這樣做是這樣的:Django非規範化和一致性:

我有2種型號:

類別:

name = m.CharField(max_length = 127) 

文章:

name = m.CharField(max_length = 127) 
category = m.ForeignKey(Category) 
category_name = m.CharField(max_length = 127) 

我想這種情況發生:

當我更改任何類別的名稱以反映所有文章中的更改。現在,當我們沒有隻有一個或兩個字段但大約10-20個字段保持同步時,最好的做法是什麼?

感謝您的幫助(:

+0

這對於非規範化看起來很奇怪。你爲什麼認爲你需要它?你確定你有獲取類別信息的瓶頸嗎? –

+0

如果我將不得不顯示與他們的品牌的文章列表,我需要進行內部加入。在這種情況下,我不需要任何加入... –

+2

這顯然是真的,但加入有什麼問題?數據庫擅長這些。如果您確定在這一點上確實存在性能問題,則非規範化是必需的。 –

回答

5

我使用關係和非規範化只有當你有性能問題只有您確認後,這個糟糕的性能與查詢類別名稱建議,否則。它只是增加了複雜性沒有充分的理由記住Donald Knuth's名言:。

過早的優化是所有罪惡的根源

關係數據庫是擅長連接,因爲基本上這就是他們設計的。在您想要的非規格化設計中,您將需要複雜的UPDATEs而不是最簡單的JOINs。這些更新會影響許多(10-20)表中的許多行。如果您在受影響的表中有大量數據並且經常更改category_name,那麼它甚至可能會使性能變差。


如果你真的堅持的category_name在10-20桌的想法考慮使用database trigger。當類別表被改變時觸發器將被執行。它可以處理數據庫內部的所有更新。無需改變Django項目中的任何內容,並且開銷更少。

所以,如果你真的堅持的category_name想法在10-20桌,你不能使用觸發器有一個在Django稱爲signals機制。這些都是嵌入到Django中的觸發器,並在定義的事件之前/之後觸發。

from django.db.models import signals 
from django.core.exceptions import DatabaseError 

class Category(m.Model): 

    def __init__(self, *args, **kwargs): 
     super(Category, self).__init__(*args, **kwargs) 

     # Store the initial name 
     self._name = self.name 

    name = m.CharField(max_length = 127) 

def update_category_name(sender, instance, **kwargs): 
    """ Callback executed when Category is about to be saved """ 

    old_category = instance._name 
    new_category = instance.name 

    if old_category != new_category:  # Name changed 

     # Start a transaction ? 

     try: 
      # Update the data: 

      # Make category_name an db_index, otherwise it will be slooooooooow 
      Article.objects.filter(category_name=old_category).update(category_name=new_category) 

      # commit transaction ? 

     except DatabaseError as e: 
      # rollback transaction ? 
      # prevent saving the category as database will be inconsistent 

      raise e 

# Bind the callback to pre_save singal 
signals.pre_save.connect(update_category_name, sender=Category)