PageRenderTime 50ms CodeModel.GetById 4ms app.highlight 40ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/modeltests/lookup/tests.py

https://code.google.com/p/mango-py/
Python | 608 lines | 557 code | 16 blank | 35 comment | 4 complexity | 4dc5e53f61357d6a185aac9b08ca7207 MD5 | raw file
  1from datetime import datetime
  2from operator import attrgetter
  3from django.core.exceptions import FieldError
  4from django.db import connection
  5from django.test import TestCase, skipUnlessDBFeature
  6from models import Author, Article, Tag
  7
  8
  9class LookupTests(TestCase):
 10
 11    #def setUp(self):
 12    def setUp(self):
 13        # Create a few Authors.
 14        self.au1 = Author(name='Author 1')
 15        self.au1.save()
 16        self.au2 = Author(name='Author 2')
 17        self.au2.save()
 18        # Create a couple of Articles.
 19        self.a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), author=self.au1)
 20        self.a1.save()
 21        self.a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), author=self.au1)
 22        self.a2.save()
 23        self.a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), author=self.au1)
 24        self.a3.save()
 25        self.a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), author=self.au1)
 26        self.a4.save()
 27        self.a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0), author=self.au2)
 28        self.a5.save()
 29        self.a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0), author=self.au2)
 30        self.a6.save()
 31        self.a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27), author=self.au2)
 32        self.a7.save()
 33        # Create a few Tags.
 34        self.t1 = Tag(name='Tag 1')
 35        self.t1.save()
 36        self.t1.articles.add(self.a1, self.a2, self.a3)
 37        self.t2 = Tag(name='Tag 2')
 38        self.t2.save()
 39        self.t2.articles.add(self.a3, self.a4, self.a5)
 40        self.t3 = Tag(name='Tag 3')
 41        self.t3.save()
 42        self.t3.articles.add(self.a5, self.a6, self.a7)
 43
 44    def test_exists(self):
 45        # We can use .exists() to check that there are some
 46        self.assertTrue(Article.objects.exists())
 47        for a in Article.objects.all():
 48            a.delete()
 49        # There should be none now!
 50        self.assertFalse(Article.objects.exists())
 51
 52    def test_lookup_int_as_str(self):
 53        # Integer value can be queried using string
 54        self.assertQuerysetEqual(Article.objects.filter(id__iexact=str(self.a1.id)),
 55                                 ['<Article: Article 1>'])
 56
 57    @skipUnlessDBFeature('supports_date_lookup_using_string')
 58    def test_lookup_date_as_str(self):
 59        # A date lookup can be performed using a string search
 60        self.assertQuerysetEqual(Article.objects.filter(pub_date__startswith='2005'),
 61            [
 62                '<Article: Article 5>',
 63                '<Article: Article 6>',
 64                '<Article: Article 4>',
 65                '<Article: Article 2>',
 66                '<Article: Article 3>',
 67                '<Article: Article 7>',
 68                '<Article: Article 1>',
 69            ])
 70
 71    def test_iterator(self):
 72        # Each QuerySet gets iterator(), which is a generator that "lazily"
 73        # returns results using database-level iteration.
 74        self.assertQuerysetEqual(Article.objects.iterator(),
 75            [
 76                'Article 5',
 77                'Article 6',
 78                'Article 4',
 79                'Article 2',
 80                'Article 3',
 81                'Article 7',
 82                'Article 1',
 83            ],
 84            transform=attrgetter('headline'))
 85        # iterator() can be used on any QuerySet.
 86        self.assertQuerysetEqual(
 87            Article.objects.filter(headline__endswith='4').iterator(),
 88            ['Article 4'],
 89            transform=attrgetter('headline'))
 90
 91    def test_count(self):
 92        # count() returns the number of objects matching search criteria.
 93        self.assertEqual(Article.objects.count(), 7)
 94        self.assertEqual(Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).count(), 3)
 95        self.assertEqual(Article.objects.filter(headline__startswith='Blah blah').count(), 0)
 96
 97        # count() should respect sliced query sets.
 98        articles = Article.objects.all()
 99        self.assertEqual(articles.count(), 7)
