I’ve got a model UserGroup
with many-to-many fields for managers and
members:
class UserGroup(models.Model):
managers = models.ManyToManyField(User)
members = models.ManyToManyField(User)
# Note: some stuff stripped out for brevity
I wanted every manager to be a member, too, automatically. So I added a custom
save()
method:
class UserGroup(models.Model):
managers = models.ManyToManyField(User)
members = models.ManyToManyField(User)
# Note: some stuff stripped out for brevity
def save(self, *args, **kwargs):
if self.id:
members = self.members.all()
for manager in self.managers.all():
if manager not in members:
self.members.add(manager)
super(UserGroup, self).save(*args, **kwargs)
Which worked fine in my unittest, but not in the actual admin interface.
I found the reason on stackoverflow:
the Django admin clears the many-to-many fields after saving the model and
sets them anew with the data it knows about. So my save()
method worked
fine, but saw its work zapped by Django’s admin…
Django’s development docs say that 1.4 will have
a save_related()
model admin method. Which sounds like it could help work
around this issue.
The solution I ended up with was to add a custom model admin form and to
use the clean()
method to just modify the form data. I got the idea also
from stackoverflow. Here’s
the relevant part of my admin.py
:
class UserGroupAdminForm(ModelForm):
class Meta:
model = UserGroup
def clean(self):
"""Make sure all managers are also members."""
for manager in self.cleaned_data['managers']:
if manager not in self.cleaned_data['members']:
self.cleaned_data['members'].append(manager)
return self.cleaned_data
class UserGroupAdmin(admin.ModelAdmin):
model = UserGroup
form = UserGroupAdminForm
It works fine now.
Addition 2011-1202. It didnt’ work so fine after all. There’s one problem,
which you can also find on stackoverflow. Django
converts the raw form data to python objects. In the case of these user object
you get a queryset in the latest Django version instead of a list of user
objects. .append()
doesn’t work on a queryset. So I took one of the
suggestions on stackoverflow and converted the queryset to a list, first:
def clean(self):
"""Make sure all managers are also members."""
members = list(self.cleaned_data['members'])
for manager in self.cleaned_data['managers']:
if manager not in members:
members.append(manager)
self.cleaned_data['members'] = members
return self.cleaned_data
My name is Reinout van Rees and I program in Python, I live in the Netherlands, I cycle recumbent bikes and I have a model railway.
Most of my website content is in my weblog. You can keep up to date by subscribing to the automatic feeds (for instance with Google reader):