Проблема с ManyToMany Отношения не обновляются сразу после сохранения
У меня возникают проблемы с отношениями ManytoMany, которые не обновляются
в модели, когда я ее сохраняю (через администратора) и пытаюсь использовать новое значение в пределах
функция, привязанная к сигналу post_save
или внутри save_model
связанный AdminModel
.
Я попытался перезагрузить объект внутри этих функций, используя
получить функцию с id.. но она все еще имеет старые значения.
Это транзакция? Есть ли сигнал, когда
транзакция завершается?
Спасибо,
Ответы
Ответ 1
Когда вы сохраняете модель через формы администратора, это не атомная транзакция. Основной объект сначала сохраняется (чтобы убедиться, что он имеет PK), тогда M2M очищен, а новые значения установлены на все, что вышло из формы. Поэтому, если вы находитесь в файле save() основного объекта, вы находитесь в окне возможностей, где M2M еще не обновлен. На самом деле, если вы попытаетесь что-то сделать для M2M, изменение будет уничтожено с помощью clear(). Я столкнулся с этим примерно год назад.
Код несколько изменился с дней рефакторинга до ORM, но он сводится к коду в django.db.models.fields.ManyRelatedObjectsDescriptor
и ReverseManyRelatedObjectsDescriptor
. Посмотрите на их методы __set __(), и вы увидите manager.clear(); manager.add(*value)
Что clear() полностью очищает любые ссылки M2M для текущего основного объекта в этой таблице. Затем add() устанавливает новые значения.
Итак, чтобы ответить на ваш вопрос: да, это проблема транзакции.
Есть ли сигнал, когда транзакция заканчивается? Ничего официального, но читайте дальше:
Несколько месяцев назад существовал связанный поток, и MonkeyPatching был одним из предложенных методов. Grégoire отправил MonkeyPatch для этого. Я не пробовал, но похоже, что он должен работать.
Ответ 2
У меня есть общее решение, которое кажется немного чище, чем обезглавливание ядра или даже использование сельдерея (хотя я уверен, что кто-то может найти районы, где он терпит неудачу). В основном я добавляю метод clean() в админ для формы, которая имеет отношения m2m, и устанавливает отношения экземпляра к версии cleaned_data. Это делает правильные данные доступными для метода сохранения экземпляра, хотя он еще не "в книгах". Попробуйте и посмотрите, как это получится:
def clean(self, *args, **kwargs):
# ... actual cleaning here
# then find the m2m fields and copy from cleaned_data to the instance
for f in self.instance._meta.get_all_field_names():
if f in self.cleaned_data:
field = self.instance._meta.get_field_by_name(f)[0]
if isinstance(field, ManyToManyField):
setattr(self.instance,f,self.cleaned_data[f])
Ответ 3
Когда вы пытаетесь получить доступ к полям ManyToMany в сигнале post_save модели, связанные объекты уже удалены и не будут добавлены снова до тех пор, пока сигнал не будет завершен.
Чтобы получить доступ к этим данным, вам необходимо привязать метод save_related к вам ModelAdmin. К сожалению, вам также нужно будет включить код в сигнал post_save для запросов без администратора, требующих вашей настройки.
см. https://docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.ModelAdmin.save_related
Пример:
# admin.py
Class GroupAdmin(admin.ModelAdmin):
...
def save_related(self, request, form, formsets, change):
super(GroupAdmin, self).save_related(request, form, formsets, change)
# do something with the manytomany data from the admin
form.instance.users.add(some_user)
Затем в ваших сигналах вы можете сделать те же изменения, которые вы хотите выполнить при сохранении:
# signals.py
@receiver(post_save, sender=Group)
def group_post_save(sender, instance, created, **kwargs):
# do somethign with the manytomany data from non-admin
instance.users.add(some_user)
# note that instance.users.all() will be empty from the admin: []
Ответ 4
См. http://gterzian.github.io/Django-Cookbook/signals/2013/09/07/manipulating-m2m-with-signals.html
проблема:
Когда вы манипулируете m2m модели в приемнике сообщений post или pre_save, ваши изменения уничтожаются при последующей "очистке" m2m от Django.
решение:
В обработчике сообщений post или pre_save зарегистрируйте другой обработчик сигнала m2m_changed на промежуточной модели m2m модели, m2m которую вы хотите обновить.
Обратите внимание, что этот второй обработчик получит несколько сигналов m2m_changed, и это ключ для проверки значения аргументов "действия", переданных вместе с ними.
Внутри этого второго обработчика проверьте действие post_clear. Когда вы получаете сигнал с действием post_clear, m2m был очищен Django, и у вас есть шанс успешно его обработать.
пример:
def save_handler(sender, instance, *args, **kwargs):
m2m_changed.connect(m2m_handler, sender=sender.m2mfield.through, weak=False)
def m2m_handler(sender, instance, action, *args, **kwargs):
if action =='post_clear':
succesfully_manipulate_m2m(instance)
pre_save.connect(save_handler, sender=YouModel, weak=False)
см. https://docs.djangoproject.com/en/1.5/ref/signals/#m2m-changed
Ответ 5
Другой подход без патча Monkey - celery, вы можете сделать задачу, которая будет обращаться к правильным данным для отношения M2M, это потому, что задача выполняется асинхронно, и если вы установите задержку в 30 секунд, aproxim, вы убедитесь, что транзакция завершена.
Вы должны вызывать Task.apply_async(args=[...], countdown=30)
в post_save
или pre_save
.
Ответ 6
Вы можете найти дополнительную информацию в этой теме: Django manytomany сигналы?