/tests/modeltests/lookup/tests.py
Python | 608 lines | 557 code | 16 blank | 35 comment | 4 complexity | 4dc5e53f61357d6a185aac9b08ca7207 MD5 | raw file
Possible License(s): BSD-3-Clause
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>'])