Django получает доступ к полям ManyToMany из сигнала post_save
У меня есть модель Django, и я хочу изменить разрешения объекта или сразу после сохранения. Я пробовал несколько решений, и сигнал post_save
казался лучшим кандидатом на то, что я хочу сделать:
class Project(models.Model):
title = models.CharField(max_length=755, default='default')
assigned_to = models.ManyToManyField(
User, default=None, blank=True, null=True
)
created_by = models.ForeignKey(
User,
related_name="%(app_label)s_%(class)s_related"
)
@receiver(post_save, sender=Project)
def assign_project_perms(sender, instance, **kwargs):
print("instance title: "+str(instance.title))
print("instance assigned_to: "+str(instance.assigned_to.all()))
В этом случае, когда создается проект, срабатывает сигнал, и я вижу title
, но пустой список для поля assigned_to
.
Как я могу получить доступ к сохраненным данным assigned_to
после сохранения?
Любая помощь очень ценится.
Ответы
Ответ 1
Ты не собираешься. M2M сохраняются после сохранения экземпляров и, таким образом, не будет никакой записи на всех обновлениях m2m. Дальнейшие проблемы (даже если вы решите это) - это то, что вы все еще находитесь в транзакции, и запрос БД не приведет к тому, что вы будете иметь m2m с соответствующими состояниями.
Решение заключается в подключении к сигналу m2m_changed
вместо post_save
.
https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed
Тогда ваш отправитель будет Project.assigned_to.through
Ответ 2
Если ваш m2m может быть пустым (blank=True
), у вас есть небольшая проблема с m2m_changed
, потому что m2m_changed
не срабатывает, если m2m не был установлен. Вы можете решить эту проблему, используя post_save
и m2m_changed
одновременно. Но есть один большой недостаток этого метода - ваш код будет выполнен дважды, если поле m2m не пустое.
Итак, вы можете использовать транзакцию on_commit (только Django >= 1.9)
from django.db import transaction
def on_transaction_commit(func):
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
@receiver(post_save, sender=SomeModel)
@on_transaction_commit
def my_untimate_func(sender, **kwargs):
# Do things here