I need to prevent user from self-killing from administrators list in the model:
class Organization(models.Model):
administrators = models.ManyToManyField(User, blank=True, null=True, help_text=_('Administrators are people that manage the organization'))
def save(self, *args, **kwargs):
# --- some specific code here ---
super(Organization, self).save(*args, **kwargs)
if self.user_id not in self.administrators.values_list('id', flat=True):
self.administrators.add(self.user)
# super(Organization, self).save(*args, **kwargs)
self.save()
# assert False, self.administrators.all() # <- it works, if assert goes here
Ok, it might be some black magic casted here, let's try a post_save
signal:
def organization_post_save(sender, instance, created, **kwargs):
if instance.user_id not in instance.administrators.all().values_list('id', flat=True):
instance.administrators.add(instance.user)
instance.save()
# assert False, '?'
It adds a user in the administrators list only if assertion occures. Ok, may be black magic occured again, let's try:
def organization_m2m_changed(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if instance.user_id not in instance.administrators.all().values_list('id', flat=True):
instance.administrators.add(instance.user_id)
m2m_changed.connect(organization_m2m_changed, sender=Organization.administrators.through)
Of course, maximum recursion depth exceeded
. What's wrong? This pain is unstoppable :(
UPD1
It seems, that post_save
method called BEFORE m2m relations saved, so it ran into a race condition, and a new data was replaced with form data, that was empty. Here is a bad solution:
def organization_m2m_changed(sender, instance, action, reverse, model, pk_set, using, **kwargs):
if not instance.administrators.filter(id=instance.user_id).exists():
if action.startswith('post_'):
instance.administrators.add(instance.user)
Now i'd like to know what signal Django emits after ALL work about a model done?
P.S. No magic. :(
UPD2 lie-ryan
forms.py
class OrganizationEditForm(forms.ModelForm):
class Meta:
model = Organization
exclude = ['user']
widgets = floppyforms_widgets(Organization)
views.py
@login_required
def edit_organization(request, organization_id=None):
user = request.user
c = Context({'user': user})
instance = get_object_or_404(Organization, id=organization_id) if organization_id else Organization(user=user)
form = OrganizationEditForm(request.POST or None, request.FILES or None, instance=instance)
if form.is_valid():
form.save()
messages.success(request, _('Organization saved successfully'))
return HttpResponseRedirect(reverse('organizations'))
c['form'] = form
c['instance'] = instance
return render_to_response('cat/edit_organization.html', c, context_instance=RequestContext(request))
You're getting recursion error because you're calling self.save() inside of Organization.save(). Don't do this, if you want to save while overriding the save(), call the superclass' version of save() instead of your own save().
Before adding an instance to an m2m relationship, both sides of the relationship must already have a primary key (i.e. already inserted to the db). This means you can't add an unsaved, uncreated object to an m2m relationship.
Do this:
I'd recommend doing this inside the View or Form since it is quite unexpected if a model's save method saved the user's model as well (though it should work inside the model's save() as well)