From e57cd291cc7821be5b47d9837598b1d9fd2bcad3 Mon Sep 17 00:00:00 2001
From: Russell Keith-Magee <russell@keith-magee.com>
Date: Tue, 2 Nov 2010 04:01:36 +0000
Subject: [PATCH] Migrated lookup doctests. Thanks to George Sakkis for the
 patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@14423 bcc190cf-cafb-0310-a4f2-bffc1f526a37
---
 tests/modeltests/lookup/models.py | 401 ----------------------
 tests/modeltests/lookup/tests.py  | 543 ++++++++++++++++++++++++++++++
 2 files changed, 543 insertions(+), 401 deletions(-)
 create mode 100644 tests/modeltests/lookup/tests.py

diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py
index bb555615ea..ac63210656 100644
--- a/tests/modeltests/lookup/models.py
+++ b/tests/modeltests/lookup/models.py
@@ -15,404 +15,3 @@ class Article(models.Model):
 
     def __unicode__(self):
         return self.headline
-
-__test__ = {'API_TESTS': r"""
-# We can use .exists() to check that there are none yet
->>> Article.objects.exists()
-False
-
-# Create a couple of Articles.
->>> from datetime import datetime
->>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26))
->>> a1.save()
->>> a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27))
->>> a2.save()
->>> a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27))
->>> a3.save()
->>> a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28))
->>> a4.save()
->>> a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0))
->>> a5.save()
->>> a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0))
->>> a6.save()
->>> a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27))
->>> a7.save()
-
-# There should be some now!
->>> Article.objects.exists()
-True
-
-# Integer value can be queried using string
->>> Article.objects.filter(id__iexact='1')
-[<Article: Article 1>]
-
-"""}
-
-if connection.features.supports_date_lookup_using_string:
-    __test__['API_TESTS'] += r"""
-# A date lookup can be performed using a string search
->>> Article.objects.filter(pub_date__startswith='2005')
-[<Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
-"""
-
-__test__['API_TESTS'] += r"""
-# Each QuerySet gets iterator(), which is a generator that "lazily" returns
-# results using database-level iteration.
->>> for a in Article.objects.iterator():
-...     print a.headline
-Article 5
-Article 6
-Article 4
-Article 2
-Article 3
-Article 7
-Article 1
-
-# iterator() can be used on any QuerySet.
->>> for a in Article.objects.filter(headline__endswith='4').iterator():
-...     print a.headline
-Article 4
-
-# count() returns the number of objects matching search criteria.
->>> Article.objects.count()
-7L
->>> Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).count()
-3L
->>> Article.objects.filter(headline__startswith='Blah blah').count()
-0L
-
-# count() should respect sliced query sets.
->>> articles = Article.objects.all()
->>> articles.count()
-7L
->>> articles[:4].count()
-4
->>> articles[1:100].count()
-6L
->>> articles[10:100].count()
-0
-
-# Date and date/time lookups can also be done with strings.
->>> Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count()
-3L
-
-# in_bulk() takes a list of IDs and returns a dictionary mapping IDs
-# to objects.
->>> arts = Article.objects.in_bulk([1, 2])
->>> arts[1]
-<Article: Article 1>
->>> arts[2]
-<Article: Article 2>
->>> Article.objects.in_bulk([3])
-{3: <Article: Article 3>}
->>> Article.objects.in_bulk(set([3]))
-{3: <Article: Article 3>}
->>> Article.objects.in_bulk(frozenset([3]))
-{3: <Article: Article 3>}
->>> Article.objects.in_bulk((3,))
-{3: <Article: Article 3>}
->>> Article.objects.in_bulk([1000])
-{}
->>> Article.objects.in_bulk([])
-{}
->>> Article.objects.in_bulk('foo')
-Traceback (most recent call last):
-    ...
-AssertionError: in_bulk() must be provided with a list of IDs.
->>> Article.objects.in_bulk()
-Traceback (most recent call last):
-    ...
-TypeError: in_bulk() takes exactly 2 arguments (1 given)
->>> Article.objects.in_bulk(headline__startswith='Blah')
-Traceback (most recent call last):
-    ...
-TypeError: in_bulk() got an unexpected keyword argument 'headline__startswith'
-
-# values() returns a list of dictionaries instead of object instances -- and
-# you can specify which fields you want to retrieve.
->>> Article.objects.values('headline')
-[{'headline': u'Article 5'}, {'headline': u'Article 6'}, {'headline': u'Article 4'}, {'headline': u'Article 2'}, {'headline': u'Article 3'}, {'headline': u'Article 7'}, {'headline': u'Article 1'}]
->>> Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).values('id')
-[{'id': 2}, {'id': 3}, {'id': 7}]
->>> list(Article.objects.values('id', 'headline')) == [{'id': 5, 'headline': 'Article 5'}, {'id': 6, 'headline': 'Article 6'}, {'id': 4, 'headline': 'Article 4'}, {'id': 2, 'headline': 'Article 2'}, {'id': 3, 'headline': 'Article 3'}, {'id': 7, 'headline': 'Article 7'}, {'id': 1, 'headline': 'Article 1'}]
-True
-
->>> for d in Article.objects.values('id', 'headline'):
-...     i = d.items()
-...     i.sort()
-...     i
-[('headline', u'Article 5'), ('id', 5)]
-[('headline', u'Article 6'), ('id', 6)]
-[('headline', u'Article 4'), ('id', 4)]
-[('headline', u'Article 2'), ('id', 2)]
-[('headline', u'Article 3'), ('id', 3)]
-[('headline', u'Article 7'), ('id', 7)]
-[('headline', u'Article 1'), ('id', 1)]
-
-# You can use values() with iterator() for memory savings, because iterator()
-# uses database-level iteration.
->>> for d in Article.objects.values('id', 'headline').iterator():
-...     i = d.items()
-...     i.sort()
-...     i
-[('headline', u'Article 5'), ('id', 5)]
-[('headline', u'Article 6'), ('id', 6)]
-[('headline', u'Article 4'), ('id', 4)]
-[('headline', u'Article 2'), ('id', 2)]
-[('headline', u'Article 3'), ('id', 3)]
-[('headline', u'Article 7'), ('id', 7)]
-[('headline', u'Article 1'), ('id', 1)]
-
-# The values() method works with "extra" fields specified in extra(select).
->>> for d in Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'):
-...     i = d.items()
-...     i.sort()
-...     i
-[('id', 5), ('id_plus_one', 6)]
-[('id', 6), ('id_plus_one', 7)]
-[('id', 4), ('id_plus_one', 5)]
-[('id', 2), ('id_plus_one', 3)]
-[('id', 3), ('id_plus_one', 4)]
-[('id', 7), ('id_plus_one', 8)]
-[('id', 1), ('id_plus_one', 2)]
->>> data = {'id_plus_one': 'id+1', 'id_plus_two': 'id+2', 'id_plus_three': 'id+3',
-...         'id_plus_four': 'id+4', 'id_plus_five': 'id+5', 'id_plus_six': 'id+6',
-...         'id_plus_seven': 'id+7', 'id_plus_eight': 'id+8'}
->>> result = list(Article.objects.filter(id=1).extra(select=data).values(*data.keys()))[0]
->>> result = result.items()
->>> result.sort()
->>> result
-[('id_plus_eight', 9), ('id_plus_five', 6), ('id_plus_four', 5), ('id_plus_one', 2), ('id_plus_seven', 8), ('id_plus_six', 7), ('id_plus_three', 4), ('id_plus_two', 3)]
-
-# However, an exception FieldDoesNotExist will be thrown if you specify a
-# non-existent field name in values() (a field that is neither in the model
-# nor in extra(select)).
->>> Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_two')
-Traceback (most recent call last):
-    ...
-FieldError: Cannot resolve keyword 'id_plus_two' into field. Choices are: headline, id, id_plus_one, pub_date
-
-# If you don't specify field names to values(), all are returned.
->>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}]
-True
-
-# values_list() is similar to values(), except that the results are returned as
-# a list of tuples, rather than a list of dictionaries. Within each tuple, the
-# order of the elemnts is the same as the order of fields in the values_list()
-# call.
->>> Article.objects.values_list('headline')
-[(u'Article 5',), (u'Article 6',), (u'Article 4',), (u'Article 2',), (u'Article 3',), (u'Article 7',), (u'Article 1',)]
-
->>> Article.objects.values_list('id').order_by('id')
-[(1,), (2,), (3,), (4,), (5,), (6,), (7,)]
->>> Article.objects.values_list('id', flat=True).order_by('id')
-[1, 2, 3, 4, 5, 6, 7]
-
->>> Article.objects.extra(select={'id_plus_one': 'id+1'}).order_by('id').values_list('id')
-[(1,), (2,), (3,), (4,), (5,), (6,), (7,)]
->>> Article.objects.extra(select={'id_plus_one': 'id+1'}).order_by('id').values_list('id_plus_one', 'id')
-[(2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6), (8, 7)]
->>> Article.objects.extra(select={'id_plus_one': 'id+1'}).order_by('id').values_list('id', 'id_plus_one')
-[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)]
-
->>> Article.objects.values_list('id', 'headline', flat=True)
-Traceback (most recent call last):
-...
-TypeError: 'flat' is not valid when values_list is called with more than one field.
-
-# Every DateField and DateTimeField creates get_next_by_FOO() and
-# get_previous_by_FOO() methods.
-# In the case of identical date values, these methods will use the ID as a
-# fallback check. This guarantees that no records are skipped or duplicated.
->>> a1.get_next_by_pub_date()
-<Article: Article 2>
->>> a2.get_next_by_pub_date()
-<Article: Article 3>
->>> a2.get_next_by_pub_date(headline__endswith='6')
-<Article: Article 6>
->>> a3.get_next_by_pub_date()
-<Article: Article 7>
->>> a4.get_next_by_pub_date()
-<Article: Article 6>
->>> a5.get_next_by_pub_date()
-Traceback (most recent call last):
-    ...
-DoesNotExist: Article matching query does not exist.
->>> a6.get_next_by_pub_date()
-<Article: Article 5>
->>> a7.get_next_by_pub_date()
-<Article: Article 4>
-
->>> a7.get_previous_by_pub_date()
-<Article: Article 3>
->>> a6.get_previous_by_pub_date()
-<Article: Article 4>
->>> a5.get_previous_by_pub_date()
-<Article: Article 6>
->>> a4.get_previous_by_pub_date()
-<Article: Article 7>
->>> a3.get_previous_by_pub_date()
-<Article: Article 2>
->>> a2.get_previous_by_pub_date()
-<Article: Article 1>
-
-# Underscores and percent signs have special meaning in the underlying
-# SQL code, but Django handles the quoting of them automatically.
->>> a8 = Article(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20))
->>> a8.save()
->>> Article.objects.filter(headline__startswith='Article')
-[<Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
->>> Article.objects.filter(headline__startswith='Article_')
-[<Article: Article_ with underscore>]
-
->>> a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21))
->>> a9.save()
->>> Article.objects.filter(headline__startswith='Article')
-[<Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
->>> Article.objects.filter(headline__startswith='Article%')
-[<Article: Article% with percent sign>]
-
-# exclude() is the opposite of filter() when doing lookups:
->>> Article.objects.filter(headline__contains='Article').exclude(headline__contains='with')
-[<Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
->>> Article.objects.exclude(headline__startswith="Article_")
-[<Article: Article% with percent sign>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
->>> Article.objects.exclude(headline="Article 7")
-[<Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 1>]
-
-# Backslashes also have special meaning in the underlying SQL code, but Django
-# automatically quotes them appropriately.
->>> a10 = Article(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22))
->>> a10.save()
->>> Article.objects.filter(headline__contains='\\')
-[<Article: Article with \ backslash>]
-
-# none() returns an EmptyQuerySet that behaves like any other QuerySet object
->>> Article.objects.none()
-[]
->>> Article.objects.none().filter(headline__startswith='Article')
-[]
->>> Article.objects.filter(headline__startswith='Article').none()
-[]
->>> Article.objects.none().count()
-0
->>> Article.objects.none().update(headline="This should not take effect")
-0
->>> [article for article in Article.objects.none().iterator()]
-[]
-
-# using __in with an empty list should return an empty query set
->>> Article.objects.filter(id__in=[])
-[]
-
->>> Article.objects.exclude(id__in=[])
-[<Article: Article with \ backslash>, <Article: Article% with percent sign>, <Article: Article_ with underscore>, <Article: Article 5>, <Article: Article 6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article: Article 7>, <Article: Article 1>]
-
-# Programming errors are pointed out with nice error messages
->>> Article.objects.filter(pub_date_year='2005').count()
-Traceback (most recent call last):
-    ...
-FieldError: Cannot resolve keyword 'pub_date_year' into field. Choices are: headline, id, pub_date
-
->>> Article.objects.filter(headline__starts='Article')
-Traceback (most recent call last):
-    ...
-FieldError: Join on field 'headline' not permitted. Did you misspell 'starts' for the lookup type?
-
-# Create some articles with a bit more interesting headlines for testing field lookups:
->>> now = datetime.now()
->>> for a in Article.objects.all():
-...     a.delete()
->>> a1 = Article(pub_date=now, headline='f')
->>> a1.save()
->>> a2 = Article(pub_date=now, headline='fo')
->>> a2.save()
->>> a3 = Article(pub_date=now, headline='foo')
->>> a3.save()
->>> a4 = Article(pub_date=now, headline='fooo')
->>> a4.save()
->>> a5 = Article(pub_date=now, headline='hey-Foo')
->>> a5.save()
-
-# zero-or-more
->>> Article.objects.filter(headline__regex=r'fo*')
-[<Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>]
->>> Article.objects.filter(headline__iregex=r'fo*')
-[<Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>, <Article: hey-Foo>]
-
-# one-or-more
->>> Article.objects.filter(headline__regex=r'fo+')
-[<Article: fo>, <Article: foo>, <Article: fooo>]
-
-# wildcard
->>> Article.objects.filter(headline__regex=r'fooo?')
-[<Article: foo>, <Article: fooo>]
-
-# and some more:
->>> a6 = Article(pub_date=now, headline='bar')
->>> a6.save()
->>> a7 = Article(pub_date=now, headline='AbBa')
->>> a7.save()
->>> a8 = Article(pub_date=now, headline='baz')
->>> a8.save()
->>> a9 = Article(pub_date=now, headline='baxZ')
->>> a9.save()
-
-# leading anchor
->>> Article.objects.filter(headline__regex=r'^b')
-[<Article: bar>, <Article: baxZ>, <Article: baz>]
->>> Article.objects.filter(headline__iregex=r'^a')
-[<Article: AbBa>]
-
-# trailing anchor
->>> Article.objects.filter(headline__regex=r'z$')
-[<Article: baz>]
->>> Article.objects.filter(headline__iregex=r'z$')
-[<Article: baxZ>, <Article: baz>]
-
-# character sets
->>> Article.objects.filter(headline__regex=r'ba[rz]')
-[<Article: bar>, <Article: baz>]
->>> Article.objects.filter(headline__regex=r'ba.[RxZ]')
-[<Article: baxZ>]
->>> Article.objects.filter(headline__iregex=r'ba[RxZ]')
-[<Article: bar>, <Article: baxZ>, <Article: baz>]
-
-# and yet more:
->>> a10 = Article(pub_date=now, headline='foobar')
->>> a10.save()
->>> a11 = Article(pub_date=now, headline='foobaz')
->>> a11.save()
->>> a12 = Article(pub_date=now, headline='ooF')
->>> a12.save()
->>> a13 = Article(pub_date=now, headline='foobarbaz')
->>> a13.save()
->>> a14 = Article(pub_date=now, headline='zoocarfaz')
->>> a14.save()
->>> a15 = Article(pub_date=now, headline='barfoobaz')
->>> a15.save()
->>> a16 = Article(pub_date=now, headline='bazbaRFOO')
->>> a16.save()
-
-# alternation
->>> Article.objects.filter(headline__regex=r'oo(f|b)')
-[<Article: barfoobaz>, <Article: foobar>, <Article: foobarbaz>, <Article: foobaz>]
->>> Article.objects.filter(headline__iregex=r'oo(f|b)')
-[<Article: barfoobaz>, <Article: foobar>, <Article: foobarbaz>, <Article: foobaz>, <Article: ooF>]
->>> Article.objects.filter(headline__regex=r'^foo(f|b)')
-[<Article: foobar>, <Article: foobarbaz>, <Article: foobaz>]
-
-# greedy matching
->>> Article.objects.filter(headline__regex=r'b.*az')
-[<Article: barfoobaz>, <Article: baz>, <Article: bazbaRFOO>, <Article: foobarbaz>, <Article: foobaz>]
->>> Article.objects.filter(headline__iregex=r'b.*ar')
-[<Article: bar>, <Article: barfoobaz>, <Article: bazbaRFOO>, <Article: foobar>, <Article: foobarbaz>]
-"""
-
-
-if connection.features.supports_regex_backreferencing:
-    __test__['API_TESTS'] += r"""
-# grouping and backreferences
->>> Article.objects.filter(headline__regex=r'b(.).*b\1')
-[<Article: barfoobaz>, <Article: bazbaRFOO>, <Article: foobarbaz>]
-"""
diff --git a/tests/modeltests/lookup/tests.py b/tests/modeltests/lookup/tests.py
new file mode 100644
index 0000000000..3273349faa
--- /dev/null
+++ b/tests/modeltests/lookup/tests.py
@@ -0,0 +1,543 @@
+from datetime import datetime
+from operator import attrgetter
+from django.core.exceptions import FieldError
+from django.db import connection
+from django.test import TestCase, skipUnlessDBFeature
+from models import Article
+
+
+class LookupTests(TestCase):
+
+    #def setUp(self):
+    def setUp(self):
+        # Create a couple of Articles.
+        self.a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26))
+        self.a1.save()
+        self.a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27))
+        self.a2.save()
+        self.a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27))
+        self.a3.save()
+        self.a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28))
+        self.a4.save()
+        self.a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0))
+        self.a5.save()
+        self.a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0))
+        self.a6.save()
+        self.a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27))
+        self.a7.save()
+
+    def test_exists(self):
+        # We can use .exists() to check that there are some
+        self.assertTrue(Article.objects.exists())
+        for a in Article.objects.all():
+            a.delete()
+        # There should be none now!
+        self.assertFalse(Article.objects.exists())
+
+    def test_lookup_int_as_str(self):
+        # Integer value can be queried using string
+        self.assertQuerysetEqual(Article.objects.filter(id__iexact=str(self.a1.id)),
+                                 ['<Article: Article 1>'])
+
+    @skipUnlessDBFeature('supports_date_lookup_using_string')
+    def test_lookup_date_as_str(self):
+        # A date lookup can be performed using a string search
+        self.assertQuerysetEqual(Article.objects.filter(pub_date__startswith='2005'),
+            [
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 7>',
+                '<Article: Article 1>',
+            ])
+
+    def test_iterator(self):
+        # Each QuerySet gets iterator(), which is a generator that "lazily"
+        # returns results using database-level iteration.
+        self.assertQuerysetEqual(Article.objects.iterator(),
+            [
+                'Article 5',
+                'Article 6',
+                'Article 4',
+                'Article 2',
+                'Article 3',
+                'Article 7',
+                'Article 1',
+            ],
+            transform=attrgetter('headline'))
+        # iterator() can be used on any QuerySet.
+        self.assertQuerysetEqual(
+            Article.objects.filter(headline__endswith='4').iterator(),
+            ['Article 4'],
+            transform=attrgetter('headline'))
+
+    def test_count(self):
+        # count() returns the number of objects matching search criteria.
+        self.assertEqual(Article.objects.count(), 7)
+        self.assertEqual(Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).count(), 3)
+        self.assertEqual(Article.objects.filter(headline__startswith='Blah blah').count(), 0)
+
+        # count() should respect sliced query sets.
+        articles = Article.objects.all()
+        self.assertEqual(articles.count(), 7)
+        self.assertEqual(articles[:4].count(), 4)
+        self.assertEqual(articles[1:100].count(), 6)
+        self.assertEqual(articles[10:100].count(), 0)
+
+        # Date and date/time lookups can also be done with strings.
+        self.assertEqual(Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count(), 3)
+
+    def test_in_bulk(self):
+        # in_bulk() takes a list of IDs and returns a dictionary mapping IDs to objects.
+        arts = Article.objects.in_bulk([self.a1.id, self.a2.id])
+        self.assertEqual(arts[self.a1.id], self.a1)
+        self.assertEqual(arts[self.a2.id], self.a2)
+        self.assertEqual(Article.objects.in_bulk([self.a3.id]), {self.a3.id: self.a3})
+        self.assertEqual(Article.objects.in_bulk(set([self.a3.id])), {self.a3.id: self.a3})
+        self.assertEqual(Article.objects.in_bulk(frozenset([self.a3.id])), {self.a3.id: self.a3})
+        self.assertEqual(Article.objects.in_bulk((self.a3.id,)), {self.a3.id: self.a3})
+        self.assertEqual(Article.objects.in_bulk([1000]), {})
+        self.assertEqual(Article.objects.in_bulk([]), {})
+        self.assertRaises(AssertionError, Article.objects.in_bulk, 'foo')
+        self.assertRaises(TypeError, Article.objects.in_bulk)
+        self.assertRaises(TypeError, Article.objects.in_bulk, headline__startswith='Blah')
+
+    def test_values(self):
+        # values() returns a list of dictionaries instead of object instances --
+        # and you can specify which fields you want to retrieve.
+        identity = lambda x:x
+        self.assertQuerysetEqual(Article.objects.values('headline'),
+            [
+                {'headline': u'Article 5'},
+                {'headline': u'Article 6'},
+                {'headline': u'Article 4'},
+                {'headline': u'Article 2'},
+                {'headline': u'Article 3'},
+                {'headline': u'Article 7'},
+                {'headline': u'Article 1'},
+            ],
+            transform=identity)
+        self.assertQuerysetEqual(
+            Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).values('id'),
+            [{'id': self.a2.id}, {'id': self.a3.id}, {'id': self.a7.id}],
+            transform=identity)
+        self.assertQuerysetEqual(Article.objects.values('id', 'headline'),
+            [
+                {'id': self.a5.id, 'headline': 'Article 5'},
+                {'id': self.a6.id, 'headline': 'Article 6'},
+                {'id': self.a4.id, 'headline': 'Article 4'},
+                {'id': self.a2.id, 'headline': 'Article 2'},
+                {'id': self.a3.id, 'headline': 'Article 3'},
+                {'id': self.a7.id, 'headline': 'Article 7'},
+                {'id': self.a1.id, 'headline': 'Article 1'},
+            ],
+            transform=identity)
+        # You can use values() with iterator() for memory savings,
+        # because iterator() uses database-level iteration.
+        self.assertQuerysetEqual(Article.objects.values('id', 'headline').iterator(),
+            [
+                {'headline': u'Article 5', 'id': self.a5.id},
+                {'headline': u'Article 6', 'id': self.a6.id},
+                {'headline': u'Article 4', 'id': self.a4.id},
+                {'headline': u'Article 2', 'id': self.a2.id},
+                {'headline': u'Article 3', 'id': self.a3.id},
+                {'headline': u'Article 7', 'id': self.a7.id},
+                {'headline': u'Article 1', 'id': self.a1.id},
+            ],
+            transform=identity)
+        # The values() method works with "extra" fields specified in extra(select).
+        self.assertQuerysetEqual(
+            Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'),
+            [
+                {'id': self.a5.id, 'id_plus_one': self.a5.id + 1},
+                {'id': self.a6.id, 'id_plus_one': self.a6.id + 1},
+                {'id': self.a4.id, 'id_plus_one': self.a4.id + 1},
+                {'id': self.a2.id, 'id_plus_one': self.a2.id + 1},
+                {'id': self.a3.id, 'id_plus_one': self.a3.id + 1},
+                {'id': self.a7.id, 'id_plus_one': self.a7.id + 1},
+                {'id': self.a1.id, 'id_plus_one': self.a1.id + 1},
+            ],
+            transform=identity)
+        data = {
+            'id_plus_one': 'id+1',
+            'id_plus_two': 'id+2',
+            'id_plus_three': 'id+3',
+            'id_plus_four': 'id+4',
+            'id_plus_five': 'id+5',
+            'id_plus_six': 'id+6',
+            'id_plus_seven': 'id+7',
+            'id_plus_eight': 'id+8',
+        }
+        self.assertQuerysetEqual(
+            Article.objects.filter(id=self.a1.id).extra(select=data).values(*data.keys()),
+            [{
+                'id_plus_one': self.a1.id + 1,
+                'id_plus_two': self.a1.id + 2,
+                'id_plus_three': self.a1.id + 3,
+                'id_plus_four': self.a1.id + 4,
+                'id_plus_five': self.a1.id + 5,
+                'id_plus_six': self.a1.id + 6,
+                'id_plus_seven': self.a1.id + 7,
+                'id_plus_eight': self.a1.id + 8,
+            }], transform=identity)
+        # However, an exception FieldDoesNotExist will be thrown if you specify
+        # a non-existent field name in values() (a field that is neither in the
+        # model nor in extra(select)).
+        self.assertRaises(FieldError,
+            Article.objects.extra(select={'id_plus_one': 'id + 1'}).values,
+            'id', 'id_plus_two')
+        # If you don't specify field names to values(), all are returned.
+        self.assertQuerysetEqual(Article.objects.filter(id=self.a5.id).values(),
+            [{
+                'id': self.a5.id,
+                'headline': 'Article 5',
+                'pub_date': datetime(2005, 8, 1, 9, 0)
+            }], transform=identity)
+
+    def test_values_list(self):
+        # values_list() is similar to values(), except that the results are
+        # returned as a list of tuples, rather than a list of dictionaries.
+        # Within each tuple, the order of the elemnts is the same as the order
+        # of fields in the values_list() call.
+        identity = lambda x:x
+        self.assertQuerysetEqual(Article.objects.values_list('headline'),
+            [
+                (u'Article 5',),
+                (u'Article 6',),
+                (u'Article 4',),
+                (u'Article 2',),
+                (u'Article 3',),
+                (u'Article 7',),
+                (u'Article 1',),
+            ], transform=identity)
+        self.assertQuerysetEqual(Article.objects.values_list('id').order_by('id'),
+            [(self.a1.id,), (self.a2.id,), (self.a3.id,), (self.a4.id,), (self.a5.id,), (self.a6.id,), (self.a7.id,)],
+            transform=identity)
+        self.assertQuerysetEqual(
+            Article.objects.values_list('id', flat=True).order_by('id'),
+            [self.a1.id, self.a2.id, self.a3.id, self.a4.id, self.a5.id, self.a6.id, self.a7.id],
+            transform=identity)
+        self.assertQuerysetEqual(
+            Article.objects.extra(select={'id_plus_one': 'id+1'})
+                           .order_by('id').values_list('id'),
+            [(self.a1.id,), (self.a2.id,), (self.a3.id,), (self.a4.id,), (self.a5.id,), (self.a6.id,), (self.a7.id,)],
+            transform=identity)
+        self.assertQuerysetEqual(
+            Article.objects.extra(select={'id_plus_one': 'id+1'})
+                           .order_by('id').values_list('id_plus_one', 'id'),
+            [
+                (self.a1.id+1, self.a1.id),
+                (self.a2.id+1, self.a2.id),
+                (self.a3.id+1, self.a3.id),
+                (self.a4.id+1, self.a4.id),
+                (self.a5.id+1, self.a5.id),
+                (self.a6.id+1, self.a6.id),
+                (self.a7.id+1, self.a7.id)
+            ],
+            transform=identity)
+        self.assertQuerysetEqual(
+            Article.objects.extra(select={'id_plus_one': 'id+1'})
+                           .order_by('id').values_list('id', 'id_plus_one'),
+            [
+                (self.a1.id, self.a1.id+1),
+                (self.a2.id, self.a2.id+1),
+                (self.a3.id, self.a3.id+1),
+                (self.a4.id, self.a4.id+1),
+                (self.a5.id, self.a5.id+1),
+                (self.a6.id, self.a6.id+1),
+                (self.a7.id, self.a7.id+1)
+            ],
+            transform=identity)
+        self.assertRaises(TypeError, Article.objects.values_list, 'id', 'headline', flat=True)
+
+    def test_get_next_previous_by(self):
+        # Every DateField and DateTimeField creates get_next_by_FOO() and
+        # get_previous_by_FOO() methods. In the case of identical date values,
+        # these methods will use the ID as a fallback check. This guarantees
+        # that no records are skipped or duplicated.
+        self.assertEqual(repr(self.a1.get_next_by_pub_date()),
+                         '<Article: Article 2>')
+        self.assertEqual(repr(self.a2.get_next_by_pub_date()),
+                         '<Article: Article 3>')
+        self.assertEqual(repr(self.a2.get_next_by_pub_date(headline__endswith='6')),
+                         '<Article: Article 6>')
+        self.assertEqual(repr(self.a3.get_next_by_pub_date()),
+                         '<Article: Article 7>')
+        self.assertEqual(repr(self.a4.get_next_by_pub_date()),
+                         '<Article: Article 6>')
+        self.assertRaises(Article.DoesNotExist, self.a5.get_next_by_pub_date)
+        self.assertEqual(repr(self.a6.get_next_by_pub_date()),
+                         '<Article: Article 5>')
+        self.assertEqual(repr(self.a7.get_next_by_pub_date()),
+                         '<Article: Article 4>')
+
+        self.assertEqual(repr(self.a7.get_previous_by_pub_date()),
+                         '<Article: Article 3>')
+        self.assertEqual(repr(self.a6.get_previous_by_pub_date()),
+                         '<Article: Article 4>')
+        self.assertEqual(repr(self.a5.get_previous_by_pub_date()),
+                         '<Article: Article 6>')
+        self.assertEqual(repr(self.a4.get_previous_by_pub_date()),
+                         '<Article: Article 7>')
+        self.assertEqual(repr(self.a3.get_previous_by_pub_date()),
+                         '<Article: Article 2>')
+        self.assertEqual(repr(self.a2.get_previous_by_pub_date()),
+                         '<Article: Article 1>')
+
+    def test_escaping(self):
+        # Underscores, percent signs and backslashes have special meaning in the
+        # underlying SQL code, but Django handles the quoting of them automatically.
+        a8 = Article(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20))
+        a8.save()
+        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'),
+            [
+                '<Article: Article_ with underscore>',
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 7>',
+                '<Article: Article 1>',
+            ])
+        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article_'),
+                                 ['<Article: Article_ with underscore>'])
+        a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21))
+        a9.save()
+        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'),
+            [
+                '<Article: Article% with percent sign>',
+                '<Article: Article_ with underscore>',
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 7>',
+                '<Article: Article 1>',
+            ])
+        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article%'),
+                                 ['<Article: Article% with percent sign>'])
+        a10 = Article(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22))
+        a10.save()
+        self.assertQuerysetEqual(Article.objects.filter(headline__contains='\\'),
+                                 ['<Article: Article with \ backslash>'])
+
+    def test_exclude(self):
+        a8 = Article.objects.create(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20))
+        a9 = Article.objects.create(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21))
+        a10 = Article.objects.create(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22))
+
+        # exclude() is the opposite of filter() when doing lookups:
+        self.assertQuerysetEqual(
+            Article.objects.filter(headline__contains='Article').exclude(headline__contains='with'),
+            [
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 7>',
+                '<Article: Article 1>',
+            ])
+        self.assertQuerysetEqual(Article.objects.exclude(headline__startswith="Article_"),
+            [
+                '<Article: Article with \\ backslash>',
+                '<Article: Article% with percent sign>',
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 7>',
+                '<Article: Article 1>',
+            ])
+        self.assertQuerysetEqual(Article.objects.exclude(headline="Article 7"),
+            [
+                '<Article: Article with \\ backslash>',
+                '<Article: Article% with percent sign>',
+                '<Article: Article_ with underscore>',
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 1>',
+            ])
+
+    def test_none(self):
+       # none() returns an EmptyQuerySet that behaves like any other QuerySet object
+        self.assertQuerysetEqual(Article.objects.none(), [])
+        self.assertQuerysetEqual(
+            Article.objects.none().filter(headline__startswith='Article'), [])
+        self.assertQuerysetEqual(
+            Article.objects.filter(headline__startswith='Article').none(), [])
+        self.assertEqual(Article.objects.none().count(), 0)
+        self.assertEqual(
+            Article.objects.none().update(headline="This should not take effect"), 0)
+        self.assertQuerysetEqual(
+            [article for article in Article.objects.none().iterator()],
+            [])
+
+    def test_in(self):
+        # using __in with an empty list should return an empty query set
+        self.assertQuerysetEqual(Article.objects.filter(id__in=[]), [])
+        self.assertQuerysetEqual(Article.objects.exclude(id__in=[]),
+            [
+                '<Article: Article 5>',
+                '<Article: Article 6>',
+                '<Article: Article 4>',
+                '<Article: Article 2>',
+                '<Article: Article 3>',
+                '<Article: Article 7>',
+                '<Article: Article 1>',
+            ])
+
+    def test_error_messages(self):
+        # Programming errors are pointed out with nice error messages
+        try:
+            Article.objects.filter(pub_date_year='2005').count()
+            self.fail('FieldError not raised')
+        except FieldError, ex:
+            self.assertEqual(str(ex), "Cannot resolve keyword 'pub_date_year' "
+                             "into field. Choices are: headline, id, pub_date")
+        try:
+            Article.objects.filter(headline__starts='Article')
+            self.fail('FieldError not raised')
+        except FieldError, ex:
+            self.assertEqual(str(ex), "Join on field 'headline' not permitted. "
+                             "Did you misspell 'starts' for the lookup type?")
+
+    def test_regex(self):
+        # Create some articles with a bit more interesting headlines for testing field lookups:
+        for a in Article.objects.all():
+            a.delete()
+        now = datetime.now()
+        a1 = Article(pub_date=now, headline='f')
+        a1.save()
+        a2 = Article(pub_date=now, headline='fo')
+        a2.save()
+        a3 = Article(pub_date=now, headline='foo')
+        a3.save()
+        a4 = Article(pub_date=now, headline='fooo')
+        a4.save()
+        a5 = Article(pub_date=now, headline='hey-Foo')
+        a5.save()
+        a6 = Article(pub_date=now, headline='bar')
+        a6.save()
+        a7 = Article(pub_date=now, headline='AbBa')
+        a7.save()
+        a8 = Article(pub_date=now, headline='baz')
+        a8.save()
+        a9 = Article(pub_date=now, headline='baxZ')
+        a9.save()
+        # zero-or-more
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo*'),
+            ['<Article: f>', '<Article: fo>', '<Article: foo>', '<Article: fooo>'])
+        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'fo*'),
+            [
+                '<Article: f>',
+                '<Article: fo>',
+                '<Article: foo>',
+                '<Article: fooo>',
+                '<Article: hey-Foo>',
+            ])
+        # one-or-more
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo+'),
+            ['<Article: fo>', '<Article: foo>', '<Article: fooo>'])
+        # wildcard
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fooo?'),
+            ['<Article: foo>', '<Article: fooo>'])
+        # leading anchor
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^b'),
+            ['<Article: bar>', '<Article: baxZ>', '<Article: baz>'])
+        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'^a'),
+            ['<Article: AbBa>'])
+        # trailing anchor
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'z$'),
+            ['<Article: baz>'])
+        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'z$'),
+            ['<Article: baxZ>', '<Article: baz>'])
+        # character sets
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba[rz]'),
+            ['<Article: bar>', '<Article: baz>'])
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba.[RxZ]'),
+            ['<Article: baxZ>'])
+        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'ba[RxZ]'),
+            ['<Article: bar>', '<Article: baxZ>', '<Article: baz>'])
+
+        # and more articles:
+        a10 = Article(pub_date=now, headline='foobar')
+        a10.save()
+        a11 = Article(pub_date=now, headline='foobaz')
+        a11.save()
+        a12 = Article(pub_date=now, headline='ooF')
+        a12.save()
+        a13 = Article(pub_date=now, headline='foobarbaz')
+        a13.save()
+        a14 = Article(pub_date=now, headline='zoocarfaz')
+        a14.save()
+        a15 = Article(pub_date=now, headline='barfoobaz')
+        a15.save()
+        a16 = Article(pub_date=now, headline='bazbaRFOO')
+        a16.save()
+
+        # alternation
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'oo(f|b)'),
+            [
+                '<Article: barfoobaz>',
+                '<Article: foobar>',
+                '<Article: foobarbaz>',
+                '<Article: foobaz>',
+            ])
+        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'oo(f|b)'),
+            [
+                '<Article: barfoobaz>',
+                '<Article: foobar>',
+                '<Article: foobarbaz>',
+                '<Article: foobaz>',
+                '<Article: ooF>',
+            ])
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^foo(f|b)'),
+            ['<Article: foobar>', '<Article: foobarbaz>', '<Article: foobaz>'])
+
+        # greedy matching
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b.*az'),
+            [
+                '<Article: barfoobaz>',
+                '<Article: baz>',
+                '<Article: bazbaRFOO>',
+                '<Article: foobarbaz>',
+                '<Article: foobaz>',
+            ])
+        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'b.*ar'),
+            [
+                '<Article: bar>',
+                '<Article: barfoobaz>',
+                '<Article: bazbaRFOO>',
+                '<Article: foobar>',
+                '<Article: foobarbaz>',
+            ])
+
+    @skipUnlessDBFeature('supports_regex_backreferencing')
+    def test_regex_backreferencing(self):
+        # grouping and backreferences
+        now = datetime.now()
+        a10 = Article(pub_date=now, headline='foobar')
+        a10.save()
+        a11 = Article(pub_date=now, headline='foobaz')
+        a11.save()
+        a12 = Article(pub_date=now, headline='ooF')
+        a12.save()
+        a13 = Article(pub_date=now, headline='foobarbaz')
+        a13.save()
+        a14 = Article(pub_date=now, headline='zoocarfaz')
+        a14.save()
+        a15 = Article(pub_date=now, headline='barfoobaz')
+        a15.save()
+        a16 = Article(pub_date=now, headline='bazbaRFOO')
+        a16.save()
+        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'),
+            ['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>'])