""" 7. The lookup API This demonstrates features of the database API. """ from django.db import models from django.conf import settings class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pub_date', 'headline') def __unicode__(self): return self.headline __test__ = {'API_TESTS':r""" # Create a couple of Articles. >>> from datetime import datetime >>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26)) >>> a1.save() >>> a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27)) >>> a2.save() >>> a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27)) >>> a3.save() >>> a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28)) >>> a4.save() >>> a5 = Article(headline='Article 5', pub_date=datetime(2005, 8, 1, 9, 0)) >>> a5.save() >>> a6 = Article(headline='Article 6', pub_date=datetime(2005, 8, 1, 8, 0)) >>> a6.save() >>> a7 = Article(headline='Article 7', pub_date=datetime(2005, 7, 27)) >>> a7.save() # text matching tests for PostgreSQL 8.3 >>> Article.objects.filter(id__iexact='1') [] >>> Article.objects.filter(pub_date__startswith='2005') [, , , , , , ] # Each QuerySet gets iterator(), which is a generator that "lazily" returns # results using database-level iteration. >>> for a in Article.objects.iterator(): ... print a.headline Article 5 Article 6 Article 4 Article 2 Article 3 Article 7 Article 1 # iterator() can be used on any QuerySet. >>> for a in Article.objects.filter(headline__endswith='4').iterator(): ... print a.headline Article 4 # count() returns the number of objects matching search criteria. >>> Article.objects.count() 7L >>> Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).count() 3L >>> Article.objects.filter(headline__startswith='Blah blah').count() 0L # count() should respect sliced query sets. >>> articles = Article.objects.all() >>> articles.count() 7L >>> articles[:4].count() 4 >>> articles[1:100].count() 6L >>> articles[10:100].count() 0 # Date and date/time lookups can also be done with strings. >>> Article.objects.filter(pub_date__exact='2005-07-27 00:00:00').count() 3L # in_bulk() takes a list of IDs and returns a dictionary mapping IDs # to objects. >>> arts = Article.objects.in_bulk([1, 2]) >>> arts[1] >>> arts[2] >>> Article.objects.in_bulk([3]) {3: } >>> Article.objects.in_bulk([1000]) {} >>> Article.objects.in_bulk([]) {} >>> Article.objects.in_bulk('foo') Traceback (most recent call last): ... AssertionError: in_bulk() must be provided with a list of IDs. >>> Article.objects.in_bulk() Traceback (most recent call last): ... TypeError: in_bulk() takes exactly 2 arguments (1 given) >>> Article.objects.in_bulk(headline__startswith='Blah') Traceback (most recent call last): ... TypeError: in_bulk() got an unexpected keyword argument 'headline__startswith' # values() returns a list of dictionaries instead of object instances -- and # you can specify which fields you want to retrieve. >>> Article.objects.values('headline') [{'headline': u'Article 5'}, {'headline': u'Article 6'}, {'headline': u'Article 4'}, {'headline': u'Article 2'}, {'headline': u'Article 3'}, {'headline': u'Article 7'}, {'headline': u'Article 1'}] >>> Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).values('id') [{'id': 2}, {'id': 3}, {'id': 7}] >>> list(Article.objects.values('id', 'headline')) == [{'id': 5, 'headline': 'Article 5'}, {'id': 6, 'headline': 'Article 6'}, {'id': 4, 'headline': 'Article 4'}, {'id': 2, 'headline': 'Article 2'}, {'id': 3, 'headline': 'Article 3'}, {'id': 7, 'headline': 'Article 7'}, {'id': 1, 'headline': 'Article 1'}] True >>> for d in Article.objects.values('id', 'headline'): ... i = d.items() ... i.sort() ... i [('headline', u'Article 5'), ('id', 5)] [('headline', u'Article 6'), ('id', 6)] [('headline', u'Article 4'), ('id', 4)] [('headline', u'Article 2'), ('id', 2)] [('headline', u'Article 3'), ('id', 3)] [('headline', u'Article 7'), ('id', 7)] [('headline', u'Article 1'), ('id', 1)] # You can use values() with iterator() for memory savings, because iterator() # uses database-level iteration. >>> for d in Article.objects.values('id', 'headline').iterator(): ... i = d.items() ... i.sort() ... i [('headline', u'Article 5'), ('id', 5)] [('headline', u'Article 6'), ('id', 6)] [('headline', u'Article 4'), ('id', 4)] [('headline', u'Article 2'), ('id', 2)] [('headline', u'Article 3'), ('id', 3)] [('headline', u'Article 7'), ('id', 7)] [('headline', u'Article 1'), ('id', 1)] # The values() method works with "extra" fields specified in extra(select). >>> for d in Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'): ... i = d.items() ... i.sort() ... i [('id', 5), ('id_plus_one', 6)] [('id', 6), ('id_plus_one', 7)] [('id', 4), ('id_plus_one', 5)] [('id', 2), ('id_plus_one', 3)] [('id', 3), ('id_plus_one', 4)] [('id', 7), ('id_plus_one', 8)] [('id', 1), ('id_plus_one', 2)] >>> data = {'id_plus_one': 'id+1', 'id_plus_two': 'id+2', 'id_plus_three': 'id+3', ... 'id_plus_four': 'id+4', 'id_plus_five': 'id+5', 'id_plus_six': 'id+6', ... 'id_plus_seven': 'id+7', 'id_plus_eight': 'id+8'} >>> result = list(Article.objects.filter(id=1).extra(select=data).values(*data.keys()))[0] >>> result = result.items() >>> result.sort() >>> result [('id_plus_eight', 9), ('id_plus_five', 6), ('id_plus_four', 5), ('id_plus_one', 2), ('id_plus_seven', 8), ('id_plus_six', 7), ('id_plus_three', 4), ('id_plus_two', 3)] # However, an exception FieldDoesNotExist will be thrown if you specify a # non-existent field name in values() (a field that is neither in the model # nor in extra(select)). >>> Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_two') Traceback (most recent call last): ... FieldError: Cannot resolve keyword 'id_plus_two' into field. Choices are: headline, id, id_plus_one, pub_date # If you don't specify field names to values(), all are returned. >>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}] True # values_list() is similar to values(), except that the results are returned as # a list of tuples, rather than a list of dictionaries. Within each tuple, the # order of the elemnts is the same as the order of fields in the values_list() # call. >>> Article.objects.values_list('headline') [(u'Article 5',), (u'Article 6',), (u'Article 4',), (u'Article 2',), (u'Article 3',), (u'Article 7',), (u'Article 1',)] >>> Article.objects.values_list('id').order_by('id') [(1,), (2,), (3,), (4,), (5,), (6,), (7,)] >>> Article.objects.values_list('id', flat=True).order_by('id') [1, 2, 3, 4, 5, 6, 7] >>> Article.objects.extra(select={'id_plus_one': 'id+1'}).order_by('id').values_list('id') [(1,), (2,), (3,), (4,), (5,), (6,), (7,)] >>> Article.objects.extra(select={'id_plus_one': 'id+1'}).order_by('id').values_list('id_plus_one', 'id') [(2, 1), (3, 2), (4, 3), (5, 4), (6, 5), (7, 6), (8, 7)] >>> Article.objects.extra(select={'id_plus_one': 'id+1'}).order_by('id').values_list('id', 'id_plus_one') [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 8)] >>> Article.objects.values_list('id', 'headline', flat=True) Traceback (most recent call last): ... TypeError: 'flat' is not valid when values_list is called with more than one field. # Every DateField and DateTimeField creates get_next_by_FOO() and # get_previous_by_FOO() methods. # In the case of identical date values, these methods will use the ID as a # fallback check. This guarantees that no records are skipped or duplicated. >>> a1.get_next_by_pub_date() >>> a2.get_next_by_pub_date() >>> a2.get_next_by_pub_date(headline__endswith='6') >>> a3.get_next_by_pub_date() >>> a4.get_next_by_pub_date() >>> a5.get_next_by_pub_date() Traceback (most recent call last): ... DoesNotExist: Article matching query does not exist. >>> a6.get_next_by_pub_date() >>> a7.get_next_by_pub_date() >>> a7.get_previous_by_pub_date() >>> a6.get_previous_by_pub_date() >>> a5.get_previous_by_pub_date() >>> a4.get_previous_by_pub_date() >>> a3.get_previous_by_pub_date() >>> a2.get_previous_by_pub_date() # Underscores and percent signs have special meaning in the underlying # SQL code, but Django handles the quoting of them automatically. >>> a8 = Article(headline='Article_ with underscore', pub_date=datetime(2005, 11, 20)) >>> a8.save() >>> Article.objects.filter(headline__startswith='Article') [, , , , , , , ] >>> Article.objects.filter(headline__startswith='Article_') [] >>> a9 = Article(headline='Article% with percent sign', pub_date=datetime(2005, 11, 21)) >>> a9.save() >>> Article.objects.filter(headline__startswith='Article') [, , , , , , , , ] >>> Article.objects.filter(headline__startswith='Article%') [] # exclude() is the opposite of filter() when doing lookups: >>> Article.objects.filter(headline__contains='Article').exclude(headline__contains='with') [, , , , , , ] >>> Article.objects.exclude(headline__startswith="Article_") [, , , , , , , ] >>> Article.objects.exclude(headline="Article 7") [, , , , , , , ] # Backslashes also have special meaning in the underlying SQL code, but Django # automatically quotes them appropriately. >>> a10 = Article(headline='Article with \\ backslash', pub_date=datetime(2005, 11, 22)) >>> a10.save() >>> Article.objects.filter(headline__contains='\\') [] # none() returns an EmptyQuerySet that behaves like any other QuerySet object >>> Article.objects.none() [] >>> Article.objects.none().filter(headline__startswith='Article') [] >>> Article.objects.filter(headline__startswith='Article').none() [] >>> Article.objects.none().count() 0 >>> [article for article in Article.objects.none().iterator()] [] # using __in with an empty list should return an empty query set >>> Article.objects.filter(id__in=[]) [] >>> Article.objects.exclude(id__in=[]) [, , , , , , , , , ] # Programming errors are pointed out with nice error messages >>> Article.objects.filter(pub_date_year='2005').count() Traceback (most recent call last): ... FieldError: Cannot resolve keyword 'pub_date_year' into field. Choices are: headline, id, pub_date >>> Article.objects.filter(headline__starts='Article') Traceback (most recent call last): ... FieldError: Join on field 'headline' not permitted. # Create some articles with a bit more interesting headlines for testing field lookups: >>> now = datetime.now() >>> for a in Article.objects.all(): ... a.delete() >>> a1 = Article(pub_date=now, headline='f') >>> a1.save() >>> a2 = Article(pub_date=now, headline='fo') >>> a2.save() >>> a3 = Article(pub_date=now, headline='foo') >>> a3.save() >>> a4 = Article(pub_date=now, headline='fooo') >>> a4.save() >>> a5 = Article(pub_date=now, headline='hey-Foo') >>> a5.save() # zero-or-more >>> Article.objects.filter(headline__regex=r'fo*') [, , , ] >>> Article.objects.filter(headline__iregex=r'fo*') [, , , , ] # one-or-more >>> Article.objects.filter(headline__regex=r'fo+') [, , ] # wildcard >>> Article.objects.filter(headline__regex=r'fooo?') [, ] # and some more: >>> a6 = Article(pub_date=now, headline='bar') >>> a6.save() >>> a7 = Article(pub_date=now, headline='AbBa') >>> a7.save() >>> a8 = Article(pub_date=now, headline='baz') >>> a8.save() >>> a9 = Article(pub_date=now, headline='baxZ') >>> a9.save() # leading anchor >>> Article.objects.filter(headline__regex=r'^b') [, , ] >>> Article.objects.filter(headline__iregex=r'^a') [] # trailing anchor >>> Article.objects.filter(headline__regex=r'z$') [] >>> Article.objects.filter(headline__iregex=r'z$') [, ] # character sets >>> Article.objects.filter(headline__regex=r'ba[rz]') [, ] >>> Article.objects.filter(headline__regex=r'ba.[RxZ]') [] >>> Article.objects.filter(headline__iregex=r'ba[RxZ]') [, , ] # and yet more: >>> a10 = Article(pub_date=now, headline='foobar') >>> a10.save() >>> a11 = Article(pub_date=now, headline='foobaz') >>> a11.save() >>> a12 = Article(pub_date=now, headline='ooF') >>> a12.save() >>> a13 = Article(pub_date=now, headline='foobarbaz') >>> a13.save() >>> a14 = Article(pub_date=now, headline='zoocarfaz') >>> a14.save() >>> a15 = Article(pub_date=now, headline='barfoobaz') >>> a15.save() >>> a16 = Article(pub_date=now, headline='bazbaRFOO') >>> a16.save() # alternation >>> Article.objects.filter(headline__regex=r'oo(f|b)') [, , , ] >>> Article.objects.filter(headline__iregex=r'oo(f|b)') [, , , , ] >>> Article.objects.filter(headline__regex=r'^foo(f|b)') [, , ] # greedy matching >>> Article.objects.filter(headline__regex=r'b.*az') [, , , , ] >>> Article.objects.filter(headline__iregex=r'b.*ar') [, , , , ] """} if settings.DATABASE_ENGINE != 'mysql': __test__['API_TESTS'] += r""" # grouping and backreferences >>> Article.objects.filter(headline__regex=r'b(.).*b\1') [, , ] """