39đź‘Ť
Alasdair’s answer isn’t wrong, but it has a few sore points that could cause problems. First, by looping through the formset using form
as the variable name, you actually override the value passed into the method for form
. It’s not a huge deal, but since you can do the save without commit right from the formset, it’s better to do it that way. Second, the all important formset.save_m2m()
was left out of the answer. The actual Django docs recommend the following:
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()
The problem you’re going to run into is that the save_formset
method must go on the parent ModelAdmin
rather than the inlines, and from there, there’s no way to know which inline is actually being utilized. If you have an obj with two “types” and all the fields are the same, then you should be using proxy models and you can actually override the save method of each to set the appropriate type automatically.
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)
Then, you don’t need to do anything special at all with your inlines. Just change your existing inline admin classes to use their appropriate proxy model, and everything will sort itself out.
7đź‘Ť
There’s a save_formset
method which you could override. You’d have to work out which inline the formset
represents somehow.
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()
- [Django]-Django Local Settings
- [Django]-How do I convert a Django QuerySet into list of dicts?
- [Django]-Django REST Framework and FileField absolute url
4đź‘Ť
Other answers are right when it comes to using save_formset. They are missing a way to check what model is currently saved though. To do that, you can just:
if formset.model == CustomerCompany:
# actions for specific model
Which would make the save_formset function look like: (assuming you just want to override save for the specific model(s))
def save_formset(self, request, form, formset, change):
for obj in formset.deleted_objects:
obj.delete()
# 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()
- [Django]-Why middleware mixin declared in django.utils.deprecation.py
- [Django]-How to get the value of a Django Model Field object
- [Django]-Retrieving list items from request.POST in django/python
2đź‘Ť
For the cases where you need to take an action if the registry is new, you need to do it before saving the formset.
def save_formset(self, request, form, formset, change):
for form in formset:
model = type(form.instance)
if change 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)
In this case I’m also applying it when the model contains a "created_by" field (because this is for a mixin that I’m using in many places, not for a specific model).
Just remove the and hasattr(model, "created_by")
part if you don’t need it.
Some other properties that might be interesting for you when messing with formsets:
"""
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.
"""
I hope my digging helps someone else! ^^
- [Django]-Open file in Django app
- [Django]-Using Django Rest Framework, how can I upload a file AND send a JSON payload?
- [Django]-In a django model custom save() method, how should you identify a new object?
0đź‘Ť
You can override the formset that InlineModelAdmin uses to save new models:
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)
- [Django]-How to pass multiple values for a single URL parameter?
- [Django]-How do I make many-to-many field optional in Django?
- [Django]-Django can't find new sqlite version? (SQLite 3.8.3 or later is required (found 3.7.17))