1
0
mirror of https://github.com/django/django.git synced 2025-07-05 10:19:20 +00:00

Merged trunk changes r13307:13316 into test-refactor SoC branch.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2010/test-refactor@13317 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Paul McMillan 2010-06-02 23:49:27 +00:00
parent 6c10480e99
commit 8bdf160e05
13 changed files with 865 additions and 859 deletions

View File

@ -218,13 +218,15 @@ class BaseCommand(object):
output = self.handle(*args, **options) output = self.handle(*args, **options)
if output: if output:
if self.output_transaction: if self.output_transaction:
# This needs to be imported here, because it relies on settings. # This needs to be imported here, because it relies on
from django.db import connection # settings.
from django.db import connections, DEFAULT_DB_ALIAS
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
if connection.ops.start_transaction_sql(): if connection.ops.start_transaction_sql():
print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()) print self.style.SQL_KEYWORD(connection.ops.start_transaction_sql())
print output print output
if self.output_transaction: if self.output_transaction:
print self.style.SQL_KEYWORD("COMMIT;") print self.style.SQL_KEYWORD(connection.ops.end_transaction_sql())
except CommandError, e: except CommandError, e:
sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) sys.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e)))
sys.exit(1) sys.exit(1)

View File

@ -353,6 +353,11 @@ class BaseDatabaseOperations(object):
""" """
return "BEGIN;" return "BEGIN;"
def end_transaction_sql(self, success=True):
if not success:
return "ROLLBACK;"
return "COMMIT;"
def tablespace_sql(self, tablespace, inline=False): def tablespace_sql(self, tablespace, inline=False):
""" """
Returns the SQL that will be appended to tables or rows to define Returns the SQL that will be appended to tables or rows to define

View File

@ -177,14 +177,14 @@ class QueryDict(MultiValueDict):
super(QueryDict, self).__delitem__(key) super(QueryDict, self).__delitem__(key)
def __copy__(self): def __copy__(self):
result = self.__class__('', mutable=True) result = self.__class__('', mutable=True, encoding=self.encoding)
for key, value in dict.items(self): for key, value in dict.items(self):
dict.__setitem__(result, key, value) dict.__setitem__(result, key, value)
return result return result
def __deepcopy__(self, memo): def __deepcopy__(self, memo):
import django.utils.copycompat as copy import django.utils.copycompat as copy
result = self.__class__('', mutable=True) result = self.__class__('', mutable=True, encoding=self.encoding)
memo[id(self)] = result memo[id(self)] = result
for key, value in dict.items(self): for key, value in dict.items(self):
dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo)) dict.__setitem__(result, copy.deepcopy(key, memo), copy.deepcopy(value, memo))

View File

@ -466,6 +466,9 @@ class TransactionTestCase(unittest.TestCase):
msg_prefix + "Template '%s' was used unexpectedly in rendering" msg_prefix + "Template '%s' was used unexpectedly in rendering"
" the response" % template_name) " the response" % template_name)
def assertQuerysetEqual(self, qs, values, transform=repr):
return self.assertEqual(map(transform, qs), values)
def connections_support_transactions(): def connections_support_transactions():
""" """
Returns True if all connections support transactions. This is messy Returns True if all connections support transactions. This is messy

View File

@ -1,17 +1,17 @@
""" """
The md5 and sha modules are deprecated since Python 2.5, replaced by the The md5 and sha modules are deprecated since Python 2.5, replaced by the
hashlib module containing both hash algorithms. Here, we provide a common hashlib module containing both hash algorithms. Here, we provide a common
interface to the md5 and sha constructors, preferring the hashlib module when interface to the md5 and sha constructors, depending on system version.
available.
""" """
try: import sys
if sys.version_info >= (2, 5):
import hashlib import hashlib
md5_constructor = hashlib.md5 md5_constructor = hashlib.md5
md5_hmac = md5_constructor md5_hmac = md5_constructor
sha_constructor = hashlib.sha1 sha_constructor = hashlib.sha1
sha_hmac = sha_constructor sha_hmac = sha_constructor
except ImportError: else:
import md5 import md5
md5_constructor = md5.new md5_constructor = md5.new
md5_hmac = md5 md5_hmac = md5

View File

@ -12,7 +12,7 @@ from django.utils.importlib import import_module
from django.utils.encoding import smart_unicode, smart_str from django.utils.encoding import smart_unicode, smart_str
HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST|SIGNATURE')
def linebreak_iter(template_source): def linebreak_iter(template_source):
yield 0 yield 0

View File

@ -1189,7 +1189,7 @@ your admin page for managing the relation.
In all other respects, the ``InlineModelAdmin`` is exactly the same as any In all other respects, the ``InlineModelAdmin`` is exactly the same as any
other. You can customize the appearance using any of the normal other. You can customize the appearance using any of the normal
``InlineModelAdmin`` properties. ``ModelAdmin`` properties.
Working with Many-to-Many Intermediary Models Working with Many-to-Many Intermediary Models
---------------------------------------------- ----------------------------------------------

View File

@ -494,8 +494,9 @@ A boolean that turns on/off debug mode.
If you define custom settings, `django/views/debug.py`_ has a ``HIDDEN_SETTINGS`` If you define custom settings, `django/views/debug.py`_ has a ``HIDDEN_SETTINGS``
regular expression which will hide from the DEBUG view anything that contains regular expression which will hide from the DEBUG view anything that contains
``'SECRET'``, ``'PASSWORD'``, or ``'PROFANITIES'``. This allows untrusted users to ``'SECRET'``, ``'PASSWORD'``, ``'PROFANITIES'``, or ``'SIGNATURE'``. This allows
be able to give backtraces without seeing sensitive (or offensive) settings. untrusted users to be able to give backtraces without seeing sensitive (or
offensive) settings.
Still, note that there are always going to be sections of your debug output that Still, note that there are always going to be sections of your debug output that
are inappropriate for public consumption. File paths, configuration options, and are inappropriate for public consumption. File paths, configuration options, and
@ -615,7 +616,7 @@ EMAIL_BACKEND
.. versionadded:: 1.2 .. versionadded:: 1.2
Default: ``'django.core.mail.backends.smtp'`` Default: ``'django.core.mail.backends.smtp.EmailBackend'``
The backend to use for sending emails. For the list of available backends see The backend to use for sending emails. For the list of available backends see
:ref:`topics-email`. :ref:`topics-email`.

View File

@ -1248,6 +1248,19 @@ cause of an failure in your test suite.
``target_status_code`` will be the url and status code for the final ``target_status_code`` will be the url and status code for the final
point of the redirect chain. point of the redirect chain.
.. method:: TestCase.assertQuerysetEqual(response, qs, values, transform=repr)
Asserts that a queryset ``qs`` returns a particular list of values ``values``.
The comparison of the contents of ``qs`` and ``values`` is performed using
the function ``transform``; by default, this means that the ``repr()`` of
each value is compared. Any other callable can be used if ``repr()`` doesn't
provide a unique or helpful comparison.
The comparison is also ordering dependent. If ``qs`` doesn't provide an
implicit ordering, you will need to apply a ``order_by()`` clause to your
queryset to ensure that the test will pass reliably.
.. _topics-testing-email: .. _topics-testing-email:
E-mail services E-mail services

View File

