Opened 15 years ago
Last modified 5 years ago
#14645 new
Exclude query with multiple conditions for the same multi-value relation not correct — at Initial Version
| Reported by: | 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))