Лучший способ денормализации данных в Django?

Я разрабатываю простое веб-приложение, и имеет смысл хранить некоторые денормализованные данные.

Представьте себе платформу для ведения блогов, которая отслеживает комментарии, а в модели BlogEntry есть поле "CommentCount", которое я хотел бы поддерживать в актуальном состоянии.

Один из способов сделать это - использовать сигналы Django.

Другим способом сделать это было бы привязать крючки непосредственно в моем коде, который создает и уничтожает объекты Comment для синхронного вызова некоторых методов в BlogEntry для увеличения/уменьшения количества комментариев.

Я полагаю, что есть другие способы питонизации этого с декораторами или другим вуду.

Каков стандартный шаблон проектирования для денормализации в Django? На практике вам также приходится писать проверки целостности и фиксаторы данных в случае ошибок?

Ответы

Ответ 1

У вас есть менеджеры в Django.

Используйте настраиваемый менеджер для создания и поддержки отношений FK.

Менеджер может обновлять счетчики по мере обновления наборов дочерних элементов.

Если вы не хотите создавать настраиваемые менеджеры, просто расширяйте метод save. Все, что вы хотите сделать для денормализации подсчетов и сумм, можно сделать в save.

Вам не нужны сигналы. Просто растяните save.

Ответ 2

Я нашел django-denorm, чтобы быть полезным. Он использует триггеры уровня базы данных вместо сигналов, но, насколько я знаю, существует также ветвь, основанная на другом подходе.

Ответ 3

Первый подход (сигналы) имеет преимущество для ослабления связи между моделями.
Однако, сигналы как-то более сложно поддерживать, потому что зависимости менее явные (по крайней мере, на мой взгляд).
Если правильность подсчета комментариев не так важна, вы также можете подумать о задании cron, которое будет обновлять его каждые n минут.

Однако, независимо от решения, денормализация затруднит обслуживание; по этой причине я постараюсь максимально избежать этого, разрешая вместо этого использовать кеши или другие методы - например, использование with comments.count as cnt в шаблонах может значительно повысить производительность. Затем, если все остальное терпит неудачу и только в этом случае, подумайте о том, что может быть лучшим подходом к конкретной проблеме.

Ответ 4

Django предлагает отличную и эффективную (хотя и не очень известную) альтернативу денормализации счетчика.

Он сохранит ваши многочисленные строки кода, и он будет очень медленным, так как вы получите счетчик в том же запросе SQL.

Я предполагаю, что у вас есть эти классы:

class BlogEntry(models.Model):
     title = models.CharField()
     ...


class Comment(models.Model):
     body = models.TextField()
     blog_entry = models.ForeignKey(BlogEntry)

В вашем view.py используйте аннотации:

from django.db.models import Count

def blog_entry_list(Request):
    blog_entries = BlogEntry.objects.annotate(count=Count('comment_set')).all()
    ...

И у вас будет дополнительное поле для каждого BlogEntry, которое содержит количество комментариев, а также остальные поля BlobEntry.

Вы также можете использовать это дополнительное поле в шаблонах:

{% for blog_entry in blog_entries %}
  {{ blog_entry.title }} has {{ blog_entry.count }} comments!
{% endfor %}

Это позволит не только сохранить время кодирования и обслуживания, но и эффективно (запрос выполняется только немного дольше).

Ответ 5

Почему бы просто не получить набор комментариев и найти количество элементов, используя метод count():

count = blog_entry.comment_set.count()

Затем вы можете передать это в свой шаблон.

Или, альтернативно, в самом шаблоне вы можете сделать:

{{ blog_entry.comment_set.count }}

чтобы получить количество комментариев.