Давным давно описывал решение проблемы с сигналами и ManyToMany полями. Вопрос остался актуальным и по сей день и частенько всплывает в форумах.
Напомню суть: в save методе (или в обработчике pre_/post_save сигналов) модели родителе (Post) нельзя узнать об изменениях в отношениях с чайлдами (Comment). Это связано с тем, что чайлды присоединяются к родителю после его сохранения.
С тех пор много воды утекло и в джанге появилась возможность решить данную задачу иначе. И мне кажется даже более правильно, нежели выдумывать новое поле.
В версии 1.0 нам дали возможность задать для M2M связывания свою модель. Применительно к моему старому примеру это будет выглядеть так:
class Comment(models.Model):
#...
class Post(models.Model):
comments = models.ManyToManyField(Comment, through='PostToCommentRelation')
#...
class PostToCommentRelation(models.Model):
post = models.ForeignKey(Post)
comment = models.ForeignKey(Comment)
И вот как только мы сделали эту модель и явно указали её в качестве промежуточной, у нас появилась возможность абсолютно стандартным путем отслеживать изменения связей. Поскольку это обычная модель, то она сама имеет этот самый пресловутый post_save сигнал, повесив обработчик на который, вы всегда сможете отследить какие два объекта были связаны и как-то на это прореагировать.
def relation_change_handler(sender, instance, created, **kwargs):
# работа с instance.post или instance.comment
pass
signals.post_save.connect(relation_change_handler, sender=PostToCommentRelation)
Теперь такой подход мне кажется более рациональным и религиозно правильным:-)
P.S.: Очередное обновление в блоге - каптчи больше нет. Подружил движок с Akismet и теперь как все белые люди защищен им. Так что если вы не пользуетесь OpenID (кому легко уже сейчас), то комментировать стало ещё проще! Welcome!:-)
Комментарии 8
Такой вариант все равно не даст удобно обновлять какой-нибудь comment_count (которому нужно обрабатывать всю пачку комментов целиком).
Оставлен 09 Апрель 2009 в 15:22 ¶Хм... Это как посмотреть. Такой подход дает нам понять, когда "что-то поменялось". И мы можем просто пересчитывать в этом случае денормализованное поле:
А вообще, в таких случаях конечно нужно пользоваться композиционным полем:)
Оставлен 09 Апрель 2009 в 16:05 ¶Дык это то же самое, только в профиль, в смысле - в одно поле.
Для комментов такой вариант годится, но если объекты надо аттачить не по одному, а пачками - то оверхэд большой получается, каждый раз родителя дергать.
Оставлен 09 Апрель 2009 в 16:16 ¶Избыточность конечно есть, но зато способ действительно очень прост
Оставлен 09 Апрель 2009 в 16:23 ¶Опа, полезно.
Оставлен 09 Апрель 2009 в 19:59 ¶Если честно, я вообще никогда не понимал этой проблемы -- описывать где-то в моделях поведение на сохранение M2M-связей. Потому что на самом деле это должно делаться на уровне бизнес-логики -- во view, формах и т.д. Обычно это не только удобней, но и эффективней, потому что пересчет чего-либо можно вызвать один раз при добавлении нескольких связанных объектов, а не на каждый.
Оставлен 10 Апрель 2009 в 00:09 ¶Вань, больше года назад ты ответил практически тоже самое.:-) Да, вешать какую-то серьезную логику не надо. Но вот что-то подсчитать, что-то куда-то записать - как мне кажется это вполне подходящее решение для таких задач.
Оставлен 10 Апрель 2009 в 13:18 ¶М-да... Ну что ж, по крайней мере, меня нельзя упрекнуть в непоследовательности :-)
Оставлен 10 Апрель 2009 в 15:57 ¶Оставьте комментарий