Поле ManyToMany не сохраняется при использовании администратора Django

У меня возникла странная проблема, и я надеюсь, что кто-то здесь сможет пролить свет.

Я переопределяю метод save() модели, чтобы добавить некоторые значения в поле ManyToMany после запуска super(). Моя проблема в том, что когда я сохраняю в администраторе Django, значения, кажется, добавляются к отношениям, но затем снова пусты.

Однако, если я делаю это с manage.py shell, это работает без проблем.

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

class Store(models.Model):
    holidays = models.ManyToManyField(StoreHoliday, blank=True)
    copy_holidays_from = models.ForeignKey('Store', blank=True, null=True)

    def save(self):
        print '====  BEFORE SAVE:', self.holidays.all()
        super(Store, self).save()
        self.copy_holidays()
        print '====  AFTER SAVE:', self.holidays.all()

    def copy_holidays(self):
        if self.pk and self.copy_holidays_from:
            self.holidays.clear()
            for h in self.copy_holidays_from.holidays.all():
                self.holidays.add( h )

Это вывод операторов print:

====  BEFORE SAVE: []
====  AFTER SAVE: [<StoreHoliday: 10 Mar 2010, Chuck Norris birthday (Closed)>]

Есть ли у кого-нибудь предложения о том, что может быть причиной этого?

Редактировать: все ручные изменения отношения m2m в save(), по-видимому, отбрасываются Django при сохранении через интерфейс администратора. Это связано с тем, как он обрабатывает форму?


person kb.    schedule 01.06.2011    source источник
comment
Я не знаю, в чем ваша проблема, но self.holidays = self.copy_holidays_from.holidays.all() выглядит намного лучше, чем очистка и итерация.   -  person DrTyrsa    schedule 01.06.2011
comment
Спасибо за подсказку, не знала, что так можно. Ниже вы можете увидеть, что я сделал не так.   -  person kb.    schedule 01.06.2011


Ответы (5)


Итак, оказывается, что вышеизложенное было неправильным способом его реализации. Код принадлежал StoreAdmin путем переопределения model_save().

Вот как я это решил:

class StoreAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        if obj.copy_holidays_from:
            form.cleaned_data['holidays'] = obj.copy_holidays_from.holidays.all()

        super(StoreAdmin, self).save_model(request, obj, form, change)
person kb.    schedule 01.06.2011

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

Администратор django вносит изменения в поле ManyToMany отдельно от изменения фактического объекта. (Помните, что m2m сохраняется в другой таблице базы данных).

В моем случае, если бы я ничего не выбрал в поле ManyToMany на сайте администратора, это привело бы к операции clear() над отношением ManyToMany. Все, что вы делаете в методе save(), немедленно удаляется этой очисткой. То же самое с тем, что я сделал в обработчике сигнала post_save.

Решение (для меня) заключалось в том, чтобы разделить поле ManyToMany на встроенное, чтобы оно автоматически не сохранялось как пустое при изменении объекта.

person Jyrsa    schedule 16.09.2011
comment
Привет, Юрса, у меня похожая проблема с Django Admin. У нас есть поле m2m, которое иногда очищается при сохранении остальной части страницы. Я не знал, что Django сохраняет его отдельно. Не могли бы вы дать мне дополнительную информацию о том, как обрабатывается m2m? Можете ли вы уточнить, при каких обстоятельствах действия метода сохранения очищаются? Спасибо! - person iank; 22.10.2014

В django 2,1,4 моим решением было использовать save_related()

def save_related(self, request, form, formsets, change):
    super().save_related(request, form, formsets, change)
    form.instance.permissions.add(request.user)
person Tomba    schedule 24.01.2019

Для меня проблема в том, что администратор сохранял только последний выбранный экземпляр многих полей (выбран последний «праздник»). Поэтому мне пришлось переопределить метод save_model следующим образом:

@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):

    def save_model(self, request, obj, form, change):
        form.cleaned_data['holidays'] = StoreHoliday.objects.filter(pk__in=dict(request.POST).get('holidays'))
        super(StoreAdmin, self).save_model(request, obj, form, change)

Я потратил на это много времени, и другие решения не работали, поэтому я надеюсь, что это поможет.

person Léo Chaz Maltrait    schedule 28.05.2018

Одно из решений для обновления m2m вместе с обновлением одной из ваших моделей.

Django 1.11 and higher

Поведение, которое вы можете наблюдать при обновлении, когда изменения, сделанные вами с m2m-записями, не сохранялись, даже после того, как вы сделали их в методе сохранения одной из ваших моделей или в сигнале, происходит только потому, что m2m-форма перезаписывает все записи после основного объект обновляется.

Вот почему, шаг за шагом:

  1. Основной объект обновлен.

  2. В вашем коде (в методе сохранения или в сигнале) были внесены изменения (их можно посмотреть, просто поставив точку останова в ModelAdmin):

 def save_related(self, request, form, formsets, change):
     breakpoint()
     form.save_m2m()
     for formset in formsets:
         self.save_formset(request, form, formset, change=change)
  1. form.save_m2m() принимает все значения m2m, которые были размещены на странице (грубо говоря), и заменяет все записи m2m через соответствующий менеджер. Вот почему вы не можете увидеть свои изменения в конце транзакции.

Решение есть: внесите свои изменения в m2m через transaction.on_commit. transaction.on_commit внесет ваши изменения после form.save_m2m(), когда транзакция будет зафиксирована.

К сожалению, обратная сторона этого решения — ваши изменения с m2m будут выполняться в отдельной транзакции.

person Greg Eremeev    schedule 14.02.2019