summaryrefslogtreecommitdiff
path: root/tests/modeltests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/modeltests')
-rw-r--r--tests/modeltests/aggregation/models.py321
-rw-r--r--tests/modeltests/aggregation/tests.py565
-rw-r--r--tests/modeltests/choices/models.py26
-rw-r--r--tests/modeltests/choices/tests.py23
-rw-r--r--tests/modeltests/custom_columns/models.py65
-rw-r--r--tests/modeltests/custom_columns/tests.py71
-rw-r--r--tests/modeltests/custom_managers/models.py48
-rw-r--r--tests/modeltests/custom_managers/tests.py71
-rw-r--r--tests/modeltests/custom_methods/models.py23
-rw-r--r--tests/modeltests/custom_methods/tests.py42
-rw-r--r--tests/modeltests/custom_pk/fields.py1
-rw-r--r--tests/modeltests/custom_pk/models.py135
-rw-r--r--tests/modeltests/custom_pk/tests.py183
-rw-r--r--tests/modeltests/defer/models.py164
-rw-r--r--tests/modeltests/defer/tests.py137
-rw-r--r--tests/modeltests/delete/models.py165
-rw-r--r--tests/modeltests/delete/tests.py135
-rw-r--r--tests/modeltests/empty/models.py16
-rw-r--r--tests/modeltests/empty/tests.py15
-rw-r--r--tests/modeltests/expressions/models.py105
-rw-r--r--tests/modeltests/expressions/tests.py218
-rw-r--r--tests/modeltests/field_defaults/models.py38
-rw-r--r--tests/modeltests/field_defaults/tests.py16
-rw-r--r--tests/modeltests/field_subclassing/fields.py8
-rw-r--r--tests/modeltests/field_subclassing/models.py54
-rw-r--r--tests/modeltests/field_subclassing/tests.py64
-rw-r--r--tests/modeltests/files/models.py124
-rw-r--r--tests/modeltests/files/tests.py100
-rw-r--r--tests/modeltests/fixtures/models.py235
-rw-r--r--tests/modeltests/fixtures/tests.py336
-rw-r--r--tests/modeltests/fixtures_model_package/models/__init__.py40
-rw-r--r--tests/modeltests/fixtures_model_package/tests.py71
-rw-r--r--tests/modeltests/force_insert_update/models.py51
-rw-r--r--tests/modeltests/force_insert_update/tests.py38
-rw-r--r--tests/modeltests/get_latest/models.py49
-rw-r--r--tests/modeltests/get_latest/tests.py53
-rw-r--r--tests/modeltests/get_object_or_404/models.py73
-rw-r--r--tests/modeltests/get_object_or_404/tests.py80
-rw-r--r--tests/modeltests/get_or_create/models.py62
-rw-r--r--tests/modeltests/get_or_create/tests.py52
-rw-r--r--tests/modeltests/m2m_and_m2o/models.py44
-rw-r--r--tests/modeltests/m2m_and_m2o/tests.py75
-rw-r--r--tests/modeltests/m2m_intermediary/models.py32
-rw-r--r--tests/modeltests/m2m_intermediary/tests.py38
-rw-r--r--tests/modeltests/m2m_multiple/models.py49
-rw-r--r--tests/modeltests/m2m_multiple/tests.py84
-rw-r--r--tests/modeltests/model_forms/models.py14
-rw-r--r--tests/modeltests/model_forms/tests.py4
-rw-r--r--tests/modeltests/proxy_model_inheritance/tests.py2
-rw-r--r--tests/modeltests/signals/models.py34
-rw-r--r--tests/modeltests/test_client/models.py24
-rw-r--r--tests/modeltests/validation/test_unique.py9
52 files changed, 2559 insertions, 1923 deletions
diff --git a/tests/modeltests/aggregation/models.py b/tests/modeltests/aggregation/models.py
index f50abe651b..ccc12898b7 100644
--- a/tests/modeltests/aggregation/models.py
+++ b/tests/modeltests/aggregation/models.py
@@ -1,6 +1,7 @@
# coding: utf-8
from django.db import models
+
class Author(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
@@ -39,323 +40,3 @@ class Store(models.Model):
def __unicode__(self):
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
-
-"""}
diff --git a/tests/modeltests/aggregation/tests.py b/tests/modeltests/aggregation/tests.py
new file mode 100644
index 0000000000..c830368b9d
--- /dev/null
+++ b/tests/modeltests/aggregation/tests.py
@@ -0,0 +1,565 @@
+import datetime
+from decimal import Decimal
+
+from django.db.models import Avg, Sum, Count, Max, Min
+from django.test import TestCase, Approximate
+
+from models import Author, Publisher, Book, Store
+
+
+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),
+ ]
+ )
diff --git a/tests/modeltests/choices/models.py b/tests/modeltests/choices/models.py
index e378260598..27316f5dea 100644
--- a/tests/modeltests/choices/models.py
+++ b/tests/modeltests/choices/models.py
@@ -22,29 +22,3 @@ class Person(models.Model):
def __unicode__(self):
return self.name
-
-__test__ = {'API_TESTS':"""
->>> a = Person(name='Adrian', gender='M')
->>> a.save()
->>> s = Person(name='Sara', gender='F')
->>> s.save()
->>> a.gender
-'M'
->>> s.gender
-'F'
->>> a.get_gender_display()
-u'Male'
->>> s.get_gender_display()
-u'Female'
-
-# If the value for the field doesn't correspond to a valid choice,
-# the value itself is provided as a display value.
->>> a.gender = ''
->>> a.get_gender_display()
-u''
-
->>> a.gender = 'U'
->>> a.get_gender_display()
-u'U'
-
-"""}
diff --git a/tests/modeltests/choices/tests.py b/tests/modeltests/choices/tests.py
new file mode 100644
index 0000000000..09023d8113
--- /dev/null
+++ b/tests/modeltests/choices/tests.py
@@ -0,0 +1,23 @@
+from django.test import TestCase
+
+from models import Person
+
+
+class ChoicesTests(TestCase):
+ def test_display(self):
+ a = Person.objects.create(name='Adrian', gender='M')
+ s = Person.objects.create(name='Sara', gender='F')
+ self.assertEqual(a.gender, 'M')
+ self.assertEqual(s.gender, 'F')
+
+ self.assertEqual(a.get_gender_display(), 'Male')
+ self.assertEqual(s.get_gender_display(), 'Female')
+
+ # If the value for the field doesn't correspond to a valid choice,
+ # the value itself is provided as a display value.
+ a.gender = ''
+ self.assertEqual(a.get_gender_display(), '')
+
+ a.gender = 'U'
+ self.assertEqual(a.get_gender_display(), 'U')
+
diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py
index 74691cd1bc..651f8a61b2 100644
--- a/tests/modeltests/custom_columns/models.py
+++ b/tests/modeltests/custom_columns/models.py
@@ -38,68 +38,3 @@ class Article(models.Model):
class Meta:
ordering = ('headline',)
-__test__ = {'API_TESTS':"""
-# Create a Author.
->>> a = Author(first_name='John', last_name='Smith')
->>> a.save()
-
->>> a.id
-1
-
-# Create another author
->>> a2 = Author(first_name='Peter', last_name='Jones')
->>> a2.save()
-
-# Create an article
->>> art = Article(headline='Django lets you build web apps easily')
->>> art.save()
->>> art.authors = [a, a2]
-
-# Although the table and column names on Author have been set to custom values,
-# nothing about using the Author model has changed...
-
-# Query the available authors
->>> Author.objects.all()
-[<Author: Peter Jones>, <Author: John Smith>]
-
->>> Author.objects.filter(first_name__exact='John')
-[<Author: John Smith>]
-
->>> Author.objects.get(first_name__exact='John')
-<Author: John Smith>
-
->>> Author.objects.filter(firstname__exact='John')
-Traceback (most recent call last):
- ...
-FieldError: Cannot resolve keyword 'firstname' into field. Choices are: article, first_name, id, last_name
-
->>> a = Author.objects.get(last_name__exact='Smith')
->>> a.first_name
-u'John'
->>> a.last_name
-u'Smith'
->>> a.firstname
-Traceback (most recent call last):
- ...
-AttributeError: 'Author' object has no attribute 'firstname'
->>> a.last
-Traceback (most recent call last):
- ...
-AttributeError: 'Author' object has no attribute 'last'
-
-# Although the Article table uses a custom m2m table,
-# nothing about using the m2m relationship has changed...
-
-# Get all the authors for an article
->>> art.authors.all()
-[<Author: Peter Jones>, <Author: John Smith>]
-
-# Get the articles for an author
->>> a.article_set.all()
-[<Article: Django lets you build web apps easily>]
-
-# Query the authors across the m2m relation
->>> art.authors.filter(last_name='Jones')
-[<Author: Peter Jones>]
-
-"""}
diff --git a/tests/modeltests/custom_columns/tests.py b/tests/modeltests/custom_columns/tests.py
new file mode 100644
index 0000000000..e8ec21f06f
--- /dev/null
+++ b/tests/modeltests/custom_columns/tests.py
@@ -0,0 +1,71 @@
+from django.core.exceptions import FieldError
+from django.test import TestCase
+
+from models import Author, Article
+
+
+class CustomColumnsTests(TestCase):
+ def test_db_column(self):
+ a1 = Author.objects.create(first_name="John", last_name="Smith")
+ a2 = Author.objects.create(first_name="Peter", last_name="Jones")
+
+ art = Article.objects.create(headline="Django lets you build web apps easily")
+ art.authors = [a1, a2]
+
+ # Although the table and column names on Author have been set to custom
+ # values, nothing about using the Author model has changed...
+
+ # Query the available authors
+ self.assertQuerysetEqual(
+ Author.objects.all(), [
+ "Peter Jones", "John Smith",
+ ],
+ unicode
+ )
+ self.assertQuerysetEqual(
+ Author.objects.filter(first_name__exact="John"), [
+ "John Smith",
+ ],
+ unicode
+ )
+ self.assertEqual(
+ Author.objects.get(first_name__exact="John"),
+ a1,
+ )
+
+ self.assertRaises(FieldError,
+ lambda: Author.objects.filter(firstname__exact="John")
+ )
+
+ a = Author.objects.get(last_name__exact="Smith")
+ a.first_name = "John"
+ a.last_name = "Smith"
+
+ self.assertRaises(AttributeError, lambda: a.firstname)
+ self.assertRaises(AttributeError, lambda: a.last)
+
+ # Although the Article table uses a custom m2m table,
+ # nothing about using the m2m relationship has changed...
+
+ # Get all the authors for an article
+ self.assertQuerysetEqual(
+ art.authors.all(), [
+ "Peter Jones",
+ "John Smith",
+ ],
+ unicode
+ )
+ # Get the articles for an author
+ self.assertQuerysetEqual(
+ a.article_set.all(), [
+ "Django lets you build web apps easily",
+ ],
+ lambda a: a.headline
+ )
+ # Query the authors across the m2m relation
+ self.assertQuerysetEqual(
+ art.authors.filter(last_name='Jones'), [
+ "Peter Jones"
+ ],
+ unicode
+ )
diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py
index 40bf77e273..1052552bb3 100644
--- a/tests/modeltests/custom_managers/models.py
+++ b/tests/modeltests/custom_managers/models.py
@@ -57,51 +57,3 @@ class Car(models.Model):
def __unicode__(self):
return self.name
-
-__test__ = {'API_TESTS':"""
->>> p1 = Person(first_name='Bugs', last_name='Bunny', fun=True)
->>> p1.save()
->>> p2 = Person(first_name='Droopy', last_name='Dog', fun=False)
->>> p2.save()
->>> Person.objects.get_fun_people()
-[<Person: Bugs Bunny>]
-
-# The RelatedManager used on the 'books' descriptor extends the default manager
->>> from modeltests.custom_managers.models import PublishedBookManager
->>> isinstance(p2.books, PublishedBookManager)
-True
-
->>> b1 = Book(title='How to program', author='Rodney Dangerfield', is_published=True)
->>> b1.save()
->>> b2 = Book(title='How to be smart', author='Albert Einstein', is_published=False)
->>> b2.save()
-
-# The default manager, "objects", doesn't exist,
-# because a custom one was provided.
->>> Book.objects
-Traceback (most recent call last):
- ...
-AttributeError: type object 'Book' has no attribute 'objects'
-
-# The RelatedManager used on the 'authors' descriptor extends the default manager
->>> from modeltests.custom_managers.models import PersonManager
->>> isinstance(b2.authors, PersonManager)
-True
-
->>> Book.published_objects.all()
-[<Book: How to program>]
-
->>> c1 = Car(name='Corvette', mileage=21, top_speed=180)
->>> c1.save()
->>> c2 = Car(name='Neon', mileage=31, top_speed=100)
->>> c2.save()
->>> Car.cars.order_by('name')
-[<Car: Corvette>, <Car: Neon>]
->>> Car.fast_cars.all()
-[<Car: Corvette>]
-
-# Each model class gets a "_default_manager" attribute, which is a reference
-# to the first manager defined in the class. In this case, it's "cars".
->>> Car._default_manager.order_by('name')
-[<Car: Corvette>, <Car: Neon>]
-"""}
diff --git a/tests/modeltests/custom_managers/tests.py b/tests/modeltests/custom_managers/tests.py
new file mode 100644
index 0000000000..8721e9ac52
--- /dev/null
+++ b/tests/modeltests/custom_managers/tests.py
@@ -0,0 +1,71 @@
+from django.test import TestCase
+
+from models import Person, Book, Car, PersonManager, PublishedBookManager
+
+
+class CustomManagerTests(TestCase):
+ def test_manager(self):
+ p1 = Person.objects.create(first_name="Bugs", last_name="Bunny", fun=True)
+ p2 = Person.objects.create(first_name="Droopy", last_name="Dog", fun=False)
+
+ self.assertQuerysetEqual(
+ Person.objects.get_fun_people(), [
+ "Bugs Bunny"
+ ],
+ unicode
+ )
+ # The RelatedManager used on the 'books' descriptor extends the default
+ # manager
+ self.assertTrue(isinstance(p2.books, PublishedBookManager))
+
+ b1 = Book.published_objects.create(
+ title="How to program", author="Rodney Dangerfield", is_published=True
+ )
+ b2 = Book.published_objects.create(
+ title="How to be smart", author="Albert Einstein", is_published=False
+ )
+
+ # The default manager, "objects", doesn't exist, because a custom one
+ # was provided.
+ self.assertRaises(AttributeError, lambda: Book.objects)
+
+ # The RelatedManager used on the 'authors' descriptor extends the
+ # default manager
+ self.assertTrue(isinstance(b2.authors, PersonManager))
+
+ self.assertQuerysetEqual(
+ Book.published_objects.all(), [
+ "How to program",
+ ],
+ lambda b: b.title
+ )
+
+ c1 = Car.cars.create(name="Corvette", mileage=21, top_speed=180)
+ c2 = Car.cars.create(name="Neon", mileage=31, top_speed=100)
+
+ self.assertQuerysetEqual(
+ Car.cars.order_by("name"), [
+ "Corvette",
+ "Neon",
+ ],
+ lambda c: c.name
+ )
+
+ self.assertQuerysetEqual(
+ Car.fast_cars.all(), [
+ "Corvette",
+ ],
+ lambda c: c.name
+ )
+
+ # Each model class gets a "_default_manager" attribute, which is a
+ # reference to the first manager defined in the class. In this case,
+ # it's "cars".
+
+ self.assertQuerysetEqual(
+ Car._default_manager.order_by("name"), [
+ "Corvette",
+ "Neon",
+ ],
+ lambda c: c.name
+ )
diff --git a/tests/modeltests/custom_methods/models.py b/tests/modeltests/custom_methods/models.py
index d420871373..15150a6c3f 100644
--- a/tests/modeltests/custom_methods/models.py
+++ b/tests/modeltests/custom_methods/models.py
@@ -33,27 +33,4 @@ class Article(models.Model):
WHERE pub_date = %s
AND id != %s""", [connection.ops.value_to_db_date(self.pub_date),
self.id])
- # The asterisk in "(*row)" tells Python to expand the list into
- # positional arguments to Article().
return [self.__class__(*row) for row in cursor.fetchall()]
-
-__test__ = {'API_TESTS':"""
-# Create a couple of Articles.
->>> from datetime import date
->>> a = Article(id=None, headline='Area man programs in Python', pub_date=date(2005, 7, 27))
->>> a.save()
->>> b = Article(id=None, headline='Beatles reunite', pub_date=date(2005, 7, 27))
->>> b.save()
-
-# Test the custom methods.
->>> a.was_published_today()
-False
->>> a.articles_from_same_day_1()
-[<Article: Beatles reunite>]
->>> a.articles_from_same_day_2()
-[<Article: Beatles reunite>]
->>> b.articles_from_same_day_1()
-[<Article: Area man programs in Python>]
->>> b.articles_from_same_day_2()
-[<Article: Area man programs in Python>]
-"""}
diff --git a/tests/modeltests/custom_methods/tests.py b/tests/modeltests/custom_methods/tests.py
new file mode 100644
index 0000000000..90a7f0da29
--- /dev/null
+++ b/tests/modeltests/custom_methods/tests.py
@@ -0,0 +1,42 @@
+from datetime import date
+
+from django.test import TestCase
+
+from models import Article
+
+
+class MethodsTests(TestCase):
+ def test_custom_methods(self):
+ a = Article.objects.create(
+ headline="Area man programs in Python", pub_date=date(2005, 7, 27)
+ )
+ b = Article.objects.create(
+ headline="Beatles reunite", pub_date=date(2005, 7, 27)
+ )
+
+ self.assertFalse(a.was_published_today())
+ self.assertQuerysetEqual(
+ a.articles_from_same_day_1(), [
+ "Beatles reunite",
+ ],
+ lambda a: a.headline,
+ )
+ self.assertQuerysetEqual(
+ a.articles_from_same_day_2(), [
+ "Beatles reunite",
+ ],
+ lambda a: a.headline
+ )
+
+ self.assertQuerysetEqual(
+ b.articles_from_same_day_1(), [
+ "Area man programs in Python",
+ ],
+ lambda a: a.headline,
+ )
+ self.assertQuerysetEqual(
+ b.articles_from_same_day_2(), [
+ "Area man programs in Python",
+ ],
+ lambda a: a.headline
+ )
diff --git a/tests/modeltests/custom_pk/fields.py b/tests/modeltests/custom_pk/fields.py
index 319e42f974..2eeb80e6ac 100644
--- a/tests/modeltests/custom_pk/fields.py
+++ b/tests/modeltests/custom_pk/fields.py
@@ -3,6 +3,7 @@ import string
from django.db import models
+
class MyWrapper(object):
def __init__(self, value):
self.value = value
diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py
index 84e6480af9..ff2f2bac1b 100644
--- a/tests/modeltests/custom_pk/models.py
+++ b/tests/modeltests/custom_pk/models.py
@@ -40,138 +40,3 @@ class Bar(models.Model):
class Foo(models.Model):
bar = models.ForeignKey(Bar)
-__test__ = {'API_TESTS':"""
->>> dan = Employee(employee_code=123, first_name='Dan', last_name='Jones')
->>> dan.save()
->>> Employee.objects.all()
-[<Employee: Dan Jones>]
-
->>> fran = Employee(employee_code=456, first_name='Fran', last_name='Bones')
->>> fran.save()
->>> Employee.objects.all()
-[<Employee: Fran Bones>, <Employee: Dan Jones>]
-
->>> Employee.objects.get(pk=123)
-<Employee: Dan Jones>
->>> Employee.objects.get(pk=456)
-<Employee: Fran Bones>
->>> Employee.objects.get(pk=42)
-Traceback (most recent call last):
- ...
-DoesNotExist: Employee matching query does not exist.
-
-# Use the name of the primary key, rather than pk.
->>> Employee.objects.get(employee_code__exact=123)
-<Employee: Dan Jones>
-
-# pk can be used as a substitute for the primary key.
->>> Employee.objects.filter(pk__in=[123, 456])
-[<Employee: Fran Bones>, <Employee: Dan Jones>]
-
-# The primary key can be accessed via the pk property on the model.
->>> e = Employee.objects.get(pk=123)
->>> e.pk
-123
-
-# Or we can use the real attribute name for the primary key:
->>> e.employee_code
-123
-
-# Fran got married and changed her last name.
->>> fran = Employee.objects.get(pk=456)
->>> fran.last_name = 'Jones'
->>> fran.save()
->>> Employee.objects.filter(last_name__exact='Jones')
-[<Employee: Dan Jones>, <Employee: Fran Jones>]
->>> emps = Employee.objects.in_bulk([123, 456])
->>> emps[123]
-<Employee: Dan Jones>
-
->>> b = Business(name='Sears')
->>> b.save()
->>> b.employees.add(dan, fran)
->>> b.employees.all()
-[<Employee: Dan Jones>, <Employee: Fran Jones>]
->>> fran.business_set.all()
-[<Business: Sears>]
->>> Business.objects.in_bulk(['Sears'])
-{u'Sears': <Business: Sears>}
-
->>> Business.objects.filter(name__exact='Sears')
-[<Business: Sears>]
->>> Business.objects.filter(pk='Sears')
-[<Business: Sears>]
-
-# Queries across tables, involving primary key
->>> Employee.objects.filter(business__name__exact='Sears')
-[<Employee: Dan Jones>, <Employee: Fran Jones>]
->>> Employee.objects.filter(business__pk='Sears')
-[<Employee: Dan Jones>, <Employee: Fran Jones>]
-
->>> Business.objects.filter(employees__employee_code__exact=123)
-[<Business: Sears>]
->>> Business.objects.filter(employees__pk=123)
-[<Business: Sears>]
->>> Business.objects.filter(employees__first_name__startswith='Fran')
-[<Business: Sears>]
-
-# Primary key may be unicode string
->>> bus = Business(name=u'jaźń')
->>> bus.save()
-
-# The primary key must also obviously be unique, so trying to create a new
-# object with the same primary key will fail.
->>> try:
-... sid = transaction.savepoint()
-... Employee.objects.create(employee_code=123, first_name='Fred', last_name='Jones')
-... transaction.savepoint_commit(sid)
-... except Exception, e:
-... if isinstance(e, IntegrityError):
-... transaction.savepoint_rollback(sid)
-... print "Pass"
-... else:
-... print "Fail with %s" % type(e)
-Pass
-
-# Regression for #10785 -- Custom fields can be used for primary keys.
->>> new_bar = Bar.objects.create()
->>> new_foo = Foo.objects.create(bar=new_bar)
-
-# FIXME: This still doesn't work, but will require some changes in
-# get_db_prep_lookup to fix it.
-# >>> f = Foo.objects.get(bar=new_bar.pk)
-# >>> f == new_foo
-# True
-# >>> f.bar == new_bar
-# True
-
->>> f = Foo.objects.get(bar=new_bar)
->>> f == new_foo
-True
->>> f.bar == new_bar
-True
-
-"""}
-
-# SQLite lets objects be saved with an empty primary key, even though an
-# integer is expected. So we can't check for an error being raised in that case
-# for SQLite. Remove it from the suite for this next bit.
-if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3':
- __test__["API_TESTS"] += """
-# The primary key must be specified, so an error is raised if you try to create
-# an object without it.
->>> try:
-... sid = transaction.savepoint()
-... Employee.objects.create(first_name='Tom', last_name='Smith')
-... print 'hello'
-... transaction.savepoint_commit(sid)
-... print 'hello2'
-... except Exception, e:
-... if isinstance(e, IntegrityError):
-... transaction.savepoint_rollback(sid)
-... print "Pass"
-... else:
-... print "Fail with %s" % type(e)
-Pass
-
-"""
diff --git a/tests/modeltests/custom_pk/tests.py b/tests/modeltests/custom_pk/tests.py
new file mode 100644
index 0000000000..6ef4bdd433
--- /dev/null
+++ b/tests/modeltests/custom_pk/tests.py
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+from django.conf import settings
+from django.db import DEFAULT_DB_ALIAS, transaction, IntegrityError
+from django.test import TestCase
+
+from models import Employee, Business, Bar, Foo
+
+
+class CustomPKTests(TestCase):
+ def test_custom_pk(self):
+ dan = Employee.objects.create(
+ employee_code=123, first_name="Dan", last_name="Jones"
+ )
+ self.assertQuerysetEqual(
+ Employee.objects.all(), [
+ "Dan Jones",
+ ],
+ unicode
+ )
+
+ fran = Employee.objects.create(
+ employee_code=456, first_name="Fran", last_name="Bones"
+ )
+ self.assertQuerysetEqual(
+ Employee.objects.all(), [
+ "Fran Bones",
+ "Dan Jones",
+ ],
+ unicode
+ )
+
+ self.assertEqual(Employee.objects.get(pk=123), dan)
+ self.assertEqual(Employee.objects.get(pk=456), fran)
+
+ self.assertRaises(Employee.DoesNotExist,
+ lambda: Employee.objects.get(pk=42)
+ )
+
+ # Use the name of the primary key, rather than pk.
+ self.assertEqual(Employee.objects.get(employee_code=123), dan)
+ # pk can be used as a substitute for the primary key.
+ self.assertQuerysetEqual(
+ Employee.objects.filter(pk__in=[123, 456]), [
+ "Fran Bones",
+ "Dan Jones",
+ ],
+ unicode
+ )
+ # The primary key can be accessed via the pk property on the model.
+ e = Employee.objects.get(pk=123)
+ self.assertEqual(e.pk, 123)
+ # Or we can use the real attribute name for the primary key:
+ self.assertEqual(e.employee_code, 123)
+
+ # Fran got married and changed her last name.
+ fran = Employee.objects.get(pk=456)
+ fran.last_name = "Jones"
+ fran.save()
+
+ self.assertQuerysetEqual(
+ Employee.objects.filter(last_name="Jones"), [
+ "Dan Jones",
+ "Fran Jones",
+ ],
+ unicode
+ )
+
+ emps = Employee.objects.in_bulk([123, 456])
+ self.assertEqual(emps[123], dan)
+
+ b = Business.objects.create(name="Sears")
+ b.employees.add(dan, fran)
+ self.assertQuerysetEqual(
+ b.employees.all(), [
+ "Dan Jones",
+ "Fran Jones",
+ ],
+ unicode
+ )
+ self.assertQuerysetEqual(
+ fran.business_set.all(), [
+ "Sears",
+ ],
+ lambda b: b.name
+ )
+
+ self.assertEqual(Business.objects.in_bulk(["Sears"]), {
+ "Sears": b,
+ })
+
+ self.assertQuerysetEqual(
+ Business.objects.filter(name="Sears"), [
+ "Sears"
+ ],
+ lambda b: b.name
+ )
+ self.assertQuerysetEqual(
+ Business.objects.filter(pk="Sears"), [
+ "Sears",
+ ],
+ lambda b: b.name
+ )
+
+ # Queries across tables, involving primary key
+ self.assertQuerysetEqual(
+ Employee.objects.filter(business__name="Sears"), [
+ "Dan Jones",
+ "Fran Jones",
+ ],
+ unicode,
+ )
+ self.assertQuerysetEqual(
+ Employee.objects.filter(business__pk="Sears"), [
+ "Dan Jones",
+ "Fran Jones",
+ ],
+ unicode,
+ )
+
+ self.assertQuerysetEqual(
+ Business.objects.filter(employees__employee_code=123), [
+ "Sears",
+ ],
+ lambda b: b.name
+ )
+ self.assertQuerysetEqual(
+ Business.objects.filter(employees__pk=123), [
+ "Sears",
+ ],
+ lambda b: b.name,
+ )
+
+ self.assertQuerysetEqual(
+ Business.objects.filter(employees__first_name__startswith="Fran"), [
+ "Sears",
+ ],
+ lambda b: b.name
+ )
+
+ def test_unicode_pk(self):
+ # Primary key may be unicode string
+ bus = Business.objects.create(name=u'jaźń')
+
+ def test_unique_pk(self):
+ # The primary key must also obviously be unique, so trying to create a
+ # new object with the same primary key will fail.
+ e = Employee.objects.create(
+ employee_code=123, first_name="Frank", last_name="Jones"
+ )
+ sid = transaction.savepoint()
+ self.assertRaises(IntegrityError,
+ Employee.objects.create, employee_code=123, first_name="Fred", last_name="Jones"
+ )
+ transaction.savepoint_rollback(sid)
+
+ def test_custom_field_pk(self):
+ # Regression for #10785 -- Custom fields can be used for primary keys.
+ new_bar = Bar.objects.create()
+ new_foo = Foo.objects.create(bar=new_bar)
+
+ # FIXME: This still doesn't work, but will require some changes in
+ # get_db_prep_lookup to fix it.
+ # f = Foo.objects.get(bar=new_bar.pk)
+ # self.assertEqual(f, new_foo)
+ # self.assertEqual(f.bar, new_bar)
+
+ f = Foo.objects.get(bar=new_bar)
+ self.assertEqual(f, new_foo),
+ self.assertEqual(f.bar, new_bar)
+
+
+ # SQLite lets objects be saved with an empty primary key, even though an
+ # integer is expected. So we can't check for an error being raised in that
+ # case for SQLite. Remove it from the suite for this next bit.
+ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3':
+ def test_required_pk(self):
+ # The primary key must be specified, so an error is raised if you
+ # try to create an object without it.
+ sid = transaction.savepoint()
+ self.assertRaises(IntegrityError,
+ Employee.objects.create, first_name="Tom", last_name="Smith"
+ )
+ transaction.savepoint_rollback(sid)
diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py
index ac3c876a57..4fddd39d26 100644
--- a/tests/modeltests/defer/models.py
+++ b/tests/modeltests/defer/models.py
@@ -3,7 +3,7 @@ Tests for defer() and only().
"""
from django.db import models
-from django.db.models.query_utils import DeferredAttribute
+
class Secondary(models.Model):
first = models.CharField(max_length=50)
@@ -22,165 +22,3 @@ class Child(Primary):
class BigChild(Primary):
other = models.CharField(max_length=50)
-
-def count_delayed_fields(obj, debug=False):
- """
- Returns the number of delayed attributes on the given model instance.
- """
- count = 0
- for field in obj._meta.fields:
- if isinstance(obj.__class__.__dict__.get(field.attname),
- DeferredAttribute):
- if debug:
- print field.name, field.attname
- count += 1
- return count
-
-
-__test__ = {"API_TEST": """
-To all outward appearances, instances with deferred fields look the same as
-normal instances when we examine attribute values. Therefore we test for the
-number of deferred fields on returned instances (by poking at the internals),
-as a way to observe what is going on.
-
->>> s1 = Secondary.objects.create(first="x1", second="y1")
->>> p1 = Primary.objects.create(name="p1", value="xx", related=s1)
-
->>> qs = Primary.objects.all()
-
->>> count_delayed_fields(qs.defer('name')[0])
-1
->>> count_delayed_fields(qs.only('name')[0])
-2
->>> count_delayed_fields(qs.defer('related__first')[0])
-0
->>> obj = qs.select_related().only('related__first')[0]
->>> count_delayed_fields(obj)
-2
->>> obj.related_id == s1.pk
-True
->>> count_delayed_fields(qs.defer('name').extra(select={'a': 1})[0])
-1
->>> count_delayed_fields(qs.extra(select={'a': 1}).defer('name')[0])
-1
->>> count_delayed_fields(qs.defer('name').defer('value')[0])
-2
->>> count_delayed_fields(qs.only('name').only('value')[0])
-2
->>> count_delayed_fields(qs.only('name').defer('value')[0])
-2
->>> count_delayed_fields(qs.only('name', 'value').defer('value')[0])
-2
->>> count_delayed_fields(qs.defer('name').only('value')[0])
-2
->>> obj = qs.only()[0]
->>> count_delayed_fields(qs.defer(None)[0])
-0
->>> count_delayed_fields(qs.only('name').defer(None)[0])
-0
-
-User values() won't defer anything (you get the full list of dictionaries
-back), but it still works.
->>> qs.defer('name').values()[0] == {'id': p1.id, 'name': u'p1', 'value': 'xx', 'related_id': s1.id}
-True
->>> qs.only('name').values()[0] == {'id': p1.id, 'name': u'p1', 'value': 'xx', 'related_id': s1.id}
-True
-
-Using defer() and only() with get() is also valid.
->>> count_delayed_fields(qs.defer('name').get(pk=p1.pk))
-1
->>> count_delayed_fields(qs.only('name').get(pk=p1.pk))
-2
-
-# KNOWN NOT TO WORK: >>> count_delayed_fields(qs.only('name').select_related('related')[0])
-# KNOWN NOT TO WORK >>> count_delayed_fields(qs.defer('related').select_related('related')[0])
-
-# Saving models with deferred fields is possible (but inefficient, since every
-# field has to be retrieved first).
-
->>> obj = Primary.objects.defer("value").get(name="p1")
->>> obj.name = "a new name"
->>> obj.save()
->>> Primary.objects.all()
-[<Primary: a new name>]
-
-# Regression for #10572 - A subclass with no extra fields can defer fields from the base class
->>> _ = Child.objects.create(name="c1", value="foo", related=s1)
-
-# You can defer a field on a baseclass when the subclass has no fields
->>> obj = Child.objects.defer("value").get(name="c1")
->>> count_delayed_fields(obj)
-1
->>> obj.name
-u"c1"
->>> obj.value
-u"foo"
->>> obj.name = "c2"
->>> obj.save()
-
-# You can retrive a single column on a base class with no fields
->>> obj = Child.objects.only("name").get(name="c2")
->>> count_delayed_fields(obj)
-3
->>> obj.name
-u"c2"
->>> obj.value
-u"foo"
->>> obj.name = "cc"
->>> obj.save()
-
->>> _ = BigChild.objects.create(name="b1", value="foo", related=s1, other="bar")
-
-# You can defer a field on a baseclass
->>> obj = BigChild.objects.defer("value").get(name="b1")
->>> count_delayed_fields(obj)
-1
->>> obj.name
-u"b1"
->>> obj.value
-u"foo"
->>> obj.other
-u"bar"
->>> obj.name = "b2"
->>> obj.save()
-
-# You can defer a field on a subclass
->>> obj = BigChild.objects.defer("other").get(name="b2")
->>> count_delayed_fields(obj)
-1
->>> obj.name
-u"b2"
->>> obj.value
-u"foo"
->>> obj.other
-u"bar"
->>> obj.name = "b3"
->>> obj.save()
-
-# You can retrieve a single field on a baseclass
->>> obj = BigChild.objects.only("name").get(name="b3")
->>> count_delayed_fields(obj)
-4
->>> obj.name
-u"b3"
->>> obj.value
-u"foo"
->>> obj.other
-u"bar"
->>> obj.name = "b4"
->>> obj.save()
-
-# You can retrieve a single field on a baseclass
->>> obj = BigChild.objects.only("other").get(name="b4")
->>> count_delayed_fields(obj)
-4
->>> obj.name
-u"b4"
->>> obj.value
-u"foo"
->>> obj.other
-u"bar"
->>> obj.name = "bb"
->>> obj.save()
-
-"""}
diff --git a/tests/modeltests/defer/tests.py b/tests/modeltests/defer/tests.py
new file mode 100644
index 0000000000..5f6c53dee2
--- /dev/null
+++ b/tests/modeltests/defer/tests.py
@@ -0,0 +1,137 @@
+from django.db.models.query_utils import DeferredAttribute
+from django.test import TestCase
+
+from models import Secondary, Primary, Child, BigChild
+
+
+class DeferTests(TestCase):
+ def assert_delayed(self, obj, num):
+ count = 0
+ for field in obj._meta.fields:
+ if isinstance(obj.__class__.__dict__.get(field.attname),
+ DeferredAttribute):
+ count += 1
+ self.assertEqual(count, num)
+
+ def test_defer(self):
+ # To all outward appearances, instances with deferred fields look the
+ # same as normal instances when we examine attribute values. Therefore
+ # we test for the number of deferred fields on returned instances (by
+ # poking at the internals), as a way to observe what is going on.
+
+ s1 = Secondary.objects.create(first="x1", second="y1")
+ p1 = Primary.objects.create(name="p1", value="xx", related=s1)
+
+ qs = Primary.objects.all()
+
+ self.assert_delayed(qs.defer("name")[0], 1)
+ self.assert_delayed(qs.only("name")[0], 2)
+ self.assert_delayed(qs.defer("related__first")[0], 0)
+
+ obj = qs.select_related().only("related__first")[0]
+ self.assert_delayed(obj, 2)
+
+ self.assertEqual(obj.related_id, s1.pk)
+
+ self.assert_delayed(qs.defer("name").extra(select={"a": 1})[0], 1)
+ self.assert_delayed(qs.extra(select={"a": 1}).defer("name")[0], 1)
+ self.assert_delayed(qs.defer("name").defer("value")[0], 2)
+ self.assert_delayed(qs.only("name").only("value")[0], 2)
+ self.assert_delayed(qs.only("name").defer("value")[0], 2)
+ self.assert_delayed(qs.only("name", "value").defer("value")[0], 2)
+ self.assert_delayed(qs.defer("name").only("value")[0], 2)
+
+ obj = qs.only()[0]
+ self.assert_delayed(qs.defer(None)[0], 0)
+ self.assert_delayed(qs.only("name").defer(None)[0], 0)
+
+ # User values() won't defer anything (you get the full list of
+ # dictionaries back), but it still works.
+ self.assertEqual(qs.defer("name").values()[0], {
+ "id": p1.id,
+ "name": "p1",
+ "value": "xx",
+ "related_id": s1.id,
+ })
+ self.assertEqual(qs.only("name").values()[0], {
+ "id": p1.id,
+ "name": "p1",
+ "value": "xx",
+ "related_id": s1.id,
+ })
+
+ # Using defer() and only() with get() is also valid.
+ self.assert_delayed(qs.defer("name").get(pk=p1.pk), 1)
+ self.assert_delayed(qs.only("name").get(pk=p1.pk), 2)
+
+ # DOES THIS WORK?
+ self.assert_delayed(qs.only("name").select_related("related")[0], 1)
+ self.assert_delayed(qs.defer("related").select_related("related")[0], 0)
+
+ # Saving models with deferred fields is possible (but inefficient,
+ # since every field has to be retrieved first).
+ obj = Primary.objects.defer("value").get(name="p1")
+ obj.name = "a new name"
+ obj.save()
+ self.assertQuerysetEqual(
+ Primary.objects.all(), [
+ "a new name",
+ ],
+ lambda p: p.name
+ )
+
+ # Regression for #10572 - A subclass with no extra fields can defer
+ # fields from the base class
+ Child.objects.create(name="c1", value="foo", related=s1)
+ # You can defer a field on a baseclass when the subclass has no fields
+ obj = Child.objects.defer("value").get(name="c1")
+ self.assert_delayed(obj, 1)
+ self.assertEqual(obj.name, "c1")
+ self.assertEqual(obj.value, "foo")
+ obj.name = "c2"
+ obj.save()
+
+ # You can retrive a single column on a base class with no fields
+ obj = Child.objects.only("name").get(name="c2")
+ self.assert_delayed(obj, 3)
+ self.assertEqual(obj.name, "c2")
+ self.assertEqual(obj.value, "foo")
+ obj.name = "cc"
+ obj.save()
+
+ BigChild.objects.create(name="b1", value="foo", related=s1, other="bar")
+ # You can defer a field on a baseclass
+ obj = BigChild.objects.defer("value").get(name="b1")
+ self.assert_delayed(obj, 1)
+ self.assertEqual(obj.name, "b1")
+ self.assertEqual(obj.value, "foo")
+ self.assertEqual(obj.other, "bar")
+ obj.name = "b2"
+ obj.save()
+
+ # You can defer a field on a subclass
+ obj = BigChild.objects.defer("other").get(name="b2")
+ self.assert_delayed(obj, 1)
+ self.assertEqual(obj.name, "b2")
+ self.assertEqual(obj.value, "foo")
+ self.assertEqual(obj.other, "bar")
+ obj.name = "b3"
+ obj.save()
+
+ # You can retrieve a single field on a baseclass
+ obj = BigChild.objects.only("name").get(name="b3")
+ self.assert_delayed(obj, 4)
+ self.assertEqual(obj.name, "b3")
+ self.assertEqual(obj.value, "foo")
+ self.assertEqual(obj.other, "bar")
+ obj.name = "b4"
+ obj.save()
+
+ # You can retrieve a single field on a baseclass
+ obj = BigChild.objects.only("other").get(name="b4")
+ self.assert_delayed(obj, 4)
+ self.assertEqual(obj.name, "b4")
+ self.assertEqual(obj.value, "foo")
+ self.assertEqual(obj.other, "bar")
+ obj.name = "bb"
+ obj.save()
diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py
index 0e063fd8f0..9c81f6b8f8 100644
--- a/tests/modeltests/delete/models.py
+++ b/tests/modeltests/delete/models.py
@@ -40,168 +40,3 @@ class E(DefaultRepr, models.Model):
class F(DefaultRepr, models.Model):
e = models.ForeignKey(E, related_name='f_rel')
-
-__test__ = {'API_TESTS': """
-### Tests for models A,B,C,D ###
-
-## First, test the CollectedObjects data structure directly
-
->>> from django.db.models.query import CollectedObjects
-
->>> g = CollectedObjects()
->>> g.add("key1", 1, "item1", None)
-False
->>> g["key1"]
-{1: 'item1'}
->>> g.add("key2", 1, "item1", "key1")
-False
->>> g.add("key2", 2, "item2", "key1")
-False
->>> g["key2"]
-{1: 'item1', 2: 'item2'}
->>> g.add("key3", 1, "item1", "key1")
-False
->>> g.add("key3", 1, "item1", "key2")
-True
->>> g.ordered_keys()
-['key3', 'key2', 'key1']
-
->>> g.add("key2", 1, "item1", "key3")
-True
->>> g.ordered_keys()
-Traceback (most recent call last):
- ...
-CyclicDependency: There is a cyclic dependency of items to be processed.
-
-
-## Second, test the usage of CollectedObjects by Model.delete()
-
-# Due to the way that transactions work in the test harness,
-# doing m.delete() here can work but fail in a real situation,
-# since it may delete all objects, but not in the right order.
-# So we manually check that the order of deletion is correct.
-
-# Also, it is possible that the order is correct 'accidentally', due
-# solely to order of imports etc. To check this, we set the order
-# that 'get_models()' will retrieve to a known 'nice' order, and
-# then try again with a known 'tricky' order. Slightly naughty
-# access to internals here :-)
-
-# If implementation changes, then the tests may need to be simplified:
-# - remove the lines that set the .keyOrder and clear the related
-# object caches
-# - remove the second set of tests (with a2, b2 etc)
-
->>> from django.db.models.loading import cache
-
->>> def clear_rel_obj_caches(models):
-... for m in models:
-... if hasattr(m._meta, '_related_objects_cache'):
-... del m._meta._related_objects_cache
-
-# Nice order
->>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd']
->>> clear_rel_obj_caches([A, B, C, D])
-
->>> a1 = A()
->>> a1.save()
->>> b1 = B(a=a1)
->>> b1.save()
->>> c1 = C(b=b1)
->>> c1.save()
->>> d1 = D(c=c1, a=a1)
->>> d1.save()
-
->>> o = CollectedObjects()
->>> a1._collect_sub_objects(o)
->>> o.keys()
-[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
->>> a1.delete()
-
-# Same again with a known bad order
->>> cache.app_models['delete'].keyOrder = ['d', 'c', 'b', 'a']
->>> clear_rel_obj_caches([A, B, C, D])
-
->>> a2 = A()
->>> a2.save()
->>> b2 = B(a=a2)
->>> b2.save()
->>> c2 = C(b=b2)
->>> c2.save()
->>> d2 = D(c=c2, a=a2)
->>> d2.save()
-
->>> o = CollectedObjects()
->>> a2._collect_sub_objects(o)
->>> o.keys()
-[<class 'modeltests.delete.models.D'>, <class 'modeltests.delete.models.C'>, <class 'modeltests.delete.models.B'>, <class 'modeltests.delete.models.A'>]
->>> a2.delete()
-
-### Tests for models E,F - nullable related fields ###
-
-## First, test the CollectedObjects data structure directly
-
->>> g = CollectedObjects()
->>> g.add("key1", 1, "item1", None)
-False
->>> g.add("key2", 1, "item1", "key1", nullable=True)
-False
->>> g.add("key1", 1, "item1", "key2")
-True
->>> g.ordered_keys()
-['key1', 'key2']
-
-## Second, test the usage of CollectedObjects by Model.delete()
-
->>> e1 = E()
->>> e1.save()
->>> f1 = F(e=e1)
->>> f1.save()
->>> e1.f = f1
->>> e1.save()
-
-# Since E.f is nullable, we should delete F first (after nulling out
-# the E.f field), then E.
-
->>> o = CollectedObjects()
->>> e1._collect_sub_objects(o)
->>> o.keys()
-[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]
-
-# temporarily replace the UpdateQuery class to verify that E.f is actually nulled out first
->>> import django.db.models.sql
->>> class LoggingUpdateQuery(django.db.models.sql.UpdateQuery):
-... def clear_related(self, related_field, pk_list, using):
-... print "CLEARING FIELD",related_field.name
-... return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using)
->>> original_class = django.db.models.sql.UpdateQuery
->>> django.db.models.sql.UpdateQuery = LoggingUpdateQuery
->>> e1.delete()
-CLEARING FIELD f
-
->>> e2 = E()
->>> e2.save()
->>> f2 = F(e=e2)
->>> f2.save()
->>> e2.f = f2
->>> e2.save()
-
-# Same deal as before, though we are starting from the other object.
-
->>> o = CollectedObjects()
->>> f2._collect_sub_objects(o)
->>> o.keys()
-[<class 'modeltests.delete.models.F'>, <class 'modeltests.delete.models.E'>]
-
->>> f2.delete()
-CLEARING FIELD f
-
-# Put this back to normal
->>> django.db.models.sql.UpdateQuery = original_class
-
-# Restore the app cache to previous condition so that all models are accounted for.
->>> cache.app_models['delete'].keyOrder = ['a', 'b', 'c', 'd', 'e', 'f']
->>> clear_rel_obj_caches([A, B, C, D, E, F])
-
-"""
-}
diff --git a/tests/modeltests/delete/tests.py b/tests/modeltests/delete/tests.py
new file mode 100644
index 0000000000..7927cce1c1
--- /dev/null
+++ b/tests/modeltests/delete/tests.py
@@ -0,0 +1,135 @@
+from django.db.models import sql
+from django.db.models.loading import cache
+from django.db.models.query import CollectedObjects
+from django.db.models.query_utils import CyclicDependency
+from django.test import TestCase
+
+from models import A, B, C, D, E, F
+
+
+class DeleteTests(TestCase):
+ def clear_rel_obj_caches(self, *models):
+ for m in models:
+ if hasattr(m._meta, '_related_objects_cache'):
+ del m._meta._related_objects_cache
+
+ def order_models(self, *models):
+ cache.app_models["delete"].keyOrder = models
+
+ def setUp(self):
+ self.order_models("a", "b", "c", "d", "e", "f")
+ self.clear_rel_obj_caches(A, B, C, D, E, F)
+
+ def tearDown(self):
+ self.order_models("a", "b", "c", "d", "e", "f")
+ self.clear_rel_obj_caches(A, B, C, D, E, F)
+
+ def test_collected_objects(self):
+ g = CollectedObjects()
+ self.assertFalse(g.add("key1", 1, "item1", None))
+ self.assertEqual(g["key1"], {1: "item1"})
+
+ self.assertFalse(g.add("key2", 1, "item1", "key1"))
+ self.assertFalse(g.add("key2", 2, "item2", "key1"))
+
+ self.assertEqual(g["key2"], {1: "item1", 2: "item2"})
+
+ self.assertFalse(g.add("key3", 1, "item1", "key1"))
+ self.assertTrue(g.add("key3", 1, "item1", "key2"))
+ self.assertEqual(g.ordered_keys(), ["key3", "key2", "key1"])
+
+ self.assertTrue(g.add("key2", 1, "item1", "key3"))
+ self.assertRaises(CyclicDependency, g.ordered_keys)
+
+ def test_delete(self):
+ ## Second, test the usage of CollectedObjects by Model.delete()
+
+ # Due to the way that transactions work in the test harness, doing
+ # m.delete() here can work but fail in a real situation, since it may
+ # delete all objects, but not in the right order. So we manually check
+ # that the order of deletion is correct.
+
+ # Also, it is possible that the order is correct 'accidentally', due
+ # solely to order of imports etc. To check this, we set the order that
+ # 'get_models()' will retrieve to a known 'nice' order, and then try
+ # again with a known 'tricky' order. Slightly naughty access to
+ # internals here :-)
+
+ # If implementation changes, then the tests may need to be simplified:
+ # - remove the lines that set the .keyOrder and clear the related
+ # object caches
+ # - remove the second set of tests (with a2, b2 etc)
+
+ a1 = A.objects.create()
+ b1 = B.objects.create(a=a1)
+ c1 = C.objects.create(b=b1)
+ d1 = D.objects.create(c=c1, a=a1)
+
+ o = CollectedObjects()
+ a1._collect_sub_objects(o)
+ self.assertEqual(o.keys(), [D, C, B, A])
+ a1.delete()
+
+ # Same again with a known bad order
+ self.order_models("d", "c", "b", "a")
+ self.clear_rel_obj_caches(A, B, C, D)
+
+ a2 = A.objects.create()
+ b2 = B.objects.create(a=a2)
+ c2 = C.objects.create(b=b2)
+ d2 = D.objects.create(c=c2, a=a2)
+
+ o = CollectedObjects()
+ a2._collect_sub_objects(o)
+ self.assertEqual(o.keys(), [D, C, B, A])
+ a2.delete()
+
+ def test_collected_objects_null(self):
+ g = CollectedObjects()
+ self.assertFalse(g.add("key1", 1, "item1", None))
+ self.assertFalse(g.add("key2", 1, "item1", "key1", nullable=True))
+ self.assertTrue(g.add("key1", 1, "item1", "key2"))
+ self.assertEqual(g.ordered_keys(), ["key1", "key2"])
+
+ def test_delete_nullable(self):
+ e1 = E.objects.create()
+ f1 = F.objects.create(e=e1)
+ e1.f = f1
+ e1.save()
+
+ # Since E.f is nullable, we should delete F first (after nulling out
+ # the E.f field), then E.
+
+ o = CollectedObjects()
+ e1._collect_sub_objects(o)
+ self.assertEqual(o.keys(), [F, E])
+
+ # temporarily replace the UpdateQuery class to verify that E.f is
+ # actually nulled out first
+
+ logged = []
+ class LoggingUpdateQuery(sql.UpdateQuery):
+ def clear_related(self, related_field, pk_list, using):
+ logged.append(related_field.name)
+ return super(LoggingUpdateQuery, self).clear_related(related_field, pk_list, using)
+ original = sql.UpdateQuery
+ sql.UpdateQuery = LoggingUpdateQuery
+
+ e1.delete()
+ self.assertEqual(logged, ["f"])
+ logged = []
+
+ e2 = E.objects.create()
+ f2 = F.objects.create(e=e2)
+ e2.f = f2
+ e2.save()
+
+ # Same deal as before, though we are starting from the other object.
+ o = CollectedObjects()
+ f2._collect_sub_objects(o)
+ self.assertEqual(o.keys(), [F, E])
+ f2.delete()
+ self.assertEqual(logged, ["f"])
+ logged = []
+
+ sql.UpdateQuery = original
diff --git a/tests/modeltests/empty/models.py b/tests/modeltests/empty/models.py
index d57087134e..a6cdb0aa22 100644
--- a/tests/modeltests/empty/models.py
+++ b/tests/modeltests/empty/models.py
@@ -7,20 +7,6 @@ no fields.
from django.db import models
+
class Empty(models.Model):
pass
-
-__test__ = {'API_TESTS':"""
->>> m = Empty()
->>> m.id
->>> m.save()
->>> m2 = Empty()
->>> m2.save()
->>> len(Empty.objects.all())
-2
->>> m.id is not None
-True
->>> existing = Empty(m.id)
->>> existing.save()
-
-"""}
diff --git a/tests/modeltests/empty/tests.py b/tests/modeltests/empty/tests.py
new file mode 100644
index 0000000000..01fa1c58cd
--- /dev/null
+++ b/tests/modeltests/empty/tests.py
@@ -0,0 +1,15 @@
+from django.test import TestCase
+
+from models import Empty
+
+
+class EmptyModelTests(TestCase):
+ def test_empty(self):
+ m = Empty()
+ self.assertEqual(m.id, None)
+ m.save()
+ m2 = Empty.objects.create()
+ self.assertEqual(len(Empty.objects.all()), 2)
+ self.assertTrue(m.id is not None)
+ existing = Empty(m.id)
+ existing.save()
diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py
index f6292f5d9b..b004408536 100644
--- a/tests/modeltests/expressions/models.py
+++ b/tests/modeltests/expressions/models.py
@@ -25,108 +25,3 @@ class Company(models.Model):
def __unicode__(self):
return self.name
-
-
-__test__ = {'API_TESTS': """
->>> from django.db.models import F
-
->>> Company(name='Example Inc.', num_employees=2300, num_chairs=5,
-... ceo=Employee.objects.create(firstname='Joe', lastname='Smith')).save()
->>> Company(name='Foobar Ltd.', num_employees=3, num_chairs=3,
-... ceo=Employee.objects.create(firstname='Frank', lastname='Meyer')).save()
->>> Company(name='Test GmbH', num_employees=32, num_chairs=1,
-... ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save()
-
->>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs')
-
-# We can filter for companies where the number of employees is greater than the
-# number of chairs.
->>> company_query.filter(num_employees__gt=F('num_chairs'))
-[{'num_chairs': 5, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 1, 'name': u'Test GmbH', 'num_employees': 32}]
-
-# We can set one field to have the value of another field
-# Make sure we have enough chairs
->>> _ = company_query.update(num_chairs=F('num_employees'))
->>> company_query
-[{'num_chairs': 2300, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 3, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 32, 'name': u'Test GmbH', 'num_employees': 32}]
-
-# We can perform arithmetic operations in expressions
-# Make sure we have 2 spare chairs
->>> _ =company_query.update(num_chairs=F('num_employees')+2)
->>> company_query
-[{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}]
-
-# Law of order of operations is followed
->>> _ =company_query.update(num_chairs=F('num_employees') + 2 * F('num_employees'))
->>> company_query
-[{'num_chairs': 6900, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 9, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 96, 'name': u'Test GmbH', 'num_employees': 32}]
-
-# Law of order of operations can be overridden by parentheses
->>> _ =company_query.update(num_chairs=((F('num_employees') + 2) * F('num_employees')))
->>> company_query
-[{'num_chairs': 5294600, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 15, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 1088, 'name': u'Test GmbH', 'num_employees': 32}]
-
-# The relation of a foreign key can become copied over to an other foreign key.
->>> Company.objects.update(point_of_contact=F('ceo'))
-3
-
->>> [c.point_of_contact for c in Company.objects.all()]
-[<Employee: Joe Smith>, <Employee: Frank Meyer>, <Employee: Max Mustermann>]
-
->>> c = Company.objects.all()[0]
->>> c.point_of_contact = Employee.objects.create(firstname="Guido", lastname="van Rossum")
->>> c.save()
-
-# F Expressions can also span joins
->>> Company.objects.filter(ceo__firstname=F('point_of_contact__firstname')).distinct().order_by('name')
-[<Company: Foobar Ltd.>, <Company: Test GmbH>]
-
->>> _ = Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).update(name='foo')
->>> Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).get().name
-u'foo'
-
->>> _ = Company.objects.exclude(ceo__firstname=F('point_of_contact__firstname')).update(name=F('point_of_contact__lastname'))
-Traceback (most recent call last):
-...
-FieldError: Joined field references are not permitted in this query
-
-# F expressions can be used to update attributes on single objects
->>> test_gmbh = Company.objects.get(name='Test GmbH')
->>> test_gmbh.num_employees
-32
->>> test_gmbh.num_employees = F('num_employees') + 4
->>> test_gmbh.save()
->>> test_gmbh = Company.objects.get(pk=test_gmbh.pk)
->>> test_gmbh.num_employees
-36
-
-# F expressions cannot be used to update attributes which are foreign keys, or
-# attributes which involve joins.
->>> test_gmbh.point_of_contact = None
->>> test_gmbh.save()
->>> test_gmbh.point_of_contact is None
-True
->>> test_gmbh.point_of_contact = F('ceo')
-Traceback (most recent call last):
-...
-ValueError: Cannot assign "<django.db.models.expressions.F object at ...>": "Company.point_of_contact" must be a "Employee" instance.
-
->>> test_gmbh.point_of_contact = test_gmbh.ceo
->>> test_gmbh.save()
->>> test_gmbh.name = F('ceo__last_name')
->>> test_gmbh.save()
-Traceback (most recent call last):
-...
-FieldError: Joined field references are not permitted in this query
-
-# F expressions cannot be used to update attributes on objects which do not yet
-# exist in the database
->>> acme = Company(name='The Acme Widget Co.', num_employees=12, num_chairs=5,
-... ceo=test_gmbh.ceo)
->>> acme.num_employees = F('num_employees') + 16
->>> acme.save()
-Traceback (most recent call last):
-...
-TypeError: ...
-
-"""}
diff --git a/tests/modeltests/expressions/tests.py b/tests/modeltests/expressions/tests.py
new file mode 100644
index 0000000000..0a136ae5d8
--- /dev/null
+++ b/tests/modeltests/expressions/tests.py
@@ -0,0 +1,218 @@
+from django.core.exceptions import FieldError
+from django.db.models import F
+from django.test import TestCase
+
+from models import Company, Employee
+
+
+class ExpressionsTests(TestCase):
+ def test_filter(self):
+ Company.objects.create(
+ name="Example Inc.", num_employees=2300, num_chairs=5,
+ ceo=Employee.objects.create(firstname="Joe", lastname="Smith")
+ )
+ Company.objects.create(
+ name="Foobar Ltd.", num_employees=3, num_chairs=4,
+ ceo=Employee.objects.create(firstname="Frank", lastname="Meyer")
+ )
+ Company.objects.create(
+ name="Test GmbH", num_employees=32, num_chairs=1,
+ ceo=Employee.objects.create(firstname="Max", lastname="Mustermann")
+ )
+
+ company_query = Company.objects.values(
+ "name", "num_employees", "num_chairs"
+ ).order_by(
+ "name", "num_employees", "num_chairs"
+ )
+
+ # We can filter for companies where the number of employees is greater
+ # than the number of chairs.
+ self.assertQuerysetEqual(
+ company_query.filter(num_employees__gt=F("num_chairs")), [
+ {
+ "num_chairs": 5,
+ "name": "Example Inc.",
+ "num_employees": 2300,
+ },
+ {
+ "num_chairs": 1,
+ "name": "Test GmbH",
+ "num_employees": 32
+ },
+ ],
+ lambda o: o
+ )
+
+ # We can set one field to have the value of another field
+ # Make sure we have enough chairs
+ company_query.update(num_chairs=F("num_employees"))
+ self.assertQuerysetEqual(
+ company_query, [
+ {
+ "num_chairs": 2300,
+ "name": "Example Inc.",
+ "num_employees": 2300
+ },
+ {
+ "num_chairs": 3,
+ "name": "Foobar Ltd.",
+ "num_employees": 3
+ },
+ {
+ "num_chairs": 32,
+ "name": "Test GmbH",
+ "num_employees": 32
+ }
+ ],
+ lambda o: o
+ )
+
+ # We can perform arithmetic operations in expressions
+ # Make sure we have 2 spare chairs
+ company_query.update(num_chairs=F("num_employees")+2)
+ self.assertQuerysetEqual(
+ company_query, [
+ {
+ 'num_chairs': 2302,
+ 'name': u'Example Inc.',
+ 'num_employees': 2300
+ },
+ {
+ 'num_chairs': 5,
+ 'name': u'Foobar Ltd.',
+ 'num_employees': 3
+ },
+ {
+ 'num_chairs': 34,
+ 'name': u'Test GmbH',
+ 'num_employees': 32
+ }
+ ],
+ lambda o: o,
+ )
+
+ # Law of order of operations is followed
+ company_query.update(
+ num_chairs=F('num_employees') + 2 * F('num_employees')
+ )
+ self.assertQuerysetEqual(
+ company_query, [
+ {
+ 'num_chairs': 6900,
+ 'name': u'Example Inc.',
+ 'num_employees': 2300
+ },
+ {
+ 'num_chairs': 9,
+ 'name': u'Foobar Ltd.',
+ 'num_employees': 3
+ },
+ {
+ 'num_chairs': 96,
+ 'name': u'Test GmbH',
+ 'num_employees': 32
+ }
+ ],
+ lambda o: o,
+ )
+
+ # Law of order of operations can be overridden by parentheses
+ company_query.update(
+ num_chairs=((F('num_employees') + 2) * F('num_employees'))
+ )
+ self.assertQuerysetEqual(
+ company_query, [
+ {
+ 'num_chairs': 5294600,
+ 'name': u'Example Inc.',
+ 'num_employees': 2300
+ },
+ {
+ 'num_chairs': 15,
+ 'name': u'Foobar Ltd.',
+ 'num_employees': 3
+ },
+ {
+ 'num_chairs': 1088,
+ 'name': u'Test GmbH',
+ 'num_employees': 32
+ }
+ ],
+ lambda o: o,
+ )
+
+ # The relation of a foreign key can become copied over to an other
+ # foreign key.
+ self.assertEqual(
+ Company.objects.update(point_of_contact=F('ceo')),
+ 3
+ )
+ self.assertQuerysetEqual(
+ Company.objects.all(), [
+ "Joe Smith",
+ "Frank Meyer",
+ "Max Mustermann",
+ ],
+ lambda c: unicode(c.point_of_contact),
+ )
+
+ c = Company.objects.all()[0]
+ c.point_of_contact = Employee.objects.create(firstname="Guido", lastname="van Rossum")
+ c.save()
+
+ # F Expressions can also span joins
+ self.assertQuerysetEqual(
+ Company.objects.filter(ceo__firstname=F("point_of_contact__firstname")), [
+ "Foobar Ltd.",
+ "Test GmbH",
+ ],
+ lambda c: c.name
+ )
+
+ Company.objects.exclude(
+ ceo__firstname=F("point_of_contact__firstname")
+ ).update(name="foo")
+ self.assertEqual(
+ Company.objects.exclude(
+ ceo__firstname=F('point_of_contact__firstname')
+ ).get().name,
+ "foo",
+ )
+
+ self.assertRaises(FieldError,
+ lambda: Company.objects.exclude(
+ ceo__firstname=F('point_of_contact__firstname')
+ ).update(name=F('point_of_contact__lastname'))
+ )
+
+ # F expressions can be used to update attributes on single objects
+ test_gmbh = Company.objects.get(name="Test GmbH")
+ self.assertEqual(test_gmbh.num_employees, 32)
+ test_gmbh.num_employees = F("num_employees") + 4
+ test_gmbh.save()
+ test_gmbh = Company.objects.get(pk=test_gmbh.pk)
+ self.assertEqual(test_gmbh.num_employees, 36)
+
+ # F expressions cannot be used to update attributes which are foreign
+ # keys, or attributes which involve joins.
+ test_gmbh.point_of_contact = None
+ test_gmbh.save()
+ self.assertTrue(test_gmbh.point_of_contact is None)
+ def test():
+ test_gmbh.point_of_contact = F("ceo")
+ self.assertRaises(ValueError, test)
+
+ test_gmbh.point_of_contact = test_gmbh.ceo
+ test_gmbh.save()
+ test_gmbh.name = F("ceo__last_name")
+ self.assertRaises(FieldError, test_gmbh.save)
+
+ # F expressions cannot be used to update attributes on objects which do
+ # not yet exist in the database
+ acme = Company(
+ name="The Acme Widget Co.", num_employees=12, num_chairs=5,
+ ceo=test_gmbh.ceo
+ )
+ acme.num_employees = F("num_employees") + 16
+ self.assertRaises(TypeError, acme.save)
diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py
index f258134147..0dd1f72934 100644
--- a/tests/modeltests/field_defaults/models.py
+++ b/tests/modeltests/field_defaults/models.py
@@ -19,41 +19,3 @@ class Article(models.Model):
def __unicode__(self):
return self.headline
-
-__test__ = {'API_TESTS': u"""
->>> from datetime import datetime
-
-# No articles are in the system yet.
->>> Article.objects.all()
-[]
-
-# Create an Article.
->>> a = Article(id=None)
-
-# Grab the current datetime it should be very close to the default that just
-# got saved as a.pub_date
->>> now = datetime.now()
-
-# Save it into the database. You have to call save() explicitly.
->>> a.save()
-
-# Now it has an ID. Note it's a long integer, as designated by the trailing "L".
->>> a.id
-1L
-
-# Access database columns via Python attributes.
->>> a.headline
-u'Default headline'
-
-# make sure the two dates are sufficiently close
->>> d = now - a.pub_date
->>> d.seconds < 5
-True
-
-# make sure that SafeString/SafeUnicode fields work
->>> from django.utils.safestring import SafeUnicode, SafeString
->>> a.headline = SafeUnicode(u'Iñtërnâtiônàlizætiøn1')
->>> a.save()
->>> a.headline = SafeString(u'Iñtërnâtiônàlizætiøn1'.encode('utf-8'))
->>> a.save()
-"""}
diff --git a/tests/modeltests/field_defaults/tests.py b/tests/modeltests/field_defaults/tests.py
new file mode 100644
index 0000000000..a23f64404a
--- /dev/null
+++ b/tests/modeltests/field_defaults/tests.py
@@ -0,0 +1,16 @@
+from datetime import datetime
+
+from django.test import TestCase
+
+from models import Article
+
+
+class DefaultTests(TestCase):
+ def test_field_defaults(self):
+ a = Article()
+ now = datetime.now()
+ a.save()
+
+ self.assertTrue(isinstance(a.id, (int, long)))
+ self.assertEqual(a.headline, "Default headline")
+ self.assertTrue((now - a.pub_date).seconds < 5)
diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py
index a43714dcf5..1f9bdf5e0a 100644
--- a/tests/modeltests/field_subclassing/fields.py
+++ b/tests/modeltests/field_subclassing/fields.py
@@ -53,18 +53,18 @@ class SmallField(models.Field):
class JSONField(models.TextField):
__metaclass__ = models.SubfieldBase
-
+
description = ("JSONField automatically serializes and desializes values to "
"and from JSON.")
-
+
def to_python(self, value):
if not value:
return None
-
+
if isinstance(value, basestring):
value = json.loads(value)
return value
-
+
def get_db_prep_save(self, value):
if value is None:
return None
diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py
index a9fe88fe77..4a55b72961 100644
--- a/tests/modeltests/field_subclassing/models.py
+++ b/tests/modeltests/field_subclassing/models.py
@@ -2,7 +2,6 @@
Tests for field subclassing.
"""
-from django.core import serializers
from django.db import models
from django.utils.encoding import force_unicode
@@ -18,56 +17,3 @@ class MyModel(models.Model):
class DataModel(models.Model):
data = JSONField()
-
-__test__ = {'API_TESTS': ur"""
-# Creating a model with custom fields is done as per normal.
->>> s = Small(1, 2)
->>> print s
-12
->>> m = MyModel(name='m', data=s)
->>> m.save()
-
-# Custom fields still have normal field's attributes.
->>> m._meta.get_field('data').verbose_name
-'small field'
-
-# The m.data attribute has been initialised correctly. It's a Small object.
->>> m.data.first, m.data.second
-(1, 2)
-
-# The data loads back from the database correctly and 'data' has the right type.
->>> m1 = MyModel.objects.get(pk=m.pk)
->>> isinstance(m1.data, Small)
-True
->>> print m1.data
-12
-
-# We can do normal filtering on the custom field (and will get an error when we
-# use a lookup type that does not make sense).
->>> s1 = Small(1, 3)
->>> s2 = Small('a', 'b')
->>> MyModel.objects.filter(data__in=[s, s1, s2])
-[<MyModel: m>]
->>> MyModel.objects.filter(data__lt=s)
-Traceback (most recent call last):
-...
-TypeError: Invalid lookup type: 'lt'
-
-# Serialization works, too.
->>> stream = serializers.serialize("json", MyModel.objects.all())
->>> stream
-'[{"pk": 1, "model": "field_subclassing.mymodel", "fields": {"data": "12", "name": "m"}}]'
->>> obj = list(serializers.deserialize("json", stream))[0]
->>> obj.object == m
-True
-
-# Test retrieving custom field data
->>> m.delete()
->>> m1 = MyModel(name="1", data=Small(1, 2))
->>> m1.save()
->>> m2 = MyModel(name="2", data=Small(2, 3))
->>> m2.save()
->>> for m in MyModel.objects.all(): print unicode(m.data)
-12
-23
-"""}
diff --git a/tests/modeltests/field_subclassing/tests.py b/tests/modeltests/field_subclassing/tests.py
index 731ab51d24..ba7148a654 100644
--- a/tests/modeltests/field_subclassing/tests.py
+++ b/tests/modeltests/field_subclassing/tests.py
@@ -1,21 +1,75 @@
+from django.core import serializers
from django.test import TestCase
-from models import DataModel
+from fields import Small
+from models import DataModel, MyModel
class CustomField(TestCase):
def test_defer(self):
d = DataModel.objects.create(data=[1, 2, 3])
-
+
self.assertTrue(isinstance(d.data, list))
-
+
d = DataModel.objects.get(pk=d.pk)
self.assertTrue(isinstance(d.data, list))
self.assertEqual(d.data, [1, 2, 3])
-
+
d = DataModel.objects.defer("data").get(pk=d.pk)
d.save()
-
+
d = DataModel.objects.get(pk=d.pk)
self.assertTrue(isinstance(d.data, list))
self.assertEqual(d.data, [1, 2, 3])
+
+ def test_custom_field(self):
+ # Creating a model with custom fields is done as per normal.
+ s = Small(1, 2)
+ self.assertEqual(str(s), "12")
+
+ m = MyModel.objects.create(name="m", data=s)
+ # Custom fields still have normal field's attributes.
+ self.assertEqual(m._meta.get_field("data").verbose_name, "small field")
+
+ # The m.data attribute has been initialised correctly. It's a Small
+ # object.
+ self.assertEqual((m.data.first, m.data.second), (1, 2))
+
+ # The data loads back from the database correctly and 'data' has the
+ # right type.
+ m1 = MyModel.objects.get(pk=m.pk)
+ self.assertTrue(isinstance(m1.data, Small))
+ self.assertEqual(str(m1.data), "12")
+
+ # We can do normal filtering on the custom field (and will get an error
+ # when we use a lookup type that does not make sense).
+ s1 = Small(1, 3)
+ s2 = Small("a", "b")
+ self.assertQuerysetEqual(
+ MyModel.objects.filter(data__in=[s, s1, s2]), [
+ "m",
+ ],
+ lambda m: m.name,
+ )
+ self.assertRaises(TypeError, lambda: MyModel.objects.filter(data__lt=s))
+
+ # Serialization works, too.
+ stream = serializers.serialize("json", MyModel.objects.all())
+ self.assertEqual(stream, '[{"pk": 1, "model": "field_subclassing.mymodel", "fields": {"data": "12", "name": "m"}}]')
+
+ obj = list(serializers.deserialize("json", stream))[0]
+ self.assertEqual(obj.object, m)
+
+ # Test retrieving custom field data
+ m.delete()
+
+ m1 = MyModel.objects.create(name="1", data=Small(1, 2))
+ m2 = MyModel.objects.create(name="2", data=Small(2, 3))
+
+ self.assertQuerysetEqual(
+ MyModel.objects.all(), [
+ "12",
+ "23",
+ ],
+ lambda m: str(m.data)
+ )
diff --git a/tests/modeltests/files/models.py b/tests/modeltests/files/models.py
index 67c27b54b5..f798f74df9 100644
--- a/tests/modeltests/files/models.py
+++ b/tests/modeltests/files/models.py
@@ -7,10 +7,12 @@ and where files should be stored.
import random
import tempfile
+
from django.db import models
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage
+
temp_storage_location = tempfile.mkdtemp()
temp_storage = FileSystemStorage(location=temp_storage_location)
@@ -30,125 +32,3 @@ class Storage(models.Model):
custom = models.FileField(storage=temp_storage, upload_to=custom_upload_to)
random = models.FileField(storage=temp_storage, upload_to=random_upload_to)
default = models.FileField(storage=temp_storage, upload_to='tests', default='tests/default.txt')
-
-__test__ = {'API_TESTS':"""
-# Attempting to access a FileField from the class raises a descriptive error
->>> Storage.normal
-Traceback (most recent call last):
-...
-AttributeError: The 'normal' attribute can only be accessed from Storage instances.
-
-# An object without a file has limited functionality.
-
->>> obj1 = Storage()
->>> obj1.normal
-<FieldFile: None>
->>> obj1.normal.size
-Traceback (most recent call last):
-...
-ValueError: The 'normal' attribute has no file associated with it.
-
-# Saving a file enables full functionality.
-
->>> obj1.normal.save('django_test.txt', ContentFile('content'))
->>> obj1.normal
-<FieldFile: tests/django_test.txt>
->>> obj1.normal.size
-7
->>> obj1.normal.read()
-'content'
-
-# File objects can be assigned to FileField attributes, but shouldn't get
-# committed until the model it's attached to is saved.
-
->>> from django.core.files.uploadedfile import SimpleUploadedFile
->>> obj1.normal = SimpleUploadedFile('assignment.txt', 'content')
->>> dirs, files = temp_storage.listdir('tests')
->>> dirs
-[]
->>> files.sort()
->>> files == ['default.txt', 'django_test.txt']
-True
-
->>> obj1.save()
->>> dirs, files = temp_storage.listdir('tests')
->>> files.sort()
->>> files == ['assignment.txt', 'default.txt', 'django_test.txt']
-True
-
-# Files can be read in a little at a time, if necessary.
-
->>> obj1.normal.open()
->>> obj1.normal.read(3)
-'con'
->>> obj1.normal.read()
-'tent'
->>> '-'.join(obj1.normal.chunks(chunk_size=2))
-'co-nt-en-t'
-
-# Save another file with the same name.
-
->>> obj2 = Storage()
->>> obj2.normal.save('django_test.txt', ContentFile('more content'))
->>> obj2.normal
-<FieldFile: tests/django_test_1.txt>
->>> obj2.normal.size
-12
-
-# Push the objects into the cache to make sure they pickle properly
-
->>> from django.core.cache import cache
->>> cache.set('obj1', obj1)
->>> cache.set('obj2', obj2)
->>> cache.get('obj2').normal
-<FieldFile: tests/django_test_1.txt>
-
-# Deleting an object deletes the file it uses, if there are no other objects
-# still using that file.
-
->>> obj2.delete()
->>> obj2.normal.save('django_test.txt', ContentFile('more content'))
->>> obj2.normal
-<FieldFile: tests/django_test_1.txt>
-
-# Multiple files with the same name get _N appended to them.
-
->>> objs = [Storage() for i in range(3)]
->>> for o in objs:
-... o.normal.save('multiple_files.txt', ContentFile('Same Content'))
->>> [o.normal for o in objs]
-[<FieldFile: tests/multiple_files.txt>, <FieldFile: tests/multiple_files_1.txt>, <FieldFile: tests/multiple_files_2.txt>]
->>> for o in objs:
-... o.delete()
-
-# Default values allow an object to access a single file.
-
->>> obj3 = Storage.objects.create()
->>> obj3.default
-<FieldFile: tests/default.txt>
->>> obj3.default.read()
-'default content'
-
-# But it shouldn't be deleted, even if there are no more objects using it.
-
->>> obj3.delete()
->>> obj3 = Storage()
->>> obj3.default.read()
-'default content'
-
-# Verify the fix for #5655, making sure the directory is only determined once.
-
->>> obj4 = Storage()
->>> obj4.random.save('random_file', ContentFile('random content'))
->>> obj4.random
-<FieldFile: .../random_file>
-
-# Clean up the temporary files and dir.
->>> obj1.normal.delete()
->>> obj2.normal.delete()
->>> obj3.default.delete()
->>> obj4.random.delete()
-
->>> import shutil
->>> shutil.rmtree(temp_storage_location)
-"""}
diff --git a/tests/modeltests/files/tests.py b/tests/modeltests/files/tests.py
new file mode 100644
index 0000000000..025fcc574a
--- /dev/null
+++ b/tests/modeltests/files/tests.py
@@ -0,0 +1,100 @@
+import shutil
+
+from django.core.cache import cache
+from django.core.files.base import ContentFile
+from django.core.files.uploadedfile import SimpleUploadedFile
+from django.test import TestCase
+
+from models import Storage, temp_storage, temp_storage_location
+
+
+class FileTests(TestCase):
+ def tearDown(self):
+ shutil.rmtree(temp_storage_location)
+
+ def test_files(self):
+ # Attempting to access a FileField from the class raises a descriptive
+ # error
+ self.assertRaises(AttributeError, lambda: Storage.normal)
+
+ # An object without a file has limited functionality.
+ obj1 = Storage()
+ self.assertEqual(obj1.normal.name, "")
+ self.assertRaises(ValueError, lambda: obj1.normal.size)
+
+ # Saving a file enables full functionality.
+ obj1.normal.save("django_test.txt", ContentFile("content"))
+ self.assertEqual(obj1.normal.name, "tests/django_test.txt")
+ self.assertEqual(obj1.normal.size, 7)
+ self.assertEqual(obj1.normal.read(), "content")
+
+ # File objects can be assigned to FileField attributes, but shouldn't
+ # get committed until the model it's attached to is saved.
+ obj1.normal = SimpleUploadedFile("assignment.txt", "content")
+ dirs, files = temp_storage.listdir("tests")
+ self.assertEqual(dirs, [])
+ self.assertEqual(sorted(files), ["default.txt", "django_test.txt"])
+
+ obj1.save()
+ dirs, files = temp_storage.listdir("tests")
+ self.assertEqual(
+ sorted(files), ["assignment.txt", "default.txt", "django_test.txt"]
+ )
+
+ # Files can be read in a little at a time, if necessary.
+ obj1.normal.open()
+ self.assertEqual(obj1.normal.read(3), "con")
+ self.assertEqual(obj1.normal.read(), "tent")
+ self.assertEqual(list(obj1.normal.chunks(chunk_size=2)), ["co", "nt", "en", "t"])
+
+ # Save another file with the same name.
+ obj2 = Storage()
+ obj2.normal.save("django_test.txt", ContentFile("more content"))
+ self.assertEqual(obj2.normal.name, "tests/django_test_1.txt")
+ self.assertEqual(obj2.normal.size, 12)
+
+ # Push the objects into the cache to make sure they pickle properly
+ cache.set("obj1", obj1)
+ cache.set("obj2", obj2)
+ self.assertEqual(cache.get("obj2").normal.name, "tests/django_test_1.txt")
+
+ # Deleting an object deletes the file it uses, if there are no other
+ # objects still using that file.
+ obj2.delete()
+ obj2.normal.save("django_test.txt", ContentFile("more content"))
+ self.assertEqual(obj2.normal.name, "tests/django_test_1.txt")
+
+ # Multiple files with the same name get _N appended to them.
+ objs = [Storage() for i in range(3)]
+ for o in objs:
+ o.normal.save("multiple_files.txt", ContentFile("Same Content"))
+ self.assertEqual(
+ [o.normal.name for o in objs],
+ ["tests/multiple_files.txt", "tests/multiple_files_1.txt", "tests/multiple_files_2.txt"]
+ )
+ for o in objs:
+ o.delete()
+
+ # Default values allow an object to access a single file.
+ obj3 = Storage.objects.create()
+ self.assertEqual(obj3.default.name, "tests/default.txt")
+ self.assertEqual(obj3.default.read(), "default content")
+
+ # But it shouldn't be deleted, even if there are no more objects using
+ # it.
+ obj3.delete()
+ obj3 = Storage()
+ self.assertEqual(obj3.default.read(), "default content")
+
+ # Verify the fix for #5655, making sure the directory is only
+ # determined once.
+ obj4 = Storage()
+ obj4.random.save("random_file", ContentFile("random content"))
+ self.assertTrue(obj4.random.name.endswith("/random_file"))
+
+ # Clean up the temporary files and dir.
+ obj1.normal.delete()
+ obj2.normal.delete()
+ obj3.default.delete()
+ obj4.random.delete()
+
diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py
index 46e07a5e6b..0035bcc7e0 100644
--- a/tests/modeltests/fixtures/models.py
+++ b/tests/modeltests/fixtures/models.py
@@ -72,6 +72,14 @@ class Person(models.Model):
def natural_key(self):
return (self.name,)
+class SpyManager(PersonManager):
+ def get_query_set(self):
+ return super(SpyManager, self).get_query_set().filter(cover_blown=False)
+
+class Spy(Person):
+ objects = SpyManager()
+ cover_blown = models.BooleanField(default=False)
+
class Visa(models.Model):
person = models.ForeignKey(Person)
permissions = models.ManyToManyField(Permission, blank=True)
@@ -90,230 +98,3 @@ class Book(models.Model):
class Meta:
ordering = ('name',)
-
-__test__ = {'API_TESTS': """
->>> from django.core import management
->>> from django.db.models import get_app
-
-# 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)
-
-# Syncdb introduces 1 initial data object from initial_data.json.
->>> Article.objects.all()
-[<Article: Python program becomes self aware>]
-
-# Load fixture 1. Single JSON file, with two objects.
->>> management.call_command('loaddata', 'fixture1.json', verbosity=0)
->>> Article.objects.all()
-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
-# Dump the current contents of the database as a JSON fixture
->>> management.call_command('dumpdata', 'fixtures', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# Try just dumping the contents of fixtures.Category
->>> management.call_command('dumpdata', 'fixtures.Category', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]
-
-# ...and just fixtures.Article
->>> management.call_command('dumpdata', 'fixtures.Article', format='json')
-[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# ...and both
->>> management.call_command('dumpdata', 'fixtures.Category', 'fixtures.Article', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# Specify a specific model twice
->>> management.call_command('dumpdata', 'fixtures.Article', 'fixtures.Article', format='json')
-[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# Specify a dump that specifies Article both explicitly and implicitly
->>> management.call_command('dumpdata', 'fixtures.Article', 'fixtures', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# Same again, but specify in the reverse order
->>> management.call_command('dumpdata', 'fixtures', 'fixtures.Article', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# Specify one model from one application, and an entire other application.
->>> management.call_command('dumpdata', 'fixtures.Category', 'sites', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]
-
-# Load fixture 2. JSON file imported by default. Overwrites some existing objects
->>> management.call_command('loaddata', 'fixture2.json', verbosity=0)
->>> Article.objects.all()
-[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
-# Load fixture 3, XML format.
->>> management.call_command('loaddata', 'fixture3.xml', verbosity=0)
->>> Article.objects.all()
-[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>]
-
-# Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne.
->>> management.call_command('loaddata', 'fixture6.json', verbosity=0)
->>> Tag.objects.all()
-[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "law">]
-
-# Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne.
->>> management.call_command('loaddata', 'fixture7.xml', verbosity=0)
->>> Tag.objects.all()
-[<Tag: <Article: Copyright is fine the way it is> tagged "copyright">, <Tag: <Article: Copyright is fine the way it is> tagged "legal">, <Tag: <Article: Django conquers world!> tagged "django">, <Tag: <Article: Django conquers world!> tagged "world domination">]
-
-# Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany.
->>> management.call_command('loaddata', 'fixture8.json', verbosity=0)
->>> Visa.objects.all()
-[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user>, <Visa: Prince >]
-
-# Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany.
->>> management.call_command('loaddata', 'fixture9.xml', verbosity=0)
->>> Visa.objects.all()
-[<Visa: Django Reinhardt Can add user, Can change user, Can delete user>, <Visa: Stephane Grappelli Can add user, Can delete user>, <Visa: Artist formerly known as "Prince" Can change user>]
-
->>> Book.objects.all()
-[<Book: Music for all ages by Artist formerly known as "Prince" and Django Reinhardt>]
-
-# Load a fixture that doesn't exist
->>> management.call_command('loaddata', 'unknown.json', verbosity=0)
-
-# object list is unaffected
->>> Article.objects.all()
-[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>]
-
-# By default, you get raw keys on dumpdata
->>> management.call_command('dumpdata', 'fixtures.book', format='json')
-[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}]
-
-# But you can get natural keys if you ask for them and they are available
->>> management.call_command('dumpdata', 'fixtures.book', format='json', use_natural_keys=True)
-[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]
-
-# Dump the current contents of the database as a JSON fixture
->>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True)
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]
-
-# Dump the current contents of the database as an XML fixture
->>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True)
-<?xml version="1.0" encoding="utf-8"?>
-<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>
-
-"""}
-
-# Database flushing does not work on MySQL with the default storage engine
-# because it requires transaction support.
-if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
- __test__['API_TESTS'] += \
-"""
-# Reset the database representation of this app. This will delete all data.
->>> management.call_command('flush', verbosity=0, interactive=False)
->>> Article.objects.all()
-[<Article: Python program becomes self aware>]
-
-# Load fixture 1 again, using format discovery
->>> management.call_command('loaddata', 'fixture1', verbosity=0)
->>> Article.objects.all()
-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
-# Try to load fixture 2 using format discovery; this will fail
-# because there are two fixture2's in the fixtures directory
->>> management.call_command('loaddata', 'fixture2', verbosity=0) # doctest: +ELLIPSIS
-Multiple fixtures named 'fixture2' in '...fixtures'. Aborting.
-
-# object list is unaffected
->>> Article.objects.all()
-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
-# Dump the current contents of the database as a JSON fixture
->>> management.call_command('dumpdata', 'fixtures', format='json')
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
-
-# Load fixture 4 (compressed), using format discovery
->>> management.call_command('loaddata', 'fixture4', verbosity=0)
->>> Article.objects.all()
-[<Article: Django pets kitten>, <Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Load fixture 4 (compressed), using format specification
->>> management.call_command('loaddata', 'fixture4.json', verbosity=0)
->>> Article.objects.all()
-[<Article: Django pets kitten>, <Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Load fixture 5 (compressed), using format *and* compression specification
->>> management.call_command('loaddata', 'fixture5.json.zip', verbosity=0)
->>> Article.objects.all()
-[<Article: WoW subscribers now outnumber readers>, <Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Load fixture 5 (compressed), only compression specification
->>> management.call_command('loaddata', 'fixture5.zip', verbosity=0)
->>> Article.objects.all()
-[<Article: WoW subscribers now outnumber readers>, <Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Try to load fixture 5 using format and compression discovery; this will fail
-# because there are two fixture5's in the fixtures directory
->>> management.call_command('loaddata', 'fixture5', verbosity=0) # doctest: +ELLIPSIS
-Multiple fixtures named 'fixture5' in '...fixtures'. Aborting.
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly
->>> management.call_command('loaddata', 'db_fixture_1', verbosity=0)
->>> management.call_command('loaddata', 'db_fixture_2', verbosity=0)
->>> Article.objects.all()
-[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly
->>> management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default')
->>> management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default')
->>> Article.objects.all()
-[<Article: Who needs more than one database?>, <Article: Who needs to use compressed data?>, <Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Try to load db fixture 3. This won't load because the database identifier doesn't match
->>> management.call_command('loaddata', 'db_fixture_3', verbosity=0)
->>> Article.objects.all()
-[<Article: Python program becomes self aware>]
-
->>> management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default')
->>> Article.objects.all()
-[<Article: Python program becomes self aware>]
-
->>> management.call_command('flush', verbosity=0, interactive=False)
-
-# Load back in fixture 1, we need the articles from it
->>> management.call_command('loaddata', 'fixture1', verbosity=0)
-
-# Try to load fixture 6 using format discovery
->>> management.call_command('loaddata', 'fixture6', verbosity=0)
->>> Tag.objects.all()
-[<Tag: <Article: Time to reform copyright> tagged "copyright">, <Tag: <Article: Time to reform copyright> tagged "law">]
-
-# Dump the current contents of the database as a JSON fixture
->>> management.call_command('dumpdata', 'fixtures', format='json', use_natural_keys=True)
-[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]
-
-# Dump the current contents of the database as an XML fixture
->>> management.call_command('dumpdata', 'fixtures', format='xml', use_natural_keys=True)
-<?xml version="1.0" encoding="utf-8"?>
-<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>
-
-"""
-
-from django.test import TestCase
-
-class SampleTestCase(TestCase):
- fixtures = ['fixture1.json', 'fixture2.json']
-
- def testClassFixtures(self):
- "Check that test case has installed 4 fixture objects"
- self.assertEqual(Article.objects.count(), 4)
- self.assertEquals(str(Article.objects.all()), "[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]")
diff --git a/tests/modeltests/fixtures/tests.py b/tests/modeltests/fixtures/tests.py
new file mode 100644
index 0000000000..799a7328da
--- /dev/null
+++ b/tests/modeltests/fixtures/tests.py
@@ -0,0 +1,336 @@
+import StringIO
+import sys
+
+from django.test import TestCase, TransactionTestCase
+from django.conf import settings
+from django.core import management
+from django.db import DEFAULT_DB_ALIAS
+
+from models import Article, Blog, Book, Category, Person, Spy, Tag, Visa
+
+class TestCaseFixtureLoadingTests(TestCase):
+ fixtures = ['fixture1.json', 'fixture2.json']
+
+ def testClassFixtures(self):
+ "Check that test case has installed 4 fixture objects"
+ self.assertEqual(Article.objects.count(), 4)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Django conquers world!>',
+ '<Article: Copyright is fine the way it is>',
+ '<Article: Poker has no place on ESPN>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+class FixtureLoadingTests(TestCase):
+
+ def _dumpdata_assert(self, args, output, format='json', natural_keys=False,
+ use_base_manager=False, exclude_list=[]):
+ new_io = StringIO.StringIO()
+ management.call_command('dumpdata', *args, **{'format':format,
+ 'stdout':new_io,
+ 'stderr':new_io,
+ 'use_natural_keys':natural_keys,
+ 'use_base_manager':use_base_manager,
+ 'exclude': exclude_list})
+ command_output = new_io.getvalue().strip()
+ self.assertEqual(command_output, output)
+
+ def test_initial_data(self):
+ # Syncdb introduces 1 initial data object from initial_data.json.
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_loading_and_dumping(self):
+ new_io = StringIO.StringIO()
+
+ # Load fixture 1. Single JSON file, with two objects.
+ management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Time to reform copyright>',
+ '<Article: Poker has no place on ESPN>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ # Dump the current contents of the database as a JSON fixture
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # Try just dumping the contents of fixtures.Category
+ self._dumpdata_assert(['fixtures.Category'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]')
+
+ # ...and just fixtures.Article
+ self._dumpdata_assert(['fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # ...and both
+ self._dumpdata_assert(['fixtures.Category', 'fixtures.Article'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # Specify a specific model twice
+ self._dumpdata_assert(['fixtures.Article', 'fixtures.Article'], '[{"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # Specify a dump that specifies Article both explicitly and implicitly
+ self._dumpdata_assert(['fixtures.Article', 'fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # Same again, but specify in the reverse order
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # Specify one model from one application, and an entire other application.
+ self._dumpdata_assert(['fixtures.Category', 'sites'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]')
+
+ # Load fixture 2. JSON file imported by default. Overwrites some existing objects
+ management.call_command('loaddata', 'fixture2.json', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Django conquers world!>',
+ '<Article: Copyright is fine the way it is>',
+ '<Article: Poker has no place on ESPN>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ # Load fixture 3, XML format.
+ management.call_command('loaddata', 'fixture3.xml', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: XML identified as leading cause of cancer>',
+ '<Article: Django conquers world!>',
+ '<Article: Copyright is fine the way it is>',
+ '<Article: Poker on TV is great!>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ # Load fixture 6, JSON file with dynamic ContentType fields. Testing ManyToOne.
+ management.call_command('loaddata', 'fixture6.json', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Tag.objects.all(), [
+ '<Tag: <Article: Copyright is fine the way it is> tagged "copyright">',
+ '<Tag: <Article: Copyright is fine the way it is> tagged "law">'
+ ])
+
+ # Load fixture 7, XML file with dynamic ContentType fields. Testing ManyToOne.
+ management.call_command('loaddata', 'fixture7.xml', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Tag.objects.all(), [
+ '<Tag: <Article: Copyright is fine the way it is> tagged "copyright">',
+ '<Tag: <Article: Copyright is fine the way it is> tagged "legal">',
+ '<Tag: <Article: Django conquers world!> tagged "django">',
+ '<Tag: <Article: Django conquers world!> tagged "world domination">'
+ ])
+
+ # Load fixture 8, JSON file with dynamic Permission fields. Testing ManyToMany.
+ management.call_command('loaddata', 'fixture8.json', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Visa.objects.all(), [
+ '<Visa: Django Reinhardt Can add user, Can change user, Can delete user>',
+ '<Visa: Stephane Grappelli Can add user>',
+ '<Visa: Prince >'
+ ])
+
+ # Load fixture 9, XML file with dynamic Permission fields. Testing ManyToMany.
+ management.call_command('loaddata', 'fixture9.xml', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Visa.objects.all(), [
+ '<Visa: Django Reinhardt Can add user, Can change user, Can delete user>',
+ '<Visa: Stephane Grappelli Can add user, Can delete user>',
+ '<Visa: Artist formerly known as "Prince" Can change user>'
+ ])
+
+ self.assertQuerysetEqual(Book.objects.all(), [
+ '<Book: Music for all ages by Artist formerly known as "Prince" and Django Reinhardt>'
+ ])
+
+ # Load a fixture that doesn't exist
+ management.call_command('loaddata', 'unknown.json', verbosity=0, commit=False)
+
+ # object list is unaffected
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: XML identified as leading cause of cancer>',
+ '<Article: Django conquers world!>',
+ '<Article: Copyright is fine the way it is>',
+ '<Article: Poker on TV is great!>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ # By default, you get raw keys on dumpdata
+ self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [3, 1]}}]')
+
+ # But you can get natural keys if you ask for them and they are available
+ self._dumpdata_assert(['fixtures.book'], '[{"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
+
+ # Dump the current contents of the database as a JSON fixture
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 5, "model": "fixtures.article", "fields": {"headline": "XML identified as leading cause of cancer", "pub_date": "2006-06-16 16:00:00"}}, {"pk": 4, "model": "fixtures.article", "fields": {"headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker on TV is great!", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "legal", "tagged_id": 3}}, {"pk": 3, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "django", "tagged_id": 4}}, {"pk": 4, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "world domination", "tagged_id": 4}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Artist formerly known as \\"Prince\\""}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}, {"pk": 1, "model": "fixtures.visa", "fields": {"person": ["Django Reinhardt"], "permissions": [["add_user", "auth", "user"], ["change_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 2, "model": "fixtures.visa", "fields": {"person": ["Stephane Grappelli"], "permissions": [["add_user", "auth", "user"], ["delete_user", "auth", "user"]]}}, {"pk": 3, "model": "fixtures.visa", "fields": {"person": ["Artist formerly known as \\"Prince\\""], "permissions": [["change_user", "auth", "user"]]}}, {"pk": 1, "model": "fixtures.book", "fields": {"name": "Music for all ages", "authors": [["Artist formerly known as \\"Prince\\""], ["Django Reinhardt"]]}}]', natural_keys=True)
+
+ # Dump the current contents of the database as an XML fixture
+ self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="5" model="fixtures.article"><field type="CharField" name="headline">XML identified as leading cause of cancer</field><field type="DateTimeField" name="pub_date">2006-06-16 16:00:00</field></object><object pk="4" model="fixtures.article"><field type="CharField" name="headline">Django conquers world!</field><field type="DateTimeField" name="pub_date">2006-06-16 15:00:00</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Copyright is fine the way it is</field><field type="DateTimeField" name="pub_date">2006-06-16 14:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker on TV is great!</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">legal</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="3" model="fixtures.tag"><field type="CharField" name="name">django</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="4" model="fixtures.tag"><field type="CharField" name="name">world domination</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">4</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Artist formerly known as "Prince"</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object><object pk="1" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Django Reinhardt</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="2" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Stephane Grappelli</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>add_user</natural><natural>auth</natural><natural>user</natural></object><object><natural>delete_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="3" model="fixtures.visa"><field to="fixtures.person" name="person" rel="ManyToOneRel"><natural>Artist formerly known as "Prince"</natural></field><field to="auth.permission" name="permissions" rel="ManyToManyRel"><object><natural>change_user</natural><natural>auth</natural><natural>user</natural></object></field></object><object pk="1" model="fixtures.book"><field type="CharField" name="name">Music for all ages</field><field to="fixtures.person" name="authors" rel="ManyToManyRel"><object><natural>Artist formerly known as "Prince"</natural></object><object><natural>Django Reinhardt</natural></object></field></object></django-objects>""", format='xml', natural_keys=True)
+
+ def test_dumpdata_with_excludes(self):
+ # Load fixture1 which has a site, two articles, and a category
+ management.call_command('loaddata', 'fixture1.json', verbosity=0, commit=False)
+
+ # Excluding fixtures app should only leave sites
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}]',
+ exclude_list=['fixtures'])
+
+ # Excluding fixtures.Article should leave fixtures.Category
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]',
+ exclude_list=['fixtures.Article'])
+
+ # Excluding fixtures and fixtures.Article should be a no-op
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "sites.site", "fields": {"domain": "example.com", "name": "example.com"}}, {"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]',
+ exclude_list=['fixtures.Article'])
+
+ # Excluding sites and fixtures.Article should only leave fixtures.Category
+ self._dumpdata_assert(
+ ['sites', 'fixtures'],
+ '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}]',
+ exclude_list=['fixtures.Article', 'sites'])
+
+ # Excluding a bogus app should throw an error
+ self.assertRaises(SystemExit,
+ self._dumpdata_assert,
+ ['fixtures', 'sites'],
+ '',
+ exclude_list=['foo_app'])
+
+ # Excluding a bogus model should throw an error
+ self.assertRaises(SystemExit,
+ self._dumpdata_assert,
+ ['fixtures', 'sites'],
+ '',
+ exclude_list=['fixtures.FooModel'])
+
+ def test_dumpdata_with_filtering_manager(self):
+ Spy(name='Paul').save()
+ Spy(name='Alex', cover_blown=True).save()
+ self.assertQuerysetEqual(Spy.objects.all(),
+ ['<Spy: Paul>'])
+ # Use the default manager
+ self._dumpdata_assert(['fixtures.Spy'],'[{"pk": 1, "model": "fixtures.spy", "fields": {"cover_blown": false}}]')
+ # Dump using Django's base manager. Should return all objects,
+ # even those normally filtered by the manager
+ self._dumpdata_assert(['fixtures.Spy'], '[{"pk": 2, "model": "fixtures.spy", "fields": {"cover_blown": true}}, {"pk": 1, "model": "fixtures.spy", "fields": {"cover_blown": false}}]', use_base_manager=True)
+
+ def test_compress_format_loading(self):
+ # Load fixture 4 (compressed), using format specification
+ management.call_command('loaddata', 'fixture4.json', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Django pets kitten>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_compressed_specified_loading(self):
+ # Load fixture 5 (compressed), using format *and* compression specification
+ management.call_command('loaddata', 'fixture5.json.zip', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: WoW subscribers now outnumber readers>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_compressed_loading(self):
+ # Load fixture 5 (compressed), only compression specification
+ management.call_command('loaddata', 'fixture5.zip', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: WoW subscribers now outnumber readers>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_ambiguous_compressed_fixture(self):
+ # The name "fixture5" is ambigous, so loading it will raise an error
+ new_io = StringIO.StringIO()
+ management.call_command('loaddata', 'fixture5', verbosity=0, stderr=new_io, commit=False)
+ output = new_io.getvalue().strip().split('\n')
+ self.assertEqual(len(output), 1)
+ self.assertTrue(output[0].startswith("Multiple fixtures named 'fixture5'"))
+
+ def test_db_loading(self):
+ # Load db fixtures 1 and 2. These will load using the 'default' database identifier implicitly
+ management.call_command('loaddata', 'db_fixture_1', verbosity=0, commit=False)
+ management.call_command('loaddata', 'db_fixture_2', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Who needs more than one database?>',
+ '<Article: Who needs to use compressed data?>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_loading_using(self):
+ # Load db fixtures 1 and 2. These will load using the 'default' database identifier explicitly
+ management.call_command('loaddata', 'db_fixture_1', verbosity=0, using='default', commit=False)
+ management.call_command('loaddata', 'db_fixture_2', verbosity=0, using='default', commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Who needs more than one database?>',
+ '<Article: Who needs to use compressed data?>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_unmatched_identifier_loading(self):
+ # Try to load db fixture 3. This won't load because the database identifier doesn't match
+ management.call_command('loaddata', 'db_fixture_3', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Python program becomes self aware>'
+ ])
+
+ management.call_command('loaddata', 'db_fixture_3', verbosity=0, using='default', commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Python program becomes self aware>'
+ ])
+
+ def test_output_formats(self):
+ # Load back in fixture 1, we need the articles from it
+ management.call_command('loaddata', 'fixture1', verbosity=0, commit=False)
+
+ # Try to load fixture 6 using format discovery
+ management.call_command('loaddata', 'fixture6', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Tag.objects.all(), [
+ '<Tag: <Article: Time to reform copyright> tagged "copyright">',
+ '<Tag: <Article: Time to reform copyright> tagged "law">'
+ ])
+
+ # Dump the current contents of the database as a JSON fixture
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}, {"pk": 1, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "copyright", "tagged_id": 3}}, {"pk": 2, "model": "fixtures.tag", "fields": {"tagged_type": ["fixtures", "article"], "name": "law", "tagged_id": 3}}, {"pk": 1, "model": "fixtures.person", "fields": {"name": "Django Reinhardt"}}, {"pk": 3, "model": "fixtures.person", "fields": {"name": "Prince"}}, {"pk": 2, "model": "fixtures.person", "fields": {"name": "Stephane Grappelli"}}]', natural_keys=True)
+
+ # Dump the current contents of the database as an XML fixture
+ self._dumpdata_assert(['fixtures'], """<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0"><object pk="1" model="fixtures.category"><field type="CharField" name="title">News Stories</field><field type="TextField" name="description">Latest news stories</field></object><object pk="3" model="fixtures.article"><field type="CharField" name="headline">Time to reform copyright</field><field type="DateTimeField" name="pub_date">2006-06-16 13:00:00</field></object><object pk="2" model="fixtures.article"><field type="CharField" name="headline">Poker has no place on ESPN</field><field type="DateTimeField" name="pub_date">2006-06-16 12:00:00</field></object><object pk="1" model="fixtures.article"><field type="CharField" name="headline">Python program becomes self aware</field><field type="DateTimeField" name="pub_date">2006-06-16 11:00:00</field></object><object pk="1" model="fixtures.tag"><field type="CharField" name="name">copyright</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="2" model="fixtures.tag"><field type="CharField" name="name">law</field><field to="contenttypes.contenttype" name="tagged_type" rel="ManyToOneRel"><natural>fixtures</natural><natural>article</natural></field><field type="PositiveIntegerField" name="tagged_id">3</field></object><object pk="1" model="fixtures.person"><field type="CharField" name="name">Django Reinhardt</field></object><object pk="3" model="fixtures.person"><field type="CharField" name="name">Prince</field></object><object pk="2" model="fixtures.person"><field type="CharField" name="name">Stephane Grappelli</field></object></django-objects>""", format='xml', natural_keys=True)
+
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
+ class FixtureTransactionTests(TransactionTestCase):
+ def _dumpdata_assert(self, args, output, format='json'):
+ new_io = StringIO.StringIO()
+ management.call_command('dumpdata', *args, **{'format':format, 'stdout':new_io})
+ command_output = new_io.getvalue().strip()
+ self.assertEqual(command_output, output)
+
+ def test_format_discovery(self):
+ # Load fixture 1 again, using format discovery
+ management.call_command('loaddata', 'fixture1', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Time to reform copyright>',
+ '<Article: Poker has no place on ESPN>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ # Try to load fixture 2 using format discovery; this will fail
+ # because there are two fixture2's in the fixtures directory
+ new_io = StringIO.StringIO()
+ management.call_command('loaddata', 'fixture2', verbosity=0, stderr=new_io)
+ output = new_io.getvalue().strip().split('\n')
+ self.assertEqual(len(output), 1)
+ self.assertTrue(output[0].startswith("Multiple fixtures named 'fixture2'"))
+
+ # object list is unaffected
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Time to reform copyright>',
+ '<Article: Poker has no place on ESPN>',
+ '<Article: Python program becomes self aware>'
+ ])
+
+ # Dump the current contents of the database as a JSON fixture
+ self._dumpdata_assert(['fixtures'], '[{"pk": 1, "model": "fixtures.category", "fields": {"description": "Latest news stories", "title": "News Stories"}}, {"pk": 3, "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": 2, "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": 1, "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]')
+
+ # Load fixture 4 (compressed), using format discovery
+ management.call_command('loaddata', 'fixture4', verbosity=0, commit=False)
+ self.assertQuerysetEqual(Article.objects.all(), [
+ '<Article: Django pets kitten>',
+ '<Article: Time to reform copyright>',
+ '<Article: Poker has no place on ESPN>',
+ '<Article: Python program becomes self aware>'
+ ])
diff --git a/tests/modeltests/fixtures_model_package/models/__init__.py b/tests/modeltests/fixtures_model_package/models/__init__.py
index 1581102b88..c0450b27bf 100644
--- a/tests/modeltests/fixtures_model_package/models/__init__.py
+++ b/tests/modeltests/fixtures_model_package/models/__init__.py
@@ -12,43 +12,3 @@ class Article(models.Model):
app_label = 'fixtures_model_package'
ordering = ('-pub_date', 'headline')
-__test__ = {'API_TESTS': """
->>> from django.core import management
->>> from django.db.models import get_app
-
-# 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)
-
-# Syncdb introduces 1 initial data object from initial_data.json.
->>> Article.objects.all()
-[<Article: Python program becomes self aware>]
-
-# Load fixture 1. Single JSON file, with two objects.
->>> management.call_command('loaddata', 'fixture1.json', verbosity=0)
->>> Article.objects.all()
-[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
-# Load fixture 2. JSON file imported by default. Overwrites some existing objects
->>> management.call_command('loaddata', 'fixture2.json', verbosity=0)
->>> Article.objects.all()
-[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-
-# Load a fixture that doesn't exist
->>> management.call_command('loaddata', 'unknown.json', verbosity=0)
-
-# object list is unaffected
->>> Article.objects.all()
-[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-"""}
-
-
-from django.test import TestCase
-
-class SampleTestCase(TestCase):
- fixtures = ['fixture1.json', 'fixture2.json']
-
- def testClassFixtures(self):
- "Check that test case has installed 4 fixture objects"
- self.assertEqual(Article.objects.count(), 4)
- self.assertEquals(str(Article.objects.all()), "[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]")
diff --git a/tests/modeltests/fixtures_model_package/tests.py b/tests/modeltests/fixtures_model_package/tests.py
new file mode 100644
index 0000000000..1fae5ee807
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/tests.py
@@ -0,0 +1,71 @@
+from django.core import management
+from django.test import TestCase
+
+from models import Article
+
+
+class SampleTestCase(TestCase):
+ fixtures = ['fixture1.json', 'fixture2.json']
+
+ def testClassFixtures(self):
+ "Test cases can load fixture objects into models defined in packages"
+ self.assertEqual(Article.objects.count(), 4)
+ self.assertQuerysetEqual(
+ Article.objects.all(),[
+ "Django conquers world!",
+ "Copyright is fine the way it is",
+ "Poker has no place on ESPN",
+ "Python program becomes self aware"
+ ],
+ lambda a: a.headline
+ )
+
+
+class FixtureTestCase(TestCase):
+ def test_initial_data(self):
+ "Fixtures can load initial data into models defined in packages"
+ #Syncdb introduces 1 initial data object from initial_data.json
+ self.assertQuerysetEqual(
+ Article.objects.all(), [
+ "Python program becomes self aware"
+ ],
+ lambda a: a.headline
+ )
+
+ def test_loaddata(self):
+ "Fixtures can load data into models defined in packages"
+ # Load fixture 1. Single JSON file, with two objects
+ management.call_command("loaddata", "fixture1.json", verbosity=0, commit=False)
+ self.assertQuerysetEqual(
+ Article.objects.all(), [
+ "Time to reform copyright",
+ "Poker has no place on ESPN",
+ "Python program becomes self aware",
+ ],
+ lambda a: a.headline,
+ )
+
+ # Load fixture 2. JSON file imported by default. Overwrites some
+ # existing objects
+ management.call_command("loaddata", "fixture2.json", verbosity=0, commit=False)
+ self.assertQuerysetEqual(
+ Article.objects.all(), [
+ "Django conquers world!",
+ "Copyright is fine the way it is",
+ "Poker has no place on ESPN",
+ "Python program becomes self aware",
+ ],
+ lambda a: a.headline,
+ )
+
+ # Load a fixture that doesn't exist
+ management.call_command("loaddata", "unknown.json", verbosity=0, commit=False)
+ self.assertQuerysetEqual(
+ Article.objects.all(), [
+ "Django conquers world!",
+ "Copyright is fine the way it is",
+ "Poker has no place on ESPN",
+ "Python program becomes self aware",
+ ],
+ lambda a: a.headline,
+ )
diff --git a/tests/modeltests/force_insert_update/models.py b/tests/modeltests/force_insert_update/models.py
index 2489740e98..9516be7718 100644
--- a/tests/modeltests/force_insert_update/models.py
+++ b/tests/modeltests/force_insert_update/models.py
@@ -11,54 +11,3 @@ class Counter(models.Model):
class WithCustomPK(models.Model):
name = models.IntegerField(primary_key=True)
value = models.IntegerField()
-
-__test__ = {"API_TESTS": """
->>> c = Counter.objects.create(name="one", value=1)
-
-# The normal case
->>> c.value = 2
->>> c.save()
-
-# Same thing, via an update
->>> c.value = 3
->>> c.save(force_update=True)
-
-# Won't work because force_update and force_insert are mutually exclusive
->>> c.value = 4
->>> c.save(force_insert=True, force_update=True)
-Traceback (most recent call last):
-...
-ValueError: Cannot force both insert and updating in model saving.
-
-# Try to update something that doesn't have a primary key in the first place.
->>> c1 = Counter(name="two", value=2)
->>> c1.save(force_update=True)
-Traceback (most recent call last):
-...
-ValueError: Cannot force an update in save() with no primary key.
-
->>> c1.save(force_insert=True)
-
-# Won't work because we can't insert a pk of the same value.
->>> sid = transaction.savepoint()
->>> c.value = 5
->>> try:
-... c.save(force_insert=True)
-... except Exception, e:
-... if isinstance(e, IntegrityError):
-... print "Pass"
-... else:
-... print "Fail with %s" % type(e)
-Pass
->>> transaction.savepoint_rollback(sid)
-
-# Trying to update should still fail, even with manual primary keys, if the
-# data isn't in the database already.
->>> obj = WithCustomPK(name=1, value=1)
->>> obj.save(force_update=True)
-Traceback (most recent call last):
-...
-DatabaseError: ...
-
-"""
-}
diff --git a/tests/modeltests/force_insert_update/tests.py b/tests/modeltests/force_insert_update/tests.py
new file mode 100644
index 0000000000..bd3eb7dcf6
--- /dev/null
+++ b/tests/modeltests/force_insert_update/tests.py
@@ -0,0 +1,38 @@
+from django.db import transaction, IntegrityError, DatabaseError
+from django.test import TestCase
+
+from models import Counter, WithCustomPK
+
+
+class ForceTests(TestCase):
+ def test_force_update(self):
+ c = Counter.objects.create(name="one", value=1)
+ # The normal case
+
+ c.value = 2
+ c.save()
+ # Same thing, via an update
+ c.value = 3
+ c.save(force_update=True)
+
+ # Won't work because force_update and force_insert are mutually
+ # exclusive
+ c.value = 4
+ self.assertRaises(ValueError, c.save, force_insert=True, force_update=True)
+
+ # Try to update something that doesn't have a primary key in the first
+ # place.
+ c1 = Counter(name="two", value=2)
+ self.assertRaises(ValueError, c1.save, force_update=True)
+ c1.save(force_insert=True)
+
+ # Won't work because we can't insert a pk of the same value.
+ sid = transaction.savepoint()
+ c.value = 5
+ self.assertRaises(IntegrityError, c.save, force_insert=True)
+ transaction.savepoint_rollback(sid)
+
+ # Trying to update should still fail, even with manual primary keys, if
+ # the data isn't in the database already.
+ obj = WithCustomPK(name=1, value=1)
+ self.assertRaises(DatabaseError, obj.save, force_update=True)
diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py
index 624f3a879a..1eeb299267 100644
--- a/tests/modeltests/get_latest/models.py
+++ b/tests/modeltests/get_latest/models.py
@@ -28,52 +28,3 @@ class Person(models.Model):
def __unicode__(self):
return self.name
-
-__test__ = {'API_TESTS':"""
-# Because no Articles exist yet, latest() raises ArticleDoesNotExist.
->>> Article.objects.latest()
-Traceback (most recent call last):
- ...
-DoesNotExist: Article matching query does not exist.
-
-# Create a couple of Articles.
->>> from datetime import datetime
->>> a1 = Article(headline='Article 1', pub_date=datetime(2005, 7, 26), expire_date=datetime(2005, 9, 1))
->>> a1.save()
->>> a2 = Article(headline='Article 2', pub_date=datetime(2005, 7, 27), expire_date=datetime(2005, 7, 28))
->>> a2.save()
->>> a3 = Article(headline='Article 3', pub_date=datetime(2005, 7, 27), expire_date=datetime(2005, 8, 27))
->>> a3.save()
->>> a4 = Article(headline='Article 4', pub_date=datetime(2005, 7, 28), expire_date=datetime(2005, 7, 30))
->>> a4.save()
-
-# Get the latest Article.
->>> Article.objects.latest()
-<Article: Article 4>
-
-# Get the latest Article that matches certain filters.
->>> Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest()
-<Article: Article 1>
-
-# Pass a custom field name to latest() to change the field that's used to
-# determine the latest object.
->>> Article.objects.latest('expire_date')
-<Article: Article 1>
-
->>> Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date')
-<Article: Article 3>
-
-# You can still use latest() with a model that doesn't have "get_latest_by"
-# set -- just pass in the field name manually.
->>> p1 = Person(name='Ralph', birthday=datetime(1950, 1, 1))
->>> p1.save()
->>> p2 = Person(name='Stephanie', birthday=datetime(1960, 2, 3))
->>> p2.save()
->>> Person.objects.latest()
-Traceback (most recent call last):
- ...
-AssertionError: latest() requires either a field_name parameter or 'get_latest_by' in the model
-
->>> Person.objects.latest('birthday')
-<Person: Stephanie>
-"""}
diff --git a/tests/modeltests/get_latest/tests.py b/tests/modeltests/get_latest/tests.py
new file mode 100644
index 0000000000..3c3588bba0
--- /dev/null
+++ b/tests/modeltests/get_latest/tests.py
@@ -0,0 +1,53 @@
+from datetime import datetime
+
+from django.test import TestCase
+
+from models import Article, Person
+
+
+class LatestTests(TestCase):
+ def test_latest(self):
+ # Because no Articles exist yet, latest() raises ArticleDoesNotExist.
+ self.assertRaises(Article.DoesNotExist, Article.objects.latest)
+
+ a1 = Article.objects.create(
+ headline="Article 1", pub_date=datetime(2005, 7, 26),
+ expire_date=datetime(2005, 9, 1)
+ )
+ a2 = Article.objects.create(
+ headline="Article 2", pub_date=datetime(2005, 7, 27),
+ expire_date=datetime(2005, 7, 28)
+ )
+ a3 = Article.objects.create(
+ headline="Article 3", pub_date=datetime(2005, 7, 27),
+ expire_date=datetime(2005, 8, 27)
+ )
+ a4 = Article.objects.create(
+ headline="Article 4", pub_date=datetime(2005, 7, 28),
+ expire_date=datetime(2005, 7, 30)
+ )
+
+ # Get the latest Article.
+ self.assertEqual(Article.objects.latest(), a4)
+ # Get the latest Article that matches certain filters.
+ self.assertEqual(
+ Article.objects.filter(pub_date__lt=datetime(2005, 7, 27)).latest(),
+ a1
+ )
+
+ # Pass a custom field name to latest() to change the field that's used
+ # to determine the latest object.
+ self.assertEqual(Article.objects.latest('expire_date'), a1)
+ self.assertEqual(
+ Article.objects.filter(pub_date__gt=datetime(2005, 7, 26)).latest('expire_date'),
+ a3,
+ )
+
+ def test_latest_manual(self):
+ # You can still use latest() with a model that doesn't have
+ # "get_latest_by" set -- just pass in the field name manually.
+ p1 = Person.objects.create(name="Ralph", birthday=datetime(1950, 1, 1))
+ p2 = Person.objects.create(name="Stephanie", birthday=datetime(1960, 2, 3))
+ self.assertRaises(AssertionError, Person.objects.latest)
+
+ self.assertEqual(Person.objects.latest("birthday"), p2)
diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py
index b2812e61e7..eb3cd8254d 100644
--- a/tests/modeltests/get_object_or_404/models.py
+++ b/tests/modeltests/get_object_or_404/models.py
@@ -32,76 +32,3 @@ class Article(models.Model):
def __unicode__(self):
return self.title
-
-__test__ = {'API_TESTS':"""
-# Create some Authors.
->>> a = Author.objects.create(name="Brave Sir Robin")
->>> a.save()
->>> a2 = Author.objects.create(name="Patsy")
->>> a2.save()
-
-# No Articles yet, so we should get a Http404 error.
->>> get_object_or_404(Article, title="Foo")
-Traceback (most recent call last):
-...
-Http404: No Article matches the given query.
-
-# Create an Article.
->>> article = Article.objects.create(title="Run away!")
->>> article.authors = [a, a2]
->>> article.save()
-
-# get_object_or_404 can be passed a Model to query.
->>> get_object_or_404(Article, title__contains="Run")
-<Article: Run away!>
-
-# We can also use the Article manager through an Author object.
->>> get_object_or_404(a.article_set, title__contains="Run")
-<Article: Run away!>
-
-# No articles containing "Camelot". This should raise a Http404 error.
->>> get_object_or_404(a.article_set, title__contains="Camelot")
-Traceback (most recent call last):
-...
-Http404: No Article matches the given query.
-
-# Custom managers can be used too.
->>> get_object_or_404(Article.by_a_sir, title="Run away!")
-<Article: Run away!>
-
-# QuerySets can be used too.
->>> get_object_or_404(Article.objects.all(), title__contains="Run")
-<Article: Run away!>
-
-# Just as when using a get() lookup, you will get an error if more than one
-# object is returned.
->>> get_object_or_404(Author.objects.all())
-Traceback (most recent call last):
-...
-MultipleObjectsReturned: get() returned more than one Author -- it returned ...! Lookup parameters were {}
-
-# Using an EmptyQuerySet raises a Http404 error.
->>> get_object_or_404(Article.objects.none(), title__contains="Run")
-Traceback (most recent call last):
-...
-Http404: No Article matches the given query.
-
-# get_list_or_404 can be used to get lists of objects
->>> get_list_or_404(a.article_set, title__icontains='Run')
-[<Article: Run away!>]
-
-# Http404 is returned if the list is empty.
->>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
-Traceback (most recent call last):
-...
-Http404: No Article matches the given query.
-
-# Custom managers can be used too.
->>> get_list_or_404(Article.by_a_sir, title__icontains="Run")
-[<Article: Run away!>]
-
-# QuerySets can be used too.
->>> get_list_or_404(Article.objects.all(), title__icontains="Run")
-[<Article: Run away!>]
-
-"""}
diff --git a/tests/modeltests/get_object_or_404/tests.py b/tests/modeltests/get_object_or_404/tests.py
new file mode 100644
index 0000000000..b8c4f7510b
--- /dev/null
+++ b/tests/modeltests/get_object_or_404/tests.py
@@ -0,0 +1,80 @@
+from django.http import Http404
+from django.shortcuts import get_object_or_404, get_list_or_404
+from django.test import TestCase
+
+from models import Author, Article
+
+
+class GetObjectOr404Tests(TestCase):
+ def test_get_object_or_404(self):
+ a1 = Author.objects.create(name="Brave Sir Robin")
+ a2 = Author.objects.create(name="Patsy")
+
+ # No Articles yet, so we should get a Http404 error.
+ self.assertRaises(Http404, get_object_or_404, Article, title="Foo")
+
+ article = Article.objects.create(title="Run away!")
+ article.authors = [a1, a2]
+ # get_object_or_404 can be passed a Model to query.
+ self.assertEqual(
+ get_object_or_404(Article, title__contains="Run"),
+ article
+ )
+
+ # We can also use the Article manager through an Author object.
+ self.assertEqual(
+ get_object_or_404(a1.article_set, title__contains="Run"),
+ article
+ )
+
+ # No articles containing "Camelot". This should raise a Http404 error.
+ self.assertRaises(Http404,
+ get_object_or_404, a1.article_set, title__contains="Camelot"
+ )
+
+ # Custom managers can be used too.
+ self.assertEqual(
+ get_object_or_404(Article.by_a_sir, title="Run away!"),
+ article
+ )
+
+ # QuerySets can be used too.
+ self.assertEqual(
+ get_object_or_404(Article.objects.all(), title__contains="Run"),
+ article
+ )
+
+ # Just as when using a get() lookup, you will get an error if more than
+ # one object is returned.
+
+ self.assertRaises(Author.MultipleObjectsReturned,
+ get_object_or_404, Author.objects.all()
+ )
+
+ # Using an EmptyQuerySet raises a Http404 error.
+ self.assertRaises(Http404,
+ get_object_or_404, Article.objects.none(), title__contains="Run"
+ )
+
+ # get_list_or_404 can be used to get lists of objects
+ self.assertEqual(
+ get_list_or_404(a1.article_set, title__icontains="Run"),
+ [article]
+ )
+
+ # Http404 is returned if the list is empty.
+ self.assertRaises(Http404,
+ get_list_or_404, a1.article_set, title__icontains="Shrubbery"
+ )
+
+ # Custom managers can be used too.
+ self.assertEqual(
+ get_list_or_404(Article.by_a_sir, title__icontains="Run"),
+ [article]
+ )
+
+ # QuerySets can be used too.
+ self.assertEqual(
+ get_list_or_404(Article.objects.all(), title__icontains="Run"),
+ [article]
+ )
diff --git a/tests/modeltests/get_or_create/models.py b/tests/modeltests/get_or_create/models.py
index 56baa5c1ed..db5719b79e 100644
--- a/tests/modeltests/get_or_create/models.py
+++ b/tests/modeltests/get_or_create/models.py
@@ -19,65 +19,3 @@ class Person(models.Model):
class ManualPrimaryKeyTest(models.Model):
id = models.IntegerField(primary_key=True)
data = models.CharField(max_length=100)
-
-__test__ = {'API_TESTS':"""
-# Acting as a divine being, create an Person.
->>> from datetime import date
->>> p = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
->>> p.save()
-
-# Only one Person is in the database at this point.
->>> Person.objects.count()
-1
-
-# get_or_create() a person with similar first names.
->>> p, created = Person.objects.get_or_create(first_name='John', last_name='Lennon', defaults={'birthday': date(1940, 10, 9)})
-
-# get_or_create() didn't have to create an object.
->>> created
-False
-
-# There's still only one Person in the database.
->>> Person.objects.count()
-1
-
-# get_or_create() a Person with a different name.
->>> p, created = Person.objects.get_or_create(first_name='George', last_name='Harrison', defaults={'birthday': date(1943, 2, 25)})
->>> created
-True
->>> Person.objects.count()
-2
-
-# If we execute the exact same statement, it won't create a Person.
->>> p, created = Person.objects.get_or_create(first_name='George', last_name='Harrison', defaults={'birthday': date(1943, 2, 25)})
->>> created
-False
->>> Person.objects.count()
-2
-
-# If you don't specify a value or default value for all required fields, you
-# will get an error.
->>> try:
-... p, created = Person.objects.get_or_create(first_name='Tom', last_name='Smith')
-... except Exception, e:
-... if isinstance(e, IntegrityError):
-... print "Pass"
-... else:
-... print "Fail with %s" % type(e)
-Pass
-
-# If you specify an existing primary key, but different other fields, then you
-# will get an error and data will not be updated.
->>> m = ManualPrimaryKeyTest(id=1, data='Original')
->>> m.save()
->>> try:
-... m, created = ManualPrimaryKeyTest.objects.get_or_create(id=1, data='Different')
-... except Exception, e:
-... if isinstance(e, IntegrityError):
-... print "Pass"
-... else:
-... print "Fail with %s" % type(e)
-Pass
->>> ManualPrimaryKeyTest.objects.get(id=1).data == 'Original'
-True
-"""}
diff --git a/tests/modeltests/get_or_create/tests.py b/tests/modeltests/get_or_create/tests.py
new file mode 100644
index 0000000000..1999b20c76
--- /dev/null
+++ b/tests/modeltests/get_or_create/tests.py
@@ -0,0 +1,52 @@
+from datetime import date
+
+from django.db import IntegrityError
+from django.test import TransactionTestCase
+
+from models import Person, ManualPrimaryKeyTest
+
+
+class GetOrCreateTests(TransactionTestCase):
+ def test_get_or_create(self):
+ p = Person.objects.create(
+ first_name='John', last_name='Lennon', birthday=date(1940, 10, 9)
+ )
+
+ p, created = Person.objects.get_or_create(
+ first_name="John", last_name="Lennon", defaults={
+ "birthday": date(1940, 10, 9)
+ }
+ )
+ self.assertFalse(created)
+ self.assertEqual(Person.objects.count(), 1)
+
+ p, created = Person.objects.get_or_create(
+ first_name='George', last_name='Harrison', defaults={
+ 'birthday': date(1943, 2, 25)
+ }
+ )
+ self.assertTrue(created)
+ self.assertEqual(Person.objects.count(), 2)
+
+ # If we execute the exact same statement, it won't create a Person.
+ p, created = Person.objects.get_or_create(
+ first_name='George', last_name='Harrison', defaults={
+ 'birthday': date(1943, 2, 25)
+ }
+ )
+ self.assertFalse(created)
+ self.assertEqual(Person.objects.count(), 2)
+
+ # If you don't specify a value or default value for all required
+ # fields, you will get an error.
+ self.assertRaises(IntegrityError,
+ Person.objects.get_or_create, first_name="Tom", last_name="Smith"
+ )
+
+ # If you specify an existing primary key, but different other fields,
+ # then you will get an error and data will not be updated.
+ m = ManualPrimaryKeyTest.objects.create(id=1, data="Original")
+ self.assertRaises(IntegrityError,
+ ManualPrimaryKeyTest.objects.get_or_create, id=1, data="Different"
+ )
+ self.assertEqual(ManualPrimaryKeyTest.objects.get(id=1).data, "Original")
diff --git a/tests/modeltests/m2m_and_m2o/models.py b/tests/modeltests/m2m_and_m2o/models.py
index 0ab7a72d57..0fea1a2e7b 100644
--- a/tests/modeltests/m2m_and_m2o/models.py
+++ b/tests/modeltests/m2m_and_m2o/models.py
@@ -19,47 +19,3 @@ class Issue(models.Model):
class Meta:
ordering = ('num',)
-
-
-__test__ = {'API_TESTS':"""
->>> Issue.objects.all()
-[]
->>> r = User(username='russell')
->>> r.save()
->>> g = User(username='gustav')
->>> g.save()
-
->>> i = Issue(num=1)
->>> i.client = r
->>> i.save()
-
->>> i2 = Issue(num=2)
->>> i2.client = r
->>> i2.save()
->>> i2.cc.add(r)
-
->>> i3 = Issue(num=3)
->>> i3.client = g
->>> i3.save()
->>> i3.cc.add(r)
-
->>> from django.db.models.query import Q
-
->>> Issue.objects.filter(client=r.id)
-[<Issue: 1>, <Issue: 2>]
->>> Issue.objects.filter(client=g.id)
-[<Issue: 3>]
->>> Issue.objects.filter(cc__id__exact=g.id)
-[]
->>> Issue.objects.filter(cc__id__exact=r.id)
-[<Issue: 2>, <Issue: 3>]
-
-# These queries combine results from the m2m and the m2o relationships.
-# They're three ways of saying the same thing.
->>> Issue.objects.filter(Q(cc__id__exact=r.id) | Q(client=r.id))
-[<Issue: 1>, <Issue: 2>, <Issue: 3>]
->>> Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id)
-[<Issue: 1>, <Issue: 2>, <Issue: 3>]
->>> Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id))
-[<Issue: 1>, <Issue: 2>, <Issue: 3>]
-"""}
diff --git a/tests/modeltests/m2m_and_m2o/tests.py b/tests/modeltests/m2m_and_m2o/tests.py
new file mode 100644
index 0000000000..dedf9cdf26
--- /dev/null
+++ b/tests/modeltests/m2m_and_m2o/tests.py
@@ -0,0 +1,75 @@
+from django.db.models import Q
+from django.test import TestCase
+
+from models import Issue, User
+
+
+class RelatedObjectTests(TestCase):
+ def test_m2m_and_m2o(self):
+ r = User.objects.create(username="russell")
+ g = User.objects.create(username="gustav")
+
+ i1 = Issue(num=1)
+ i1.client = r
+ i1.save()
+
+ i2 = Issue(num=2)
+ i2.client = r
+ i2.save()
+ i2.cc.add(r)
+
+ i3 = Issue(num=3)
+ i3.client = g
+ i3.save()
+ i3.cc.add(r)
+
+ self.assertQuerysetEqual(
+ Issue.objects.filter(client=r.id), [
+ 1,
+ 2,
+ ],
+ lambda i: i.num
+ )
+ self.assertQuerysetEqual(
+ Issue.objects.filter(client=g.id), [
+ 3,
+ ],
+ lambda i: i.num
+ )
+ self.assertQuerysetEqual(
+ Issue.objects.filter(cc__id__exact=g.id), []
+ )
+ self.assertQuerysetEqual(
+ Issue.objects.filter(cc__id__exact=r.id), [
+ 2,
+ 3,
+ ],
+ lambda i: i.num
+ )
+
+ # These queries combine results from the m2m and the m2o relationships.
+ # They're three ways of saying the same thing.
+ self.assertQuerysetEqual(
+ Issue.objects.filter(Q(cc__id__exact = r.id) | Q(client=r.id)), [
+ 1,
+ 2,
+ 3,
+ ],
+ lambda i: i.num
+ )
+ self.assertQuerysetEqual(
+ Issue.objects.filter(cc__id__exact=r.id) | Issue.objects.filter(client=r.id), [
+ 1,
+ 2,
+ 3,
+ ],
+ lambda i: i.num
+ )
+ self.assertQuerysetEqual(
+ Issue.objects.filter(Q(client=r.id) | Q(cc__id__exact=r.id)), [
+ 1,
+ 2,
+ 3,
+ ],
+ lambda i: i.num
+ )
diff --git a/tests/modeltests/m2m_intermediary/models.py b/tests/modeltests/m2m_intermediary/models.py
index e9f964aa4e..8042a52b38 100644
--- a/tests/modeltests/m2m_intermediary/models.py
+++ b/tests/modeltests/m2m_intermediary/models.py
@@ -34,35 +34,3 @@ class Writer(models.Model):
def __unicode__(self):
return u'%s (%s)' % (self.reporter, self.position)
-__test__ = {'API_TESTS':"""
-# Create a few Reporters.
->>> r1 = Reporter(first_name='John', last_name='Smith')
->>> r1.save()
->>> r2 = Reporter(first_name='Jane', last_name='Doe')
->>> r2.save()
-
-# Create an Article.
->>> from datetime import datetime
->>> a = Article(headline='This is a test', pub_date=datetime(2005, 7, 27))
->>> a.save()
-
-# Create a few Writers.
->>> w1 = Writer(reporter=r1, article=a, position='Main writer')
->>> w1.save()
->>> w2 = Writer(reporter=r2, article=a, position='Contributor')
->>> w2.save()
-
-# Play around with the API.
->>> a.writer_set.select_related().order_by('-position')
-[<Writer: John Smith (Main writer)>, <Writer: Jane Doe (Contributor)>]
->>> w1.reporter
-<Reporter: John Smith>
->>> w2.reporter
-<Reporter: Jane Doe>
->>> w1.article
-<Article: This is a test>
->>> w2.article
-<Article: This is a test>
->>> r1.writer_set.all()
-[<Writer: John Smith (Main writer)>]
-"""}
diff --git a/tests/modeltests/m2m_intermediary/tests.py b/tests/modeltests/m2m_intermediary/tests.py
new file mode 100644
index 0000000000..5f357412a5
--- /dev/null
+++ b/tests/modeltests/m2m_intermediary/tests.py
@@ -0,0 +1,38 @@
+from datetime import datetime
+
+from django.test import TestCase
+
+from models import Reporter, Article, Writer
+
+
+class M2MIntermediaryTests(TestCase):
+ def test_intermeiary(self):
+ r1 = Reporter.objects.create(first_name="John", last_name="Smith")
+ r2 = Reporter.objects.create(first_name="Jane", last_name="Doe")
+
+ a = Article.objects.create(
+ headline="This is a test", pub_date=datetime(2005, 7, 27)
+ )
+
+ w1 = Writer.objects.create(reporter=r1, article=a, position="Main writer")
+ w2 = Writer.objects.create(reporter=r2, article=a, position="Contributor")
+
+ self.assertQuerysetEqual(
+ a.writer_set.select_related().order_by("-position"), [
+ ("John Smith", "Main writer"),
+ ("Jane Doe", "Contributor"),
+ ],
+ lambda w: (unicode(w.reporter), w.position)
+ )
+ self.assertEqual(w1.reporter, r1)
+ self.assertEqual(w2.reporter, r2)
+
+ self.assertEqual(w1.article, a)
+ self.assertEqual(w2.article, a)
+
+ self.assertQuerysetEqual(
+ r1.writer_set.all(), [
+ ("John Smith", "Main writer")
+ ],
+ lambda w: (unicode(w.reporter), w.position)
+ )
diff --git a/tests/modeltests/m2m_multiple/models.py b/tests/modeltests/m2m_multiple/models.py
index 42e74553d9..e53f840653 100644
--- a/tests/modeltests/m2m_multiple/models.py
+++ b/tests/modeltests/m2m_multiple/models.py
@@ -28,52 +28,3 @@ class Article(models.Model):
def __unicode__(self):
return self.headline
-__test__ = {'API_TESTS':"""
->>> from datetime import datetime
-
->>> c1 = Category(name='Sports')
->>> c1.save()
->>> c2 = Category(name='News')
->>> c2.save()
->>> c3 = Category(name='Crime')
->>> c3.save()
->>> c4 = Category(name='Life')
->>> c4.save()
-
->>> a1 = Article(headline='Area man steals', pub_date=datetime(2005, 11, 27))
->>> a1.save()
->>> a1.primary_categories.add(c2, c3)
->>> a1.secondary_categories.add(c4)
-
->>> a2 = Article(headline='Area man runs', pub_date=datetime(2005, 11, 28))
->>> a2.save()
->>> a2.primary_categories.add(c1, c2)
->>> a2.secondary_categories.add(c4)
-
->>> a1.primary_categories.all()
-[<Category: Crime>, <Category: News>]
-
->>> a2.primary_categories.all()
-[<Category: News>, <Category: Sports>]
-
->>> a1.secondary_categories.all()
-[<Category: Life>]
-
-
->>> c1.primary_article_set.all()
-[<Article: Area man runs>]
->>> c1.secondary_article_set.all()
-[]
->>> c2.primary_article_set.all()
-[<Article: Area man steals>, <Article: Area man runs>]
->>> c2.secondary_article_set.all()
-[]
->>> c3.primary_article_set.all()
-[<Article: Area man steals>]
->>> c3.secondary_article_set.all()
-[]
->>> c4.primary_article_set.all()
-[]
->>> c4.secondary_article_set.all()
-[<Article: Area man steals>, <Article: Area man runs>]
-"""}
diff --git a/tests/modeltests/m2m_multiple/tests.py b/tests/modeltests/m2m_multiple/tests.py
new file mode 100644
index 0000000000..1f4503a483
--- /dev/null
+++ b/tests/modeltests/m2m_multiple/tests.py
@@ -0,0 +1,84 @@
+from datetime import datetime
+
+from django.test import TestCase
+
+from models import Article, Category
+
+
+class M2MMultipleTests(TestCase):
+ def test_multiple(self):
+ c1, c2, c3, c4 = [
+ Category.objects.create(name=name)
+ for name in ["Sports", "News", "Crime", "Life"]
+ ]
+
+ a1 = Article.objects.create(
+ headline="Area man steals", pub_date=datetime(2005, 11, 27)
+ )
+ a1.primary_categories.add(c2, c3)
+ a1.secondary_categories.add(c4)
+
+ a2 = Article.objects.create(
+ headline="Area man runs", pub_date=datetime(2005, 11, 28)
+ )
+ a2.primary_categories.add(c1, c2)
+ a2.secondary_categories.add(c4)
+
+ self.assertQuerysetEqual(
+ a1.primary_categories.all(), [
+ "Crime",
+ "News",
+ ],
+ lambda c: c.name
+ )
+ self.assertQuerysetEqual(
+ a2.primary_categories.all(), [
+ "News",
+ "Sports",
+ ],
+ lambda c: c.name
+ )
+ self.assertQuerysetEqual(
+ a1.secondary_categories.all(), [
+ "Life",
+ ],
+ lambda c: c.name
+ )
+ self.assertQuerysetEqual(
+ c1.primary_article_set.all(), [
+ "Area man runs",
+ ],
+ lambda a: a.headline
+ )
+ self.assertQuerysetEqual(
+ c1.secondary_article_set.all(), []
+ )
+ self.assertQuerysetEqual(
+ c2.primary_article_set.all(), [
+ "Area man steals",
+ "Area man runs",
+ ],
+ lambda a: a.headline
+ )
+ self.assertQuerysetEqual(
+ c2.secondary_article_set.all(), []
+ )
+ self.assertQuerysetEqual(
+ c3.primary_article_set.all(), [
+ "Area man steals",
+ ],
+ lambda a: a.headline
+ )
+ self.assertQuerysetEqual(
+ c3.secondary_article_set.all(), []
+ )
+ self.assertQuerysetEqual(
+ c4.primary_article_set.all(), []
+ )
+ self.assertQuerysetEqual(
+ c4.secondary_article_set.all(), [
+ "Area man steals",
+ "Area man runs",
+ ],
+ lambda a: a.headline
+ )
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index 1087cf8795..7ded82bb7c 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -554,7 +554,7 @@ fields with the 'choices' attribute are represented by a ChoiceField.
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
-</select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr>
+</select><br /><span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></td></tr>
You can restrict a form to a subset of the complete list of fields
by providing a 'fields' argument. If you try to save a
@@ -579,7 +579,7 @@ inserted as 'initial' data in each Field.
... model = Writer
>>> f = RoykoForm(auto_id=False, instance=w)
>>> print f
-<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br />Use both first and last names.</td></tr>
+<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" /><br /><span class="helptext">Use both first and last names.</span></td></tr>
>>> art = Article(headline='Test article', slug='test-article', pub_date=datetime.date(1988, 1, 4), writer=w, article='Hello.')
>>> art.save()
@@ -609,7 +609,7 @@ inserted as 'initial' data in each Field.
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
>>> f = TestArticleForm({'headline': u'Test headline', 'slug': 'test-headline', 'pub_date': u'1984-02-06', 'writer': unicode(w_royko.pk), 'article': 'Hello.'}, instance=art)
>>> f.errors
{}
@@ -672,7 +672,7 @@ Add some categories and test the many-to-many form output.
<option value="1" selected="selected">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third test</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
Initial values can be provided for model forms
>>> f = TestArticleForm(auto_id=False, initial={'headline': 'Your headline here', 'categories': ['1','2']})
@@ -696,7 +696,7 @@ Initial values can be provided for model forms
<option value="1" selected="selected">Entertainment</option>
<option value="2" selected="selected">It&#39;s a test</option>
<option value="3">Third test</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
>>> f = TestArticleForm({'headline': u'New headline', 'slug': u'new-headline', 'pub_date': u'1988-01-04',
... 'writer': unicode(w_royko.pk), 'article': u'Hello.', 'categories': [u'1', u'2']}, instance=new_art)
@@ -812,7 +812,7 @@ the data in the database when the form is instantiated.
<option value="1">Entertainment</option>
<option value="2">It&#39;s a test</option>
<option value="3">Third</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
>>> Category.objects.create(name='Fourth', url='4th')
<Category: Fourth>
>>> Writer.objects.create(name='Carl Bernstein')
@@ -839,7 +839,7 @@ the data in the database when the form is instantiated.
<option value="2">It&#39;s a test</option>
<option value="3">Third</option>
<option value="4">Fourth</option>
-</select> Hold down "Control", or "Command" on a Mac, to select more than one.</li>
+</select> <span class="helptext"> Hold down "Control", or "Command" on a Mac, to select more than one.</span></li>
# ModelChoiceField ############################################################
diff --git a/tests/modeltests/model_forms/tests.py b/tests/modeltests/model_forms/tests.py
index 6a5f9395cc..c5647c714f 100644
--- a/tests/modeltests/model_forms/tests.py
+++ b/tests/modeltests/model_forms/tests.py
@@ -156,6 +156,10 @@ class UniqueTest(TestCase):
form = PostForm({'subtitle': "Finally", "title": "Django 1.0 is released",
"slug": "Django 1.0", 'posted': '2008-09-03'}, instance=p)
self.assertTrue(form.is_valid())
+ form = PostForm({'title': "Django 1.0 is released"})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(len(form.errors), 1)
+ self.assertEqual(form.errors['posted'], [u'This field is required.'])
def test_inherited_unique_for_date(self):
p = Post.objects.create(title="Django 1.0 is released",
diff --git a/tests/modeltests/proxy_model_inheritance/tests.py b/tests/modeltests/proxy_model_inheritance/tests.py
index a07958a13b..d10d6a4ac1 100644
--- a/tests/modeltests/proxy_model_inheritance/tests.py
+++ b/tests/modeltests/proxy_model_inheritance/tests.py
@@ -23,9 +23,9 @@ class ProxyModelInheritanceTests(TransactionTestCase):
settings.INSTALLED_APPS = ('app1', 'app2')
map(load_app, settings.INSTALLED_APPS)
call_command('syncdb', verbosity=0)
+ global ProxyModel, NiceModel
from app1.models import ProxyModel
from app2.models import NiceModel
- global ProxyModel, NiceModel
def tearDown(self):
settings.INSTALLED_APPS = self.old_installed_apps
diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py
index ea8137f657..8a500752be 100644
--- a/tests/modeltests/signals/models.py
+++ b/tests/modeltests/signals/models.py
@@ -3,6 +3,7 @@ Testing signals before/after saving and deleting.
"""
from django.db import models
+from django.dispatch import receiver
class Person(models.Model):
first_name = models.CharField(max_length=20)
@@ -11,6 +12,13 @@ class Person(models.Model):
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
+class Car(models.Model):
+ make = models.CharField(max_length=20)
+ model = models.CharField(max_length=20)
+
+ def __unicode__(self):
+ return u"%s %s" % (self.make, self.model)
+
def pre_save_test(signal, sender, instance, **kwargs):
print 'pre_save signal,', instance
if kwargs.get('raw'):
@@ -52,22 +60,44 @@ __test__ = {'API_TESTS':"""
>>> models.signals.pre_delete.connect(pre_delete_test)
>>> models.signals.post_delete.connect(post_delete_test)
+# throw a decorator syntax receiver into the mix
+>>> @receiver(models.signals.pre_save)
+... def pre_save_decorator_test(signal, sender, instance, **kwargs):
+... print "pre_save signal decorator,", instance
+
+# throw a decorator syntax receiver into the mix
+>>> @receiver(models.signals.pre_save, sender=Car)
+... def pre_save_decorator_sender_test(signal, sender, instance, **kwargs):
+... print "pre_save signal decorator sender,", instance
+
>>> p1 = Person(first_name='John', last_name='Smith')
>>> p1.save()
pre_save signal, John Smith
+pre_save signal decorator, John Smith
post_save signal, John Smith
Is created
>>> p1.first_name = 'Tom'
>>> p1.save()
pre_save signal, Tom Smith
+pre_save signal decorator, Tom Smith
post_save signal, Tom Smith
Is updated
+# Car signal (sender defined)
+>>> c1 = Car(make="Volkswagon", model="Passat")
+>>> c1.save()
+pre_save signal, Volkswagon Passat
+pre_save signal decorator, Volkswagon Passat
+pre_save signal decorator sender, Volkswagon Passat
+post_save signal, Volkswagon Passat
+Is created
+
# Calling an internal method purely so that we can trigger a "raw" save.
>>> p1.save_base(raw=True)
pre_save signal, Tom Smith
Is raw
+pre_save signal decorator, Tom Smith
post_save signal, Tom Smith
Is updated
Is raw
@@ -82,12 +112,14 @@ instance.id is None: False
>>> p2.id = 99999
>>> p2.save()
pre_save signal, James Jones
+pre_save signal decorator, James Jones
post_save signal, James Jones
Is created
>>> p2.id = 99998
>>> p2.save()
pre_save signal, James Jones
+pre_save signal decorator, James Jones
post_save signal, James Jones
Is created
@@ -104,6 +136,8 @@ instance.id is None: False
>>> models.signals.pre_delete.disconnect(pre_delete_test)
>>> models.signals.post_save.disconnect(post_save_test)
>>> models.signals.pre_save.disconnect(pre_save_test)
+>>> models.signals.pre_save.disconnect(pre_save_decorator_test)
+>>> models.signals.pre_save.disconnect(pre_save_decorator_sender_test, sender=Car)
# Check that all our signals got disconnected properly.
>>> post_signals = (len(models.signals.pre_save.receivers),
diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py
index c51323d843..30520082da 100644
--- a/tests/modeltests/test_client/models.py
+++ b/tests/modeltests/test_client/models.py
@@ -21,6 +21,7 @@ rather than the HTML rendered to the end-user.
"""
from django.test import Client, TestCase
+from django.conf import settings
from django.core import mail
class ClientTest(TestCase):
@@ -433,3 +434,26 @@ class ClientTest(TestCase):
self.assertEqual(mail.outbox[1].from_email, 'from@example.com')
self.assertEqual(mail.outbox[1].to[0], 'second@example.com')
self.assertEqual(mail.outbox[1].to[1], 'third@example.com')
+
+class CSRFEnabledClientTests(TestCase):
+ def setUp(self):
+ # Enable the CSRF middleware for this test
+ self.old_MIDDLEWARE_CLASSES = settings.MIDDLEWARE_CLASSES
+ csrf_middleware_class = 'django.middleware.csrf.CsrfViewMiddleware'
+ if csrf_middleware_class not in settings.MIDDLEWARE_CLASSES:
+ settings.MIDDLEWARE_CLASSES += (csrf_middleware_class,)
+
+ def tearDown(self):
+ settings.MIDDLEWARE_CLASSES = self.old_MIDDLEWARE_CLASSES
+
+ def test_csrf_enabled_client(self):
+ "A client can be instantiated with CSRF checks enabled"
+ csrf_client = Client(enforce_csrf_checks=True)
+
+ # The normal client allows the post
+ response = self.client.post('/test_client/post_view/', {})
+ self.assertEqual(response.status_code, 200)
+
+ # The CSRF-enabled client rejects it
+ response = csrf_client.post('/test_client/post_view/', {})
+ self.assertEqual(response.status_code, 403)
diff --git a/tests/modeltests/validation/test_unique.py b/tests/modeltests/validation/test_unique.py
index 1b966390c4..fb77c4d28c 100644
--- a/tests/modeltests/validation/test_unique.py
+++ b/tests/modeltests/validation/test_unique.py
@@ -40,6 +40,15 @@ class GetUniqueCheckTests(unittest.TestCase):
), m._get_unique_checks()
)
+ def test_unique_for_date_exclusion(self):
+ m = UniqueForDateModel()
+ self.assertEqual((
+ [(UniqueForDateModel, ('id',))],
+ [(UniqueForDateModel, 'year', 'count', 'end_date'),
+ (UniqueForDateModel, 'month', 'order', 'end_date')]
+ ), m._get_unique_checks(exclude='start_date')
+ )
+
class PerformUniqueChecksTest(unittest.TestCase):
def setUp(self):
# Set debug to True to gain access to connection.queries.