@ -1,6 +1,7 @@
# coding: utf-8 # coding: utf-8
from django.db import models from django.db import models
class Author(models.Model): class Author(models.Model):
name = models.CharField(max_length=100) name = models.CharField(max_length=100)
age = models.IntegerField() age = models.IntegerField()
@ -39,323 +40,3 @@ class Store(models.Model):
def __unicode__(self): def __unicode__(self):
return self.name return self.name
# Tests on 'aggregate'
# Different backends and numbers.
__test__ = {'API_TESTS': """
>>> from django.core import management
>>> from decimal import Decimal
>>> from datetime import date
# Reset the database representation of this app.
# This will return the database to a clean initial state.
>>> management.call_command('flush', verbosity=0, interactive=False)
# Empty Call - request nothing, get nothing.
>>> Author.objects.all().aggregate()
{}
>>> from django.db.models import Avg, Sum, Count, Max, Min
# Single model aggregation
#
# Single aggregate
# Average age of Authors
>>> Author.objects.all().aggregate(Avg('age'))
{'age__avg': 37.4...}
# Multiple aggregates
# Average and Sum of Author ages
>>> Author.objects.all().aggregate(Sum('age'), Avg('age'))
{'age__sum': 337, 'age__avg': 37.4...}
# Aggreates interact with filters, and only
# generate aggregate values for the filtered values
# Sum of the age of those older than 29 years old
>>> Author.objects.all().filter(age__gt=29).aggregate(Sum('age'))
{'age__sum': 254}
# Depth-1 Joins
#
# On Relationships with self
# Average age of the friends of each author
>>> Author.objects.all().aggregate(Avg('friends__age'))
{'friends__age__avg': 34.07...}
# On ManyToMany Relationships
#
# Forward
# Average age of the Authors of Books with a rating of less than 4.5
>>> Book.objects.all().filter(rating__lt=4.5).aggregate(Avg('authors__age'))
{'authors__age__avg': 38.2...}
# Backward
# Average rating of the Books whose Author's name contains the letter 'a'
>>> Author.objects.all().filter(name__contains='a').aggregate(Avg('book__rating'))
{'book__rating__avg': 4.0}
# On OneToMany Relationships
#
# Forward
# Sum of the number of awards of each Book's Publisher
>>> Book.objects.all().aggregate(Sum('publisher__num_awards'))
{'publisher__num_awards__sum': 30}
# Backward
# Sum of the price of every Book that has a Publisher
>>> Publisher.objects.all().aggregate(Sum('book__price'))
{'book__price__sum': Decimal("270.27")}
# Multiple Joins
#
# Forward
>>> Store.objects.all().aggregate(Max('books__authors__age'))
{'books__authors__age__max': 57}
# Backward
# Note that the very long default alias may be truncated
>>> Author.objects.all().aggregate(Min('book__publisher__num_awards'))
{'book__publisher__num_award...': 1}
# Aggregate outputs can also be aliased.
# Average amazon.com Book rating
>>> Store.objects.filter(name='Amazon.com').aggregate(amazon_mean=Avg('books__rating'))
{'amazon_mean': 4.08...}
# Tests on annotate()
# An empty annotate call does nothing but return the same QuerySet
>>> Book.objects.all().annotate().order_by('pk')
[<Book: The Definitive Guide to Django: Web Development Done Right>, <Book: Sams Teach Yourself Django in 24 Hours>, <Book: Practical Django Projects>, <Book: Python Web Development with Django>, <Book: Artificial Intelligence: A Modern Approach>, <Book: Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp>]
# Annotate inserts the alias into the model object with the aggregated result
>>> books = Book.objects.all().annotate(mean_age=Avg('authors__age'))
>>> books.get(pk=1).name
u'The Definitive Guide to Django: Web Development Done Right'
>>> books.get(pk=1).mean_age
34.5
# On ManyToMany Relationships
# Forward
# Average age of the Authors of each book with a rating less than 4.5
>>> books = Book.objects.all().filter(rating__lt=4.5).annotate(Avg('authors__age'))
>>> sorted([(b.name, b.authors__age__avg) for b in books])
[(u'Artificial Intelligence: A Modern Approach', 51.5), (u'Practical Django Projects', 29.0), (u'Python Web Development with Django', 30.3...), (u'Sams Teach Yourself Django in 24 Hours', 45.0)]
# Count the number of authors of each book
>>> books = Book.objects.annotate(num_authors=Count('authors'))
>>> sorted([(b.name, b.num_authors) for b in books])
[(u'Artificial Intelligence: A Modern Approach', 2), (u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1), (u'Practical Django Projects', 1), (u'Python Web Development with Django', 3), (u'Sams Teach Yourself Django in 24 Hours', 1), (u'The Definitive Guide to Django: Web Development Done Right', 2)]
# Backward
# Average rating of the Books whose Author's names contains the letter 'a'
>>> authors = Author.objects.all().filter(name__contains='a').annotate(Avg('book__rating'))
>>> sorted([(a.name, a.book__rating__avg) for a in authors])
[(u'Adrian Holovaty', 4.5), (u'Brad Dayley', 3.0), (u'Jacob Kaplan-Moss', 4.5), (u'James Bennett', 4.0), (u'Paul Bissex', 4.0), (u'Stuart Russell', 4.0)]
# Count the number of books written by each author
>>> authors = Author.objects.annotate(num_books=Count('book'))
>>> sorted([(a.name, a.num_books) for a in authors])
[(u'Adrian Holovaty', 1), (u'Brad Dayley', 1), (u'Jacob Kaplan-Moss', 1), (u'James Bennett', 1), (u'Jeffrey Forcier', 1), (u'Paul Bissex', 1), (u'Peter Norvig', 2), (u'Stuart Russell', 1), (u'Wesley J. Chun', 1)]
# On OneToMany Relationships
# Forward
# Annotate each book with the number of awards of each Book's Publisher
>>> books = Book.objects.all().annotate(Sum('publisher__num_awards'))
>>> sorted([(b.name, b.publisher__num_awards__sum) for b in books])
[(u'Artificial Intelligence: A Modern Approach', 7), (u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 9), (u'Practical Django Projects', 3), (u'Python Web Development with Django', 7), (u'Sams Teach Yourself Django in 24 Hours', 1), (u'The Definitive Guide to Django: Web Development Done Right', 3)]
# Backward
# Annotate each publisher with the sum of the price of all books sold
>>> publishers = Publisher.objects.all().annotate(Sum('book__price'))
>>> sorted([(p.name, p.book__price__sum) for p in publishers])
[(u'Apress', Decimal("59.69")), (u"Jonno's House of Books", None), (u'Morgan Kaufmann', Decimal("75.00")), (u'Prentice Hall', Decimal("112.49")), (u'Sams', Decimal("23.09"))]
# Calls to values() are not commutative over annotate().
# Calling values on a queryset that has annotations returns the output
# as a dictionary
>>> [sorted(o.iteritems()) for o in Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values()]
[[('contact_id', 1), ('id', 1), ('isbn', u'159059725'), ('mean_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right'), ('pages', 447), ('price', Decimal("30...")), ('pubdate', datetime.date(2007, 12, 6)), ('publisher_id', 1), ('rating', 4.5)]]
>>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('pk', 'isbn', 'mean_age')
[{'pk': 1, 'isbn': u'159059725', 'mean_age': 34.5}]
# Calling values() with parameters reduces the output
>>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('name')
[{'name': u'The Definitive Guide to Django: Web Development Done Right'}]
# An empty values() call before annotating has the same effect as an
# empty values() call after annotating
>>> [sorted(o.iteritems()) for o in Book.objects.filter(pk=1).values().annotate(mean_age=Avg('authors__age'))]
[[('contact_id', 1), ('id', 1), ('isbn', u'159059725'), ('mean_age', 34.5), ('name', u'The Definitive Guide to Django: Web Development Done Right'), ('pages', 447), ('price', Decimal("30...")), ('pubdate', datetime.date(2007, 12, 6)), ('publisher_id', 1), ('rating', 4.5)]]
# Calling annotate() on a ValuesQuerySet annotates over the groups of
# fields to be selected by the ValuesQuerySet.
# Note that an extra parameter is added to each dictionary. This
# parameter is a queryset representing the objects that have been
# grouped to generate the annotation
>>> Book.objects.all().values('rating').annotate(n_authors=Count('authors__id'), mean_age=Avg('authors__age')).order_by('rating')
[{'rating': 3.0, 'n_authors': 1, 'mean_age': 45.0}, {'rating': 4.0, 'n_authors': 6, 'mean_age': 37.1...}, {'rating': 4.5, 'n_authors': 2, 'mean_age': 34.5}, {'rating': 5.0, 'n_authors': 1, 'mean_age': 57.0}]
# If a join doesn't match any objects, an aggregate returns None
>>> authors = Author.objects.all().annotate(Avg('friends__age')).order_by('id')
>>> len(authors)
9
>>> sorted([(a.name, a.friends__age__avg) for a in authors])
[(u'Adrian Holovaty', 32.0), (u'Brad Dayley', None), (u'Jacob Kaplan-Moss', 29.5), (u'James Bennett', 34.0), (u'Jeffrey Forcier', 27.0), (u'Paul Bissex', 31.0), (u'Peter Norvig', 46.0), (u'Stuart Russell', 57.0), (u'Wesley J. Chun', 33.6...)]
# The Count aggregation function allows an extra parameter: distinct.
# This restricts the count results to unique items
>>> Book.objects.all().aggregate(Count('rating'))
{'rating__count': 6}
>>> Book.objects.all().aggregate(Count('rating', distinct=True))
{'rating__count': 4}
# Retreiving the grouped objects
# When using Count you can also omit the primary key and refer only to
# the related field name if you want to count all the related objects
# and not a specific column
>>> explicit = list(Author.objects.annotate(Count('book__id')))
>>> implicit = list(Author.objects.annotate(Count('book')))
>>> explicit == implicit
True
# Ordering is allowed on aggregates
>>> Book.objects.values('rating').annotate(oldest=Max('authors__age')).order_by('oldest', 'rating')
[{'rating': 4.5, 'oldest': 35}, {'rating': 3.0, 'oldest': 45}, {'rating': 4.0, 'oldest': 57}, {'rating': 5.0, 'oldest': 57}]
>>> Book.objects.values('rating').annotate(oldest=Max('authors__age')).order_by('-oldest', '-rating')
[{'rating': 5.0, 'oldest': 57}, {'rating': 4.0, 'oldest': 57}, {'rating': 3.0, 'oldest': 45}, {'rating': 4.5, 'oldest': 35}]
# It is possible to aggregate over anotated values
>>> Book.objects.all().annotate(num_authors=Count('authors__id')).aggregate(Avg('num_authors'))
{'num_authors__avg': 1.66...}
# You can filter the results based on the aggregation alias.
# Lets add a publisher to test the different possibilities for filtering
>>> p = Publisher(name='Expensive Publisher', num_awards=0)
>>> p.save()
>>> Book(name='ExpensiveBook1', pages=1, isbn='111', rating=3.5, price=Decimal("1000"), publisher=p, contact_id=1, pubdate=date(2008,12,1)).save()
>>> Book(name='ExpensiveBook2', pages=1, isbn='222', rating=4.0, price=Decimal("1000"), publisher=p, contact_id=1, pubdate=date(2008,12,2)).save()
>>> Book(name='ExpensiveBook3', pages=1, isbn='333', rating=4.5, price=Decimal("35"), publisher=p, contact_id=1, pubdate=date(2008,12,3)).save()
# Publishers that have:
# (i) more than one book
>>> Publisher.objects.annotate(num_books=Count('book__id')).filter(num_books__gt=1).order_by('pk')
[<Publisher: Apress>, <Publisher: Prentice Hall>, <Publisher: Expensive Publisher>]
# (ii) a book that cost less than 40
>>> Publisher.objects.filter(book__price__lt=Decimal("40.0")).order_by('pk')
[<Publisher: Apress>, <Publisher: Apress>, <Publisher: Sams>, <Publisher: Prentice Hall>, <Publisher: Expensive Publisher>]
# (iii) more than one book and (at least) a book that cost less than 40
>>> Publisher.objects.annotate(num_books=Count('book__id')).filter(num_books__gt=1, book__price__lt=Decimal("40.0")).order_by('pk')
[<Publisher: Apress>, <Publisher: Prentice Hall>, <Publisher: Expensive Publisher>]
# (iv) more than one book that costs less than $40
>>> Publisher.objects.filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count('book__id')).filter(num_books__gt=1).order_by('pk')
[<Publisher: Apress>]
# Now a bit of testing on the different lookup types
#
>>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__range=[1, 3]).order_by('pk')
[<Publisher: Apress>, <Publisher: Sams>, <Publisher: Prentice Hall>, <Publisher: Morgan Kaufmann>, <Publisher: Expensive Publisher>]
>>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__range=[1, 2]).order_by('pk')
[<Publisher: Apress>, <Publisher: Sams>, <Publisher: Prentice Hall>, <Publisher: Morgan Kaufmann>]
>>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__in=[1, 3]).order_by('pk')
[<Publisher: Sams>, <Publisher: Morgan Kaufmann>, <Publisher: Expensive Publisher>]
>>> Publisher.objects.annotate(num_books=Count('book')).filter(num_books__isnull=True)
[]
>>> p.delete()
# Does Author X have any friends? (or better, how many friends does author X have)
>> Author.objects.filter(pk=1).aggregate(Count('friends__id'))
{'friends__id__count': 2.0}
# Give me a list of all Books with more than 1 authors
>>> Book.objects.all().annotate(num_authors=Count('authors__name')).filter(num_authors__ge=2).order_by('pk')
[<Book: The Definitive Guide to Django: Web Development Done Right>, <Book: Artificial Intelligence: A Modern Approach>]
# Give me a list of all Authors that have no friends
>>> Author.objects.all().annotate(num_friends=Count('friends__id', distinct=True)).filter(num_friends=0).order_by('pk')
[<Author: Brad Dayley>]
# Give me a list of all publishers that have published more than 1 books
>>> Publisher.objects.all().annotate(num_books=Count('book__id')).filter(num_books__gt=1).order_by('pk')
[<Publisher: Apress>, <Publisher: Prentice Hall>]
# Give me a list of all publishers that have published more than 1 books that cost less than 40
>>> Publisher.objects.all().filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count('book__id')).filter(num_books__gt=1)
[<Publisher: Apress>]
# Give me a list of all Books that were written by X and one other author.
>>> Book.objects.all().annotate(num_authors=Count('authors__id')).filter(authors__name__contains='Norvig', num_authors__gt=1)
[<Book: Artificial Intelligence: A Modern Approach>]
# Give me the average rating of all Books that were written by X and one other author.
#(Aggregate over objects discovered using membership of the m2m set)
# Adding an existing author to another book to test it the right way
>>> a = Author.objects.get(name__contains='Norvig')
>>> b = Book.objects.get(name__contains='Done Right')
>>> b.authors.add(a)
>>> b.save()
# This should do it
>>> Book.objects.all().annotate(num_authors=Count('authors__id')).filter(authors__name__contains='Norvig', num_authors__gt=1).aggregate(Avg('rating'))
{'rating__avg': 4.25}
>>> b.authors.remove(a)
# Give me a list of all Authors that have published a book with at least one other person
# (Filters over a count generated on a related object)
#
# Cheating: [a for a in Author.objects.all().annotate(num_coleagues=Count('book__authors__id'), num_books=Count('book__id', distinct=True)) if a.num_coleagues - a.num_books > 0]
# F-Syntax is required. Will be fixed after F objects are available
# Aggregates also work on dates, times and datetimes
>>> Publisher.objects.annotate(earliest_book=Min('book__pubdate')).exclude(earliest_book=None).order_by('earliest_book').values()
[{'earliest_book': datetime.date(1991, 10, 15), 'num_awards': 9, 'id': 4, 'name': u'Morgan Kaufmann'}, {'earliest_book': datetime.date(1995, 1, 15), 'num_awards': 7, 'id': 3, 'name': u'Prentice Hall'}, {'earliest_book': datetime.date(2007, 12, 6), 'num_awards': 3, 'id': 1, 'name': u'Apress'}, {'earliest_book': datetime.date(2008, 3, 3), 'num_awards': 1, 'id': 2, 'name': u'Sams'}]
>>> Store.objects.aggregate(Max('friday_night_closing'), Min("original_opening"))
{'friday_night_closing__max': datetime.time(23, 59, 59), 'original_opening__min': datetime.datetime(1945, 4, 25, 16, 24, 14)}
# values_list() can also be used
>>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('pk', 'isbn', 'mean_age')
[(1, u'159059725', 34.5)]
>>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('isbn')
[(u'159059725',)]
>>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('mean_age')
[(34.5,)]
>>> Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values_list('mean_age', flat=True)
[34.5]
>>> qs = Book.objects.values_list('price').annotate(count=Count('price')).order_by('-count', 'price')
>>> list(qs) == [(Decimal('29.69'), 2), (Decimal('23.09'), 1), (Decimal('30'), 1), (Decimal('75'), 1), (Decimal('82.8'), 1)]
True
"""}

