Django非规范化和一致性:

时间:2011-11-04 09:28:33

标签: django denormalization consistency

由于我是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个字段保持同步时,最佳做法是什么?

感谢您的帮助(:

1 个答案:

答案 0 :(得分:5)

如果您遇到性能问题,我建议使用关系并使用非正规化 分类名称。否则它只是在没有充分理由的情况下增加复杂性。请记住Donald Knuth's名言:

  

过早优化是万恶之源。

关系数据库擅长连接,因为基本上就是它们的设计目的。在您想要的非规范化设计中,您需要复杂的JOINs,而不是最简单的UPDATEs。这些更新将影响许多(10-20)表中的许多行。如果受影响的表中有大量数据并且经常更改category_name,它甚至会使性能变差。


如果您真的坚持在10-20个表中使用category_name的想法,请考虑使用database trigger。更改类别表时将执行触发器。它可以在数据库内部处理所有更新。无需更改Django项目中的任何内容,而且开销较小。

因此,如果你真的坚持在10-20个表中使用category_name并且不能使用触发器,那么在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)