Почему добавление сайта к объекту не работает в переопределении save () в администраторе Django? - PullRequest
1 голос
/ 12 мая 2011

Я переопределил метод save() одной из моих моделей, чтобы он мог наследовать от объекта sites и тегов от его родителя.

def save(self, *args, **kwargs):

    ret = models.Model.save(self, *args, **kwargs)

    if self.id:

        for site in self.parent.sites.all():
            self.sites.add(site.id)

        for tag in self.parent.tags_set.all():
            Tag.objects.add_tag(self, tag)

Используя ipdb,Я вижу, что self.sites.all() возвращает 4 сайта в конце метода, но, как ни странно, после завершения запроса тот же self.sites.all() больше ничего не возвращает.

Я не использую транзакции(по крайней мере, явно), и я использую Django 1.3 и Ubuntu 11.04

РЕДАКТИРОВАТЬ: обнаружил, что это работает где-нибудь, кроме администратора.Разве админ не вызывает сохранение?Если нет, то как я могу подключиться к созданию / обновлению объекта?

EDIT2: протестировано и вызывает save.У меня есть печатные заявления, чтобы доказать это.Но это не добавляет сайты.Это загадка.

Ответы [ 2 ]

2 голосов
/ 13 мая 2011

Фактически, это проблема добавления программно многих ко многим отношениям при сохранении модели, если вы используете администратора Django.

Django сохраняет отношения m2m в админке, вызывая команду «clear», чтобы стереть ихзатем установите их снова.Это означает, что форма уничтожает любые прикрепленные данные (включая ваши программно присоединенные) к объекту, а затем добавляет те, которые вы ввели в администратор.

Это работает вне администратора, потому что мы не используем форму администратора, которая очищаетотношение m2m.

Причина, по которой это работает для тегов в администраторе, заключается в том, что приложение тегирования не использует m2m, а эмулирует его, помещая объект TaggedItem с внешним ключом в тег и в вашу модель сродовое отношение.Кроме того, это встроенное поле.

Я много чего перепробовал и, наконец, взглянул на исходный код Django, чтобы понять, что Django не обрабатывает формы администратора обычным способом.Что он делает:

  1. call ModelAdmin.save_form: он вызывает form.save с commit = False, возвращает несохраненный экземпляр и добавляет в форму метод save_m2m.
  2. вызов ModelAdmin.save_model, который фактически вызывает метод экземпляра save.
  3. вызов form.save_m2m

для этого:

  • вы не можете переопределить свойsave метод, так как save_m2m вызывается после и очищает отношения m2m.
  • вы не можете переопределить save_model по той же причине.
  • вы не можете переопределить save_m2mпотому что он добавляется патчем обезьяны к модели формы в form.save, стирая собственное определение метода.

Я не нашел чистого решения, но кое-что, что работает:

Предоставьте форму для класса ModelAdmin с методом для переопределения save_m2m с вашим собственным методом:

class MyModelForm(forms.ModelForm):

    class Meta:
        model = MyModel

    def set_m2m_method(self, update_tags_and_sites=True):

        alias = self.save_m2m

        def new_save_m2m(): # this is your new method 
            alias() # we need to call the original method as well
            self.instance.add_niche_sites_and_tags()

        self.save_m2m = new_save_m2m # we erase Django erasing :-)

Вызовите этот метод в ModelAdmin.model_save переопределении:

class MyModelAdmin(admin.ModelAdmin):

    form = MyModelForm

    def save_model(self, request, obj, form, change):
        obj.save()
        form.set_m2m_method()

Это вызывает следующее:

  1. Джанго звонит save_model, заменяя его патч обезьяны на ваш
  2. Джанго звонит нашему form.save_m2m, который первымвсе его старый метод, который очищает отношения, а затем прикрепляет m2m к объекту.

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

0 голосов
/ 12 мая 2011

Поскольку проблема, похоже, зарезервирована для администратора, я попытался добавить некоторую логику для этого в методе ModelAdmin s save_model, но, похоже, это не помогло совсем:

class SomeModelAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.save()
        for site in Site.objects.all():
            obj.sites.add(site.id)
        print obj.sites.all()

Как ни странно print obj.sites.all() перечисляет все сайты, однако они не сохраняются.Возможно, какая-то проблема с M2M?

...