2👍
In fact, this is a problem about adding programatically many to many relationships when saving a model if you use the Django admin.
Django save m2m relationships in the admin by calling ‘clear’ to wipe them out, then setting them again. It means that the form destroy any attached data (including your programatically attached) to the object then add the ones you entered in the admin.
It works outside the admin because we don’t use the admin form that clear the m2m relationship.
The reason it works for tags in the admin is that the tagging application doesn’t use m2m but emulate it by placing a TaggedItem object with a foreign key to a tag and to your model with a generic relation. Plus it’s an inline field inclusion.
I tried a lot of things and finally had to look at the Django source code to realize that Django does not process admin forms in the usual way. What it does it:
- call
ModelAdmin.save_form
: it callsform.save
withcommit = False
, returning an unsaved instance and adding asave_m2m
method to the form. - call
ModelAdmin.save_model
that actually calls the instancesave
method. - call
form.save_m2m
Therefor:
- you can’t override your
save
method sincesave_m2m
is called after and clear the m2m relations. - you can’t override
save_model
for the same reason. - you can’t override
save_m2m
because it is added by monkey patch to the form model inform.save
, erasing you own method definition.
I didn’t find a clean solution, but something that works is:
Provide a form for the ModelAdmin class with a method to override save_m2m
with your own method:
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 :-)
Call this method in a ModelAdmin.model_save
override:
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def save_model(self, request, obj, form, change):
obj.save()
form.set_m2m_method()
This cause the following:
- Django calls
save_model
, replacing its monkey patch by yours - django calls our
form.save_m2m
that first call its old method that clears relations, then attach the m2m to the object.
I’m completely open to any better way to do this as this is twisted and plain ugly.
0👍
Since the problem seems to be reserved to admin, I tried to add some logic to do this in the ModelAdmin
‘s save_model
method, but it doesn’t seem to help at all:
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()
Oddly print obj.sites.all()
does list all the sites, however, they don’t stay saved. Some sort of M2M issue perhaps?
- [Answered ]-Django URL explanation
- [Answered ]-How I can display one field as string from serializer?
- [Answered ]-Django single checkbox questionaire
- [Answered ]-Incorporating Python code in a JavaScript web application
- [Answered ]-Url captured regex (\w+) not matching certain urls