#18676: Django should do m2m deletes in a single query when possible
-------------------------------------+-------------------------------------
     Reporter:  jdunck               |                    Owner:  nobody
         Type:                       |                   Status:  closed
  Cleanup/optimization               |                  Version:  1.4
    Component:  Database layer       |               Resolution:  fixed
  (models, ORM)                      |             Triage Stage:  Ready for
     Severity:  Normal               |  checkin
     Keywords:                       |      Needs documentation:  0
    Has patch:  1                    |  Patch needs improvement:  0
  Needs tests:  0                    |                    UI/UX:  0
Easy pickings:  0                    |
-------------------------------------+-------------------------------------

Comment (by pressureman):

 After doing a git bisect, I discovered that the commit
 1cd6e04cd4f768bcd4385b75de433d497d938f82 caused a regression in an
 existing project of mine, that uses a custom object manager. The setup is
 as follows (simplified - other models also have `ForeignKey` back to
 `OrgUnit` model):

 {{{
 #!python
 class OrgUnit(models.Model):
     name = models.CharField(max_length=64, unique=True)
     groups = models.ManyToManyField(Group, blank=True)
     users = models.ManyToManyField(User, blank=True)

 class LoginManager(models.Manager):

     def restricted(self, user):
         q_group = Q(orgunit__groups__user=user)
         q_user = Q(orgunit__users=user)
         return self.filter(q_group | q_user).distinct()

 class Login(models.Model):
     description = models.CharField(max_length=32)
     orgunit = models.ForeignKey(OrgUnit)
     private_data = models.TextField(editable=False)

     objects = LoginManager()
 }}}

 My view POSTs a list of ids to a view that deletes Login objects, and it
 throws a DB exception "subquery has too many columns". I can see quite
 clearly from the generated SQL that the sub-select is including
 unnecessary fields for the "DELETE FROM foo WHERE id in (SELECT id,
 description FROM login ... )". I don't understand why the sub-select is
 including the `Login.description` field :-?

 The following is the code in the view that triggers the exception:

 {{{
 #!python
 id_list = map(int, request.POST.getlist('_id_list'))
 Login.objects.restricted(request.user).filter(id__in=id_list).delete()
 }}}

 What I've found is that if I modify the `LoginManager.restricted()` to
 either not `filter()` (undesirable) or not `.distinct()` then the bug does
 not occur.

 This code is a few years old now, and I don't remember why exactly I put a
 `.distinct()` in there. It doesn't seem to cause any undesirable side
 effects if I remove it, but I'd like to try to understand why this Django
 commit has caused this regression in the first place, as it may affect
 other people.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/18676#comment:8>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to django-updates@googlegroups.com.
To unsubscribe from this group, send email to 
django-updates+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to