I’ve got a model
UserGroup with many-to-many fields for managers and
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
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
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
clean() method to just modify the form data. I got the idea also
from stackoverflow. Here’s
the relevant part of my
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
.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 work a lot with Python (programming language) and Django (website framework). I live in The Netherlands and I'm happily married to Annie van Rees-Kooiman.
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):