100        self.assertEqual(articles[:4].count(), 4)
101        self.assertEqual(articles[1:100].count(), 6)
102        self.assertEqual(articles[10:100].count(), 0)
103
104        # Date and date/time lookups can also be done with strings.
105        self.assertEqual(Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count(), 3)
106
107    def test_in_bulk(self):
108        # in_bulk() takes a list of IDs and returns a dictionary mapping IDs to objects.
109        arts = Article.objects.in_bulk([self.a1.id, self.a2.id])
110        self.assertEqual(arts[self.a1.id], self.a1)
111        self.assertEqual(arts[self.a2.id], self.a2)
112        self.assertEqual(Article.objects.in_bulk([self.a3.id]), {self.a3.id: self.a3})
113        self.assertEqual(Article.objects.in_bulk(set([self.a3.id])), {self.a3.id: self.a3})
114        self.assertEqual(Article.objects.in_bulk(frozenset([self.a3.id])), {self.a3.id: self.a3})
115        self.assertEqual(Article.objects.in_bulk((self.a3.id,)), {self.a3.id: self.a3})
116        self.assertEqual(Article.objects.in_bulk([1000]), {})
117        self.assertEqual(Article.objects.in_bulk([]), {})
118        self.assertRaises(AssertionError, Article.objects.in_bulk, 'foo')
119        self.assertRaises(TypeError, Article.objects.in_bulk)
120        self.assertRaises(TypeError, Article.objects.in_bulk, headline__startswith='Blah')
121
122    def test_values(self):
123        # values() returns a list of dictionaries instead of object instances --
124        # and you can specify which fields you want to retrieve.
125        identity = lambda x:x
126        self.assertQuerysetEqual(Article.objects.values('headline'),
127            [
128                {'headline': u'Article 5'},
129                {'headline': u'Article 6'},
130                {'headline': u'Article 4'},
131                {'headline': u'Article 2'},
132                {'headline': u'Article 3'},
133                {'headline': u'Article 7'},
134                {'headline': u'Article 1'},
135            ],
136            transform=identity)
137        self.assertQuerysetEqual(
138            Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).values('id'),
139            [{'id': self.a2.id}, {'id': self.a3.id}, {'id': self.a7.id}],
140            transform=identity)
141        self.assertQuerysetEqual(Article.objects.values('id', 'headline'),
142            [
143                {'id': self.a5.id, 'headline': 'Article 5'},
144                {'id': self.a6.id, 'headline': 'Article 6'},
145                {'id': self.a4.id, 'headline': 'Article 4'},
146                {'id': self.a2.id, 'headline': 'Article 2'},
147                {'id': self.a3.id, 'headline': 'Article 3'},
148                {'id': self.a7.id, 'headline': 'Article 7'},
149                {'id': self.a1.id, 'headline': 'Article 1'},
150            ],
151            transform=identity)
152        # You can use values() with iterator() for memory savings,
153        # because iterator() uses database-level iteration.
154        self.assertQuerysetEqual(Article.objects.values('id', 'headline').iterator(),
155            [
156                {'headline': u'Article 5', 'id': self.a5.id},
157                {'headline': u'Article 6', 'id': self.a6.id},
158                {'headline': u'Article 4', 'id': self.a4.id},
159                {'headline': u'Article 2', 'id': self.a2.id},
160                {'headline': u'Article 3', 'id': self.a3.id},
161                {'headline': u'Article 7', 'id': self.a7.id},
162                {'headline': u'Article 1', 'id': self.a1.id},
163            ],
164            transform=identity)
165        # The values() method works with "extra" fields specified in extra(select).
166        self.assertQuerysetEqual(
167            Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'),
168            [
169                {'id': self.a5.id, 'id_plus_one': self.a5.id + 1},
170                {'id': self.a6.id, 'id_plus_one': self.a6.id + 1},
171                {'id': self.a4.id, 'id_plus_one': self.a4.id + 1},
172                {'id': self.a2.id, 'id_plus_one': self.a2.id + 1},
173                {'id': self.a3.id, 'id_plus_one': self.a3.id + 1},
174                {'id': self.a7.id, 'id_plus_one': self.a7.id + 1},
175                {'id': self.a1.id, 'id_plus_one': self.a1.id + 1},
176            ],
177            transform=identity)
178        data = {
179            'id_plus_one': 'id+1',
180            'id_plus_two': 'id+2',
181            'id_plus_three': 'id+3',
182            'id_plus_four': 'id+4',
183            'id_plus_five': 'id+5',
184            'id_plus_six': 'id+6',
185            'id_plus_seven': 'id+7',
186            'id_plus_eight': 'id+8',
187        }
188        self.assertQuerysetEqual(
189            Article.objects.filter(id=self.a1.id).extra(select=data).values(*data.keys()),
190            [{
191                'id_plus_one': self.a1.id + 1,
192                'id_plus_two': self.a1.id + 2,
193                'id_plus_three': self.a1.id + 3,
194                'id_plus_four': self.a1.id + 4,
195                'id_plus_five': self.a1.id + 5,
196                'id_plus_six': self.a1.id + 6,
197                'id_plus_seven': self.a1.id + 7,
198                'id_plus_eight': self.a1.id + 8,
199            }], transform=identity)
200        # You can specify fields from forward and reverse relations, just like filter().
201        self.assertQuerysetEqual(
202            Article.objects.values('headline', 'author__name'),
203            [
204                {'headline': self.a5.headline, 'author__name': self.au2.name},
205                {'headline': self.a6.headline, 'author__name': self.au2.name},
206                {'headline': self.a4.headline, 'author__name': self.au1.name},
207                {'headline': self.a2.headline, 'author__name': self.au1.name},
208                {'headline': self.a3.headline, 'author__name': self.au1.name},
209                {'headline': self.a7.headline, 'author__name': self.au2.name},
210                {'headline': self.a1.headline, 'author__name': self.au1.name},
211            ], transform=identity)
212        self.assertQuerysetEqual(
213            Author.objects.values('name', 'article__headline').order_by('name', 'article__headline'),
214            [
215                {'name': self.au1.name, 'article__headline': self.a1.headline},
216                {'name': self.au1.name, 'article__headline': self.a2.headline},
217                {'name': self.au1.name, 'article__headline': self.a3.headline},
218                {'name': self.au1.name, 'article__headline': self.a4.headline},
219                {'name': self.au2.name, 'article__headline': self.a5.headline},
220                {'name': self.au2.name, 'article__headline': self.a6.headline},
221                {'name': self.au2.name, 'article__headline': self.a7.headline},
222            ], transform=identity)
223        self.assertQuerysetEqual(
224            Author.objects.values('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'),
225            [
226                {'name': self.au1.name, 'article__headline': self.a1.headline, 'article__tag__name': self.t1.name},
227                {'name': self.au1.name, 'article__headline': self.a2.headline, 'article__tag__name': self.t1.name},
228                {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t1.name},
229                {'name': self.au1.name, 'article__headline': self.a3.headline, 'article__tag__name': self.t2.name},
230                {'name': self.au1.name, 'article__headline': self.a4.headline, 'article__tag__name': self.t2.name},
231                {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t2.name},
232                {'name': self.au2.name, 'article__headline': self.a5.headline, 'article__tag__name': self.t3.name},
233                {'name': self.au2.name, 'article__headline': self.a6.headline, 'article__tag__name': self.t3.name},
234                {'name': self.au2.name, 'article__headline': self.a7.headline, 'article__tag__name': self.t3.name},
235            ], transform=identity)
236        # However, an exception FieldDoesNotExist will be thrown if you specify
237        # a non-existent field name in values() (a field that is neither in the
238        # model nor in extra(select)).
239        self.assertRaises(FieldError,
240            Article.objects.extra(select={'id_plus_one': 'id + 1'}).values,
241            'id', 'id_plus_two')
242        # If you don't specify field names to values(), all are returned.
243        self.assertQuerysetEqual(Article.objects.filter(id=self.a5.id).values(),
244            [{
245                'id': self.a5.id,
246                'author_id': self.au2.id, 
247                'headline': 'Article 5',
248                'pub_date': datetime(2005, 8, 1, 9, 0)
249            }], transform=identity)
250
251    def test_values_list(self):
252        # values_list() is similar to values(), except that the results are
253        # returned as a list of tuples, rather than a list of dictionaries.
254        # Within each tuple, the order of the elemnts is the same as the order
255        # of fields in the values_list() call.
256        identity = lambda x:x
257        self.assertQuerysetEqual(Article.objects.values_list('headline'),
258            [
259                (u'Article 5',),
260                (u'Article 6',),
261                (u'Article 4',),
262                (u'Article 2',),
263                (u'Article 3',),
264                (u'Article 7',),
265                (u'Article 1',),
266            ], transform=identity)
267        self.assertQuerysetEqual(Article.objects.values_list('id').order_by('id'),
268            [(self.a1.id,), (self.a2.id,), (self.a3.id,), (self.a4.id,), (self.a5.id,), (self.a6.id,), (self.a7.id,)],
269            transform=identity)
270        self.assertQuerysetEqual(
271            Article.objects.values_list('id', flat=True).order_by('id'),
272            [self.a1.id, self.a2.id, self.a3.id, self.a4.id, self.a5.id, self.a6.id, self.a7.id],
273            transform=identity)
274        self.assertQuerysetEqual(
275            Article.objects.extra(select={'id_plus_one': 'id+1'})
276                           .order_by('id').values_list('id'),
277            [(self.a1.id,), (self.a2.id,), (self.a3.id,), (self.a4.id,), (self.a5.id,), (self.a6.id,), (self.a7.id,)],
278            transform=identity)
279        self.assertQuerysetEqual(
280            Article.objects.extra(select={'id_plus_one': 'id+1'})
281                           .order_by('id').values_list('id_plus_one', 'id'),
282            [
283                (self.a1.id+1, self.a1.id),
284                (self.a2.id+1, self.a2.id),
285                (self.a3.id+1, self.a3.id),
286                (self.a4.id+1, self.a4.id),
287                (self.a5.id+1, self.a5.id),
288                (self.a6.id+1, self.a6.id),
289                (self.a7.id+1, self.a7.id)
290            ],
291            transform=identity)
292        self.assertQuerysetEqual(
293            Article.objects.extra(select={'id_plus_one': 'id+1'})
294                           .order_by('id').values_list('id', 'id_plus_one'),
295            [
296                (self.a1.id, self.a1.id+1),
297                (self.a2.id, self.a2.id+1),
298                (self.a3.id, self.a3.id+1),
299                (self.a4.id, self.a4.id+1),
300                (self.a5.id, self.a5.id+1),
301                (self.a6.id, self.a6.id+1),
302                (self.a7.id, self.a7.id+1)
303            ],
304            transform=identity)
305        self.assertQuerysetEqual(
306            Author.objects.values_list('name', 'article__headline', 'article__tag__name').order_by('name', 'article__headline', 'article__tag__name'),
307            [
308                (self.au1.name, self.a1.headline, self.t1.name),
309                (self.au1.name, self.a2.headline, self.t1.name),
310                (self.au1.name, self.a3.headline, self.t1.name),
311                (self.au1.name, self.a3.headline, self.t2.name),
312                (self.au1.name, self.a4.headline, self.t2.name),
313                (self.au2.name, self.a5.headline, self.t2.name),
314                (self.au2.name, self.a5.headline, self.t3.name),
315                (self.au2.name, self.a6.headline, self.t3.name),
316                (self.au2.name, self.a7.headline, self.t3.name),
317            ], transform=identity)
318        self.assertRaises(TypeError, Article.objects.values_list, 'id', 'headline', flat=True)
319
320    def test_get_next_previous_by(self):
321        # Every DateField and DateTimeField creates get_next_by_FOO() and
322        # get_previous_by_FOO() methods. In the case of identical date values,
323        # these methods will use the ID as a fallback check. This guarantees
324        # that no records are skipped or duplicated.
325        self.assertEqual(repr(self.a1.get_next_by_pub_date()),
326                         '<Article: Article 2>')
327        self.assertEqual(repr(self.a2.get_next_by_pub_date()),
328                         '<Article: Article 3>')
329        self.assertEqual(repr(self.a2.get_next_by_pub_date(headline__endswith='6')),
330                         '<Article: Article 6>')
331        self.assertEqual(repr(self.a3.get_next_by_pub_date()),
332                         '<Article: Article 7>')
333        self.assertEqual(repr(self.a4.get_next_by_pub_date()),
334                         '<Article: Article 6>')
335        self.assertRaises(Article.DoesNotExist, self.a5.get_next_by_pub_date)
336        self.assertEqual(repr(self.a6.get_next_by_pub_date()),
337                         '<Article: Article 5>')
338        self.assertEqual(repr(self.a7.get_next_by_pub_date()),
339                         '<Article: Article 4>')
340
341        self.assertEqual(repr(self.a7.get_previous_by_pub_date()),
342                         '<Article: Article 3>')
343        self.assertEqual(repr(self.a6.get_previous_by_pub_date()),
344                         '<Article: Article 4>')
345        self.assertEqual(repr(self.a5.get_previous_by_pub_date()),
346                         '<Article: Article 6>')
347        self.assertEqual(repr(self.a4.get_previous_by_pub_date()),
348                         '<Article: Article 7>')
349        self.assertEqual(repr(self.a3.get_previous_by_pub_date()),
350                         '<Article: Article 2>')
351        self.assertEqual(repr(self.a2.get_previous_by_pub_date()),
352                         '<Article: Article 1>')
353
354    def test_escaping(self):
355        # Underscores, percent signs and backslashes have special meaning in the
356        # underlying SQL code, but Django handles the quoting of them automatically.
357        a8 = Article(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20))
358        a8.save()
359        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'),
360            [
361                '<Article: Article_ with underscore>',
362                '<Article: Article 5>',
363                '<Article: Article 6>',
364                '<Article: Article 4>',
365                '<Article: Article 2>',
366                '<Article: Article 3>',
367                '<Article: Article 7>',
368                '<Article: Article 1>',
369            ])
370        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article_'),
371                                 ['<Article: Article_ with underscore>'])
372        a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21))
373        a9.save()
374        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article'),
375            [
376                '<Article: Article% with percent sign>',
377                '<Article: Article_ with underscore>',
378                '<Article: Article 5>',
379                '<Article: Article 6>',
380                '<Article: Article 4>',
381                '<Article: Article 2>',
382                '<Article: Article 3>',
383                '<Article: Article 7>',
384                '<Article: Article 1>',
385            ])
386        self.assertQuerysetEqual(Article.objects.filter(headline__startswith='Article%'),
387                                 ['<Article: Article% with percent sign>'])
388        a10 = Article(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22))
389        a10.save()
390        self.assertQuerysetEqual(Article.objects.filter(headline__contains='\\'),
391                                 ['<Article: Article with \ backslash>'])
392
393    def test_exclude(self):
394        a8 = Article.objects.create(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20))
395        a9 = Article.objects.create(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21))
396        a10 = Article.objects.create(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22))
397
398        # exclude() is the opposite of filter() when doing lookups:
399        self.assertQuerysetEqual(
400            Article.objects.filter(headline__contains='Article').exclude(headline__contains='with'),
401            [
402                '<Article: Article 5>',
403                '<Article: Article 6>',
404                '<Article: Article 4>',
405                '<Article: Article 2>',
406                '<Article: Article 3>',
407                '<Article: Article 7>',
408                '<Article: Article 1>',
409            ])
410        self.assertQuerysetEqual(Article.objects.exclude(headline__startswith="Article_"),
411            [
412                '<Article: Article with \\ backslash>',
413                '<Article: Article% with percent sign>',
414                '<Article: Article 5>',
415                '<Article: Article 6>',
416                '<Article: Article 4>',
417                '<Article: Article 2>',
418                '<Article: Article 3>',
419                '<Article: Article 7>',
420                '<Article: Article 1>',
421            ])
422        self.assertQuerysetEqual(Article.objects.exclude(headline="Article 7"),
423            [
424                '<Article: Article with \\ backslash>',
425                '<Article: Article% with percent sign>',
426                '<Article: Article_ with underscore>',
427                '<Article: Article 5>',
428                '<Article: Article 6>',
429                '<Article: Article 4>',
430                '<Article: Article 2>',
431                '<Article: Article 3>',
432                '<Article: Article 1>',
433            ])
434
435    def test_none(self):
436       # none() returns an EmptyQuerySet that behaves like any other QuerySet object
437        self.assertQuerysetEqual(Article.objects.none(), [])
438        self.assertQuerysetEqual(
439            Article.objects.none().filter(headline__startswith='Article'), [])
440        self.assertQuerysetEqual(
441            Article.objects.filter(headline__startswith='Article').none(), [])
442        self.assertEqual(Article.objects.none().count(), 0)
443        self.assertEqual(
444            Article.objects.none().update(headline="This should not take effect"), 0)
445        self.assertQuerysetEqual(
446            [article for article in Article.objects.none().iterator()],
447            [])
448
449    def test_in(self):
450        # using __in with an empty list should return an empty query set
451        self.assertQuerysetEqual(Article.objects.filter(id__in=[]), [])
452        self.assertQuerysetEqual(Article.objects.exclude(id__in=[]),
453            [
454                '<Article: Article 5>',
455                '<Article: Article 6>',
456                '<Article: Article 4>',
457                '<Article: Article 2>',
458                '<Article: Article 3>',
459                '<Article: Article 7>',
460                '<Article: Article 1>',
461            ])
462
463    def test_error_messages(self):
464        # Programming errors are pointed out with nice error messages
465        try:
466            Article.objects.filter(pub_date_year='2005').count()
467            self.fail('FieldError not raised')
468        except FieldError, ex:
469            self.assertEqual(str(ex), "Cannot resolve keyword 'pub_date_year' "
470                             "into field. Choices are: author, headline, id, pub_date, tag")
471        try:
472            Article.objects.filter(headline__starts='Article')
473            self.fail('FieldError not raised')
474        except FieldError, ex:
475            self.assertEqual(str(ex), "Join on field 'headline' not permitted. "
476                             "Did you misspell 'starts' for the lookup type?")
477
478    def test_regex(self):
479        # Create some articles with a bit more interesting headlines for testing field lookups:
480        for a in Article.objects.all():
481            a.delete()
482        now = datetime.now()
483        a1 = Article(pub_date=now, headline='f')
484        a1.save()
485        a2 = Article(pub_date=now, headline='fo')
486        a2.save()
487        a3 = Article(pub_date=now, headline='foo')
488        a3.save()
489        a4 = Article(pub_date=now, headline='fooo')
490        a4.save()
491        a5 = Article(pub_date=now, headline='hey-Foo')
492        a5.save()
493        a6 = Article(pub_date=now, headline='bar')
494        a6.save()
495        a7 = Article(pub_date=now, headline='AbBa')
496        a7.save()
497        a8 = Article(pub_date=now, headline='baz')
498        a8.save()
499        a9 = Article(pub_date=now, headline='baxZ')
500        a9.save()
501        # zero-or-more
502        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo*'),
503            ['<Article: f>', '<Article: fo>', '<Article: foo>', '<Article: fooo>'])
504        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'fo*'),
505            [
506                '<Article: f>',
507                '<Article: fo>',
508                '<Article: foo>',
509                '<Article: fooo>',
510                '<Article: hey-Foo>',
511            ])
512        # one-or-more
513        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fo+'),
514            ['<Article: fo>', '<Article: foo>', '<Article: fooo>'])
515        # wildcard
516        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'fooo?'),
517            ['<Article: foo>', '<Article: fooo>'])
518        # leading anchor
519        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^b'),
520            ['<Article: bar>', '<Article: baxZ>', '<Article: baz>'])
521        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'^a'),
522            ['<Article: AbBa>'])
523        # trailing anchor
524        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'z$'),
525            ['<Article: baz>'])
526        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'z$'),
527            ['<Article: baxZ>', '<Article: baz>'])
528        # character sets
529        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba[rz]'),
530            ['<Article: bar>', '<Article: baz>'])
531        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'ba.[RxZ]'),
532            ['<Article: baxZ>'])
533        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'ba[RxZ]'),
534            ['<Article: bar>', '<Article: baxZ>', '<Article: baz>'])
535
536        # and more articles:
537        a10 = Article(pub_date=now, headline='foobar')
538        a10.save()
539        a11 = Article(pub_date=now, headline='foobaz')
540        a11.save()
541        a12 = Article(pub_date=now, headline='ooF')
542        a12.save()
543        a13 = Article(pub_date=now, headline='foobarbaz')
544        a13.save()
545        a14 = Article(pub_date=now, headline='zoocarfaz')
546        a14.save()
547        a15 = Article(pub_date=now, headline='barfoobaz')
548        a15.save()
549        a16 = Article(pub_date=now, headline='bazbaRFOO')
550        a16.save()
551
552        # alternation
553        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'oo(f|b)'),
554            [
555                '<Article: barfoobaz>',
556                '<Article: foobar>',
557                '<Article: foobarbaz>',
558                '<Article: foobaz>',
559            ])
560        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'oo(f|b)'),
561            [
562                '<Article: barfoobaz>',
563                '<Article: foobar>',
564                '<Article: foobarbaz>',
565                '<Article: foobaz>',
566                '<Article: ooF>',
567            ])
568        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'^foo(f|b)'),
569            ['<Article: foobar>', '<Article: foobarbaz>', '<Article: foobaz>'])
570
571        # greedy matching
572        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b.*az'),
573            [
574                '<Article: barfoobaz>',
575                '<Article: baz>',
576                '<Article: bazbaRFOO>',
577                '<Article: foobarbaz>',
578                '<Article: foobaz>',
579            ])
580        self.assertQuerysetEqual(Article.objects.filter(headline__iregex=r'b.*ar'),
581            [
582                '<Article: bar>',
583                '<Article: barfoobaz>',
584                '<Article: bazbaRFOO>',
585                '<Article: foobar>',
586                '<Article: foobarbaz>',
587            ])
588
589    @skipUnlessDBFeature('supports_regex_backreferencing')
590    def test_regex_backreferencing(self):
591        # grouping and backreferences
592        now = datetime.now()
593        a10 = Article(pub_date=now, headline='foobar')
594        a10.save()
595        a11 = Article(pub_date=now, headline='foobaz')
596        a11.save()
597        a12 = Article(pub_date=now, headline='ooF')
598        a12.save()
599        a13 = Article(pub_date=now, headline='foobarbaz')
600        a13.save()
601        a14 = Article(pub_date=now, headline='zoocarfaz')
602        a14.save()
603        a15 = Article(pub_date=now, headline='barfoobaz')
604        a15.save()
605        a16 = Article(pub_date=now, headline='bazbaRFOO')
606        a16.save()
607        self.assertQuerysetEqual(Article.objects.filter(headline__regex=r'b(.).*b\1'),
608            ['<Article: barfoobaz>', '<Article: bazbaRFOO>', '<Article: foobarbaz>'])