Opened 13 years ago

Last modified 4 years ago

#14645 new

Exclude query with multiple conditions for the same multi-value relation not correct — at Initial Version

Reported by: Ben Buchwald <bb2@…> Owned by: nobody
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: exclude manytomany
Cc: rma, Chris, PhiR_42, benkraft, bugs@…, Can Sarıgöl Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

According to http://docs.djangoproject.com/en/1.2/topics/db/queries/#spanning-multi-valued-relationships: "all the conditions in a single exclude() statement apply to a single instance (if those conditions are talking about the same multi-valued relation)." This works correctly for filter() but for exclude() it is operating the same as if it was 2 separate exclude() calls. Here's an example set of models:

class Song(models.Model):
   name = models.CharField(max_length=30)

class Release(models.Model):
   song = models.ForeignKey(Song)
   format = models.CharField(max_length=3,choices=(('cd',"CD"),('mp3',"MP3")))
   released = models.BooleanField()
   class Meta:
      unique_together = ['song','format']

If I want to ask for all of the songs that have a CD release that has already gone out this filter works:

Song.objects.filter(release_set__format='cd',release_set__released=1)

But if I want to find all the songs that don't have a CD release that has gone out (either it has one that hasn't been released yet, or it doesn't have a release record at all), this exclude statement is not working:

Song.objects.exclude(release_set__format='cd',release_set__released=1)

It produces this SQL:

('SELECT U0.`id` FROM `songs_song` U0 
  WHERE NOT (U0.`id` IN (SELECT U1.`id` FROM `songs_release` U1 
                         WHERE U1.`format` = %s) AND 
             U0.`id` IN (SELECT U1.`id` FROM `songs_release` U1 
                         WHERE U1.`released` = %s ))',
('cd', True))

Instead of what I would expect (and give me the right results):

('SELECT U0.`id` FROM `songs_song` U0 
  WHERE NOT (U0.`id` IN (SELECT U1.`id` FROM `songs_release` U1 
                         WHERE U1.`format` = %s AND 
                               U1.`released` = %s ))',
('cd', True))

Song.objects.filter(~Q(release_set__format='cd',release_set__released=1)) produces the same result, but Song.objects.exclude(Q(release_set__format='cd',release_set__released=1)) produces the even more wrong:

('SELECT U0.`id` FROM `songs_song` U0 INNER JOIN `songs_release` U1 ON (U0.`id` = U1.`song_id`)
  WHERE NOT ((U1.`format` = %s  AND U1.`released` = %s ))',
('cd', True))

Change History (0)

Note: See TracTickets for help on using tickets.
Back to Top