Skip to content

Instantly share code, notes, and snippets.

@seddonym
Created March 19, 2015 16:43
Show Gist options
  • Save seddonym/84407891a11389419c14 to your computer and use it in GitHub Desktop.
Save seddonym/84407891a11389419c14 to your computer and use it in GitHub Desktop.
Clarification of behaviour of Django exclude() filters that span relationships
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def __unicode__(self): # __unicode__ on Python 2
return self.name
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self): # __unicode__ on Python 2
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=255)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField(auto_now=True)
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField(default=0)
n_pingbacks = models.IntegerField(default=0)
rating = models.IntegerField(default=0)
def __unicode__(self): # __unicode__ on Python 2
return self.headline
from datetime import date
from django.test import TestCase
from .models import Author, Blog, Entry
class Test(TestCase):
def test_queries(self):
Blog.objects.create(name='No entries')
neither = Blog.objects.create(name='Neither')
lennon = Blog.objects.create(name='Lennon')
published = Blog.objects.create(name='Published')
both_same_entry = Blog.objects.create(name='Both same')
both_different_entry = Blog.objects.create(name='Both different')
Entry.objects.create(headline='Foo', pub_date=date(2009, 1, 1), blog=neither)
Entry.objects.create(headline='Lennon', pub_date=date(2009, 6, 1), blog=lennon)
Entry.objects.create(headline='Published', pub_date=date(2008, 6, 1), blog=published)
Entry.objects.create(headline='Lennon', pub_date=date(2008, 1, 1), blog=both_same_entry)
Entry.objects.create(headline='Lennon', pub_date=date(2009, 1, 1), blog=both_different_entry)
Entry.objects.create(headline='Published', pub_date=date(2008, 1, 1), blog=both_different_entry)
# Exclude blogs that have BOTH:
# - entries published in 2008; AND
# - entries with "Lennon" in the headline
q1 = Blog.objects.exclude(
entry__headline__contains='Lennon',
entry__pub_date__year=2008,
).order_by('name')
self.assertQuerysetEqual(q1, ['<Blog: Lennon>', '<Blog: Neither>', '<Blog: No entries>', '<Blog: Published>'])
# Exclude blogs that have EITHER:
# - entries published in 2008; OR
# - entries with "Lennon" in the headline
q2 = Blog.objects.exclude(
entry__headline__contains='Lennon').exclude(
entry__pub_date__year=2008,
).order_by('name')
self.assertQuerysetEqual(q2, ['<Blog: Neither>', '<Blog: No entries>'])
# Exclude blogs that contain individual entries that are BOTH:
# - published in 2008; AND
# - have "Lennon" in the headline
q3 = Blog.objects.exclude(
entry=Entry.objects.filter(
headline__contains='Lennon', pub_date__year=2008,
).values('id'),
).order_by('name')
self.assertQuerysetEqual(q3, ['<Blog: Both different>', '<Blog: Lennon>', '<Blog: Neither>', '<Blog: No entries>', '<Blog: Published>'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment