Переопределить сохранение в Django InlineModelAdmin
Этот вопрос может выглядеть похожим на этот, но это не так...
У меня есть структура модели, как:
class Customer(models.Model):
....
class CustomerCompany(models.Model):
customer = models.ForeignKey(Customer)
type = models.SmallIntegerField(....)
я использую InlineModels
и имеют два типа CustomerCampany.type
, Так что я определяю два различных встроенных для CustomerCompany
и переопределить InlineModelAdmin.queryset
class CustomerAdmin(admin.ModelAdmin):
inlines=[CustomerCompanyType1Inline, CustomerCompanyType2Inline]
class CustomerCompanyType1Inline(admin.TabularInline):
model = CustomerCompany
def queryset(self, request):
return super(CustomerCompanyType1Inline, self).queryset(request).filter(type=1)
class CustomerCompanyType2Inline(admin.TabularInline):
model = CustomerCompany
def queryset(self, request):
return super(CustomerCompanyType2Inline, self).queryset(request).filter(type=2)
Все хорошо и хорошо до здесь, но для добавления новых записей для InlineModelAdmin
мне все еще нужно отобразить type
поле CustomerCompany
на AdminForm
, так как я не могу переопределить save
метод InlineModelAdmin
лайк:
class CustomerCompanyType2Inline(admin.TabularInline):
model = CustomerCompany
def queryset(self, request):
return super(CustomerCompanyType2Inline, self).queryset(request).filter(type=2)
#Following override do not work
def save_model(self, request, obj, form, change):
obj.type=2
obj.save()
Использование сигнала также не является решением, так как мой сигнал sender
будет таким же Model
так что я не могу определить какой InlineModelAdmin
отправь и что type
должно быть...
Есть ли способ, который позволит мне установить type
поле перед сохранением?
5 ответов
Ответ Alasdair не является неправильным, но у него есть несколько болезненных моментов, которые могут вызвать проблемы. Во-первых, перебирая набор форм с помощью form
в качестве имени переменной вы фактически переопределяете значение, переданное в метод для form
, Это не большое дело, но, поскольку вы можете делать сохранение без фиксации прямо из набора форм, лучше сделать это таким образом. Во-вторых, все важные formset.save_m2m()
был оставлен вне ответа. Фактические документы Django рекомендуют следующее:
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
# Do something with `instance`
instance.save()
formset.save_m2m()
Проблема, с которой вы столкнетесь, заключается в том, что save_formset
метод должен идти на родителя ModelAdmin
а не inlines, и оттуда, нет никакого способа узнать, какой inline фактически используется. Если у вас есть объект с двумя "типами" и все поля одинаковы, то вы должны использовать прокси-модели, и вы можете переопределить метод save каждого для автоматической установки соответствующего типа.
class CustomerCompanyType1(CustomerCompany):
class Meta:
proxy = True
def save(self, *args, **kwargs):
self.type = 1
super(CustomerCompanyType1, self).save(*args, **kwargs)
class CustomerCompanyType2(CustomerCompany):
class Meta:
proxy = True
def save(self, *args, **kwargs):
self.type = 2
super(CustomerCompanyType2, self).save(*args, **kwargs)
Тогда вам не нужно делать ничего особенного со своими строками. Просто измените свои существующие классы встроенного администратора, чтобы использовать их соответствующую модель прокси, и все само уладится.
Есть save_formset
метод, который вы могли бы переопределить. Вы должны решить, какие formset
представляет как-то.
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
# Do something with `instance`
instance.save()
formset.save_m2m()
Другие ответы правильны, когда дело доходит до использования save_formset. Им не хватает способа проверить, какая модель в данный момент сохранена. Для этого вы можете просто:
if formset.model == CustomerCompany:
# actions for specific model
Что заставило бы функцию save_formset выглядеть следующим образом: (при условии, что вы просто хотите переопределить сохранение для конкретной модели (ей))
def save_formset(self, request, form, formset, change):
# if it's not the model we want to change
# just call the default function
if formset.model != CustomerCompany:
return super(CustomerAdmin, self).save_formset(request, form, formset, change)
# if it is, do our custom stuff
instances = formset.save(commit=False)
for instance in instances:
instance.type = 2
instance.save()
formset.save_m2m()
В тех случаях, когда вам нужно выполнить действие, если реестр новый, вам нужно сделать это перед сохранением набора форм.
def save_formset(self, request, form, formset, change):
for form in formset:
model = type(form.instance)
if not form["id"].initial and hasattr(model, "created_by"):
# craeted_by will not appear in the form dictionary because
# is read_only, but we can anyway set it directly at the yet-
# to-be-saved instance.
form.instance.created_by = request.user
super().save_formset(request, form, formset, change)
В этом случае я также применяю его, когда модель содержит поле «created_by» (потому что это для примеси, которую я использую во многих местах, а не для конкретной модели).
Просто удалите
and hasattr(model, "created_by")
часть, если она вам не нужна.
Некоторые другие свойства, которые могут быть вам интересны при работе с наборами форм:
"""
The interesting fields to play with are:
for form in formset:
print("Instance str representation:", form.instance)
print("Instance dict:", form.instance.__dict__)
print("Initial for ID field:", form["id"].initial)
print("Has changed:", form.has_changed())
form["id"].initial will be None if it's a new entry.
"""
Я надеюсь, что мои копания помогут кому-то еще! ^^
Вы можете переопределить набор форм , который InlineModelAdmin использует для сохранения новых моделей:
def formset_cls(type):
class CustomerCompanyInlineFormset(BaseInlineFormSet):
def save_new(self, form, commit=True): # override
form.instance.type = type
return super().save_new(form, commit=commit)
return CustomerCompanyInlineFormset
class CustomerCompanyType1Inline(admin.TabularInline):
model = CustomerCompany
formset = formset_cls(type=1)
def queryset(self, request):
return super(CustomerCompanyType1Inline, self).queryset(request).filter(type=1)