diff --git a/django/core/urlresolvers.py b/django/core/urlresolvers.py index e8d6a46c689..ffe74bc6505 100644 --- a/django/core/urlresolvers.py +++ b/django/core/urlresolvers.py @@ -388,7 +388,7 @@ class RegexURLResolver(LocaleRegexProvider): if len(args) != len(params) + len(prefix_args): continue unicode_args = [force_text(val) for val in args] - candidate = (prefix_norm + result) % dict(zip(prefix_args + params, unicode_args)) + candidate = (prefix_norm.replace('%', '%%') + result) % dict(zip(prefix_args + params, unicode_args)) else: if set(kwargs.keys()) | set(defaults.keys()) != set(params) | set(defaults.keys()) | set(prefix_args): continue diff --git a/tests/lookup/models.py b/tests/lookup/models.py index f388ddf403d..f60c4962fdc 100644 --- a/tests/lookup/models.py +++ b/tests/lookup/models.py @@ -10,29 +10,16 @@ from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible +from shared_models.models import Author, Book -class Author(models.Model): - name = models.CharField(max_length=100) - class Meta: - ordering = ('name', ) - -@python_2_unicode_compatible -class Article(models.Model): - headline = models.CharField(max_length=100) - pub_date = models.DateTimeField() - author = models.ForeignKey(Author, blank=True, null=True) - class Meta: - ordering = ('-pub_date', 'headline') - - def __str__(self): - return self.headline class Tag(models.Model): - articles = models.ManyToManyField(Article) + articles = models.ManyToManyField(Book) name = models.CharField(max_length=100) class Meta: ordering = ('name', ) + @python_2_unicode_compatible class Season(models.Model): year = models.PositiveSmallIntegerField() @@ -41,6 +28,7 @@ class Season(models.Model): def __str__(self): return six.text_type(self.year) + @python_2_unicode_compatible class Game(models.Model): season = models.ForeignKey(Season, related_name='games') @@ -50,6 +38,7 @@ class Game(models.Model): def __str__(self): return "%s at %s" % (self.away, self.home) + @python_2_unicode_compatible class Player(models.Model): name = models.CharField(max_length=100) diff --git a/tests/lookup/tests.py b/tests/lookup/tests.py index de7105f92d7..d0ffc25852c 100644 --- a/tests/lookup/tests.py +++ b/tests/lookup/tests.py @@ -6,7 +6,9 @@ from operator import attrgetter from django.core.exceptions import FieldError from django.test import TestCase, skipUnlessDBFeature -from .models import Author, Article, Tag, Game, Season, Player +from shared_models.models import Author, Book + +from .models import Tag, Game, Season, Player class LookupTests(TestCase): @@ -17,165 +19,165 @@ class LookupTests(TestCase): self.au1.save() self.au2 = Author(name='Author 2') self.au2.save() - # Create a couple of Articles. - self.a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), author=self.au1) - self.a1.save() - self.a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), author=self.au1) - self.a2.save() - self.a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), author=self.au1) - self.a3.save() - self.a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), author=self.au1) - self.a4.save() - self.a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0), author=self.au2) - self.a5.save() - self.a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0), author=self.au2) - self.a6.save() - self.a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27), author=self.au2) - self.a7.save() + # Create a couple of Books. + self.b1 = Book(name='Book 1', pubdate=datetime(2005, 7, 26), author=self.au1) + self.b1.save() + self.b2 = Book(name='Book 2', pubdate=datetime(2005, 7, 27), author=self.au1) + self.b2.save() + self.b3 = Book(name='Book 3', pubdate=datetime(2005, 7, 27), author=self.au1) + self.b3.save() + self.b4 = Book(name='Book 4', pubdate=datetime(2005, 7, 28), author=self.au1) + self.b4.save() + self.b5 = Book(name='Book 5', pubdate=datetime(2005, 8, 1, 9, 0), author=self.au2) + self.b5.save() + self.b6 = Book(name='Book 6', pubdate=datetime(2005, 8, 1, 8, 0), author=self.au2) + self.b6.save() + self.b7 = Book(name='Book 7', pubdate=datetime(2005, 7, 27), author=self.au2) + self.b7.save() # Create a few Tags. self.t1 = Tag(name='Tag 1') self.t1.save() - self.t1.articles.add(self.a1, self.a2, self.a3) + self.t1.articles.add(self.b1, self.b2, self.b3) self.t2 = Tag(name='Tag 2') self.t2.save() - self.t2.articles.add(self.a3, self.a4, self.a5) + self.t2.articles.add(self.b3, self.b4, self.b5) self.t3 = Tag(name='Tag 3') self.t3.save() - self.t3.articles.add(self.a5, self.a6, self.a7) + self.t3.articles.add(self.b5, self.b6, self.b7) 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(): + self.assertTrue(Book.objects.exists()) + for a in Book.objects.all(): a.delete() # There should be none now! - self.assertFalse(Article.objects.exists()) + self.assertFalse(Book.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)), - ['']) + self.assertQuerysetEqual(Book.objects.filter(id__iexact=str(self.b1.id)), + ['']) @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'), + self.assertQuerysetEqual(Book.objects.filter(pubdate__startswith='2005'), [ - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', ]) 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(), + self.assertQuerysetEqual(Book.objects.iterator(), [ - 'Article 5', - 'Article 6', - 'Article 4', - 'Article 2', - 'Article 3', - 'Article 7', - 'Article 1', + 'Book 5', + 'Book 6', + 'Book 4', + 'Book 2', + 'Book 3', + 'Book 7', + 'Book 1', ], - transform=attrgetter('headline')) + transform=attrgetter('name')) # iterator() can be used on any QuerySet. self.assertQuerysetEqual( - Article.objects.filter(headline__endswith='4').iterator(), - ['Article 4'], - transform=attrgetter('headline')) + Book.objects.filter(name__endswith='4').iterator(), + ['Book 4'], + transform=attrgetter('name')) 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) + self.assertEqual(Book.objects.count(), 7) + self.assertEqual(Book.objects.filter(pubdate__exact=datetime(2005, 7, 27)).count(), 3) + self.assertEqual(Book.objects.filter(name__startswith='Blah blah').count(), 0) # count() should respect sliced query sets. - articles = Article.objects.all() + articles = Book.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) + self.assertEqual(Book.objects.filter(pubdate__exact='2005-07-27').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.assertEqual(Article.objects.in_bulk(iter([self.a1.id])), {self.a1.id: self.a1}) - self.assertEqual(Article.objects.in_bulk(iter([])), {}) - self.assertRaises(TypeError, Article.objects.in_bulk) - self.assertRaises(TypeError, Article.objects.in_bulk, headline__startswith='Blah') + arts = Book.objects.in_bulk([self.b1.id, self.b2.id]) + self.assertEqual(arts[self.b1.id], self.b1) + self.assertEqual(arts[self.b2.id], self.b2) + self.assertEqual(Book.objects.in_bulk([self.b3.id]), {self.b3.id: self.b3}) + self.assertEqual(Book.objects.in_bulk(set([self.b3.id])), {self.b3.id: self.b3}) + self.assertEqual(Book.objects.in_bulk(frozenset([self.b3.id])), {self.b3.id: self.b3}) + self.assertEqual(Book.objects.in_bulk((self.b3.id,)), {self.b3.id: self.b3}) + self.assertEqual(Book.objects.in_bulk([1000]), {}) + self.assertEqual(Book.objects.in_bulk([]), {}) + self.assertEqual(Book.objects.in_bulk(iter([self.b1.id])), {self.b1.id: self.b1}) + self.assertEqual(Book.objects.in_bulk(iter([])), {}) + self.assertRaises(TypeError, Book.objects.in_bulk) + self.assertRaises(TypeError, Book.objects.in_bulk, name__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'), + self.assertQuerysetEqual(Book.objects.values('name'), [ - {'headline': 'Article 5'}, - {'headline': 'Article 6'}, - {'headline': 'Article 4'}, - {'headline': 'Article 2'}, - {'headline': 'Article 3'}, - {'headline': 'Article 7'}, - {'headline': 'Article 1'}, + {'name': 'Book 5'}, + {'name': 'Book 6'}, + {'name': 'Book 4'}, + {'name': 'Book 2'}, + {'name': 'Book 3'}, + {'name': 'Book 7'}, + {'name': 'Book 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}], + Book.objects.filter(pubdate__exact=datetime(2005, 7, 27)).values('id'), + [{'id': self.b2.id}, {'id': self.b3.id}, {'id': self.b7.id}], transform=identity) - self.assertQuerysetEqual(Article.objects.values('id', 'headline'), + self.assertQuerysetEqual(Book.objects.values('id', 'name'), [ - {'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'}, + {'id': self.b5.id, 'name': 'Book 5'}, + {'id': self.b6.id, 'name': 'Book 6'}, + {'id': self.b4.id, 'name': 'Book 4'}, + {'id': self.b2.id, 'name': 'Book 2'}, + {'id': self.b3.id, 'name': 'Book 3'}, + {'id': self.b7.id, 'name': 'Book 7'}, + {'id': self.b1.id, 'name': 'Book 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(), + self.assertQuerysetEqual(Book.objects.values('id', 'name').iterator(), [ - {'headline': 'Article 5', 'id': self.a5.id}, - {'headline': 'Article 6', 'id': self.a6.id}, - {'headline': 'Article 4', 'id': self.a4.id}, - {'headline': 'Article 2', 'id': self.a2.id}, - {'headline': 'Article 3', 'id': self.a3.id}, - {'headline': 'Article 7', 'id': self.a7.id}, - {'headline': 'Article 1', 'id': self.a1.id}, + {'name': 'Book 5', 'id': self.b5.id}, + {'name': 'Book 6', 'id': self.b6.id}, + {'name': 'Book 4', 'id': self.b4.id}, + {'name': 'Book 2', 'id': self.b2.id}, + {'name': 'Book 3', 'id': self.b3.id}, + {'name': 'Book 7', 'id': self.b7.id}, + {'name': 'Book 1', 'id': self.b1.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'), + Book.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}, + {'id': self.b5.id, 'id_plus_one': self.b5.id + 1}, + {'id': self.b6.id, 'id_plus_one': self.b6.id + 1}, + {'id': self.b4.id, 'id_plus_one': self.b4.id + 1}, + {'id': self.b2.id, 'id_plus_one': self.b2.id + 1}, + {'id': self.b3.id, 'id_plus_one': self.b3.id + 1}, + {'id': self.b7.id, 'id_plus_one': self.b7.id + 1}, + {'id': self.b1.id, 'id_plus_one': self.b1.id + 1}, ], transform=identity) data = { @@ -189,66 +191,67 @@ class LookupTests(TestCase): 'id_plus_eight': 'id+8', } self.assertQuerysetEqual( - Article.objects.filter(id=self.a1.id).extra(select=data).values(*data.keys()), + Book.objects.filter(id=self.b1.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, + 'id_plus_one': self.b1.id + 1, + 'id_plus_two': self.b1.id + 2, + 'id_plus_three': self.b1.id + 3, + 'id_plus_four': self.b1.id + 4, + 'id_plus_five': self.b1.id + 5, + 'id_plus_six': self.b1.id + 6, + 'id_plus_seven': self.b1.id + 7, + 'id_plus_eight': self.b1.id + 8, }], transform=identity) # You can specify fields from forward and reverse relations, just like filter(). self.assertQuerysetEqual( - Article.objects.values('headline', 'author__name'), + Book.objects.values('name', 'author__name'), [ - {'headline': self.a5.headline, 'author__name': self.au2.name}, - {'headline': self.a6.headline, 'author__name': self.au2.name}, - {'headline': self.a4.headline, 'author__name': self.au1.name}, - {'headline': self.a2.headline, 'author__name': self.au1.name}, - {'headline': self.a3.headline, 'author__name': self.au1.name}, - {'headline': self.a7.headline, 'author__name': self.au2.name}, - {'headline': self.a1.headline, 'author__name': self.au1.name}, + {'name': self.b5.name, 'author__name': self.au2.name}, + {'name': self.b6.name, 'author__name': self.au2.name}, + {'name': self.b4.name, 'author__name': self.au1.name}, + {'name': self.b2.name, 'author__name': self.au1.name}, + {'name': self.b3.name, 'author__name': self.au1.name}, + {'name': self.b7.name, 'author__name': self.au2.name}, + {'name': self.b1.name, 'author__name': self.au1.name}, ], transform=identity) self.assertQuerysetEqual( - Author.objects.values('name', 'article__headline').order_by('name', 'article__headline'), + Author.objects.values('name', 'book__name').order_by('name', 'book__name'), [ - {'name': self.au1.name, 'article__headline': self.a1.headline}, - {'name': self.au1.name, 'article__headline': self.a2.headline}, - {'name': self.au1.name, 'article__headline': self.a3.headline}, - {'name': self.au1.name, 'article__headline': self.a4.headline}, - {'name': self.au2.name, 'article__headline': self.a5.headline}, - {'name': self.au2.name, 'article__headline': self.a6.headline}, - {'name': self.au2.name, 'article__headline': self.a7.headline}, + {'name': self.au1.name, 'book__name': self.b1.name}, + {'name': self.au1.name, 'book__name': self.b2.name}, + {'name': self.au1.name, 'book__name': self.b3.name}, + {'name': self.au1.name, 'book__name': self.b4.name}, + {'name': self.au2.name, 'book__name': self.b5.name}, + {'name': self.au2.name, 'book__name': self.b6.name}, + {'name': self.au2.name, 'book__name': self.b7.name}, ], transform=identity) self.assertQuerysetEqual( - Author.objects.values('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'), + Author.objects.values('name', 'book__name', 'book__tag__name').order_by('name', 'book__name', 'book__tag__name'), [ - {'name': self.au1.name, 'article__headline': self.a1.headline, 'article__tag__name': self.t1.name}, - {'name': self.au1.name, 'article__headline': self.a2.headline, 'article__tag__name': self.t1.name}, - {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t1.name}, - {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t2.name}, - {'name': self.au1.name, 'article__headline': self.a4.headline, 'article__tag__name': self.t2.name}, - {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t2.name}, - {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t3.name}, - {'name': self.au2.name, 'article__headline': self.a6.headline, 'article__tag__name': self.t3.name}, - {'name': self.au2.name, 'article__headline': self.a7.headline, 'article__tag__name': self.t3.name}, + {'name': self.au1.name, 'book__name': self.b1.name, 'book__tag__name': self.t1.name}, + {'name': self.au1.name, 'book__name': self.b2.name, 'book__tag__name': self.t1.name}, + {'name': self.au1.name, 'book__name': self.b3.name, 'book__tag__name': self.t1.name}, + {'name': self.au1.name, 'book__name': self.b3.name, 'book__tag__name': self.t2.name}, + {'name': self.au1.name, 'book__name': self.b4.name, 'book__tag__name': self.t2.name}, + {'name': self.au2.name, 'book__name': self.b5.name, 'book__tag__name': self.t2.name}, + {'name': self.au2.name, 'book__name': self.b5.name, 'book__tag__name': self.t3.name}, + {'name': self.au2.name, 'book__name': self.b6.name, 'book__tag__name': self.t3.name}, + {'name': self.au2.name, 'book__name': self.b7.name, 'book__tag__name': self.t3.name}, ], 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, + Book.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(), + self.assertQuerysetEqual(Book.objects.filter(id=self.b5.id).values(), [{ - 'id': self.a5.id, + 'id': self.b5.id, 'author_id': self.au2.id, - 'headline': 'Article 5', - 'pub_date': datetime(2005, 8, 1, 9, 0) + 'name': 'Book 5', + 'pages': 0, + 'pubdate': datetime(2005, 8, 1, 9, 0) }], transform=identity) def test_values_list(self): @@ -257,358 +260,358 @@ class LookupTests(TestCase): # Within each tuple, the order of the elements is the same as the order # of fields in the values_list() call. identity = lambda x:x - self.assertQuerysetEqual(Article.objects.values_list('headline'), + self.assertQuerysetEqual(Book.objects.values_list('name'), [ - ('Article 5',), - ('Article 6',), - ('Article 4',), - ('Article 2',), - ('Article 3',), - ('Article 7',), - ('Article 1',), + ('Book 5',), + ('Book 6',), + ('Book 4',), + ('Book 2',), + ('Book 3',), + ('Book 7',), + ('Book 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,)], + self.assertQuerysetEqual(Book.objects.values_list('id').order_by('id'), + [(self.b1.id,), (self.b2.id,), (self.b3.id,), (self.b4.id,), (self.b5.id,), (self.b6.id,), (self.b7.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], + Book.objects.values_list('id', flat=True).order_by('id'), + [self.b1.id, self.b2.id, self.b3.id, self.b4.id, self.b5.id, self.b6.id, self.b7.id], transform=identity) self.assertQuerysetEqual( - Article.objects.extra(select={'id_plus_one': 'id+1'}) + Book.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,)], + [(self.b1.id,), (self.b2.id,), (self.b3.id,), (self.b4.id,), (self.b5.id,), (self.b6.id,), (self.b7.id,)], transform=identity) self.assertQuerysetEqual( - Article.objects.extra(select={'id_plus_one': 'id+1'}) + Book.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) + (self.b1.id+1, self.b1.id), + (self.b2.id+1, self.b2.id), + (self.b3.id+1, self.b3.id), + (self.b4.id+1, self.b4.id), + (self.b5.id+1, self.b5.id), + (self.b6.id+1, self.b6.id), + (self.b7.id+1, self.b7.id) ], transform=identity) self.assertQuerysetEqual( - Article.objects.extra(select={'id_plus_one': 'id+1'}) + Book.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) + (self.b1.id, self.b1.id+1), + (self.b2.id, self.b2.id+1), + (self.b3.id, self.b3.id+1), + (self.b4.id, self.b4.id+1), + (self.b5.id, self.b5.id+1), + (self.b6.id, self.b6.id+1), + (self.b7.id, self.b7.id+1) ], transform=identity) self.assertQuerysetEqual( - Author.objects.values_list('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'), + Author.objects.values_list('name', 'book__name', 'book__tag__name').order_by('name', 'book__name', 'book__tag__name'), [ - (self.au1.name, self.a1.headline, self.t1.name), - (self.au1.name, self.a2.headline, self.t1.name), - (self.au1.name, self.a3.headline, self.t1.name), - (self.au1.name, self.a3.headline, self.t2.name), - (self.au1.name, self.a4.headline, self.t2.name), - (self.au2.name, self.a5.headline, self.t2.name), - (self.au2.name, self.a5.headline, self.t3.name), - (self.au2.name, self.a6.headline, self.t3.name), - (self.au2.name, self.a7.headline, self.t3.name), + (self.au1.name, self.b1.name, self.t1.name), + (self.au1.name, self.b2.name, self.t1.name), + (self.au1.name, self.b3.name, self.t1.name), + (self.au1.name, self.b3.name, self.t2.name), + (self.au1.name, self.b4.name, self.t2.name), + (self.au2.name, self.b5.name, self.t2.name), + (self.au2.name, self.b5.name, self.t3.name), + (self.au2.name, self.b6.name, self.t3.name), + (self.au2.name, self.b7.name, self.t3.name), ], transform=identity) - self.assertRaises(TypeError, Article.objects.values_list, 'id', 'headline', flat=True) + self.assertRaises(TypeError, Book.objects.values_list, 'id', 'name', 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()), - '') - self.assertEqual(repr(self.a2.get_next_by_pub_date()), - '') - self.assertEqual(repr(self.a2.get_next_by_pub_date(headline__endswith='6')), - '') - self.assertEqual(repr(self.a3.get_next_by_pub_date()), - '') - self.assertEqual(repr(self.a4.get_next_by_pub_date()), - '') - self.assertRaises(Article.DoesNotExist, self.a5.get_next_by_pub_date) - self.assertEqual(repr(self.a6.get_next_by_pub_date()), - '') - self.assertEqual(repr(self.a7.get_next_by_pub_date()), - '') + self.assertEqual(repr(self.b1.get_next_by_pubdate()), + '') + self.assertEqual(repr(self.b2.get_next_by_pubdate()), + '') + self.assertEqual(repr(self.b2.get_next_by_pubdate(name__endswith='6')), + '') + self.assertEqual(repr(self.b3.get_next_by_pubdate()), + '') + self.assertEqual(repr(self.b4.get_next_by_pubdate()), + '') + self.assertRaises(Book.DoesNotExist, self.b5.get_next_by_pubdate) + self.assertEqual(repr(self.b6.get_next_by_pubdate()), + '') + self.assertEqual(repr(self.b7.get_next_by_pubdate()), + '') - self.assertEqual(repr(self.a7.get_previous_by_pub_date()), - '') - self.assertEqual(repr(self.a6.get_previous_by_pub_date()), - '') - self.assertEqual(repr(self.a5.get_previous_by_pub_date()), - '') - self.assertEqual(repr(self.a4.get_previous_by_pub_date()), - '') - self.assertEqual(repr(self.a3.get_previous_by_pub_date()), - '') - self.assertEqual(repr(self.a2.get_previous_by_pub_date()), - '') + self.assertEqual(repr(self.b7.get_previous_by_pubdate()), + '') + self.assertEqual(repr(self.b6.get_previous_by_pubdate()), + '') + self.assertEqual(repr(self.b5.get_previous_by_pubdate()), + '') + self.assertEqual(repr(self.b4.get_previous_by_pubdate()), + '') + self.assertEqual(repr(self.b3.get_previous_by_pubdate()), + '') + self.assertEqual(repr(self.b2.get_previous_by_pubdate()), + '') 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'), + b8 = Book(name='Book_ with underscore', pubdate=datetime(2005, 11, 20)) + b8.save() + self.assertQuerysetEqual(Book.objects.filter(name__startswith='Book'), [ - '', - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article_'), - ['']) - a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21)) - a9.save() - self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'), + self.assertQuerysetEqual(Book.objects.filter(name__startswith='Book_'), + ['']) + b9 = Book(name='Book% with percent sign', pubdate=datetime(2005, 11, 21)) + b9.save() + self.assertQuerysetEqual(Book.objects.filter(name__startswith='Book'), [ - '', - '', - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article%'), - ['']) - a10 = Article(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22)) - a10.save() - self.assertQuerysetEqual(Article.objects.filter(headline__contains='\\'), - ['']) + self.assertQuerysetEqual(Book.objects.filter(name__startswith='Book%'), + ['']) + b10 = Book(name='Book with \\ backslash', pubdate=datetime(2005, 11, 22)) + b10.save() + self.assertQuerysetEqual(Book.objects.filter(name__contains='\\'), + ['']) 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)) + b8 = Book.objects.create(name='Book_ with underscore', pubdate=datetime(2005, 11, 20)) + b9 = Book.objects.create(name='Book% with percent sign', pubdate=datetime(2005, 11, 21)) + b10 = Book.objects.create(name='Book with \\ backslash', pubdate=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'), + Book.objects.filter(name__contains='Book').exclude(name__contains='with'), [ - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.exclude(headline__startswith="Article_"), + self.assertQuerysetEqual(Book.objects.exclude(name__startswith="Book_"), [ - '', - '', - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.exclude(headline="Article 7"), + self.assertQuerysetEqual(Book.objects.exclude(name="Book 7"), [ - '', - '', - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', + '', + '', ]) def test_none(self): # none() returns a QuerySet that behaves like any other QuerySet object - self.assertQuerysetEqual(Article.objects.none(), []) + self.assertQuerysetEqual(Book.objects.none(), []) self.assertQuerysetEqual( - Article.objects.none().filter(headline__startswith='Article'), []) + Book.objects.none().filter(name__startswith='Book'), []) self.assertQuerysetEqual( - Article.objects.filter(headline__startswith='Article').none(), []) - self.assertEqual(Article.objects.none().count(), 0) + Book.objects.filter(name__startswith='Book').none(), []) + self.assertEqual(Book.objects.none().count(), 0) self.assertEqual( - Article.objects.none().update(headline="This should not take effect"), 0) + Book.objects.none().update(name="This should not take effect"), 0) self.assertQuerysetEqual( - [article for article in Article.objects.none().iterator()], + [article for article in Book.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=[]), + self.assertQuerysetEqual(Book.objects.filter(id__in=[]), []) + self.assertQuerysetEqual(Book.objects.exclude(id__in=[]), [ - '', - '', - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', + '', + '', ]) def test_error_messages(self): # Programming errors are pointed out with nice error messages try: - Article.objects.filter(pub_date_year='2005').count() + Book.objects.filter(pubdate_year='2005').count() self.fail('FieldError not raised') except FieldError as ex: - self.assertEqual(str(ex), "Cannot resolve keyword 'pub_date_year' " - "into field. Choices are: author, headline, id, pub_date, tag") + self.assertEqual(str(ex), "Cannot resolve keyword 'pubdate_year' " + "into field. Choices are: author, id, name, pages, pubdate, tag, tags") try: - Article.objects.filter(headline__starts='Article') + Book.objects.filter(name__starts='Book') self.fail('FieldError not raised') except FieldError as ex: - self.assertEqual(str(ex), "Join on field 'headline' not permitted. " + self.assertEqual(str(ex), "Join on field 'name' 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(): + # Create some articles with a bit more interesting names for testing field lookups: + for a in Book.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() + b1 = Book(pubdate=now, name='f') + b1.save() + b2 = Book(pubdate=now, name='fo') + b2.save() + b3 = Book(pubdate=now, name='foo') + b3.save() + b4 = Book(pubdate=now, name='fooo') + b4.save() + b5 = Book(pubdate=now, name='hey-Foo') + b5.save() + b6 = Book(pubdate=now, name='bar') + b6.save() + b7 = Book(pubdate=now, name='AbBa') + b7.save() + b8 = Book(pubdate=now, name='baz') + b8.save() + b9 = Book(pubdate=now, name='baxZ') + b9.save() # zero-or-more - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo*'), - ['', '', '', '']) - self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'fo*'), + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'fo*'), + ['', '', '', '']) + self.assertQuerysetEqual(Book.objects.filter(name__iregex=r'fo*'), [ - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', ]) # one-or-more - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo+'), - ['', '', '']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'fo+'), + ['', '', '']) # wildcard - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fooo?'), - ['', '']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'fooo?'), + ['', '']) # leading anchor - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^b'), - ['', '', '']) - self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'^a'), - ['']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'^b'), + ['', '', '']) + self.assertQuerysetEqual(Book.objects.filter(name__iregex=r'^a'), + ['']) # trailing anchor - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'z$'), - ['']) - self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'z$'), - ['', '']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'z$'), + ['']) + self.assertQuerysetEqual(Book.objects.filter(name__iregex=r'z$'), + ['', '']) # character sets - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba[rz]'), - ['', '']) - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba.[RxZ]'), - ['']) - self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'ba[RxZ]'), - ['', '', '']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'ba[rz]'), + ['', '']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'ba.[RxZ]'), + ['']) + self.assertQuerysetEqual(Book.objects.filter(name__iregex=r'ba[RxZ]'), + ['', '', '']) # 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() + b10 = Book(pubdate=now, name='foobar') + b10.save() + b11 = Book(pubdate=now, name='foobaz') + b11.save() + b12 = Book(pubdate=now, name='ooF') + b12.save() + b13 = Book(pubdate=now, name='foobarbaz') + b13.save() + b14 = Book(pubdate=now, name='zoocarfaz') + b14.save() + b15 = Book(pubdate=now, name='barfoobaz') + b15.save() + b16 = Book(pubdate=now, name='bazbaRFOO') + b16.save() # alternation - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'oo(f|b)'), + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'oo(f|b)'), [ - '', - '', - '', - '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'oo(f|b)'), + self.assertQuerysetEqual(Book.objects.filter(name__iregex=r'oo(f|b)'), [ - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^foo(f|b)'), - ['', '', '']) + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'^foo(f|b)'), + ['', '', '']) # greedy matching - self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b.*az'), + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'b.*az'), [ - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', ]) - self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'b.*ar'), + self.assertQuerysetEqual(Book.objects.filter(name__iregex=r'b.*ar'), [ - '', - '', - '', - '', - '', + '', + '', + '', + '', + '', ]) @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'), - ['', '', '']) + b10 = Book(pubdate=now, name='foobar') + b10.save() + b11 = Book(pubdate=now, name='foobaz') + b11.save() + b12 = Book(pubdate=now, name='ooF') + b12.save() + b13 = Book(pubdate=now, name='foobarbaz') + b13.save() + b14 = Book(pubdate=now, name='zoocarfaz') + b14.save() + b15 = Book(pubdate=now, name='barfoobaz') + b15.save() + b16 = Book(pubdate=now, name='bazbaRFOO') + b16.save() + self.assertQuerysetEqual(Book.objects.filter(name__regex=r'b(.).*b\1'), + ['', '', '']) def test_nonfield_lookups(self): """ @@ -616,11 +619,11 @@ class LookupTests(TestCase): exception. """ with self.assertRaises(FieldError): - Article.objects.filter(headline__blahblah=99) + Book.objects.filter(name__blahblah=99) with self.assertRaises(FieldError): - Article.objects.filter(headline__blahblah__exact=99) + Book.objects.filter(name__blahblah__exact=99) with self.assertRaises(FieldError): - Article.objects.filter(blahblah=99) + Book.objects.filter(blahblah=99) def test_lookup_collision(self): """ diff --git a/tests/runtests.py b/tests/runtests.py index f0cba38b806..af47224fb95 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -21,6 +21,7 @@ os.environ['DJANGO_TEST_TEMP_DIR'] = TEMP_DIR SUBDIRS_TO_SKIP = ['templates'] ALWAYS_INSTALLED_APPS = [ + 'shared_models', 'django.contrib.contenttypes', 'django.contrib.auth', 'django.contrib.sites', diff --git a/tests/shared_models/__init__.py b/tests/shared_models/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/shared_models/models.py b/tests/shared_models/models.py new file mode 100644 index 00000000000..61b3669bc44 --- /dev/null +++ b/tests/shared_models/models.py @@ -0,0 +1,30 @@ +from django.db import models +from django.utils import timezone +from django.utils.encoding import python_2_unicode_compatible + + +class Tag(models.Model): + name = models.CharField(max_length=255) + + +@python_2_unicode_compatible +class Author(models.Model): + name = models.CharField(max_length=100) + + def __str__(self): + return self.name + + +@python_2_unicode_compatible +class Book(models.Model): + name = models.CharField(max_length=200) + pages = models.IntegerField(default=0) + author = models.ForeignKey(Author, null=True) + pubdate = models.DateTimeField() + tags = models.ManyToManyField(Tag) + + class Meta: + ordering = ['-pubdate', 'name'] + + def __str__(self): + return self.name diff --git a/tests/signals_regress/models.py b/tests/signals_regress/models.py index 829314c06ca..e69de29bb2d 100644 --- a/tests/signals_regress/models.py +++ b/tests/signals_regress/models.py @@ -1,18 +0,0 @@ -from django.db import models -from django.utils.encoding import python_2_unicode_compatible - - -@python_2_unicode_compatible -class Author(models.Model): - name = models.CharField(max_length=20) - - def __str__(self): - return self.name - -@python_2_unicode_compatible -class Book(models.Model): - name = models.CharField(max_length=20) - authors = models.ManyToManyField(Author) - - def __str__(self): - return self.name diff --git a/tests/signals_regress/tests.py b/tests/signals_regress/tests.py index 8fb3ad5a48e..0c92b96c24f 100644 --- a/tests/signals_regress/tests.py +++ b/tests/signals_regress/tests.py @@ -3,7 +3,7 @@ from __future__ import absolute_import from django.db import models from django.test import TestCase -from .models import Author, Book +from shared_models.models import Author, Book class SignalsRegressTests(TestCase): @@ -77,7 +77,7 @@ class SignalsRegressTests(TestCase): "Is created" ]) - b1 = Book(name='Snow Crash') + b1 = Book(name='Snow Crash', pubdate='2012-02-02 12:00') self.assertEqual(self.get_signal_output(b1.save), [ "pre_save signal, Snow Crash", "post_save signal, Snow Crash", @@ -87,7 +87,7 @@ class SignalsRegressTests(TestCase): def test_m2m_signals(self): """ Assigning and removing to/from m2m shouldn't generate an m2m signal """ - b1 = Book(name='Snow Crash') + b1 = Book(name='Snow Crash', pubdate='2012-02-02 12:00') self.get_signal_output(b1.save) a1 = Author(name='Neal Stephenson') self.get_signal_output(a1.save) diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index 8dc21e4bc98..1860c9dd2c9 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -183,6 +183,11 @@ class URLPatternReverse(TestCase): self.assertEqual('/bump%2520map/includes/non_path_include/', reverse('non_path_include', prefix='/bump%20map/')) + def test_non_urlsafe_prefix_with_args(self): + # Regression for #20022 + self.assertEqual('/%7Eme/places/1/', + reverse('places', args=[1], prefix='/~me/')) + class ResolverTests(unittest.TestCase): def test_resolver_repr(self): """