Подтвердить что ты не робот

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 после сохранения?

Любая помощь очень ценится.

4b9b3361

Ответ 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