View File

@ -0,0 +1,578 @@
import datetime
from decimal import Decimal
from django.db.models import Avg, Sum, Count, Max, Min
from django.test import TestCase
from models import Author, Publisher, Book, Store
class Approximate(object):
def __init__(self, val, places=7):
self.val = val
self.places = places
def __repr__(self):
return repr(self.val)
def __eq__(self, other):
if self.val == other:
return True
return round(abs(self.val-other), self.places) == 0
class BaseAggregateTestCase(TestCase):
fixtures = ["initial_data.json"]
def test_empty_aggregate(self):
self.assertEqual(Author.objects.all().aggregate(), {})
def test_single_aggregate(self):
vals = Author.objects.aggregate(Avg("age"))
self.assertEqual(vals, {"age__avg": Approximate(37.4, places=1)})
def test_multiple_aggregates(self):
vals = Author.objects.aggregate(Sum("age"), Avg("age"))
self.assertEqual(vals, {"age__sum": 337, "age__avg": Approximate(37.4, places=1)})
def test_filter_aggregate(self):
vals = Author.objects.filter(age__gt=29).aggregate(Sum("age"))
self.assertEqual(len(vals), 1)
self.assertEqual(vals["age__sum"], 254)
def test_related_aggregate(self):
vals = Author.objects.aggregate(Avg("friends__age"))
self.assertEqual(len(vals), 1)
self.assertAlmostEqual(vals["friends__age__avg"], 34.07, places=2)
vals = Book.objects.filter(rating__lt=4.5).aggregate(Avg("authors__age"))
self.assertEqual(len(vals), 1)
self.assertAlmostEqual(vals["authors__age__avg"], 38.2857, places=2)
vals = Author.objects.all().filter(name__contains="a").aggregate(Avg("book__rating"))
self.assertEqual(len(vals), 1)
self.assertEqual(vals["book__rating__avg"], 4.0)
vals = Book.objects.aggregate(Sum("publisher__num_awards"))
self.assertEqual(len(vals), 1)
self.assertEquals(vals["publisher__num_awards__sum"], 30)
vals = Publisher.objects.aggregate(Sum("book__price"))
self.assertEqual(len(vals), 1)
self.assertEqual(vals["book__price__sum"], Decimal("270.27"))
def test_aggregate_multi_join(self):
vals = Store.objects.aggregate(Max("books__authors__age"))
self.assertEqual(len(vals), 1)
self.assertEqual(vals["books__authors__age__max"], 57)
vals = Author.objects.aggregate(Min("book__publisher__num_awards"))
self.assertEqual(len(vals), 1)
self.assertEqual(vals["book__publisher__num_awards__min"], 1)
def test_aggregate_alias(self):
vals = Store.objects.filter(name="Amazon.com").aggregate(amazon_mean=Avg("books__rating"))
self.assertEqual(len(vals), 1)
self.assertAlmostEqual(vals["amazon_mean"], 4.08, places=2)
def test_annotate_basic(self):
self.assertQuerysetEqual(
Book.objects.annotate().order_by('pk'), [
"The Definitive Guide to Django: Web Development Done Right",
"Sams Teach Yourself Django in 24 Hours",
"Practical Django Projects",
"Python Web Development with Django",
"Artificial Intelligence: A Modern Approach",
"Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp"
],
lambda b: b.name
)
books = Book.objects.annotate(mean_age=Avg("authors__age"))
b = books.get(pk=1)
self.assertEqual(
b.name,
u'The Definitive Guide to Django: Web Development Done Right'
)
self.assertEqual(b.mean_age, 34.5)
def test_annotate_m2m(self):
books = Book.objects.filter(rating__lt=4.5).annotate(Avg("authors__age")).order_by("name")
self.assertQuerysetEqual(
books, [
(u'Artificial Intelligence: A Modern Approach', 51.5),
(u'Practical Django Projects', 29.0),
(u'Python Web Development with Django', Approximate(30.3, places=1)),
(u'Sams Teach Yourself Django in 24 Hours', 45.0)
],
lambda b: (b.name, b.authors__age__avg),
)
books = Book.objects.annotate(num_authors=Count("authors")).order_by("name")
self.assertQuerysetEqual(
books, [
(u'Artificial Intelligence: A Modern Approach', 2),
(u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 1),
(u'Practical Django Projects', 1),
(u'Python Web Development with Django', 3),
(u'Sams Teach Yourself Django in 24 Hours', 1),
(u'The Definitive Guide to Django: Web Development Done Right', 2)
],
lambda b: (b.name, b.num_authors)
)
def test_backwards_m2m_annotate(self):
authors = Author.objects.filter(name__contains="a").annotate(Avg("book__rating")).order_by("name")
self.assertQuerysetEqual(
authors, [
(u'Adrian Holovaty', 4.5),
(u'Brad Dayley', 3.0),
(u'Jacob Kaplan-Moss', 4.5),
(u'James Bennett', 4.0),
(u'Paul Bissex', 4.0),
(u'Stuart Russell', 4.0)
],
lambda a: (a.name, a.book__rating__avg)
)
authors = Author.objects.annotate(num_books=Count("book")).order_by("name")
self.assertQuerysetEqual(
authors, [
(u'Adrian Holovaty', 1),
(u'Brad Dayley', 1),
(u'Jacob Kaplan-Moss', 1),
(u'James Bennett', 1),
(u'Jeffrey Forcier', 1),
(u'Paul Bissex', 1),
(u'Peter Norvig', 2),
(u'Stuart Russell', 1),
(u'Wesley J. Chun', 1)
],
lambda a: (a.name, a.num_books)
)
def test_reverse_fkey_annotate(self):
books = Book.objects.annotate(Sum("publisher__num_awards")).order_by("name")
self.assertQuerysetEqual(
books, [
(u'Artificial Intelligence: A Modern Approach', 7),
(u'Paradigms of Artificial Intelligence Programming: Case Studies in Common Lisp', 9),
(u'Practical Django Projects', 3),
(u'Python Web Development with Django', 7),
(u'Sams Teach Yourself Django in 24 Hours', 1),
(u'The Definitive Guide to Django: Web Development Done Right', 3)
],
lambda b: (b.name, b.publisher__num_awards__sum)
)
publishers = Publisher.objects.annotate(Sum("book__price")).order_by("name")
self.assertQuerysetEqual(
publishers, [
(u'Apress', Decimal("59.69")),
(u"Jonno's House of Books", None),
(u'Morgan Kaufmann', Decimal("75.00")),
(u'Prentice Hall', Decimal("112.49")),
(u'Sams', Decimal("23.09"))
],
lambda p: (p.name, p.book__price__sum)
)
def test_annotate_values(self):
books = list(Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values())
self.assertEqual(
books, [
{
"contact_id": 1,
"id": 1,
"isbn": "159059725",
"mean_age": 34.5,
"name": "The Definitive Guide to Django: Web Development Done Right",
"pages": 447,
"price": Approximate(Decimal("30")),
"pubdate": datetime.date(2007, 12, 6),
"publisher_id": 1,
"rating": 4.5,
}
]
)
books = Book.objects.filter(pk=1).annotate(mean_age=Avg('authors__age')).values('pk', 'isbn', 'mean_age')
self.assertEqual(
list(books), [
{
"pk": 1,
"isbn": "159059725",
"mean_age": 34.5,
}
]
)
books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values("name")
self.assertEqual(
list(books), [
{
"name": "The Definitive Guide to Django: Web Development Done Right"
}
]
)
books = Book.objects.filter(pk=1).values().annotate(mean_age=Avg('authors__age'))
self.assertEqual(
list(books), [
{
"contact_id": 1,
"id": 1,
"isbn": "159059725",
"mean_age": 34.5,
"name": "The Definitive Guide to Django: Web Development Done Right",
"pages": 447,
"price": Approximate(Decimal("30")),
"pubdate": datetime.date(2007, 12, 6),
"publisher_id": 1,
"rating": 4.5,
}
]
)
books = Book.objects.values("rating").annotate(n_authors=Count("authors__id"), mean_age=Avg("authors__age")).order_by("rating")
self.assertEqual(
list(books), [
{
"rating": 3.0,
"n_authors": 1,
"mean_age": 45.0,
},
{
"rating": 4.0,
"n_authors": 6,
"mean_age": Approximate(37.16, places=1)
},
{
"rating": 4.5,
"n_authors": 2,
"mean_age": 34.5,
},
{
"rating": 5.0,
"n_authors": 1,
"mean_age": 57.0,
}
]
)
authors = Author.objects.annotate(Avg("friends__age")).order_by("name")
self.assertEqual(len(authors), 9)
self.assertQuerysetEqual(
authors, [
(u'Adrian Holovaty', 32.0),
(u'Brad Dayley', None),
(u'Jacob Kaplan-Moss', 29.5),
(u'James Bennett', 34.0),
(u'Jeffrey Forcier', 27.0),
(u'Paul Bissex', 31.0),
(u'Peter Norvig', 46.0),
(u'Stuart Russell', 57.0),
(u'Wesley J. Chun', Approximate(33.66, places=1))
],
lambda a: (a.name, a.friends__age__avg)
)
def test_count(self):
vals = Book.objects.aggregate(Count("rating"))
self.assertEqual(vals, {"rating__count": 6})
vals = Book.objects.aggregate(Count("rating", distinct=True))
self.assertEqual(vals, {"rating__count": 4})
def test_fkey_aggregate(self):
explicit = list(Author.objects.annotate(Count('book__id')))
implicit = list(Author.objects.annotate(Count('book')))
self.assertEqual(explicit, implicit)
def test_annotate_ordering(self):
books = Book.objects.values('rating').annotate(oldest=Max('authors__age')).order_by('oldest', 'rating')
self.assertEqual(
list(books), [
{
"rating": 4.5,
"oldest": 35,
},
{
"rating": 3.0,
"oldest": 45
},
{
"rating": 4.0,
"oldest": 57,
},
{
"rating": 5.0,
"oldest": 57,
}
]
)
books = Book.objects.values("rating").annotate(oldest=Max("authors__age")).order_by("-oldest", "-rating")
self.assertEqual(
list(books), [
{
"rating": 5.0,
"oldest": 57,
},
{
"rating": 4.0,
"oldest": 57,
},
{
"rating": 3.0,
"oldest": 45,
},
{
"rating": 4.5,
"oldest": 35,
}
]
)
def test_aggregate_annotation(self):
vals = Book.objects.annotate(num_authors=Count("authors__id")).aggregate(Avg("num_authors"))
self.assertEqual(vals, {"num_authors__avg": Approximate(1.66, places=1)})
def test_filtering(self):
p = Publisher.objects.create(name='Expensive Publisher', num_awards=0)
Book.objects.create(
name='ExpensiveBook1',
pages=1,
isbn='111',
rating=3.5,
price=Decimal("1000"),
publisher=p,
contact_id=1,
pubdate=datetime.date(2008,12,1)
)
Book.objects.create(
name='ExpensiveBook2',
pages=1,
isbn='222',
rating=4.0,
price=Decimal("1000"),
publisher=p,
contact_id=1,
pubdate=datetime.date(2008,12,2)
)
Book.objects.create(
name='ExpensiveBook3',
pages=1,
isbn='333',
rating=4.5,
price=Decimal("35"),
publisher=p,
contact_id=1,
pubdate=datetime.date(2008,12,3)
)
publishers = Publisher.objects.annotate(num_books=Count("book__id")).filter(num_books__gt=1).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
"Prentice Hall",
"Expensive Publisher",
],
lambda p: p.name,
)
publishers = Publisher.objects.filter(book__price__lt=Decimal("40.0")).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
"Apress",
"Sams",
"Prentice Hall",
"Expensive Publisher",
],
lambda p: p.name
)
publishers = Publisher.objects.annotate(num_books=Count("book__id")).filter(num_books__gt=1, book__price__lt=Decimal("40.0")).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
"Prentice Hall",
"Expensive Publisher",
],
lambda p: p.name,
)
publishers = Publisher.objects.filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count("book__id")).filter(num_books__gt=1).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
],
lambda p: p.name
)
publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__range=[1, 3]).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
"Sams",
"Prentice Hall",
"Morgan Kaufmann",
"Expensive Publisher",
],
lambda p: p.name
)
publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__range=[1, 2]).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
"Sams",
"Prentice Hall",
"Morgan Kaufmann",
],
lambda p: p.name
)
publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__in=[1, 3]).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Sams",
"Morgan Kaufmann",
"Expensive Publisher",
],
lambda p: p.name,
)
publishers = Publisher.objects.annotate(num_books=Count("book")).filter(num_books__isnull=True)
self.assertEqual(len(publishers), 0)
def test_annotation(self):
vals = Author.objects.filter(pk=1).aggregate(Count("friends__id"))
self.assertEqual(vals, {"friends__id__count": 2})
books = Book.objects.annotate(num_authors=Count("authors__name")).filter(num_authors__ge=2).order_by("pk")
self.assertQuerysetEqual(
books, [
"The Definitive Guide to Django: Web Development Done Right",
"Artificial Intelligence: A Modern Approach",
],
lambda b: b.name
)
authors = Author.objects.annotate(num_friends=Count("friends__id", distinct=True)).filter(num_friends=0).order_by("pk")
self.assertQuerysetEqual(
authors, [
"Brad Dayley",
],
lambda a: a.name
)
publishers = Publisher.objects.annotate(num_books=Count("book__id")).filter(num_books__gt=1).order_by("pk")
self.assertQuerysetEqual(
publishers, [
"Apress",
"Prentice Hall",
],
lambda p: p.name
)
publishers = Publisher.objects.filter(book__price__lt=Decimal("40.0")).annotate(num_books=Count("book__id")).filter(num_books__gt=1)
self.assertQuerysetEqual(
publishers, [
"Apress",
],
lambda p: p.name
)
books = Book.objects.annotate(num_authors=Count("authors__id")).filter(authors__name__contains="Norvig", num_authors__gt=1)
self.assertQuerysetEqual(
books, [
"Artificial Intelligence: A Modern Approach",
],
lambda b: b.name
)
def test_more_aggregation(self):
a = Author.objects.get(name__contains='Norvig')
b = Book.objects.get(name__contains='Done Right')
b.authors.add(a)
b.save()
vals = Book.objects.annotate(num_authors=Count("authors__id")).filter(authors__name__contains="Norvig", num_authors__gt=1).aggregate(Avg("rating"))
self.assertEqual(vals, {"rating__avg": 4.25})
def test_even_more_aggregate(self):
publishers = Publisher.objects.annotate(earliest_book=Min("book__pubdate")).exclude(earliest_book=None).order_by("earliest_book").values()
self.assertEqual(
list(publishers), [
{
'earliest_book': datetime.date(1991, 10, 15),
'num_awards': 9,
'id': 4,
'name': u'Morgan Kaufmann'
},
{
'earliest_book': datetime.date(1995, 1, 15),
'num_awards': 7,
'id': 3,
'name': u'Prentice Hall'
},
{
'earliest_book': datetime.date(2007, 12, 6),
'num_awards': 3,
'id': 1,
'name': u'Apress'
},
{
'earliest_book': datetime.date(2008, 3, 3),
'num_awards': 1,
'id': 2,
'name': u'Sams'
}
]
)
vals = Store.objects.aggregate(Max("friday_night_closing"), Min("original_opening"))
self.assertEqual(
vals,
{
"friday_night_closing__max": datetime.time(23, 59, 59),
"original_opening__min": datetime.datetime(1945, 4, 25, 16, 24, 14),
}
)
def test_annotate_values_list(self):
books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("pk", "isbn", "mean_age")
self.assertEqual(
list(books), [
(1, "159059725", 34.5),
]
)
books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("isbn")
self.assertEqual(
list(books), [
('159059725',)
]
)
books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("mean_age")
self.assertEqual(
list(books), [
(34.5,)
]
)
books = Book.objects.filter(pk=1).annotate(mean_age=Avg("authors__age")).values_list("mean_age", flat=True)
self.assertEqual(list(books), [34.5])
books = Book.objects.values_list("price").annotate(count=Count("price")).order_by("-count", "price")
self.assertEqual(
list(books), [
(Decimal("29.69"), 2),
(Decimal('23.09'), 1),
(Decimal('30'), 1),
(Decimal('75'), 1),
(Decimal('82.8'), 1),
]
)

