Допустив дальнейшего переопределения сохранить formset с на ModelAdmin
довольно простой сценарий использования здесь. Я хочу сохранить пользователя, который создал объект, и пользователя, который последний раз его изменил. Тем не менее, это встроенная модель, поэтому мне, конечно, нужно использовать save_formset
. Документы Django имеют следующий пример кода:
class ArticleAdmin(admin.ModelAdmin):
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
instance.user = request.user
instance.save()
formset.save_m2m()
дело в том, если вы заметили, так как super
никогда не вызывается, это тупик. Если ModelAdmin
является подклассом, и этот метод переопределяется таким же образом, вы теряете функциональность, присущую родителю. Этот вопрос поскольку это такой распространенный сценарий использования, что я хочу исключить функциональность, поэтому я создал следующее:
class TrackableInlineAdminMixin(admin.ModelAdmin):
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
if hasattr(instance, 'created_by') and hasattr(instance, 'modified_by'):
if not instance.pk:
instance.created_by = request.user
instance.modified_by = request.user
instance.save()
formset.save_m2m()
super(TrackableInlineAdminMixin, self).save_formset(request, form, formset, change)
Я нажал на кнопку вызова super
по привычке больше, чем что-либо еще, не думая, что это на самом деле заставит formset экономить дважды. Тем не менее, он по-прежнему работает в каждом сценарии, кроме одного: удаление. Как только вы попытаетесь удалить встроенную в Администраторе, вы получите сообщение об ошибке. Ошибка довольно расплывчата и не совсем соответствует моему вопросу здесь, но Я считаю, что это связано с попыткой сохранить formset снова после того, как вы только что удалили один из экземпляров в нем. Код работает просто отлично, когда вызов super
удалены.
длинный и короткий, есть ли способ, которым мне не хватает, чтобы настроить поведение сохранения набора форм и разрешить подклассам выполнять собственное переопределение?
2 ответов
это doozie.
я повеселился, ковыряясь вокруг, и, похоже, все действие происходит здесь в django.forms.models.BaseModelFormSet
.
проблема в том, что ModelFormSet.save()
удаляет экземпляры независимо от commit
флаг и не изменяет формы, чтобы отразить удаленном состоянии.
если вы называете save()
снова он повторяет над формами и в ModelChoiceField
очистка пытается вытащить указанный идентификатор и выдает ошибку недопустимого выбора.
def save_existing_objects(self, commit=True):
self.changed_objects = []
self.deleted_objects = []
if not self.initial_forms:
return []
saved_instances = []
for form in self.initial_forms:
pk_name = self._pk_field.name
raw_pk_value = form._raw_value(pk_name)
# clean() for different types of PK fields can sometimes return
# the model instance, and sometimes the PK. Handle either.
pk_value = form.fields[pk_name].clean(raw_pk_value)
pk_value = getattr(pk_value, 'pk', pk_value)
obj = self._existing_object(pk_value)
if self.can_delete and self._should_delete_form(form):
self.deleted_objects.append(obj)
obj.delete()
# problem here causes `clean` 6 lines up to fail next round
# patched line here for future save()
# to not attempt a second delete
self.forms.remove(form)
только способ, которым я смог исправить это, - это патч BaseModelFormset.save_existing_objects
удалить форму из self.forms
если объект удаляется.
провел некоторое тестирование, и, похоже, никаких побочных эффектов нет.
@Chris Pratt помог:
Я нажал на звонок супер по привычке больше, чем что-либо еще, не думая, что это на самом деле заставит formset экономить дважды.
Я пытался дополнительно переопределить save_formset
для того, чтобы отправить сообщение сохранить сигнал. Я просто не мог понять, что вызов super()
спасал formset с второй раз.
для того, чтобы разобраться с super()
проблема, я создал save_formset_now
метод с моим пользовательский код, который я вызываю, когда переопределяю save_formset
через admin.ModelAdmin
дети.
это код, который, похоже, также заботится об удалении проблемы, используя Django 1.10 в 2016 году.
class BaseMixinAdmin(object):
def save_formset_now(self, request, form, formset, change):
instances = formset.save(commit=False)
for obj in formset.deleted_objects:
obj.delete()
for instance in instances:
# *** Start Coding for Custom Needs ***
....
# *** End Coding for Custom Needs ***
instance.save()
formset.save_m2m()
class BaseAdmin(BaseMixinAdmin, admin.ModelAdmin):
def save_formset(self, request, form, formset, change):
self.save_formset_now(request, form, formset, change)
class ChildAdmin(BaseAdmin):
def save_formset(self, request, form, formset, change):
self.save_formset_now(request, form, formset, change)
my_signal.send(...)