View File

@ -1,476 +1,228 @@
""" import copy
################### import pickle
# Empty QueryDict # import unittest
################### from django.http import QueryDict, HttpResponse, CompatCookie, BadHeaderError
>>> q = QueryDict('') class QueryDictTests(unittest.TestCase):
def test_missing_key(self):
>>> q['foo'] q = QueryDict('')
Traceback (most recent call last): self.assertRaises(KeyError, q.__getitem__, 'foo')
...
MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" def test_immutability(self):
q = QueryDict('')
>>> q['something'] = 'bar' self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar')
Traceback (most recent call last): self.assertRaises(AttributeError, q.setlist, 'foo', ['bar'])
... self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar'])
AttributeError: This QueryDict instance is immutable self.assertRaises(AttributeError, q.update, {'foo': 'bar'})
self.assertRaises(AttributeError, q.pop, 'foo')
>>> q.get('foo', 'default') self.assertRaises(AttributeError, q.popitem)
'default' self.assertRaises(AttributeError, q.clear)
>>> q.getlist('foo') def test_immutable_get_with_default(self):
[] q = QueryDict('')
self.assertEqual(q.get('foo', 'default'), 'default')
>>> q.setlist('foo', ['bar', 'baz'])
Traceback (most recent call last): def test_immutable_basic_operations(self):
... q = QueryDict('')
AttributeError: This QueryDict instance is immutable self.assertEqual(q.getlist('foo'), [])
self.assertEqual(q.has_key('foo'), False)
>>> q.appendlist('foo', ['bar']) self.assertEqual('foo' in q, False)
Traceback (most recent call last): self.assertEqual(q.items(), [])
... self.assertEqual(q.lists(), [])
AttributeError: This QueryDict instance is immutable self.assertEqual(q.items(), [])
self.assertEqual(q.keys(), [])
>>> q.has_key('foo') self.assertEqual(q.values(), [])
False self.assertEqual(len(q), 0)
self.assertEqual(q.urlencode(), '')
>>> 'foo' in q
False def test_single_key_value(self):
"""Test QueryDict with one key/value pair"""
>>> q.items()
[] q = QueryDict('foo=bar')
self.assertEqual(q['foo'], 'bar')
>>> q.lists() self.assertRaises(KeyError, q.__getitem__, 'bar')
[] self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar')
>>> q.keys() self.assertEqual(q.get('foo', 'default'), 'bar')
[] self.assertEqual(q.get('bar', 'default'), 'default')
self.assertEqual(q.getlist('foo'), ['bar'])
>>> q.values() self.assertEqual(q.getlist('bar'), [])
[]
self.assertRaises(AttributeError, q.setlist, 'foo', ['bar'])
>>> len(q) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar'])
0
self.failUnless(q.has_key('foo'))
>>> q.update({'foo': 'bar'}) self.failUnless('foo' in q)
Traceback (most recent call last): self.failIf(q.has_key('bar'))
... self.failIf('bar' in q)
AttributeError: This QueryDict instance is immutable
self.assertEqual(q.items(), [(u'foo', u'bar')])
>>> q.pop('foo') self.assertEqual(q.lists(), [(u'foo', [u'bar'])])
Traceback (most recent call last): self.assertEqual(q.keys(), ['foo'])
... self.assertEqual(q.values(), ['bar'])
AttributeError: This QueryDict instance is immutable self.assertEqual(len(q), 1)
>>> q.popitem() self.assertRaises(AttributeError, q.update, {'foo': 'bar'})
Traceback (most recent call last): self.assertRaises(AttributeError, q.pop, 'foo')
... self.assertRaises(AttributeError, q.popitem)
AttributeError: This QueryDict instance is immutable self.assertRaises(AttributeError, q.clear)
self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar')
>>> q.clear()
Traceback (most recent call last): self.assertEqual(q.urlencode(), 'foo=bar')
...
AttributeError: This QueryDict instance is immutable def test_mutable_copy(self):
"""A copy of a QueryDict is mutable."""
>>> q.setdefault('foo', 'bar') q = QueryDict('').copy()
Traceback (most recent call last): self.assertRaises(KeyError, q.__getitem__, "foo")
... q['name'] = 'john'
AttributeError: This QueryDict instance is immutable self.assertEqual(q['name'], 'john')
>>> q.urlencode() def test_mutable_delete(self):
'' q = QueryDict('').copy()
q['name'] = 'john'
################################### del q['name']
# Mutable copy of empty QueryDict # self.failIf('name' in q)
###################################
def test_basic_mutable_operations(self):
>>> q = q.copy() q = QueryDict('').copy()
q['name'] = 'john'
>>> q['foo'] self.assertEqual(q.get('foo', 'default'), 'default')
Traceback (most recent call last): self.assertEqual(q.get('name', 'default'), 'john')
... self.assertEqual(q.getlist('name'), ['john'])
MultiValueDictKeyError: "Key 'foo' not found in <QueryDict: {}>" self.assertEqual(q.getlist('foo'), [])
>>> q['name'] = 'john' q.setlist('foo', ['bar', 'baz'])
self.assertEqual(q.get('foo', 'default'), 'baz')
>>> q['name'] self.assertEqual(q.getlist('foo'), ['bar', 'baz'])
u'john'
q.appendlist('foo', 'another')
>>> del q['name'] self.assertEqual(q.getlist('foo'), ['bar', 'baz', 'another'])
>>> 'name' in q self.assertEqual(q['foo'], 'another')
False self.failUnless(q.has_key('foo'))
self.failUnless('foo' in q)
>>> q['name'] = 'john'
self.assertEqual(q.items(), [(u'foo', u'another'), (u'name', u'john')])
>>> q.get('foo', 'default') self.assertEqual(q.lists(), [(u'foo', [u'bar', u'baz', u'another']), (u'name', [u'john'])])
'default' self.assertEqual(q.keys(), [u'foo', u'name'])
self.assertEqual(q.values(), [u'another', u'john'])
>>> q.get('name', 'default') self.assertEqual(len(q), 2)
u'john'
q.update({'foo': 'hello'})
>>> q.getlist('name') self.assertEqual(q['foo'], 'hello')
[u'john'] self.assertEqual(q.get('foo', 'not available'), 'hello')
self.assertEqual(q.getlist('foo'), [u'bar', u'baz', u'another', u'hello'])
>>> q.getlist('foo') self.assertEqual(q.pop('foo'), [u'bar', u'baz', u'another', u'hello'])
[] self.assertEqual(q.pop('foo', 'not there'), 'not there')
self.assertEqual(q.get('foo', 'not there'), 'not there')
>>> q.setlist('foo', ['bar', 'baz']) self.assertEqual(q.setdefault('foo', 'bar'), 'bar')
self.assertEqual(q['foo'], 'bar')
>>> q.get('foo', 'default') self.assertEqual(q.getlist('foo'), ['bar'])
u'baz' self.assertEqual(q.urlencode(), 'foo=bar&name=john')
>>> q.getlist('foo') q.clear()
[u'bar', u'baz'] self.assertEqual(len(q), 0)
>>> q.appendlist('foo', 'another') def test_multiple_keys(self):
"""Test QueryDict with two key/value pairs with same keys."""
>>> q.getlist('foo')
[u'bar', u'baz', u'another'] q = QueryDict('vote=yes&vote=no')
>>> q['foo'] self.assertEqual(q['vote'], u'no')
u'another' self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar')
>>> q.has_key('foo') self.assertEqual(q.get('vote', 'default'), u'no')
True self.assertEqual(q.get('foo', 'default'), 'default')
self.assertEqual(q.getlist('vote'), [u'yes', u'no'])
>>> 'foo' in q self.assertEqual(q.getlist('foo'), [])
True
self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz'])
>>> q.items() self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz'])
[(u'foo', u'another'), (u'name', u'john')] self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar'])
>>> q.lists() self.assertEqual(q.has_key('vote'), True)
[(u'foo', [u'bar', u'baz', u'another']), (u'name', [u'john'])] self.assertEqual('vote' in q, True)
self.assertEqual(q.has_key('foo'), False)
>>> q.keys() self.assertEqual('foo' in q, False)
[u'foo', u'name'] self.assertEqual(q.items(), [(u'vote', u'no')])
self.assertEqual(q.lists(), [(u'vote', [u'yes', u'no'])])
>>> q.values() self.assertEqual(q.keys(), [u'vote'])
[u'another', u'john'] self.assertEqual(q.values(), [u'no'])
self.assertEqual(len(q), 1)
>>> len(q)
2 self.assertRaises(AttributeError, q.update, {'foo': 'bar'})
self.assertRaises(AttributeError, q.pop, 'foo')
>>> q.update({'foo': 'hello'}) self.assertRaises(AttributeError, q.popitem)
self.assertRaises(AttributeError, q.clear)
# Displays last value self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar')
>>> q['foo'] self.assertRaises(AttributeError, q.__delitem__, 'vote')
u'hello'
def test_invalid_input_encoding(self):
>>> q.get('foo', 'not available') """
u'hello' QueryDicts must be able to handle invalid input encoding (in this
case, bad UTF-8 encoding).
>>> q.getlist('foo') """
[u'bar', u'baz', u'another', u'hello'] q = QueryDict('foo=bar&foo=\xff')
self.assertEqual(q['foo'], u'\ufffd')
>>> q.pop('foo') self.assertEqual(q.getlist('foo'), [u'bar', u'\ufffd'])
[u'bar', u'baz', u'another', u'hello']
def test_pickle(self):
>>> q.pop('foo', 'not there') q = QueryDict('')
'not there' q1 = pickle.loads(pickle.dumps(q, 2))
self.assertEqual(q == q1, True)
>>> q.get('foo', 'not there') q = QueryDict('a=b&c=d')
'not there' q1 = pickle.loads(pickle.dumps(q, 2))
self.assertEqual(q == q1, True)
>>> q.setdefault('foo', 'bar') q = QueryDict('a=b&c=d&a=1')
u'bar' q1 = pickle.loads(pickle.dumps(q, 2))
self.assertEqual(q == q1 , True)
>>> q['foo']
u'bar' def test_update_from_querydict(self):
"""Regression test for #8278: QueryDict.update(QueryDict)"""
>>> q.getlist('foo') x = QueryDict("a=1&a=2", mutable=True)
[u'bar'] y = QueryDict("a=3&a=4")
x.update(y)
>>> q.urlencode() self.assertEqual(x.getlist('a'), [u'1', u'2', u'3', u'4'])
'foo=bar&name=john'
def test_non_default_encoding(self):
>>> q.clear() """#13572 - QueryDict with a non-default encoding"""
q = QueryDict('sbb=one', encoding='rot_13')
>>> len(q) self.assertEqual(q.encoding , 'rot_13' )
0 self.assertEqual(q.items() , [(u'foo', u'bar')] )
self.assertEqual(q.urlencode() , 'sbb=one' )
##################################### q = q.copy()
# QueryDict with one key/value pair # self.assertEqual(q.encoding , 'rot_13' )
##################################### self.assertEqual(q.items() , [(u'foo', u'bar')] )
self.assertEqual(q.urlencode() , 'sbb=one' )
>>> q = QueryDict('foo=bar') self.assertEqual(copy.copy(q).encoding , 'rot_13' )
self.assertEqual(copy.deepcopy(q).encoding , 'rot_13')
>>> q['foo']
u'bar' class HttpResponseTests(unittest.TestCase):
def test_unicode_headers(self):
>>> q['bar'] r = HttpResponse()
Traceback (most recent call last):
... # If we insert a unicode value it will be converted to an ascii
MultiValueDictKeyError: "Key 'bar' not found in <QueryDict: {u'foo': [u'bar']}>" r['value'] = u'test value'
self.failUnless(isinstance(r['value'], str))
>>> q['something'] = 'bar'
Traceback (most recent call last): # An error is raised When a unicode object with non-ascii is assigned.
... self.assertRaises(UnicodeEncodeError, r.__setitem__, 'value', u't\xebst value')
AttributeError: This QueryDict instance is immutable
# The response also converts unicode keys to strings.)
>>> q.get('foo', 'default') r[u'test'] = 'testing key'
u'bar' l = list(r.items())
l.sort()
>>> q.get('bar', 'default') self.assertEqual(l[1], ('test', 'testing key'))
'default'
# It will also raise errors for keys with non-ascii data.
>>> q.getlist('foo') self.assertRaises(UnicodeEncodeError, r.__setitem__, u't\xebst key', 'value')
[u'bar']
def test_newlines_in_headers(self):
>>> q.getlist('bar') # Bug #10188: Do not allow newlines in headers (CR or LF)
[] r = HttpResponse()
self.assertRaises(BadHeaderError, r.__setitem__, 'test\rstr', 'test')
>>> q.setlist('foo', ['bar', 'baz']) self.assertRaises(BadHeaderError, r.__setitem__, 'test\nstr', 'test')
Traceback (most recent call last):
... class CookieTests(unittest.TestCase):
AttributeError: This QueryDict instance is immutable
>>> q.appendlist('foo', ['bar'])
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.has_key('foo')
True
>>> 'foo' in q
True
>>> q.has_key('bar')
False
>>> 'bar' in q
False
>>> q.items()
[(u'foo', u'bar')]
>>> q.lists()
[(u'foo', [u'bar'])]
>>> q.keys()
[u'foo']
>>> q.values()
[u'bar']
>>> len(q)
1
>>> q.update({'foo': 'bar'})
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.pop('foo')
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.popitem()
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.clear()
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.setdefault('foo', 'bar')
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.urlencode()
'foo=bar'
#####################################################
# QueryDict with two key/value pairs with same keys #
#####################################################
>>> q = QueryDict('vote=yes&vote=no')
>>> q['vote']
u'no'
>>> q['something'] = 'bar'
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.get('vote', 'default')
u'no'
>>> q.get('foo', 'default')
'default'
>>> q.getlist('vote')
[u'yes', u'no']
>>> q.getlist('foo')
[]
>>> q.setlist('foo', ['bar', 'baz'])
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.appendlist('foo', ['bar'])
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.has_key('vote')
True
>>> 'vote' in q
True
>>> q.has_key('foo')
False
>>> 'foo' in q
False
>>> q.items()
[(u'vote', u'no')]
>>> q.lists()
[(u'vote', [u'yes', u'no'])]
>>> q.keys()
[u'vote']
>>> q.values()
[u'no']
>>> len(q)
1
>>> q.update({'foo': 'bar'})
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.pop('foo')
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.popitem()
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.clear()
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.setdefault('foo', 'bar')
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
>>> q.urlencode()
'vote=yes&vote=no'
>>> del q['vote']
Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
# QueryDicts must be able to handle invalid input encoding (in this case, bad
# UTF-8 encoding).
>>> q = QueryDict('foo=bar&foo=\xff')
>>> q['foo']
u'\ufffd'
>>> q.getlist('foo')
[u'bar', u'\ufffd']
########################
# Pickling a QueryDict #
########################
>>> import pickle
>>> q = QueryDict('')
>>> q1 = pickle.loads(pickle.dumps(q, 2))
>>> q == q1
True
>>> q = QueryDict('a=b&c=d')
>>> q1 = pickle.loads(pickle.dumps(q, 2))
>>> q == q1
True
>>> q = QueryDict('a=b&c=d&a=1')
>>> q1 = pickle.loads(pickle.dumps(q, 2))
>>> q == q1
True
######################################
# HttpResponse with Unicode headers #
######################################
>>> r = HttpResponse()
If we insert a unicode value it will be converted to an ascii
string. This makes sure we comply with the HTTP specifications.
>>> r['value'] = u'test value'
>>> isinstance(r['value'], str)
True
An error is raised When a unicode object with non-ascii is assigned.
>>> r['value'] = u't\xebst value' # doctest:+ELLIPSIS
Traceback (most recent call last):
...
UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format
The response also converts unicode keys to strings.
>>> r[u'test'] = 'testing key'
>>> l = list(r.items())
>>> l.sort()
>>> l[1]
('test', 'testing key')
It will also raise errors for keys with non-ascii data.
>>> r[u't\xebst'] = 'testing key' # doctest:+ELLIPSIS
Traceback (most recent call last):
...
UnicodeEncodeError: ..., HTTP response headers must be in US-ASCII format
# Bug #10188: Do not allow newlines in headers (CR or LF)
>>> r['test\\rstr'] = 'test'
Traceback (most recent call last):
...
BadHeaderError: Header values can't contain newlines (got 'test\\rstr')
>>> r['test\\nstr'] = 'test'
Traceback (most recent call last):
...
BadHeaderError: Header values can't contain newlines (got 'test\\nstr')
#
# Regression test for #8278: QueryDict.update(QueryDict)
#
>>> x = QueryDict("a=1&a=2", mutable=True)
>>> y = QueryDict("a=3&a=4")
>>> x.update(y)
>>> x.getlist('a')
[u'1', u'2', u'3', u'4']
"""
from django.http import QueryDict, HttpResponse, CompatCookie
from django.test import TestCase
class Cookies(TestCase):
def test_encode(self): def test_encode(self):
""" """
Test that we don't output tricky characters in encoded value Test that we don't output tricky characters in encoded value
@ -502,7 +254,3 @@ class Cookies(TestCase):
c2 = CompatCookie() c2 = CompatCookie()
c2.load(c.output()) c2.load(c.output())
self.assertEqual(c['test'].value, c2['test'].value) self.assertEqual(c['test'].value, c2['test'].value)
if __name__ == "__main__":
import doctest
doctest.testmod()

View File

@ -10,14 +10,16 @@ forward, backwards and self references.
import datetime import datetime
import decimal import decimal
import unittest try:
from cStringIO import StringIO from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from django.utils.functional import curry
from django.core import serializers
from django.db import transaction, DEFAULT_DB_ALIAS
from django.core import management
from django.conf import settings from django.conf import settings
from django.core import serializers, management
from django.db import transaction, DEFAULT_DB_ALIAS
from django.test import TestCase
from django.utils.functional import curry
from models import * from models import *
@ -59,10 +61,10 @@ def im2m_create(pk, klass, data):
def im_create(pk, klass, data): def im_create(pk, klass, data):
instance = klass(id=pk) instance = klass(id=pk)
setattr(instance, 'right_id', data['right']) instance.right_id = data['right']
setattr(instance, 'left_id', data['left']) instance.left_id = data['left']
if 'extra' in data: if 'extra' in data:
setattr(instance, 'extra', data['extra']) instance.extra = data['extra']
models.Model.save_base(instance, raw=True) models.Model.save_base(instance, raw=True)
return [instance] return [instance]
@ -96,7 +98,9 @@ def inherited_create(pk, klass, data):
def data_compare(testcase, pk, klass, data): def data_compare(testcase, pk, klass, data):
instance = klass.objects.get(id=pk) instance = klass.objects.get(id=pk)
testcase.assertEqual(data, instance.data, testcase.assertEqual(data, instance.data,
"Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % (pk,data, type(data), instance.data, type(instance.data))) "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % (
pk, data, type(data), instance.data, type(instance.data))
)
def generic_compare(testcase, pk, klass, data): def generic_compare(testcase, pk, klass, data):
instance = klass.objects.get(id=pk) instance = klass.objects.get(id=pk)
@ -348,28 +352,16 @@ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
# Dynamically create serializer tests to ensure that all # Dynamically create serializer tests to ensure that all
# registered serializers are automatically tested. # registered serializers are automatically tested.
class SerializerTests(unittest.TestCase): class SerializerTests(TestCase):
pass pass
def serializerTest(format, self): def serializerTest(format, self):
# Clear the database first
management.call_command('flush', verbosity=0, interactive=False)
# Create all the objects defined in the test data # Create all the objects defined in the test data
objects = [] objects = []
instance_count = {} instance_count = {}
transaction.enter_transaction_management() for (func, pk, klass, datum) in test_data:
try: objects.extend(func[0](pk, klass, datum))
transaction.managed(True)
for (func, pk, klass, datum) in test_data:
objects.extend(func[0](pk, klass, datum))
instance_count[klass] = 0
transaction.commit()
except:
transaction.rollback()
transaction.leave_transaction_management()
raise
transaction.leave_transaction_management()
# Get a count of the number of objects created for each class # Get a count of the number of objects created for each class
for klass in instance_count: for klass in instance_count:
@ -381,19 +373,8 @@ def serializerTest(format, self):
# Serialize the test database # Serialize the test database
serialized_data = serializers.serialize(format, objects, indent=2) serialized_data = serializers.serialize(format, objects, indent=2)
# Flush the database and recreate from the serialized data for obj in serializers.deserialize(format, serialized_data):
management.call_command('flush', verbosity=0, interactive=False) obj.save()
transaction.enter_transaction_management()
try:
transaction.managed(True)
for obj in serializers.deserialize(format, serialized_data):
obj.save()
transaction.commit()
except:
transaction.rollback()
transaction.leave_transaction_management()
raise
transaction.leave_transaction_management()
# Assert that the deserialized data is the same # Assert that the deserialized data is the same
# as the original source # as the original source
@ -406,10 +387,7 @@ def serializerTest(format, self):
self.assertEquals(count, klass.objects.count()) self.assertEquals(count, klass.objects.count())
def fieldsTest(format, self): def fieldsTest(format, self):
# Clear the database first obj = ComplexModel(field1='first', field2='second', field3='third')
management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
obj.save_base(raw=True) obj.save_base(raw=True)
# Serialize then deserialize the test database # Serialize then deserialize the test database
@ -422,9 +400,6 @@ def fieldsTest(format, self):
self.assertEqual(result.object.field3, 'third') self.assertEqual(result.object.field3, 'third')
def streamTest(format, self): def streamTest(format, self):
# Clear the database first
management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third') obj = ComplexModel(field1='first',field2='second',field3='third')
obj.save_base(raw=True) obj.save_base(raw=True)
@ -440,7 +415,7 @@ def streamTest(format, self):
stream.close() stream.close()
for format in serializers.get_serializer_formats(): for format in serializers.get_serializer_formats():
setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format))
setattr(SerializerTests, 'test_'+format+'_serializer_fields', curry(fieldsTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format))
if format != 'python': if format != 'python':
setattr(SerializerTests, 'test_'+format+'_serializer_stream', curry(streamTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format))