summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorHonza Král <honza.kral@gmail.com>2009-12-28 16:35:23 +0000
committerHonza Král <honza.kral@gmail.com>2009-12-28 16:35:23 +0000
commitf911df19a455246198b0c8c81ab96bf2abec04f8 (patch)
tree0c0bfb72d622994419492db943d9d37857224f97 /tests
parent695de8cc9145e139c2b22e05aa44f5ac04da6f85 (diff)
[soc2009/model-validation] Merget to trunk at r12009
git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/model-validation@12014 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/basic/models.py14
-rw-r--r--tests/modeltests/custom_pk/models.py4
-rw-r--r--tests/modeltests/defer/models.py6
-rw-r--r--tests/modeltests/delete/models.py4
-rw-r--r--tests/modeltests/fixtures/fixtures/db_fixture_1.default.json10
-rw-r--r--tests/modeltests/fixtures/fixtures/db_fixture_2.default.json.gzbin0 -> 175 bytes
-rw-r--r--tests/modeltests/fixtures/fixtures/db_fixture_3.nosuchdb.json10
-rw-r--r--tests/modeltests/fixtures/fixtures/fixture6.json41
-rw-r--r--tests/modeltests/fixtures/fixtures/fixture7.xml27
-rw-r--r--tests/modeltests/fixtures/fixtures/fixture8.json32
-rw-r--r--tests/modeltests/fixtures/fixtures/fixture9.xml48
-rw-r--r--tests/modeltests/fixtures/models.py159
-rw-r--r--tests/modeltests/fixtures_model_package/__init__.py2
-rw-r--r--tests/modeltests/fixtures_model_package/fixtures/fixture1.json18
-rw-r--r--tests/modeltests/fixtures_model_package/fixtures/fixture2.json18
-rw-r--r--tests/modeltests/fixtures_model_package/fixtures/fixture2.xml11
-rw-r--r--tests/modeltests/fixtures_model_package/fixtures/initial_data.json10
-rw-r--r--tests/modeltests/fixtures_model_package/models/__init__.py54
-rw-r--r--tests/modeltests/lookup/models.py14
-rw-r--r--tests/modeltests/many_to_one/models.py2
-rw-r--r--tests/modeltests/model_forms/models.py34
-rw-r--r--tests/modeltests/model_formsets/models.py61
-rw-r--r--tests/modeltests/proxy_models/models.py5
-rw-r--r--tests/modeltests/raw_query/__init__.py (renamed from tests/regressiontests/auth_backends/__init__.py)0
-rw-r--r--tests/modeltests/raw_query/fixtures/initial_data.json102
-rw-r--r--tests/modeltests/raw_query/models.py25
-rw-r--r--tests/modeltests/raw_query/tests.py188
-rw-r--r--tests/modeltests/serializers/models.py18
-rw-r--r--tests/modeltests/transactions/models.py4
-rw-r--r--tests/regressiontests/admin_scripts/tests.py39
-rw-r--r--tests/regressiontests/admin_validation/models.py61
-rw-r--r--tests/regressiontests/admin_views/models.py66
-rw-r--r--tests/regressiontests/admin_views/tests.py122
-rw-r--r--tests/regressiontests/admin_widgets/urls2.py2
-rw-r--r--tests/regressiontests/aggregation_regress/models.py17
-rw-r--r--tests/regressiontests/auth_backends/tests.py78
-rw-r--r--tests/regressiontests/backends/tests.py10
-rw-r--r--tests/regressiontests/datatypes/models.py10
-rw-r--r--tests/regressiontests/defer_regress/models.py17
-rw-r--r--tests/regressiontests/defer_regress/tests.py9
-rw-r--r--tests/regressiontests/delete_regress/models.py17
-rw-r--r--tests/regressiontests/dispatch/tests/test_dispatcher.py2
-rw-r--r--tests/regressiontests/expressions_regress/models.py4
-rw-r--r--tests/regressiontests/extra_regress/models.py11
-rw-r--r--tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json32
-rw-r--r--tests/regressiontests/fixtures_regress/fixtures/non_natural_1.json25
-rw-r--r--tests/regressiontests/fixtures_regress/fixtures/non_natural_2.xml16
-rw-r--r--tests/regressiontests/fixtures_regress/models.py185
-rw-r--r--tests/regressiontests/forms/fields.py19
-rw-r--r--tests/regressiontests/forms/forms.py39
-rw-r--r--tests/regressiontests/forms/localflavor/ca.py44
-rw-r--r--tests/regressiontests/forms/localflavor/se.py332
-rw-r--r--tests/regressiontests/forms/tests.py4
-rw-r--r--tests/regressiontests/generic_inline_admin/models.py4
-rw-r--r--tests/regressiontests/generic_inline_admin/tests.py64
-rw-r--r--tests/regressiontests/generic_inline_admin/urls.py2
-rw-r--r--tests/regressiontests/i18n/forms.py13
-rw-r--r--tests/regressiontests/i18n/misc.py114
-rw-r--r--tests/regressiontests/i18n/tests.py456
-rw-r--r--tests/regressiontests/introspection/models.py1
-rw-r--r--tests/regressiontests/introspection/tests.py6
-rw-r--r--tests/regressiontests/localflavor/__init__.py (renamed from tests/regressiontests/auth_backends/models.py)0
-rw-r--r--tests/regressiontests/localflavor/forms.py14
-rw-r--r--tests/regressiontests/localflavor/models.py16
-rw-r--r--tests/regressiontests/localflavor/tests.py166
-rw-r--r--tests/regressiontests/model_fields/models.py3
-rw-r--r--tests/regressiontests/model_fields/tests.py50
-rw-r--r--tests/regressiontests/model_forms_regress/tests.py14
-rw-r--r--tests/regressiontests/model_inheritance_regress/models.py3
-rw-r--r--tests/regressiontests/model_regress/models.py7
-rw-r--r--tests/regressiontests/multiple_database/__init__.py0
-rw-r--r--tests/regressiontests/multiple_database/fixtures/multidb-common.json10
-rw-r--r--tests/regressiontests/multiple_database/fixtures/multidb.default.json26
-rw-r--r--tests/regressiontests/multiple_database/fixtures/multidb.other.json26
-rw-r--r--tests/regressiontests/multiple_database/models.py49
-rw-r--r--tests/regressiontests/multiple_database/tests.py705
-rw-r--r--tests/regressiontests/queries/models.py21
-rw-r--r--tests/regressiontests/serializers_regress/models.py3
-rw-r--r--tests/regressiontests/serializers_regress/tests.py31
-rw-r--r--tests/regressiontests/settings_tests/__init__.py0
-rw-r--r--tests/regressiontests/settings_tests/models.py0
-rw-r--r--tests/regressiontests/settings_tests/tests.py17
-rw-r--r--tests/regressiontests/templates/context.py4
-rw-r--r--tests/regressiontests/templates/filters.py16
-rw-r--r--tests/regressiontests/templates/smartif.py46
-rw-r--r--tests/regressiontests/templates/tests.py87
-rw-r--r--tests/regressiontests/test_client_regress/models.py6
-rw-r--r--tests/regressiontests/test_runner/__init__.py0
-rw-r--r--tests/regressiontests/test_runner/models.py0
-rw-r--r--tests/regressiontests/test_runner/tests.py29
-rw-r--r--tests/regressiontests/urlpatterns_reverse/tests.py23
-rw-r--r--tests/regressiontests/urlpatterns_reverse/urls_error_handlers.py8
-rw-r--r--tests/regressiontests/urlpatterns_reverse/urls_error_handlers_callables.py9
-rw-r--r--tests/regressiontests/utils/termcolors.py149
-rw-r--r--tests/regressiontests/utils/tests.py5
-rw-r--r--tests/regressiontests/views/tests/defaults.py28
-rwxr-xr-xtests/runtests.py12
97 files changed, 3845 insertions, 480 deletions
diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py
index 2dbde58256..c86cb3ab25 100644
--- a/tests/modeltests/basic/models.py
+++ b/tests/modeltests/basic/models.py
@@ -16,7 +16,7 @@ try:
except NameError:
from django.utils.itercompat import sorted
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
class Article(models.Model):
headline = models.CharField(max_length=100, default='Default headline')
@@ -211,6 +211,14 @@ True
>>> Article.objects.get(id__exact=8) == Article.objects.get(id__exact=7)
False
+# You can use 'in' to test for membership...
+>>> a8 in Article.objects.all()
+True
+
+# ... but there will often be more efficient ways if that is all you need:
+>>> Article.objects.filter(id=a8.id).exists()
+True
+
# dates() returns a list of available dates of the given scope for the given field.
>>> Article.objects.dates('pub_date', 'year')
[datetime.datetime(2005, 1, 1, 0, 0)]
@@ -365,7 +373,7 @@ from django.conf import settings
building_docs = getattr(settings, 'BUILDING_DOCS', False)
-if building_docs or settings.DATABASE_ENGINE == 'postgresql':
+if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.postgresql':
__test__['API_TESTS'] += """
# In PostgreSQL, microsecond-level precision is available.
>>> a9 = Article(headline='Article 9', pub_date=datetime(2005, 7, 31, 12, 30, 45, 180))
@@ -374,7 +382,7 @@ if building_docs or settings.DATABASE_ENGINE == 'postgresql':
datetime.datetime(2005, 7, 31, 12, 30, 45, 180)
"""
-if building_docs or settings.DATABASE_ENGINE == 'mysql':
+if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.mysql':
__test__['API_TESTS'] += """
# In MySQL, microsecond-level precision isn't available. You'll lose
# microsecond-level precision once the data is saved.
diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py
index b88af16782..84e6480af9 100644
--- a/tests/modeltests/custom_pk/models.py
+++ b/tests/modeltests/custom_pk/models.py
@@ -7,7 +7,7 @@ this behavior by explicitly adding ``primary_key=True`` to a field.
"""
from django.conf import settings
-from django.db import models, transaction, IntegrityError
+from django.db import models, transaction, IntegrityError, DEFAULT_DB_ALIAS
from fields import MyAutoField
@@ -156,7 +156,7 @@ 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.DATABASE_ENGINE != 'sqlite3':
+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.
diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py
index 96eb427811..ac3c876a57 100644
--- a/tests/modeltests/defer/models.py
+++ b/tests/modeltests/defer/models.py
@@ -183,10 +183,4 @@ u"bar"
>>> obj.name = "bb"
>>> obj.save()
-# Finally, we need to flush the app cache for the defer module.
-# Using only/defer creates some artifical entries in the app cache
-# that messes up later tests. Purge all entries, just to be sure.
->>> from django.db.models.loading import cache
->>> cache.app_models['defer'] = {}
-
"""}
diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py
index 58b296d8d2..943a327403 100644
--- a/tests/modeltests/delete/models.py
+++ b/tests/modeltests/delete/models.py
@@ -171,9 +171,9 @@ True
# 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):
+... 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)
+... 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()
diff --git a/tests/modeltests/fixtures/fixtures/db_fixture_1.default.json b/tests/modeltests/fixtures/fixtures/db_fixture_1.default.json
new file mode 100644
index 0000000000..9bb39e400f
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/db_fixture_1.default.json
@@ -0,0 +1,10 @@
+[
+ {
+ "pk": "6",
+ "model": "fixtures.article",
+ "fields": {
+ "headline": "Who needs more than one database?",
+ "pub_date": "2006-06-16 14:00:00"
+ }
+ }
+] \ No newline at end of file
diff --git a/tests/modeltests/fixtures/fixtures/db_fixture_2.default.json.gz b/tests/modeltests/fixtures/fixtures/db_fixture_2.default.json.gz
new file mode 100644
index 0000000000..80e4ba139f
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/db_fixture_2.default.json.gz
Binary files differ
diff --git a/tests/modeltests/fixtures/fixtures/db_fixture_3.nosuchdb.json b/tests/modeltests/fixtures/fixtures/db_fixture_3.nosuchdb.json
new file mode 100644
index 0000000000..3da326bce1
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/db_fixture_3.nosuchdb.json
@@ -0,0 +1,10 @@
+[
+ {
+ "pk": "8",
+ "model": "fixtures.article",
+ "fields": {
+ "headline": "There is no spoon.",
+ "pub_date": "2006-06-16 14:00:00"
+ }
+ }
+] \ No newline at end of file
diff --git a/tests/modeltests/fixtures/fixtures/fixture6.json b/tests/modeltests/fixtures/fixtures/fixture6.json
new file mode 100644
index 0000000000..60e4733c71
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/fixture6.json
@@ -0,0 +1,41 @@
+[
+ {
+ "pk": "1",
+ "model": "fixtures.tag",
+ "fields": {
+ "name": "copyright",
+ "tagged_type": ["fixtures", "article"],
+ "tagged_id": "3"
+ }
+ },
+ {
+ "pk": "2",
+ "model": "fixtures.tag",
+ "fields": {
+ "name": "law",
+ "tagged_type": ["fixtures", "article"],
+ "tagged_id": "3"
+ }
+ },
+ {
+ "pk": "1",
+ "model": "fixtures.person",
+ "fields": {
+ "name": "Django Reinhardt"
+ }
+ },
+ {
+ "pk": "2",
+ "model": "fixtures.person",
+ "fields": {
+ "name": "Stephane Grappelli"
+ }
+ },
+ {
+ "pk": "3",
+ "model": "fixtures.person",
+ "fields": {
+ "name": "Prince"
+ }
+ }
+]
diff --git a/tests/modeltests/fixtures/fixtures/fixture7.xml b/tests/modeltests/fixtures/fixtures/fixture7.xml
new file mode 100644
index 0000000000..547cba10d2
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/fixture7.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <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>
+</django-objects>
diff --git a/tests/modeltests/fixtures/fixtures/fixture8.json b/tests/modeltests/fixtures/fixtures/fixture8.json
new file mode 100644
index 0000000000..bc113aa00e
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/fixture8.json
@@ -0,0 +1,32 @@
+[
+ {
+ "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"]
+ ]
+ }
+ },
+ {
+ "pk": "3",
+ "model": "fixtures.visa",
+ "fields": {
+ "person": ["Prince"],
+ "permissions": []
+ }
+ }
+]
diff --git a/tests/modeltests/fixtures/fixtures/fixture9.xml b/tests/modeltests/fixtures/fixtures/fixture9.xml
new file mode 100644
index 0000000000..100f63d106
--- /dev/null
+++ b/tests/modeltests/fixtures/fixtures/fixture9.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="2" model="fixtures.visa">
+ <field type="CharField" name="person">
+ <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.person">
+ <field type="CharField" name="name">
+ <natural>Artist formerly known as &quot;Prince&quot;</natural>
+ </field>
+ </object>
+ <object pk="3" model="fixtures.visa">
+ <field type="CharField" name="person">
+ <natural>Artist formerly known as &quot;Prince&quot;</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>Django Reinhardt</natural>
+ </object>
+ <object>
+ <natural>Artist formerly known as &quot;Prince&quot;</natural>
+ </object>
+ </field>
+ </object>
+</django-objects>
diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py
index 75ff99867a..7fcf89d561 100644
--- a/tests/modeltests/fixtures/models.py
+++ b/tests/modeltests/fixtures/models.py
@@ -8,9 +8,13 @@ in the application directory, on in one of the directories named in the
``FIXTURE_DIRS`` setting.
"""
-from django.db import models
+from django.contrib.auth.models import Permission
+from django.contrib.contenttypes import generic
+from django.contrib.contenttypes.models import ContentType
+from django.db import models, DEFAULT_DB_ALIAS
from django.conf import settings
+
class Category(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
@@ -31,6 +35,62 @@ class Article(models.Model):
class Meta:
ordering = ('-pub_date', 'headline')
+class Blog(models.Model):
+ name = models.CharField(max_length=100)
+ featured = models.ForeignKey(Article, related_name='fixtures_featured_set')
+ articles = models.ManyToManyField(Article, blank=True,
+ related_name='fixtures_articles_set')
+
+ def __unicode__(self):
+ return self.name
+
+
+class Tag(models.Model):
+ name = models.CharField(max_length=100)
+ tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set")
+ tagged_id = models.PositiveIntegerField(default=0)
+ tagged = generic.GenericForeignKey(ct_field='tagged_type',
+ fk_field='tagged_id')
+
+ def __unicode__(self):
+ return '<%s: %s> tagged "%s"' % (self.tagged.__class__.__name__,
+ self.tagged, self.name)
+
+class PersonManager(models.Manager):
+ def get_by_natural_key(self, name):
+ return self.get(name=name)
+
+class Person(models.Model):
+ objects = PersonManager()
+ name = models.CharField(max_length=100)
+ def __unicode__(self):
+ return self.name
+
+ class Meta:
+ ordering = ('name',)
+
+ def natural_key(self):
+ return (self.name,)
+
+class Visa(models.Model):
+ person = models.ForeignKey(Person)
+ permissions = models.ManyToManyField(Permission, blank=True)
+
+ def __unicode__(self):
+ return '%s %s' % (self.person.name,
+ ', '.join(p.name for p in self.permissions.all()))
+
+class Book(models.Model):
+ name = models.CharField(max_length=100)
+ authors = models.ManyToManyField(Person)
+
+ def __unicode__(self):
+ return '%s by %s' % (self.name,
+ ' and '.join(a.name for a in self.authors.all()))
+
+ class Meta:
+ ordering = ('name',)
+
__test__ = {'API_TESTS': """
>>> from django.core import management
>>> from django.db.models import get_app
@@ -90,17 +150,58 @@ __test__ = {'API_TESTS': """
>>> 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.DATABASE_ENGINE != 'mysql':
+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.
@@ -159,6 +260,60 @@ Multiple fixtures named 'fixture2' in '...fixtures'. Aborting.
>>> 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)
+
+# Try to load fixture 1, but this time, exclude the 'fixtures' app.
+>>> management.call_command('loaddata', 'fixture1', verbosity=0, exclude='fixtures')
+>>> Article.objects.all()
+[<Article: Python program becomes self aware>]
+
+>>> Category.objects.all()
+[]
+
+# 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
diff --git a/tests/modeltests/fixtures_model_package/__init__.py b/tests/modeltests/fixtures_model_package/__init__.py
new file mode 100644
index 0000000000..139597f9cb
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/__init__.py
@@ -0,0 +1,2 @@
+
+
diff --git a/tests/modeltests/fixtures_model_package/fixtures/fixture1.json b/tests/modeltests/fixtures_model_package/fixtures/fixture1.json
new file mode 100644
index 0000000000..7684d84609
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/fixtures/fixture1.json
@@ -0,0 +1,18 @@
+[
+ {
+ "pk": "2",
+ "model": "fixtures_model_package.article",
+ "fields": {
+ "headline": "Poker has no place on ESPN",
+ "pub_date": "2006-06-16 12:00:00"
+ }
+ },
+ {
+ "pk": "3",
+ "model": "fixtures_model_package.article",
+ "fields": {
+ "headline": "Time to reform copyright",
+ "pub_date": "2006-06-16 13:00:00"
+ }
+ }
+]
diff --git a/tests/modeltests/fixtures_model_package/fixtures/fixture2.json b/tests/modeltests/fixtures_model_package/fixtures/fixture2.json
new file mode 100644
index 0000000000..4997627385
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/fixtures/fixture2.json
@@ -0,0 +1,18 @@
+[
+ {
+ "pk": "3",
+ "model": "fixtures_model_package.article",
+ "fields": {
+ "headline": "Copyright is fine the way it is",
+ "pub_date": "2006-06-16 14:00:00"
+ }
+ },
+ {
+ "pk": "4",
+ "model": "fixtures_model_package.article",
+ "fields": {
+ "headline": "Django conquers world!",
+ "pub_date": "2006-06-16 15:00:00"
+ }
+ }
+]
diff --git a/tests/modeltests/fixtures_model_package/fixtures/fixture2.xml b/tests/modeltests/fixtures_model_package/fixtures/fixture2.xml
new file mode 100644
index 0000000000..55337cf810
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/fixtures/fixture2.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="2" model="fixtures_model_package.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="5" model="fixtures_model_package.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>
+</django-objects>
diff --git a/tests/modeltests/fixtures_model_package/fixtures/initial_data.json b/tests/modeltests/fixtures_model_package/fixtures/initial_data.json
new file mode 100644
index 0000000000..66cb5d7b87
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/fixtures/initial_data.json
@@ -0,0 +1,10 @@
+[
+ {
+ "pk": "1",
+ "model": "fixtures_model_package.article",
+ "fields": {
+ "headline": "Python program becomes self aware",
+ "pub_date": "2006-06-16 11:00:00"
+ }
+ }
+]
diff --git a/tests/modeltests/fixtures_model_package/models/__init__.py b/tests/modeltests/fixtures_model_package/models/__init__.py
new file mode 100644
index 0000000000..1581102b88
--- /dev/null
+++ b/tests/modeltests/fixtures_model_package/models/__init__.py
@@ -0,0 +1,54 @@
+from django.db import models
+from django.conf import settings
+
+class Article(models.Model):
+ headline = models.CharField(max_length=100, default='Default headline')
+ pub_date = models.DateTimeField()
+
+ def __unicode__(self):
+ return self.headline
+
+ class Meta:
+ 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/lookup/models.py b/tests/modeltests/lookup/models.py
index 94c16ff071..512ffd7e7d 100644
--- a/tests/modeltests/lookup/models.py
+++ b/tests/modeltests/lookup/models.py
@@ -4,7 +4,7 @@
This demonstrates features of the database API.
"""
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.conf import settings
class Article(models.Model):
@@ -43,7 +43,9 @@ False
True
"""}
-if settings.DATABASE_ENGINE in ('postgresql', 'postgresql_pysycopg2'):
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] in (
+ 'django.db.backends.postgresql',
+ 'django.db.backends.postgresql_pysycopg2'):
__test__['API_TESTS'] += r"""
# text matching tests for PostgreSQL 8.3
>>> Article.objects.filter(id__iexact='1')
@@ -102,6 +104,12 @@ Article 4
<Article: Article 2>
>>> Article.objects.in_bulk([3])
{3: <Article: Article 3>}
+>>> Article.objects.in_bulk(set([3]))
+{3: <Article: Article 3>}
+>>> Article.objects.in_bulk(frozenset([3]))
+{3: <Article: Article 3>}
+>>> Article.objects.in_bulk((3,))
+{3: <Article: Article 3>}
>>> Article.objects.in_bulk([1000])
{}
>>> Article.objects.in_bulk([])
@@ -399,7 +407,7 @@ FieldError: Join on field 'headline' not permitted. Did you misspell 'starts' fo
"""
-if settings.DATABASE_ENGINE != 'mysql':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
__test__['API_TESTS'] += r"""
# grouping and backreferences
>>> Article.objects.filter(headline__regex=r'b(.).*b\1')
diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py
index 8093e73013..8ee6210500 100644
--- a/tests/modeltests/many_to_one/models.py
+++ b/tests/modeltests/many_to_one/models.py
@@ -157,7 +157,7 @@ False
# The underlying query only makes one join when a related table is referenced twice.
>>> queryset = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
->>> sql = queryset.query.as_sql()[0]
+>>> sql = queryset.query.get_compiler(queryset.db).as_sql()[0]
>>> sql.count('INNER JOIN')
1
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index 0c8dbb5c10..90b8219e08 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -13,12 +13,6 @@ import tempfile
from django.db import models
from django.core.files.storage import FileSystemStorage
-# Python 2.3 doesn't have sorted()
-try:
- sorted
-except NameError:
- from django.utils.itercompat import sorted
-
temp_storage_dir = tempfile.mkdtemp()
temp_storage = FileSystemStorage(temp_storage_dir)
@@ -201,6 +195,12 @@ class Post(models.Model):
def __unicode__(self):
return self.name
+class BigInt(models.Model):
+ biggie = models.BigIntegerField()
+
+ def __unicode__(self):
+ return unicode(self.biggie)
+
__test__ = {'API_TESTS': """
>>> from django import forms
>>> from django.forms.models import ModelForm, model_to_dict
@@ -1147,6 +1147,28 @@ True
# Delete the current file since this is not done by Django.
>>> instance.file.delete()
>>> instance.delete()
+
+# BigIntegerField ################################################################
+>>> class BigIntForm(forms.ModelForm):
+... class Meta:
+... model = BigInt
+...
+>>> bif = BigIntForm({'biggie': '-9223372036854775808'})
+>>> bif.is_valid()
+True
+>>> bif = BigIntForm({'biggie': '-9223372036854775809'})
+>>> bif.is_valid()
+False
+>>> bif.errors
+{'biggie': [u'Ensure this value is greater than or equal to -9223372036854775808.']}
+>>> bif = BigIntForm({'biggie': '9223372036854775807'})
+>>> bif.is_valid()
+True
+>>> bif = BigIntForm({'biggie': '9223372036854775808'})
+>>> bif.is_valid()
+False
+>>> bif.errors
+{'biggie': [u'Ensure this value is less than or equal to 9223372036854775807.']}
"""}
if test_images:
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py
index 692c52c982..5eab202962 100644
--- a/tests/modeltests/model_formsets/models.py
+++ b/tests/modeltests/model_formsets/models.py
@@ -512,7 +512,7 @@ book.
... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data
... 'book_set-0-id': '1',
... 'book_set-0-title': 'Les Fleurs du Mal',
-... 'book_set-1-title': 'Le Spleen de Paris',
+... 'book_set-1-title': 'Les Paradis Artificiels',
... 'book_set-2-title': '',
... }
@@ -521,14 +521,14 @@ book.
True
>>> formset.save()
-[<Book: Le Spleen de Paris>]
+[<Book: Les Paradis Artificiels>]
-As you can see, 'Le Spleen de Paris' is now a book belonging to Charles Baudelaire.
+As you can see, 'Les Paradis Artificiels' is now a book belonging to Charles Baudelaire.
>>> for book in author.book_set.order_by('id'):
... print book.title
Les Fleurs du Mal
-Le Spleen de Paris
+Les Paradis Artificiels
The save_as_new parameter lets you re-associate the data to a new instance.
This is used in the admin for save_as functionality.
@@ -539,14 +539,14 @@ This is used in the admin for save_as functionality.
... 'book_set-0-id': '1',
... 'book_set-0-title': 'Les Fleurs du Mal',
... 'book_set-1-id': '2',
-... 'book_set-1-title': 'Le Spleen de Paris',
+... 'book_set-1-title': 'Les Paradis Artificiels',
... 'book_set-2-title': '',
... }
>>> new_author = Author.objects.create(name='Charles Baudelaire')
>>> formset = AuthorBooksFormSet(data, instance=new_author, save_as_new=True)
>>> [book for book in formset.save() if book.author.pk == new_author.pk]
-[<Book: Les Fleurs du Mal>, <Book: Le Spleen de Paris>]
+[<Book: Les Fleurs du Mal>, <Book: Les Paradis Artificiels>]
Test using a custom prefix on an inline formset.
@@ -640,6 +640,53 @@ True
>>> formset.save()
[<Poem: Brooklyn Bridge>, <Poem: Brooklyn Bridge>]
+We can provide a custom queryset to our InlineFormSet:
+
+>>> custom_qs = Book.objects.order_by('-title')
+>>> formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
+>>> for form in formset.forms:
+... print form.as_p()
+<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Les Paradis Artificiels" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="2" id="id_book_set-0-id" /></p>
+<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" value="Les Fleurs du Mal" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" value="1" id="id_book_set-1-id" /></p>
+<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" value="Flowers of Evil" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" value="5" id="id_book_set-2-id" /></p>
+<p><label for="id_book_set-3-title">Title:</label> <input id="id_book_set-3-title" type="text" name="book_set-3-title" maxlength="100" /><input type="hidden" name="book_set-3-author" value="1" id="id_book_set-3-author" /><input type="hidden" name="book_set-3-id" id="id_book_set-3-id" /></p>
+<p><label for="id_book_set-4-title">Title:</label> <input id="id_book_set-4-title" type="text" name="book_set-4-title" maxlength="100" /><input type="hidden" name="book_set-4-author" value="1" id="id_book_set-4-author" /><input type="hidden" name="book_set-4-id" id="id_book_set-4-id" /></p>
+
+>>> data = {
+... 'book_set-TOTAL_FORMS': '5', # the number of forms rendered
+... 'book_set-INITIAL_FORMS': '3', # the number of forms with initial data
+... 'book_set-0-id': '1',
+... 'book_set-0-title': 'Les Fleurs du Mal',
+... 'book_set-1-id': '2',
+... 'book_set-1-title': 'Les Paradis Artificiels',
+... 'book_set-2-id': '5',
+... 'book_set-2-title': 'Flowers of Evil',
+... 'book_set-3-title': 'Revue des deux mondes',
+... 'book_set-4-title': '',
+... }
+>>> formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
+>>> formset.is_valid()
+True
+
+>>> custom_qs = Book.objects.filter(title__startswith='F')
+>>> formset = AuthorBooksFormSet(instance=author, queryset=custom_qs)
+>>> for form in formset.forms:
+... print form.as_p()
+<p><label for="id_book_set-0-title">Title:</label> <input id="id_book_set-0-title" type="text" name="book_set-0-title" value="Flowers of Evil" maxlength="100" /><input type="hidden" name="book_set-0-author" value="1" id="id_book_set-0-author" /><input type="hidden" name="book_set-0-id" value="5" id="id_book_set-0-id" /></p>
+<p><label for="id_book_set-1-title">Title:</label> <input id="id_book_set-1-title" type="text" name="book_set-1-title" maxlength="100" /><input type="hidden" name="book_set-1-author" value="1" id="id_book_set-1-author" /><input type="hidden" name="book_set-1-id" id="id_book_set-1-id" /></p>
+<p><label for="id_book_set-2-title">Title:</label> <input id="id_book_set-2-title" type="text" name="book_set-2-title" maxlength="100" /><input type="hidden" name="book_set-2-author" value="1" id="id_book_set-2-author" /><input type="hidden" name="book_set-2-id" id="id_book_set-2-id" /></p>
+>>> data = {
+... 'book_set-TOTAL_FORMS': '3', # the number of forms rendered
+... 'book_set-INITIAL_FORMS': '1', # the number of forms with initial data
+... 'book_set-0-id': '5',
+... 'book_set-0-title': 'Flowers of Evil',
+... 'book_set-1-title': 'Revue des deux mondes',
+... 'book_set-2-title': '',
+... }
+>>> formset = AuthorBooksFormSet(data, instance=author, queryset=custom_qs)
+>>> formset.is_valid()
+True
+
# Test a custom primary key ###################################################
@@ -881,7 +928,7 @@ False
>>> form = formset.forms[0] # this formset only has one form
>>> now = form.fields['date_joined'].initial
>>> print form.as_p()
-<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /></p>
+<p><label for="id_membership_set-0-date_joined">Date joined:</label> <input type="text" name="membership_set-0-date_joined" value="..." id="id_membership_set-0-date_joined" /><input type="hidden" name="initial-membership_set-0-date_joined" value="..." id="initial-membership_set-0-id_membership_set-0-date_joined" /></p>
<p><label for="id_membership_set-0-karma">Karma:</label> <input type="text" name="membership_set-0-karma" id="id_membership_set-0-karma" /><input type="hidden" name="membership_set-0-person" value="1" id="id_membership_set-0-person" /><input type="hidden" name="membership_set-0-id" id="id_membership_set-0-id" /></p>
# test for validation with callable defaults. Validations rely on hidden fields
diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py
index e38266fb70..44fee9ee6c 100644
--- a/tests/modeltests/proxy_models/models.py
+++ b/tests/modeltests/proxy_models/models.py
@@ -166,12 +166,13 @@ class ProxyImprovement(Improvement):
__test__ = {'API_TESTS' : """
# The MyPerson model should be generating the same database queries as the
# Person model (when the same manager is used in each case).
->>> MyPerson.other.all().query.as_sql() == Person.objects.order_by("name").query.as_sql()
+>>> from django.db import DEFAULT_DB_ALIAS
+>>> MyPerson.other.all().query.get_compiler(DEFAULT_DB_ALIAS).as_sql() == Person.objects.order_by("name").query.get_compiler(DEFAULT_DB_ALIAS).as_sql()
True
# The StatusPerson models should have its own table (it's using ORM-level
# inheritance).
->>> StatusPerson.objects.all().query.as_sql() == Person.objects.all().query.as_sql()
+>>> StatusPerson.objects.all().query.get_compiler(DEFAULT_DB_ALIAS).as_sql() == Person.objects.all().query.get_compiler(DEFAULT_DB_ALIAS).as_sql()
False
# Creating a Person makes them accessible through the MyPerson proxy.
diff --git a/tests/regressiontests/auth_backends/__init__.py b/tests/modeltests/raw_query/__init__.py
index e69de29bb2..e69de29bb2 100644
--- a/tests/regressiontests/auth_backends/__init__.py
+++ b/tests/modeltests/raw_query/__init__.py
diff --git a/tests/modeltests/raw_query/fixtures/initial_data.json b/tests/modeltests/raw_query/fixtures/initial_data.json
new file mode 100644
index 0000000000..3ff9810b45
--- /dev/null
+++ b/tests/modeltests/raw_query/fixtures/initial_data.json
@@ -0,0 +1,102 @@
+[
+ {
+ "pk": 1,
+ "model": "raw_query.author",
+ "fields": {
+ "dob": "1950-09-20",
+ "first_name": "Joe",
+ "last_name": "Smith"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "raw_query.author",
+ "fields": {
+ "dob": "1920-04-02",
+ "first_name": "Jill",
+ "last_name": "Doe"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "raw_query.author",
+ "fields": {
+ "dob": "1986-01-25",
+ "first_name": "Bob",
+ "last_name": "Smith"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "raw_query.author",
+ "fields": {
+ "dob": "1932-05-10",
+ "first_name": "Bill",
+ "last_name": "Jones"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "raw_query.book",
+ "fields": {
+ "author": 1,
+ "title": "The awesome book"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "raw_query.book",
+ "fields": {
+ "author": 1,
+ "title": "The horrible book"
+ }
+ },
+ {
+ "pk": 3,
+ "model": "raw_query.book",
+ "fields": {
+ "author": 1,
+ "title": "Another awesome book"
+ }
+ },
+ {
+ "pk": 4,
+ "model": "raw_query.book",
+ "fields": {
+ "author": 3,
+ "title": "Some other book"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "raw_query.coffee",
+ "fields": {
+ "brand": "dunkin doughnuts"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "raw_query.coffee",
+ "fields": {
+ "brand": "starbucks"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "raw_query.reviewer",
+ "fields": {
+ "reviewed": [
+ 2,
+ 3,
+ 4
+ ]
+ }
+ },
+ {
+ "pk": 2,
+ "model": "raw_query.reviewer",
+ "fields": {
+ "reviewed": []
+ }
+ }
+]
diff --git a/tests/modeltests/raw_query/models.py b/tests/modeltests/raw_query/models.py
new file mode 100644
index 0000000000..fb5503df3d
--- /dev/null
+++ b/tests/modeltests/raw_query/models.py
@@ -0,0 +1,25 @@
+from django.db import models
+
+class Author(models.Model):
+ first_name = models.CharField(max_length=255)
+ last_name = models.CharField(max_length=255)
+ dob = models.DateField()
+
+ def __init__(self, *args, **kwargs):
+ super(Author, self).__init__(*args, **kwargs)
+ # Protect against annotations being passed to __init__ --
+ # this'll make the test suite get angry if annotations aren't
+ # treated differently than fields.
+ for k in kwargs:
+ assert k in [f.attname for f in self._meta.fields], \
+ "Author.__init__ got an unexpected paramater: %s" % k
+
+class Book(models.Model):
+ title = models.CharField(max_length=255)
+ author = models.ForeignKey(Author)
+
+class Coffee(models.Model):
+ brand = models.CharField(max_length=255, db_column="name")
+
+class Reviewer(models.Model):
+ reviewed = models.ManyToManyField(Book) \ No newline at end of file
diff --git a/tests/modeltests/raw_query/tests.py b/tests/modeltests/raw_query/tests.py
new file mode 100644
index 0000000000..688df21598
--- /dev/null
+++ b/tests/modeltests/raw_query/tests.py
@@ -0,0 +1,188 @@
+from django.test import TestCase
+from datetime import datetime
+from models import Author, Book, Coffee, Reviewer
+from django.db.models.sql.query import InvalidQuery
+
+class RawQueryTests(TestCase):
+
+ def assertSuccessfulRawQuery(self, model, query, expected_results,
+ expected_annotations=(), params=[], translations=None):
+ """
+ Execute the passed query against the passed model and check the output
+ """
+ results = list(model.objects.raw(query, params=params, translations=translations))
+ self.assertProcessed(results, expected_results, expected_annotations)
+ self.assertAnnotations(results, expected_annotations)
+
+ def assertProcessed(self, results, orig, expected_annotations=()):
+ """
+ Compare the results of a raw query against expected results
+ """
+ self.assertEqual(len(results), len(orig))
+ for index, item in enumerate(results):
+ orig_item = orig[index]
+ for annotation in expected_annotations:
+ setattr(orig_item, *annotation)
+
+ self.assertEqual(item.id, orig_item.id)
+
+ def assertNoAnnotations(self, results):
+ """
+ Check that the results of a raw query contain no annotations
+ """
+ self.assertAnnotations(results, ())
+
+ def assertAnnotations(self, results, expected_annotations):
+ """
+ Check that the passed raw query results contain the expected
+ annotations
+ """
+ if expected_annotations:
+ for index, result in enumerate(results):
+ annotation, value = expected_annotations[index]
+ self.assertTrue(hasattr(result, annotation))
+ self.assertEqual(getattr(result, annotation), value)
+
+ def testSimpleRawQuery(self):
+ """
+ Basic test of raw query with a simple database query
+ """
+ query = "SELECT * FROM raw_query_author"
+ authors = Author.objects.all()
+ self.assertSuccessfulRawQuery(Author, query, authors)
+
+ def testRawQueryLazy(self):
+ """
+ Raw queries are lazy: they aren't actually executed until they're
+ iterated over.
+ """
+ q = Author.objects.raw('SELECT * FROM raw_query_author')
+ self.assert_(q.query.cursor is None)
+ list(q)
+ self.assert_(q.query.cursor is not None)
+
+ def testFkeyRawQuery(self):
+ """
+ Test of a simple raw query against a model containing a foreign key
+ """
+ query = "SELECT * FROM raw_query_book"
+ books = Book.objects.all()
+ self.assertSuccessfulRawQuery(Book, query, books)
+
+ def testDBColumnHandler(self):
+ """
+ Test of a simple raw query against a model containing a field with
+ db_column defined.
+ """
+ query = "SELECT * FROM raw_query_coffee"
+ coffees = Coffee.objects.all()
+ self.assertSuccessfulRawQuery(Coffee, query, coffees)
+
+ def testOrderHandler(self):
+ """
+ Test of raw raw query's tolerance for columns being returned in any
+ order
+ """
+ selects = (
+ ('dob, last_name, first_name, id'),
+ ('last_name, dob, first_name, id'),
+ ('first_name, last_name, dob, id'),
+ )
+
+ for select in selects:
+ query = "SELECT %s FROM raw_query_author" % select
+ authors = Author.objects.all()
+ self.assertSuccessfulRawQuery(Author, query, authors)
+
+ def testTranslations(self):
+ """
+ Test of raw query's optional ability to translate unexpected result
+ column names to specific model fields
+ """
+ query = "SELECT first_name AS first, last_name AS last, dob, id FROM raw_query_author"
+ translations = {'first': 'first_name', 'last': 'last_name'}
+ authors = Author.objects.all()
+ self.assertSuccessfulRawQuery(Author, query, authors, translations=translations)
+
+ def testParams(self):
+ """
+ Test passing optional query parameters
+ """
+ query = "SELECT * FROM raw_query_author WHERE first_name = %s"
+ author = Author.objects.all()[2]
+ params = [author.first_name]
+ results = list(Author.objects.raw(query, params=params))
+ self.assertProcessed(results, [author])
+ self.assertNoAnnotations(results)
+ self.assertEqual(len(results), 1)
+
+ def testManyToMany(self):
+ """
+ Test of a simple raw query against a model containing a m2m field
+ """
+ query = "SELECT * FROM raw_query_reviewer"
+ reviewers = Reviewer.objects.all()
+ self.assertSuccessfulRawQuery(Reviewer, query, reviewers)
+
+ def testExtraConversions(self):
+ """
+ Test to insure that extra translations are ignored.
+ """
+ query = "SELECT * FROM raw_query_author"
+ translations = {'something': 'else'}
+ authors = Author.objects.all()
+ self.assertSuccessfulRawQuery(Author, query, authors, translations=translations)
+
+ def testMissingFields(self):
+ query = "SELECT id, first_name, dob FROM raw_query_author"
+ for author in Author.objects.raw(query):
+ self.assertNotEqual(author.first_name, None)
+ # last_name isn't given, but it will be retrieved on demand
+ self.assertNotEqual(author.last_name, None)
+
+ def testMissingFieldsWithoutPK(self):
+ query = "SELECT first_name, dob FROM raw_query_author"
+ try:
+ list(Author.objects.raw(query))
+ self.fail('Query without primary key should fail')
+ except InvalidQuery:
+ pass
+
+ def testAnnotations(self):
+ query = "SELECT a.*, count(b.id) as book_count FROM raw_query_author a LEFT JOIN raw_query_book b ON a.id = b.author_id GROUP BY a.id, a.first_name, a.last_name, a.dob ORDER BY a.id"
+ expected_annotations = (
+ ('book_count', 3),
+ ('book_count', 0),
+ ('book_count', 1),
+ ('book_count', 0),
+ )
+ authors = Author.objects.all()
+ self.assertSuccessfulRawQuery(Author, query, authors, expected_annotations)
+
+ def testInvalidQuery(self):
+ query = "UPDATE raw_query_author SET first_name='thing' WHERE first_name='Joe'"
+ self.assertRaises(InvalidQuery, Author.objects.raw, query)
+
+ def testWhiteSpaceQuery(self):
+ query = " SELECT * FROM raw_query_author"
+ authors = Author.objects.all()
+ self.assertSuccessfulRawQuery(Author, query, authors)
+
+ def testMultipleIterations(self):
+ query = "SELECT * FROM raw_query_author"
+ normal_authors = Author.objects.all()
+ raw_authors = Author.objects.raw(query)
+
+ # First Iteration
+ first_iterations = 0
+ for index, raw_author in enumerate(raw_authors):
+ self.assertEqual(normal_authors[index], raw_author)
+ first_iterations += 1
+
+ # Second Iteration
+ second_iterations = 0
+ for index, raw_author in enumerate(raw_authors):
+ self.assertEqual(normal_authors[index], raw_author)
+ second_iterations += 1
+
+ self.assertEqual(first_iterations, second_iterations) \ No newline at end of file
diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py
index cf1bf50c24..45d57e58d4 100644
--- a/tests/modeltests/serializers/models.py
+++ b/tests/modeltests/serializers/models.py
@@ -149,8 +149,8 @@ __test__ = {'API_TESTS':"""
# database since the data was serialized (we'll simulate that below).
>>> for obj in serializers.deserialize("xml", xml):
... print obj
-<DeserializedObject: Poker has no place on ESPN>
-<DeserializedObject: Time to reform copyright>
+<DeserializedObject: serializers.Article(pk=1)>
+<DeserializedObject: serializers.Article(pk=2)>
# Deserializing data with different field values doesn't change anything in the
# database until we call save():
@@ -183,8 +183,8 @@ __test__ = {'API_TESTS':"""
>>> json = serializers.serialize("json", Article.objects.all())
>>> for obj in serializers.deserialize("json", json):
... print obj
-<DeserializedObject: Poker has no place on television>
-<DeserializedObject: Time to reform copyright>
+<DeserializedObject: serializers.Article(pk=1)>
+<DeserializedObject: serializers.Article(pk=2)>
>>> json = json.replace("Poker has no place on television", "Just kidding; I love TV poker")
>>> for obj in serializers.deserialize("json", json):
@@ -205,7 +205,7 @@ __test__ = {'API_TESTS':"""
>>> for obj in serializers.deserialize("json", json):
... print obj
-<DeserializedObject: Profile of Joe>
+<DeserializedObject: serializers.AuthorProfile(pk=1)>
# Objects ids can be referenced before they are defined in the serialization data
# However, the deserialization process will need to be contained within a transaction
@@ -275,7 +275,7 @@ None
>>> obj = list(serializers.deserialize("json", serialized))[0]
>>> print obj
-<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
+<DeserializedObject: serializers.Player(pk=1)>
"""}
@@ -310,8 +310,8 @@ try:
>>> obs = list(serializers.deserialize("yaml", serialized))
>>> for i in obs:
... print i
-<DeserializedObject: Just kidding; I love TV poker>
-<DeserializedObject: Time to reform copyright>
+<DeserializedObject: serializers.Article(pk=1)>
+<DeserializedObject: serializers.Article(pk=2)>
# Custom field with non trivial to string convertion value with YAML serializer
@@ -324,7 +324,7 @@ try:
>>> serialized = serializers.serialize("yaml", Player.objects.all())
>>> obj = list(serializers.deserialize("yaml", serialized))[0]
>>> print obj
-<DeserializedObject: Soslan Djanaev (1) playing for Spartak Moskva>
+<DeserializedObject: serializers.Player(pk=1)>
"""
diff --git a/tests/modeltests/transactions/models.py b/tests/modeltests/transactions/models.py
index 6763144ca5..6b92fd01d6 100644
--- a/tests/modeltests/transactions/models.py
+++ b/tests/modeltests/transactions/models.py
@@ -7,7 +7,7 @@ commit-on-success behavior. Alternatively, you can manage the transaction
manually.
"""
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
@@ -28,7 +28,7 @@ from django.conf import settings
building_docs = getattr(settings, 'BUILDING_DOCS', False)
-if building_docs or settings.DATABASE_ENGINE != 'mysql':
+if building_docs or settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
__test__['API_TESTS'] += """
# the default behavior is to autocommit after each save() action
>>> def create_a_reporter_then_fail(first, last):
diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py
index e67126e9a7..cdc9556afe 100644
--- a/tests/regressiontests/admin_scripts/tests.py
+++ b/tests/regressiontests/admin_scripts/tests.py
@@ -23,17 +23,15 @@ class AdminScriptTestCase(unittest.TestCase):
settings_file = open(os.path.join(test_dir, filename), 'w')
settings_file.write('# Settings file automatically generated by regressiontests.admin_scripts test case\n')
exports = [
- 'DATABASE_ENGINE',
- 'DATABASE_NAME',
- 'DATABASE_USER',
- 'DATABASE_PASSWORD',
- 'DATABASE_HOST',
- 'DATABASE_PORT',
+ 'DATABASES',
'ROOT_URLCONF'
]
for s in exports:
- if hasattr(settings,s):
- settings_file.write("%s = '%s'\n" % (s, str(getattr(settings,s))))
+ if hasattr(settings, s):
+ o = getattr(settings, s)
+ if not isinstance(o, dict):
+ o = "'%s'" % o
+ settings_file.write("%s = %s\n" % (s, o))
if apps is None:
apps = ['django.contrib.auth', 'django.contrib.contenttypes', 'admin_scripts']
@@ -63,23 +61,25 @@ class AdminScriptTestCase(unittest.TestCase):
except OSError:
pass
- def _ext_backend_path(self):
+ def _ext_backend_paths(self):
"""
- Returns the path for the external backend package, or None if no
- external backend is detected.
+ Returns the paths for any external backend packages.
"""
+ paths = []
first_package_re = re.compile(r'(^[^\.]+)\.')
- result = first_package_re.findall(settings.DATABASE_ENGINE)
- if result:
- backend_pkg = __import__(result[0])
- backend_dir = os.path.dirname(backend_pkg.__file__)
- return os.path.dirname(backend_dir)
+ for backend in settings.DATABASES.values():
+ result = first_package_re.findall(backend['ENGINE'])
+ if result and result != 'django':
+ backend_pkg = __import__(result[0])
+ backend_dir = os.path.dirname(backend_pkg.__file__)
+ paths.append(os.path.dirname(backend_dir))
+ return paths
def run_test(self, script, args, settings_file=None, apps=None):
test_dir = os.path.dirname(os.path.dirname(__file__))
project_dir = os.path.dirname(test_dir)
base_dir = os.path.dirname(project_dir)
- ext_backend_base_dir = self._ext_backend_path()
+ ext_backend_base_dirs = self._ext_backend_paths()
# Remember the old environment
old_django_settings_module = os.environ.get('DJANGO_SETTINGS_MODULE', None)
@@ -97,8 +97,7 @@ class AdminScriptTestCase(unittest.TestCase):
elif 'DJANGO_SETTINGS_MODULE' in os.environ:
del os.environ['DJANGO_SETTINGS_MODULE']
python_path = [test_dir, base_dir]
- if ext_backend_base_dir:
- python_path.append(ext_backend_base_dir)
+ python_path.extend(ext_backend_base_dirs)
os.environ[python_path_var_name] = os.pathsep.join(python_path)
# Build the command line
@@ -523,7 +522,7 @@ class DjangoAdminSettingsDirectory(AdminScriptTestCase):
A series of tests for django-admin.py when the settings file is in a
directory. (see #9751).
"""
-
+
def setUp(self):
self.write_settings('settings', is_dir=True)
diff --git a/tests/regressiontests/admin_validation/models.py b/tests/regressiontests/admin_validation/models.py
index eb53a9dd6e..682fca66de 100644
--- a/tests/regressiontests/admin_validation/models.py
+++ b/tests/regressiontests/admin_validation/models.py
@@ -19,6 +19,10 @@ class Song(models.Model):
def __unicode__(self):
return self.title
+ def readonly_method_on_model(self):
+ # does nothing
+ pass
+
class TwoAlbumFKAndAnE(models.Model):
album1 = models.ForeignKey(Album, related_name="album1_set")
@@ -110,6 +114,63 @@ Exception: <class 'regressiontests.admin_validation.models.TwoAlbumFKAndAnE'> ha
>>> validate_inline(TwoAlbumFKAndAnEInline, None, Album)
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = ("title",)
+
+>>> validate(SongAdmin, Song)
+
+>>> def my_function(obj):
+... # does nothing
+... pass
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = (my_function,)
+
+>>> validate(SongAdmin, Song)
+
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = ("readonly_method_on_modeladmin",)
+...
+... def readonly_method_on_modeladmin(self, obj):
+... # does nothing
+... pass
+
+>>> validate(SongAdmin, Song)
+
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = ("readonly_method_on_model",)
+
+>>> validate(SongAdmin, Song)
+
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = ("title", "nonexistant")
+
+>>> validate(SongAdmin, Song)
+Traceback (most recent call last):
+ ...
+ImproperlyConfigured: SongAdmin.readonly_fields[1], 'nonexistant' is not a callable or an attribute of 'SongAdmin' or found in the model 'Song'.
+
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = ("title", "awesome_song")
+... fields = ("album", "title", "awesome_song")
+
+>>> validate(SongAdmin, Song)
+Traceback (most recent call last):
+ ...
+ImproperlyConfigured: SongAdmin.readonly_fields[1], 'awesome_song' is not a callable or an attribute of 'SongAdmin' or found in the model 'Song'.
+
+>>> class SongAdmin(SongAdmin):
+... def awesome_song(self, instance):
+... if instance.title == "Born to Run":
+... return "Best Ever!"
+... return "Status unknown."
+
+>>> validate(SongAdmin, Song)
+
+>>> class SongAdmin(admin.ModelAdmin):
+... readonly_fields = (lambda obj: "test",)
+
+>>> validate(SongAdmin, Song)
+
# Regression test for #12203/#12237 - Fail more gracefully when a M2M field that
# specifies the 'through' option is included in the 'fields' or the 'fieldsets'
# ModelAdmin options.
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
index 50bc05eef0..ffdc72620e 100644
--- a/tests/regressiontests/admin_views/models.py
+++ b/tests/regressiontests/admin_views/models.py
@@ -1,10 +1,14 @@
# -*- coding: utf-8 -*-
+import datetime
import tempfile
import os
-from django.core.files.storage import FileSystemStorage
-from django.db import models
+
from django.contrib import admin
+from django.core.files.storage import FileSystemStorage
+from django.contrib.admin.views.main import ChangeList
from django.core.mail import EmailMessage
+from django.db import models
+
class Section(models.Model):
"""
@@ -418,7 +422,61 @@ class CategoryInline(admin.StackedInline):
model = Category
class CollectorAdmin(admin.ModelAdmin):
- inlines = [WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline, FancyDoodadInline, CategoryInline]
+ inlines = [
+ WidgetInline, DooHickeyInline, GrommetInline, WhatsitInline,
+ FancyDoodadInline, CategoryInline
+ ]
+
+class Link(models.Model):
+ posted = models.DateField(
+ default=lambda: datetime.date.today() - datetime.timedelta(days=7)
+ )
+ url = models.URLField()
+ post = models.ForeignKey("Post")
+
+
+class LinkInline(admin.TabularInline):
+ model = Link
+ extra = 1
+
+ readonly_fields = ("posted",)
+
+
+class Post(models.Model):
+ title = models.CharField(max_length=100)
+ content = models.TextField()
+ posted = models.DateField(default=datetime.date.today)
+
+ def awesomeness_level(self):
+ return "Very awesome."
+
+class PostAdmin(admin.ModelAdmin):
+ readonly_fields = ('posted', 'awesomeness_level', 'coolness', lambda obj: "foo")
+
+ inlines = [
+ LinkInline
+ ]
+
+ def coolness(self, instance):
+ if instance.pk:
+ return "%d amount of cool." % instance.pk
+ else:
+ return "Unkown coolness."
+
+
+class Gadget(models.Model):
+ name = models.CharField(max_length=100)
+
+ def __unicode__(self):
+ return self.name
+
+class CustomChangeList(ChangeList):
+ def get_query_set(self):
+ return self.root_query_set.filter(pk=9999) # Does not exist
+
+class GadgetAdmin(admin.ModelAdmin):
+ def get_changelist(self, request, **kwargs):
+ return CustomChangeList
admin.site.register(Article, ArticleAdmin)
admin.site.register(CustomArticle, CustomArticleAdmin)
@@ -443,6 +501,8 @@ admin.site.register(Recommendation, RecommendationAdmin)
admin.site.register(Recommender)
admin.site.register(Collector, CollectorAdmin)
admin.site.register(Category, CategoryAdmin)
+admin.site.register(Post, PostAdmin)
+admin.site.register(Gadget, GadgetAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases:
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 8607589289..320b632537 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -10,20 +10,18 @@ from django.contrib.admin.models import LogEntry, DELETION
from django.contrib.admin.sites import LOGIN_FORM_KEY
from django.contrib.admin.util import quote
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
+from django.utils import formats
from django.utils.cache import get_max_age
from django.utils.html import escape
+from django.utils.translation import get_date_formats
# local test models
from models import Article, BarAccount, CustomArticle, EmptyModel, \
ExternalSubscriber, FooAccount, Gallery, ModelWithStringPrimaryKey, \
Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, \
Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, \
- Category
+ Category, Post
-try:
- set
-except NameError:
- from sets import Set as set
class AdminViewBasicTest(TestCase):
fixtures = ['admin-views-users.xml', 'admin-views-colors.xml', 'admin-views-fabrics.xml']
@@ -610,6 +608,12 @@ class AdminViewStringPrimaryKeyTest(TestCase):
def tearDown(self):
self.client.logout()
+ def test_get_history_view(self):
+ "Retrieving the history for the object using urlencoded form of primary key should work"
+ response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/history/' % quote(self.pk))
+ self.assertContains(response, escape(self.pk))
+ self.failUnlessEqual(response.status_code, 200)
+
def test_get_change_view(self):
"Retrieving the object using urlencoded form of primary key should work"
response = self.client.get('/test_admin/admin/admin_views/modelwithstringprimarykey/%s/' % quote(self.pk))
@@ -1151,7 +1155,6 @@ class AdminActionsTest(TestCase):
self.assert_('action-checkbox-column' in response.content,
"Expected an action-checkbox-column in response")
-
def test_multiple_actions_form(self):
"""
Test that actions come from the form whose submit button was pressed (#10618).
@@ -1169,6 +1172,62 @@ class AdminActionsTest(TestCase):
self.assertEquals(len(mail.outbox), 1)
self.assertEquals(mail.outbox[0].subject, 'Greetings from a function action')
+ def test_user_message_on_none_selected(self):
+ """
+ User should see a warning when 'Go' is pressed and no items are selected.
+ """
+ action_data = {
+ ACTION_CHECKBOX_NAME: [],
+ 'action' : 'delete_selected',
+ 'index': 0,
+ }
+ response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
+ msg = """Items must be selected in order to perform actions on them. No items have been changed."""
+ self.assertContains(response, msg)
+ self.failUnlessEqual(Subscriber.objects.count(), 2)
+
+ def test_user_message_on_no_action(self):
+ """
+ User should see a warning when 'Go' is pressed and no action is selected.
+ """
+ action_data = {
+ ACTION_CHECKBOX_NAME: [1, 2],
+ 'action' : '',
+ 'index': 0,
+ }
+ response = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data)
+ msg = """No action selected."""
+ self.assertContains(response, msg)
+ self.failUnlessEqual(Subscriber.objects.count(), 2)
+
+
+class TestCustomChangeList(TestCase):
+ fixtures = ['admin-views-users.xml']
+ urlbit = 'admin'
+
+ def setUp(self):
+ result = self.client.login(username='super', password='secret')
+ self.failUnlessEqual(result, True)
+
+ def tearDown(self):
+ self.client.logout()
+
+ def test_custom_changelist(self):
+ """
+ Validate that a custom ChangeList class can be used (#9749)
+ """
+ # Insert some data
+ post_data = {"name": u"First Gadget"}
+ response = self.client.post('/test_admin/%s/admin_views/gadget/add/' % self.urlbit, post_data)
+ self.failUnlessEqual(response.status_code, 302) # redirect somewhere
+ # Hit the page once to get messages out of the queue message list
+ response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit)
+ # Ensure that that data is still not visible on the page
+ response = self.client.get('/test_admin/%s/admin_views/gadget/' % self.urlbit)
+ self.failUnlessEqual(response.status_code, 200)
+ self.assertNotContains(response, 'First Gadget')
+
+
class TestInlineNotEditable(TestCase):
fixtures = ['admin-views-users.xml']
@@ -1627,3 +1686,54 @@ class NeverCacheTests(TestCase):
"Check the never-cache status of the Javascript i18n view"
response = self.client.get('/test_admin/jsi18n/')
self.failUnlessEqual(get_max_age(response), None)
+
+
+class ReadonlyTest(TestCase):
+ fixtures = ['admin-views-users.xml']
+
+ def setUp(self):
+ self.client.login(username='super', password='secret')
+
+ def tearDown(self):
+ self.client.logout()
+
+ def test_readonly_get(self):
+ response = self.client.get('/test_admin/admin/admin_views/post/add/')
+ self.assertEqual(response.status_code, 200)
+ self.assertNotContains(response, 'name="posted"')
+ # 3 fields + 2 submit buttons + 2 inline management form fields, + 2
+ # hidden fields for inlines + 1 field for the inline
+ self.assertEqual(response.content.count("input"), 10)
+ self.assertContains(response, formats.localize(datetime.date.today()))
+ self.assertContains(response,
+ "<label>Awesomeness level:</label>")
+ self.assertContains(response, "Very awesome.")
+ self.assertContains(response, "Unkown coolness.")
+ self.assertContains(response, "foo")
+ self.assertContains(response,
+ formats.localize(datetime.date.today() - datetime.timedelta(days=7))
+ )
+
+ p = Post.objects.create(title="I worked on readonly_fields", content="Its good stuff")
+ response = self.client.get('/test_admin/admin/admin_views/post/%d/' % p.pk)
+ self.assertContains(response, "%d amount of cool" % p.pk)
+
+ def test_readonly_post(self):
+ data = {
+ "title": "Django Got Readonly Fields",
+ "content": "This is an incredible development.",
+ "link_set-TOTAL_FORMS": "1",
+ "link_set-INITIAL_FORMS": "0",
+ }
+ response = self.client.post('/test_admin/admin/admin_views/post/add/', data)
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(Post.objects.count(), 1)
+ p = Post.objects.get()
+ self.assertEqual(p.posted, datetime.date.today())
+
+ data["posted"] = "10-8-1990" # some date that's not today
+ response = self.client.post('/test_admin/admin/admin_views/post/add/', data)
+ self.assertEqual(response.status_code, 302)
+ self.assertEqual(Post.objects.count(), 2)
+ p = Post.objects.order_by('-id')[0]
+ self.assertEqual(p.posted, datetime.date.today())
diff --git a/tests/regressiontests/admin_widgets/urls2.py b/tests/regressiontests/admin_widgets/urls2.py
index 1ad060cd09..e38ff66b49 100644
--- a/tests/regressiontests/admin_widgets/urls2.py
+++ b/tests/regressiontests/admin_widgets/urls2.py
@@ -3,5 +3,5 @@ from django.conf.urls.defaults import *
import widgetadmin
urlpatterns = patterns('',
- (r'^deep/down/admin/(.*)', widgetadmin.site.root),
+ (r'^deep/down/admin/', include(widgetadmin.site.urls)),
)
diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py
index 4476b86d64..7c51cd17a7 100644
--- a/tests/regressiontests/aggregation_regress/models.py
+++ b/tests/regressiontests/aggregation_regress/models.py
@@ -1,7 +1,7 @@
# coding: utf-8
import pickle
-from django.db import connection, models
+from django.db import connection, models, DEFAULT_DB_ALIAS
from django.conf import settings
try:
@@ -97,19 +97,19 @@ __test__ = {'API_TESTS': """
{'pages__sum': 3703}
# Annotations get combined with extra select clauses
->>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).__dict__.items())
+>>> sorted((k,v) for k,v in Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).__dict__.items() if k != '_state')
[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
# Order of the annotate/extra in the query doesn't matter
->>> sorted(Book.objects.all().extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2).__dict__.items())
+>>> sorted((k,v) for k,v in Book.objects.all().extra(select={'manufacture_cost' : 'price * .5'}).annotate(mean_auth_age=Avg('authors__age')).get(pk=2).__dict__.items()if k != '_state')
[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
# Values queries can be combined with annotate and extra
->>> sorted(Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2).items())
+>>> sorted((k,v) for k,v in Book.objects.all().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).values().get(pk=2).items()if k != '_state')
[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
# The order of the (empty) values, annotate and extra clauses doesn't matter
->>> sorted(Book.objects.all().values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).items())
+>>> sorted((k,v) for k,v in Book.objects.all().values().annotate(mean_auth_age=Avg('authors__age')).extra(select={'manufacture_cost' : 'price * .5'}).get(pk=2).items()if k != '_state')
[('contact_id', 3), ('id', 2), ('isbn', u'067232959'), ('manufacture_cost', ...11.545...), ('mean_auth_age', 45.0), ('name', u'Sams Teach Yourself Django in 24 Hours'), ('pages', 528), ('price', Decimal("23.09")), ('pubdate', datetime.date(2008, 3, 3)), ('publisher_id', 2), ('rating', 3.0)]
# If the annotation precedes the values clause, it won't be included
@@ -250,10 +250,10 @@ FieldError: Cannot resolve keyword 'foo' into field. Choices are: authors, conta
>>> out = pickle.dumps(qs)
# Then check that the round trip works.
->>> query = qs.query.as_sql()[0]
+>>> query = qs.query.get_compiler(qs.db).as_sql()[0]
>>> select_fields = qs.query.select_fields
>>> query2 = pickle.loads(pickle.dumps(qs))
->>> query2.query.as_sql()[0] == query
+>>> query2.query.get_compiler(query2.db).as_sql()[0] == query
True
>>> query2.query.select_fields = select_fields
@@ -327,7 +327,7 @@ def run_stddev_tests():
Stddev and Variance are not guaranteed to be available for SQLite, and
are not available for PostgreSQL before 8.2.
"""
- if settings.DATABASE_ENGINE == 'sqlite3':
+ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.sqlite3':
return False
class StdDevPop(object):
@@ -380,5 +380,4 @@ if run_stddev_tests():
>>> Book.objects.aggregate(Variance('price', sample=True))
{'price__variance': 700.53...}
-
"""
diff --git a/tests/regressiontests/auth_backends/tests.py b/tests/regressiontests/auth_backends/tests.py
deleted file mode 100644
index d22f0bf939..0000000000
--- a/tests/regressiontests/auth_backends/tests.py
+++ /dev/null
@@ -1,78 +0,0 @@
-try:
- set
-except NameError:
- from sets import Set as set # Python 2.3 fallback
-
-__test__ = {'API_TESTS': """
->>> from django.contrib.auth.models import User, Group, Permission, AnonymousUser
->>> from django.contrib.contenttypes.models import ContentType
-
-# No Permissions assigned yet, should return False except for superuser
-
->>> user = User.objects.create_user('test', 'test@example.com', 'test')
->>> user.has_perm("auth.test")
-False
->>> user.is_staff=True
->>> user.save()
->>> user.has_perm("auth.test")
-False
->>> user.is_superuser=True
->>> user.save()
->>> user.has_perm("auth.test")
-True
->>> user.is_staff = False
->>> user.is_superuser = False
->>> user.save()
->>> user.has_perm("auth.test")
-False
->>> content_type=ContentType.objects.get_for_model(Group)
->>> perm = Permission.objects.create(name="test", content_type=content_type, codename="test")
->>> user.user_permissions.add(perm)
->>> user.save()
-
-# reloading user to purge the _perm_cache
-
->>> user = User.objects.get(username="test")
->>> user.get_all_permissions() == set([u'auth.test'])
-True
->>> user.get_group_permissions() == set([])
-True
->>> user.has_module_perms("Group")
-False
->>> user.has_module_perms("auth")
-True
->>> perm = Permission.objects.create(name="test2", content_type=content_type, codename="test2")
->>> user.user_permissions.add(perm)
->>> user.save()
->>> perm = Permission.objects.create(name="test3", content_type=content_type, codename="test3")
->>> user.user_permissions.add(perm)
->>> user.save()
->>> user = User.objects.get(username="test")
->>> user.get_all_permissions() == set([u'auth.test2', u'auth.test', u'auth.test3'])
-True
->>> user.has_perm('test')
-False
->>> user.has_perm('auth.test')
-True
->>> user.has_perms(['auth.test2', 'auth.test3'])
-True
->>> perm = Permission.objects.create(name="test_group", content_type=content_type, codename="test_group")
->>> group = Group.objects.create(name='test_group')
->>> group.permissions.add(perm)
->>> group.save()
->>> user.groups.add(group)
->>> user = User.objects.get(username="test")
->>> exp = set([u'auth.test2', u'auth.test', u'auth.test3', u'auth.test_group'])
->>> user.get_all_permissions() == exp
-True
->>> user.get_group_permissions() == set([u'auth.test_group'])
-True
->>> user.has_perms(['auth.test3', 'auth.test_group'])
-True
-
->>> user = AnonymousUser()
->>> user.has_perm('test')
-False
->>> user.has_perms(['auth.test2', 'auth.test3'])
-False
-"""}
diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py
index 628fabf04a..e4fa82403a 100644
--- a/tests/regressiontests/backends/tests.py
+++ b/tests/regressiontests/backends/tests.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Unit and doctests for specific database backends.
import unittest
-from django.db import backend, connection
+from django.db import backend, connection, DEFAULT_DB_ALIAS
from django.db.backends.signals import connection_created
from django.conf import settings
@@ -10,7 +10,7 @@ class Callproc(unittest.TestCase):
def test_dbms_session(self):
# If the backend is Oracle, test that we can call a standard
# stored procedure through our cursor wrapper.
- if settings.DATABASE_ENGINE == 'oracle':
+ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.oracle':
convert_unicode = backend.convert_unicode
cursor = connection.cursor()
cursor.callproc(convert_unicode('DBMS_SESSION.SET_IDENTIFIER'),
@@ -18,13 +18,13 @@ class Callproc(unittest.TestCase):
return True
else:
return True
-
+
class LongString(unittest.TestCase):
def test_long_string(self):
# If the backend is Oracle, test that we can save a text longer
# than 4000 chars and read it properly
- if settings.DATABASE_ENGINE == 'oracle':
+ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.oracle':
c = connection.cursor()
c.execute('CREATE TABLE ltext ("TEXT" NCLOB)')
long_str = ''.join([unicode(x) for x in xrange(4000)])
@@ -64,7 +64,7 @@ __test__ = {'API_TESTS': """
# Unfortunately with sqlite3 the in-memory test database cannot be
# closed, and so it cannot be re-opened during testing, and so we
# sadly disable this test for now.
-if settings.DATABASE_ENGINE != 'sqlite3':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3':
__test__['API_TESTS'] += """
>>> connection_created.connect(connection_created_test)
>>> connection.close() # Ensure the connection is closed
diff --git a/tests/regressiontests/datatypes/models.py b/tests/regressiontests/datatypes/models.py
index 0ad809a45d..f6cbc9f0f6 100644
--- a/tests/regressiontests/datatypes/models.py
+++ b/tests/regressiontests/datatypes/models.py
@@ -3,7 +3,7 @@ This is a basic model to test saving and loading boolean and date-related
types, which in the past were problematic for some database backends.
"""
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.conf import settings
class Donut(models.Model):
@@ -93,11 +93,11 @@ u'Outstanding'
# Regression test for #8354: the MySQL backend should raise an error if given
# a timezone-aware datetime object.
-if settings.DATABASE_ENGINE == 'mysql':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.mysql':
__test__['API_TESTS'] += """
->>> from django.utils import tzinfo
->>> dt = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=tzinfo.FixedOffset(0))
->>> d = Donut(name='Bear claw', consumed_at=dt)
+>>> from django.utils import tzinfo
+>>> dt = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=tzinfo.FixedOffset(0))
+>>> d = Donut(name='Bear claw', consumed_at=dt)
>>> d.save()
Traceback (most recent call last):
....
diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py
index a1cd19788d..c32f5bb493 100644
--- a/tests/regressiontests/defer_regress/models.py
+++ b/tests/regressiontests/defer_regress/models.py
@@ -31,6 +31,10 @@ class Leaf(models.Model):
def __unicode__(self):
return self.name
+class ResolveThis(models.Model):
+ num = models.FloatField()
+ name = models.CharField(max_length=16)
+
__test__ = {"regression_tests": """
Deferred fields should really be deferred and not accidentally use the field's
default value just because they aren't passed to __init__.
@@ -132,11 +136,14 @@ False
>>> i2._deferred # Item must still be non-deferred
False
-# Finally, we need to flush the app cache for the defer module.
-# Using only/defer creates some artifical entries in the app cache
-# that messes up later tests. Purge all entries, just to be sure.
->>> from django.db.models.loading import cache
->>> cache.app_models['defer_regress'] = {}
+# Regression for #11936 - loading.get_models should not return deferred models by default.
+>>> from django.db.models.loading import get_models
+>>> sorted(get_models(models.get_app('defer_regress')), key=lambda obj: obj._meta.object_name)
+[<class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.Leaf'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.ResolveThis'>]
+
+>>> sorted(get_models(models.get_app('defer_regress'), include_deferred=True), key=lambda obj: obj._meta.object_name)
+[<class 'regressiontests.defer_regress.models.Child'>, <class 'regressiontests.defer_regress.models.Item'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name'>, <class 'regressiontests.defer_regress.models.Item_Deferred_name_other_value_text'>, <class 'regressiontests.defer_regress.models.Item_Deferred_other_value_text_value'>, <class 'regressiontests.defer_regress.models.Item_Deferred_text_value'>, <class 'regressiontests.defer_regress.models.Leaf'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_name_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_second_child_value'>, <class 'regressiontests.defer_regress.models.Leaf_Deferred_value'>, <class 'regressiontests.defer_regress.models.RelatedItem'>, <class 'regressiontests.defer_regress.models.RelatedItem_Deferred_item_id'>, <class 'regressiontests.defer_regress.models.ResolveThis'>, <class 'regressiontests.defer_regress.models.ResolveThis_Deferred_num'>]
"""
}
+
diff --git a/tests/regressiontests/defer_regress/tests.py b/tests/regressiontests/defer_regress/tests.py
new file mode 100644
index 0000000000..860c7f8e31
--- /dev/null
+++ b/tests/regressiontests/defer_regress/tests.py
@@ -0,0 +1,9 @@
+from django.test import TestCase
+from models import ResolveThis
+
+class DeferRegressionTest(TestCase):
+ def test_resolve_columns(self):
+ rt = ResolveThis.objects.create(num=5.0, name='Foobar')
+ qs = ResolveThis.objects.defer('num')
+ self.assertEqual(1, qs.count())
+ self.assertEqual('Foobar', qs[0].name)
diff --git a/tests/regressiontests/delete_regress/models.py b/tests/regressiontests/delete_regress/models.py
index 93cadc58fa..ff561c9d20 100644
--- a/tests/regressiontests/delete_regress/models.py
+++ b/tests/regressiontests/delete_regress/models.py
@@ -1,5 +1,5 @@
from django.conf import settings
-from django.db import models, backend, connection, transaction
+from django.db import models, backend, connection, transaction, DEFAULT_DB_ALIAS
from django.db.models import sql, query
from django.test import TransactionTestCase
@@ -8,17 +8,18 @@ class Book(models.Model):
# Can't run this test under SQLite, because you can't
# get two connections to an in-memory database.
-if settings.DATABASE_ENGINE != 'sqlite3':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.sqlite3':
class DeleteLockingTest(TransactionTestCase):
def setUp(self):
# Create a second connection to the database
+ conn_settings = settings.DATABASES[DEFAULT_DB_ALIAS]
self.conn2 = backend.DatabaseWrapper({
- 'DATABASE_HOST': settings.DATABASE_HOST,
- 'DATABASE_NAME': settings.DATABASE_NAME,
- 'DATABASE_OPTIONS': settings.DATABASE_OPTIONS,
- 'DATABASE_PASSWORD': settings.DATABASE_PASSWORD,
- 'DATABASE_PORT': settings.DATABASE_PORT,
- 'DATABASE_USER': settings.DATABASE_USER,
+ 'HOST': conn_settings['HOST'],
+ 'NAME': conn_settings['NAME'],
+ 'OPTIONS': conn_settings['OPTIONS'],
+ 'PASSWORD': conn_settings['PASSWORD'],
+ 'PORT': conn_settings['PORT'],
+ 'USER': conn_settings['USER'],
'TIME_ZONE': settings.TIME_ZONE,
})
diff --git a/tests/regressiontests/dispatch/tests/test_dispatcher.py b/tests/regressiontests/dispatch/tests/test_dispatcher.py
index adf760386c..ad3a05f2cd 100644
--- a/tests/regressiontests/dispatch/tests/test_dispatcher.py
+++ b/tests/regressiontests/dispatch/tests/test_dispatcher.py
@@ -1,8 +1,8 @@
from django.dispatch import Signal
import unittest
-import copy
import sys
import gc
+import django.utils.copycompat as copy
if sys.platform.startswith('java'):
def garbage_collect():
diff --git a/tests/regressiontests/expressions_regress/models.py b/tests/regressiontests/expressions_regress/models.py
index 7c9ca61ccc..a517780323 100644
--- a/tests/regressiontests/expressions_regress/models.py
+++ b/tests/regressiontests/expressions_regress/models.py
@@ -2,7 +2,7 @@
Spanning tests for all the operations that F() expressions can perform.
"""
from django.conf import settings
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
#
# Model for testing arithmetic expressions.
@@ -121,7 +121,7 @@ Complex expressions of different connection types are possible.
"""}
# Oracle doesn't support the Bitwise OR operator.
-if settings.DATABASE_ENGINE != 'oracle':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.oracle':
__test__['API_TESTS'] += """
>>> _ = Number.objects.filter(pk=n.pk).update(integer=42, float=15.5)
diff --git a/tests/regressiontests/extra_regress/models.py b/tests/regressiontests/extra_regress/models.py
index 5d22d6cc07..d4d7cb86e5 100644
--- a/tests/regressiontests/extra_regress/models.py
+++ b/tests/regressiontests/extra_regress/models.py
@@ -1,6 +1,7 @@
-import copy
import datetime
+import django.utils.copycompat as copy
+
from django.contrib.auth.models import User
from django.db import models
from django.db.models.query import Q
@@ -15,11 +16,13 @@ class RevisionableModel(models.Model):
def __unicode__(self):
return u"%s (%s, %s)" % (self.title, self.id, self.base.id)
- def save(self, force_insert=False, force_update=False):
- super(RevisionableModel, self).save(force_insert, force_update)
+ def save(self, *args, **kwargs):
+ super(RevisionableModel, self).save(*args, **kwargs)
if not self.base:
self.base = self
- super(RevisionableModel, self).save()
+ kwargs.pop('force_insert', None)
+ kwargs.pop('force_update', None)
+ super(RevisionableModel, self).save(*args, **kwargs)
def new_revision(self):
new_revision = copy.copy(self)
diff --git a/tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json b/tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json
new file mode 100644
index 0000000000..fe50c653cc
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/forward_ref_lookup.json
@@ -0,0 +1,32 @@
+[
+ {
+ "pk": "4",
+ "model": "fixtures_regress.person",
+ "fields": {
+ "name": "Neal Stephenson"
+ }
+ },
+ {
+ "pk": "2",
+ "model": "fixtures_regress.store",
+ "fields": {
+ "name": "Amazon"
+ }
+ },
+ {
+ "pk": "3",
+ "model": "fixtures_regress.store",
+ "fields": {
+ "name": "Borders"
+ }
+ },
+ {
+ "pk": 1,
+ "model": "fixtures_regress.book",
+ "fields": {
+ "name": "Cryptonomicon",
+ "author": ["Neal Stephenson"],
+ "stores": [["Amazon"], ["Borders"]]
+ }
+ }
+] \ No newline at end of file
diff --git a/tests/regressiontests/fixtures_regress/fixtures/non_natural_1.json b/tests/regressiontests/fixtures_regress/fixtures/non_natural_1.json
new file mode 100644
index 0000000000..4bce792e35
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/non_natural_1.json
@@ -0,0 +1,25 @@
+[
+ {
+ "pk": 12,
+ "model": "fixtures_regress.person",
+ "fields": {
+ "name": "Greg Egan"
+ }
+ },
+ {
+ "pk": 11,
+ "model": "fixtures_regress.store",
+ "fields": {
+ "name": "Angus and Robertson"
+ }
+ },
+ {
+ "pk": 10,
+ "model": "fixtures_regress.book",
+ "fields": {
+ "name": "Permutation City",
+ "author": 12,
+ "stores": [11]
+ }
+ }
+] \ No newline at end of file
diff --git a/tests/regressiontests/fixtures_regress/fixtures/non_natural_2.xml b/tests/regressiontests/fixtures_regress/fixtures/non_natural_2.xml
new file mode 100644
index 0000000000..280ad3758b
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/non_natural_2.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="22" model="fixtures_regress.person">
+ <field type="CharField" name="name">Orson Scott Card</field>
+ </object>
+ <object pk="21" model="fixtures_regress.store">
+ <field type="CharField" name="name">Collins Bookstore</field>
+ </object>
+ <object pk="20" model="fixtures_regress.book">
+ <field type="CharField" name="name">Ender's Game</field>
+ <field to="fixtures_regress.person" name="author" rel="ManyToOneRel">22</field>
+ <field to="fixtures_regress.store" name="stores" rel="ManyToManyRel">
+ <object pk="21"/>
+ </field>
+ </object>
+</django-objects> \ No newline at end of file
diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py
index e33471646c..4294ffb293 100644
--- a/tests/regressiontests/fixtures_regress/models.py
+++ b/tests/regressiontests/fixtures_regress/models.py
@@ -1,4 +1,4 @@
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.contrib.auth.models import User
from django.conf import settings
import os
@@ -13,7 +13,7 @@ class Animal(models.Model):
specimens = models.Manager()
def __unicode__(self):
- return self.common_name
+ return self.name
def animal_pre_save_check(signal, sender, instance, **kwargs):
"A signal that is used to check the type of data loaded from fixtures"
@@ -35,7 +35,8 @@ class Stuff(models.Model):
# Oracle doesn't distinguish between None and the empty string.
# This hack makes the test case pass using Oracle.
name = self.name
- if settings.DATABASE_ENGINE == 'oracle' and name == u'':
+ if (settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.oracle'
+ and name == u''):
name = None
return unicode(name) + u' is owned by ' + unicode(self.owner)
@@ -69,10 +70,109 @@ class Article(models.Model):
class Widget(models.Model):
name = models.CharField(max_length=255)
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
class WidgetProxy(Widget):
class Meta:
proxy = True
+# Check for forward references in FKs and M2Ms with natural keys
+
+class TestManager(models.Manager):
+ def get_by_natural_key(self, key):
+ return self.get(name=key)
+
+class Store(models.Model):
+ objects = TestManager()
+ name = models.CharField(max_length=255)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+ def natural_key(self):
+ return (self.name,)
+
+class Person(models.Model):
+ objects = TestManager()
+ name = models.CharField(max_length=255)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return self.name
+
+ # Person doesn't actually have a dependency on store, but we need to define
+ # one to test the behaviour of the dependency resolution algorithm.
+ def natural_key(self):
+ return (self.name,)
+ natural_key.dependencies = ['fixtures_regress.store']
+
+class Book(models.Model):
+ name = models.CharField(max_length=255)
+ author = models.ForeignKey(Person)
+ stores = models.ManyToManyField(Store)
+
+ class Meta:
+ ordering = ('name',)
+
+ def __unicode__(self):
+ return u'%s by %s (available at %s)' % (
+ self.name,
+ self.author.name,
+ ', '.join(s.name for s in self.stores.all())
+ )
+
+# ome models with pathological circular dependencies
+class Circle1(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.circle2']
+
+class Circle2(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.circle1']
+
+class Circle3(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.circle3']
+
+class Circle4(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.circle5']
+
+class Circle5(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.circle6']
+
+class Circle6(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.circle4']
+
+class ExternalDependency(models.Model):
+ name = models.CharField(max_length=255)
+ def natural_key(self):
+ return self.name
+ natural_key.dependencies = ['fixtures_regress.book']
+
__test__ = {'API_TESTS':"""
>>> from django.core import management
@@ -179,8 +279,7 @@ Weight = 1.2 (<type 'float'>)
[{"pk": 1, "model": "fixtures_regress.animal", "fields": {"count": 3, "weight": 1.2, "name": "Lion", "latin_name": "Panthera leo"}}, {"pk": 2, "model": "fixtures_regress.animal", "fields": {"count": 2, "weight": 2.29..., "name": "Platypus", "latin_name": "Ornithorhynchus anatinus"}}, {"pk": 10, "model": "fixtures_regress.animal", "fields": {"count": 42, "weight": 1.2, "name": "Emu", "latin_name": "Dromaius novaehollandiae"}}]
###############################################
-# Regression for #11428 - Proxy models aren't included
-# when you run dumpdata over an entire app
+# Regression for #11428 - Proxy models aren't included when you dumpdata
# Flush out the database first
>>> management.call_command('reset', 'fixtures_regress', interactive=False, verbosity=0)
@@ -189,7 +288,81 @@ Weight = 1.2 (<type 'float'>)
>>> Widget(name='grommet').save()
# Dump data for the entire app. The proxy class shouldn't be included
->>> management.call_command('dumpdata', 'fixtures_regress', format='json')
+>>> management.call_command('dumpdata', 'fixtures_regress.widget', 'fixtures_regress.widgetproxy', format='json')
[{"pk": 1, "model": "fixtures_regress.widget", "fields": {"name": "grommet"}}]
+###############################################
+# Check that natural key requirements are taken into account
+# when serializing models
+>>> management.call_command('loaddata', 'forward_ref_lookup.json', verbosity=0)
+
+>>> management.call_command('dumpdata', 'fixtures_regress.book', 'fixtures_regress.person', 'fixtures_regress.store', verbosity=0, use_natural_keys=True)
+[{"pk": 2, "model": "fixtures_regress.store", "fields": {"name": "Amazon"}}, {"pk": 3, "model": "fixtures_regress.store", "fields": {"name": "Borders"}}, {"pk": 4, "model": "fixtures_regress.person", "fields": {"name": "Neal Stephenson"}}, {"pk": 1, "model": "fixtures_regress.book", "fields": {"stores": [["Amazon"], ["Borders"]], "name": "Cryptonomicon", "author": ["Neal Stephenson"]}}]
+
+# Now lets check the dependency sorting explicitly
+
+# It doesn't matter what order you mention the models
+# Store *must* be serialized before then Person, and both
+# must be serialized before Book.
+>>> from django.core.management.commands.dumpdata import sort_dependencies
+>>> sort_dependencies([('fixtures_regress', [Book, Person, Store])])
+[<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+>>> sort_dependencies([('fixtures_regress', [Book, Store, Person])])
+[<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+>>> sort_dependencies([('fixtures_regress', [Store, Book, Person])])
+[<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+>>> sort_dependencies([('fixtures_regress', [Store, Person, Book])])
+[<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+>>> sort_dependencies([('fixtures_regress', [Person, Book, Store])])
+[<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+>>> sort_dependencies([('fixtures_regress', [Person, Store, Book])])
+[<class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+# A dangling dependency - assume the user knows what they are doing.
+>>> sort_dependencies([('fixtures_regress', [Person, Circle1, Store, Book])])
+[<class 'regressiontests.fixtures_regress.models.Circle1'>, <class 'regressiontests.fixtures_regress.models.Store'>, <class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>]
+
+# A tight circular dependency
+>>> sort_dependencies([('fixtures_regress', [Person, Circle2, Circle1, Store, Book])])
+Traceback (most recent call last):
+...
+CommandError: Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2 in serialized app list.
+
+>>> sort_dependencies([('fixtures_regress', [Circle1, Book, Circle2])])
+Traceback (most recent call last):
+...
+CommandError: Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2 in serialized app list.
+
+# A self referential dependency
+>>> sort_dependencies([('fixtures_regress', [Book, Circle3])])
+Traceback (most recent call last):
+...
+CommandError: Can't resolve dependencies for fixtures_regress.Circle3 in serialized app list.
+
+# A long circular dependency
+>>> sort_dependencies([('fixtures_regress', [Person, Circle2, Circle1, Circle3, Store, Book])])
+Traceback (most recent call last):
+...
+CommandError: Can't resolve dependencies for fixtures_regress.Circle1, fixtures_regress.Circle2, fixtures_regress.Circle3 in serialized app list.
+
+# A dependency on a normal, non-natural-key model
+>>> sort_dependencies([('fixtures_regress', [Person, ExternalDependency, Book])])
+[<class 'regressiontests.fixtures_regress.models.Person'>, <class 'regressiontests.fixtures_regress.models.Book'>, <class 'regressiontests.fixtures_regress.models.ExternalDependency'>]
+
+###############################################
+# Check that normal primary keys still work
+# on a model with natural key capabilities
+
+>>> management.call_command('loaddata', 'non_natural_1.json', verbosity=0)
+>>> management.call_command('loaddata', 'non_natural_2.xml', verbosity=0)
+
+>>> Book.objects.all()
+[<Book: Cryptonomicon by Neal Stephenson (available at Amazon, Borders)>, <Book: Ender's Game by Orson Scott Card (available at Collins Bookstore)>, <Book: Permutation City by Greg Egan (available at Angus and Robertson)>]
+
"""}
+
diff --git a/tests/regressiontests/forms/fields.py b/tests/regressiontests/forms/fields.py
index eeff56761d..1b96450f63 100644
--- a/tests/regressiontests/forms/fields.py
+++ b/tests/regressiontests/forms/fields.py
@@ -27,18 +27,20 @@ __init__(). For example, CharField has a max_length option.
import datetime
import time
import re
-from unittest import TestCase
import os
+from unittest import TestCase
+
+from django.core.files.uploadedfile import SimpleUploadedFile
from django.forms import *
from django.forms.widgets import RadioFieldRenderer
-from django.core.files.uploadedfile import SimpleUploadedFile
try:
from decimal import Decimal
except ImportError:
from django.utils._decimal import Decimal
+
def fix_os_paths(x):
if isinstance(x, basestring):
return x.replace('\\', '/')
@@ -50,7 +52,8 @@ def fix_os_paths(x):
return x
-class TestFields(TestCase):
+class FieldsTests(TestCase):
+
def assertRaisesErrorWithMessage(self, error, message, callable, *args, **kwargs):
self.assertRaises(error, callable, *args, **kwargs)
try:
@@ -187,7 +190,7 @@ class TestFields(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Ensure this value is greater than or equal to 0.5.']", f.clean, '0.4')
self.assertEqual(1.5, f.clean('1.5'))
self.assertEqual(0.5, f.clean('0.5'))
-
+
# DecimalField ################################################################
def test_decimalfield_13(self):
@@ -487,11 +490,11 @@ class TestFields(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://inv-.-alid.com')
self.assertEqual(u'http://valid-----hyphens.com/', f.clean('http://valid-----hyphens.com'))
- def test_url_regexp_for_performance(self):
+ def test_url_regex_ticket11198(self):
f = URLField()
# hangs "forever" if catastrophic backtracking in ticket:#11198 not fixed
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://%s' % ("X"*200,))
-
+
# a second test, to make sure the problem is really addressed, even on
# domains that don't fail the domain label length check in the regex
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid URL.']", f.clean, 'http://%s' % ("X"*60,))
@@ -575,7 +578,7 @@ class TestFields(TestCase):
self.assertEqual(False, f.clean('0'))
self.assertEqual(True, f.clean('Django rocks'))
self.assertEqual(False, f.clean('False'))
-
+
# ChoiceField #################################################################
def test_choicefield_46(self):
@@ -750,7 +753,7 @@ class TestFields(TestCase):
self.assertRaisesErrorWithMessage(ValidationError, "[u'Enter a valid e-mail address.']", f.clean, 'not an e-mail')
self.assertEqual(u'', f.clean(''))
self.assertEqual(u'', f.clean(None))
-
+
# FilePathField ###############################################################
def test_filepathfield_65(self):
diff --git a/tests/regressiontests/forms/forms.py b/tests/regressiontests/forms/forms.py
index bf9623fe77..627f50a6fe 100644
--- a/tests/regressiontests/forms/forms.py
+++ b/tests/regressiontests/forms/forms.py
@@ -1807,4 +1807,43 @@ True
>>> [f.name for f in form.visible_fields()]
['artist', 'name']
+# Hidden initial input gets its own unique id ################################
+
+>>> class MyForm(Form):
+... field1 = CharField(max_length=50, show_hidden_initial=True)
+>>> print MyForm()
+<tr><th><label for="id_field1">Field1:</label></th><td><input id="id_field1" type="text" name="field1" maxlength="50" /><input type="hidden" name="initial-field1" id="initial-id_field1" /></td></tr>
+
+# The error_html_class and required_html_class attributes ####################
+
+>>> p = Person({})
+>>> p.error_css_class = 'error'
+>>> p.required_css_class = 'required'
+
+>>> print p.as_ul()
+<li class="required error"><ul class="errorlist"><li>This field is required.</li></ul><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></li>
+<li class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool">
+<option value="1" selected="selected">Unknown</option>
+<option value="2">Yes</option>
+<option value="3">No</option>
+</select></li>
+
+>>> print p.as_p()
+<ul class="errorlist"><li>This field is required.</li></ul>
+<p class="required error"><label for="id_name">Name:</label> <input type="text" name="name" id="id_name" /></p>
+<p class="required"><label for="id_is_cool">Is cool:</label> <select name="is_cool" id="id_is_cool">
+<option value="1" selected="selected">Unknown</option>
+<option value="2">Yes</option>
+<option value="3">No</option>
+</select></p>
+
+>>> print p.as_table()
+<tr class="required error"><th><label for="id_name">Name:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="text" name="name" id="id_name" /></td></tr>
+<tr class="required"><th><label for="id_is_cool">Is cool:</label></th><td><select name="is_cool" id="id_is_cool">
+<option value="1" selected="selected">Unknown</option>
+<option value="2">Yes</option>
+<option value="3">No</option>
+</select></td></tr>
+
+
"""
diff --git a/tests/regressiontests/forms/localflavor/ca.py b/tests/regressiontests/forms/localflavor/ca.py
index 48171b0558..7f4b3ac89c 100644
--- a/tests/regressiontests/forms/localflavor/ca.py
+++ b/tests/regressiontests/forms/localflavor/ca.py
@@ -60,6 +60,50 @@ ValidationError: [u'Enter a postal code in the format XXX XXX.']
u''
>>> f.clean('')
u''
+>>> f.clean('W2S 2H3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('T2W 2H7')
+u'T2W 2H7'
+>>> f.clean('T2S 2W7')
+u'T2S 2W7'
+>>> f.clean('Z2S 2H3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('T2Z 2H7')
+u'T2Z 2H7'
+>>> f.clean('T2S 2Z7')
+u'T2S 2Z7'
+>>> f.clean('F2S 2H3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('A2S 2D3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('A2I 2R3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('A2I 2R3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('A2Q 2R3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('U2B 2R3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
+>>> f.clean('O2B 2R3')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXX XXX.']
# CAPhoneNumberField ##########################################################
diff --git a/tests/regressiontests/forms/localflavor/se.py b/tests/regressiontests/forms/localflavor/se.py
new file mode 100644
index 0000000000..44bd953c6e
--- /dev/null
+++ b/tests/regressiontests/forms/localflavor/se.py
@@ -0,0 +1,332 @@
+# -*- coding: utf-8 -*-
+# Tests for the contrib/localflavor/se form fields.
+
+tests = r"""
+# Monkey-patch datetime.date
+>>> import datetime
+>>> class MockDate(datetime.date):
+... def today(cls):
+... return datetime.date(2008, 5, 14)
+... today = classmethod(today)
+...
+>>> olddate = datetime.date
+>>> datetime.date = MockDate
+>>> datetime.date.today()
+MockDate(2008, 5, 14)
+
+
+# SECountySelect #####################################################
+>>> from django.contrib.localflavor.se.forms import SECountySelect
+
+>>> w = SECountySelect()
+>>> w.render('swedish_county', 'E')
+u'<select name="swedish_county">\n<option value="AB">Stockholm</option>\n<option value="AC">V\xe4sterbotten</option>\n<option value="BD">Norrbotten</option>\n<option value="C">Uppsala</option>\n<option value="D">S\xf6dermanland</option>\n<option value="E" selected="selected">\xd6sterg\xf6tland</option>\n<option value="F">J\xf6nk\xf6ping</option>\n<option value="G">Kronoberg</option>\n<option value="H">Kalmar</option>\n<option value="I">Gotland</option>\n<option value="K">Blekinge</option>\n<option value="M">Sk\xe5ne</option>\n<option value="N">Halland</option>\n<option value="O">V\xe4stra G\xf6taland</option>\n<option value="S">V\xe4rmland</option>\n<option value="T">\xd6rebro</option>\n<option value="U">V\xe4stmanland</option>\n<option value="W">Dalarna</option>\n<option value="X">G\xe4vleborg</option>\n<option value="Y">V\xe4sternorrland</option>\n<option value="Z">J\xe4mtland</option>\n</select>'
+
+# SEOrganisationNumberField #######################################
+
+>>> from django.contrib.localflavor.se.forms import SEOrganisationNumberField
+
+>>> f = SEOrganisationNumberField()
+
+# Ordinary personal identity numbers for sole proprietors
+# The same rules as for SEPersonalIdentityField applies here
+>>> f.clean('870512-1989')
+u'198705121989'
+>>> f.clean('19870512-1989')
+u'198705121989'
+>>> f.clean('870512-2128')
+u'198705122128'
+>>> f.clean('081015-6315')
+u'190810156315'
+>>> f.clean('081015+6315')
+u'180810156315'
+>>> f.clean('0810156315')
+u'190810156315'
+
+>>> f.clean('081015 6315')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+>>> f.clean('950231-4496')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+>>> f.clean('6914104499')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+>>> f.clean('950d314496')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+>>> f.clean('invalid!!!')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+>>> f.clean('870514-1111')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+
+
+# Empty values
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+# Co-ordination number checking
+# Co-ordination numbers are not valid organisation numbers
+>>> f.clean('870574-1315')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+
+>>> f.clean('870573-1311')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+
+# Test some different organisation numbers
+>>> f.clean('556074-7569') # IKEA Linköping
+u'5560747569'
+
+>>> f.clean('556074-3089') # Volvo Personvagnar
+u'5560743089'
+
+>>> f.clean('822001-5476') # LJS (organisation)
+u'8220015476'
+
+>>> f.clean('8220015476') # LJS (organisation)
+u'8220015476'
+
+>>> f.clean('2120000449') # Katedralskolan Linköping (school)
+u'2120000449'
+
+# Faux organisation number, which tests that the checksum can be 0
+>>> f.clean('232518-5060')
+u'2325185060'
+
+>>> f.clean('556074+3089') # Volvo Personvagnar, bad format
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+
+
+# Invalid checksum
+>>> f.clean('2120000441')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+
+# Valid checksum but invalid organisation type
+f.clean('1120000441')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish organisation number.']
+
+# Empty values with required=False
+>>> f = SEOrganisationNumberField(required=False)
+
+>>> f.clean(None)
+u''
+
+>>> f.clean('')
+u''
+
+
+# SEPersonalIdentityNumberField #######################################
+
+>>> from django.contrib.localflavor.se.forms import SEPersonalIdentityNumberField
+
+>>> f = SEPersonalIdentityNumberField()
+
+# Valid id numbers
+>>> f.clean('870512-1989')
+u'198705121989'
+
+>>> f.clean('870512-2128')
+u'198705122128'
+
+>>> f.clean('19870512-1989')
+u'198705121989'
+
+>>> f.clean('198705121989')
+u'198705121989'
+
+>>> f.clean('081015-6315')
+u'190810156315'
+
+>>> f.clean('0810156315')
+u'190810156315'
+
+# This is a "special-case" in the checksum calculation,
+# where the sum is divisible by 10 (the checksum digit == 0)
+>>> f.clean('8705141060')
+u'198705141060'
+
+# + means that the person is older than 100 years
+>>> f.clean('081015+6315')
+u'180810156315'
+
+# Bogus values
+>>> f.clean('081015 6315')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+>>> f.clean('950d314496')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+>>> f.clean('invalid!!!')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+
+# Invalid dates
+
+# February 31st does not exist
+>>> f.clean('950231-4496')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+# Month 14 does not exist
+>>> f.clean('6914104499')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+# There are no Swedish personal id numbers where year < 1800
+>>> f.clean('17430309-7135')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+# Invalid checksum
+>>> f.clean('870514-1111')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+# Empty values
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+# Co-ordination number checking
+>>> f.clean('870574-1315')
+u'198705741315'
+
+>>> f.clean('870574+1315')
+u'188705741315'
+
+>>> f.clean('198705741315')
+u'198705741315'
+
+# Co-ordination number with bad checksum
+>>> f.clean('870573-1311')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+
+# Check valid co-ordination numbers, that should not be accepted
+# because of coordination_number=False
+>>> f = SEPersonalIdentityNumberField(coordination_number=False)
+
+>>> f.clean('870574-1315')
+Traceback (most recent call last):
+...
+ValidationError: [u'Co-ordination numbers are not allowed.']
+
+>>> f.clean('870574+1315')
+Traceback (most recent call last):
+...
+ValidationError: [u'Co-ordination numbers are not allowed.']
+
+>>> f.clean('8705741315')
+Traceback (most recent call last):
+...
+ValidationError: [u'Co-ordination numbers are not allowed.']
+
+# Invalid co-ordination numbers should be treated as invalid, and not
+# as co-ordination numbers
+>>> f.clean('870573-1311')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a valid Swedish personal identity number.']
+
+# Empty values with required=False
+>>> f = SEPersonalIdentityNumberField(required=False)
+
+>>> f.clean(None)
+u''
+
+>>> f.clean('')
+u''
+
+# SEPostalCodeField ###############################################
+>>> from django.contrib.localflavor.se.forms import SEPostalCodeField
+>>> f = SEPostalCodeField()
+>>>
+Postal codes can have spaces
+>>> f.clean('589 37')
+u'58937'
+
+... but the dont have to
+>>> f.clean('58937')
+u'58937'
+>>> f.clean('abcasfassadf')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a Swedish postal code in the format XXXXX.']
+
+# Only one space is allowed for separation
+>>> f.clean('589 37')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a Swedish postal code in the format XXXXX.']
+
+# The postal code must not start with 0
+>>> f.clean('01234')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a Swedish postal code in the format XXXXX.']
+
+# Empty values
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+>>> f.clean(None)
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+
+# Empty values, required=False
+>>> f = SEPostalCodeField(required=False)
+>>> f.clean('')
+u''
+>>> f.clean(None)
+u''
+
+# Revert the monkey patching
+>>> datetime.date = olddate
+
+"""
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index ef7326735d..b6b7752f5a 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -21,6 +21,7 @@ from localflavor.jp import tests as localflavor_jp_tests
from localflavor.nl import tests as localflavor_nl_tests
from localflavor.pl import tests as localflavor_pl_tests
from localflavor.ro import tests as localflavor_ro_tests
+from localflavor.se import tests as localflavor_se_tests
from localflavor.sk import tests as localflavor_sk_tests
from localflavor.uk import tests as localflavor_uk_tests
from localflavor.us import tests as localflavor_us_tests
@@ -31,7 +32,7 @@ from widgets import tests as widgets_tests
from formsets import tests as formset_tests
from media import media_tests
-from fields import TestFields
+from fields import FieldsTests
from validators import TestFormWithValidators, TestFieldWithValidators
__test__ = {
@@ -57,6 +58,7 @@ __test__ = {
'localflavor_nl_tests': localflavor_nl_tests,
'localflavor_pl_tests': localflavor_pl_tests,
'localflavor_ro_tests': localflavor_ro_tests,
+ 'localflavor_se_tests': localflavor_se_tests,
'localflavor_sk_tests': localflavor_sk_tests,
'localflavor_uk_tests': localflavor_uk_tests,
'localflavor_us_tests': localflavor_us_tests,
diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py
index bc6764d8b2..9f1bbadd3e 100644
--- a/tests/regressiontests/generic_inline_admin/models.py
+++ b/tests/regressiontests/generic_inline_admin/models.py
@@ -20,7 +20,7 @@ class Media(models.Model):
class MediaInline(generic.GenericTabularInline):
model = Media
-
+
class EpisodeAdmin(admin.ModelAdmin):
inlines = [
MediaInline,
@@ -56,7 +56,7 @@ class MediaMaxNumInline(generic.GenericTabularInline):
model = Media
extra = 5
max_num = 2
-
+
admin.site.register(EpisodeMaxNum, inlines=[MediaMaxNumInline])
#
diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py
index 60a370fb2d..0cf1f4ea69 100644
--- a/tests/regressiontests/generic_inline_admin/tests.py
+++ b/tests/regressiontests/generic_inline_admin/tests.py
@@ -17,7 +17,7 @@ class GenericAdminViewTest(TestCase):
self.original_template_debug = settings.TEMPLATE_DEBUG
settings.TEMPLATE_DEBUG = True
self.client.login(username='super', password='secret')
-
+
# Can't load content via a fixture (since the GenericForeignKey
# relies on content type IDs, which will vary depending on what
# other tests have been run), thus we do it here.
@@ -25,26 +25,30 @@ class GenericAdminViewTest(TestCase):
self.episode_pk = e.pk
m = Media(content_object=e, url='http://example.com/podcast.mp3')
m.save()
- self.media_pk = m.pk
-
+ self.mp3_media_pk = m.pk
+
+ m = Media(content_object=e, url='http://example.com/logo.png')
+ m.save()
+ self.png_media_pk = m.pk
+
def tearDown(self):
self.client.logout()
settings.TEMPLATE_DEBUG = self.original_template_debug
-
+
def testBasicAddGet(self):
"""
A smoke test to ensure GET on the add_view works.
"""
response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/add/')
self.failUnlessEqual(response.status_code, 200)
-
+
def testBasicEditGet(self):
"""
A smoke test to ensure GET on the change_view works.
"""
response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk)
self.failUnlessEqual(response.status_code, 200)
-
+
def testBasicAddPost(self):
"""
A smoke test to ensure POST on add_view works.
@@ -57,7 +61,7 @@ class GenericAdminViewTest(TestCase):
}
response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/add/', post_data)
self.failUnlessEqual(response.status_code, 302) # redirect somewhere
-
+
def testBasicEditPost(self):
"""
A smoke test to ensure POST on edit_view works.
@@ -65,22 +69,54 @@ class GenericAdminViewTest(TestCase):
post_data = {
"name": u"This Week in Django",
# inline data
- "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"2",
- "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"1",
- "generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.media_pk,
+ "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": u"3",
+ "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": u"2",
+ "generic_inline_admin-media-content_type-object_id-0-id": u"%d" % self.mp3_media_pk,
"generic_inline_admin-media-content_type-object_id-0-url": u"http://example.com/podcast.mp3",
- "generic_inline_admin-media-content_type-object_id-1-id": u"",
- "generic_inline_admin-media-content_type-object_id-1-url": u"",
+ "generic_inline_admin-media-content_type-object_id-1-id": u"%d" % self.png_media_pk,
+ "generic_inline_admin-media-content_type-object_id-1-url": u"http://example.com/logo.png",
+ "generic_inline_admin-media-content_type-object_id-2-id": u"",
+ "generic_inline_admin-media-content_type-object_id-2-url": u"",
}
url = '/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk
response = self.client.post(url, post_data)
self.failUnlessEqual(response.status_code, 302) # redirect somewhere
-
+
+ def testGenericInlineFormset(self):
+ EpisodeMediaFormSet = generic_inlineformset_factory(Media, can_delete=False, extra=3)
+ e = Episode.objects.get(name='This Week in Django')
+
+ # Works with no queryset
+ formset = EpisodeMediaFormSet(instance=e)
+ self.assertEquals(len(formset.forms), 5)
+ self.assertEquals(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.mp3_media_pk)
+ self.assertEquals(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.png_media_pk)
+ self.assertEquals(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
+
+ # A queryset can be used to alter display ordering
+ formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.order_by('url'))
+ self.assertEquals(len(formset.forms), 5)
+ self.assertEquals(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.png_media_pk)
+ self.assertEquals(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" value="http://example.com/podcast.mp3" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>' % self.mp3_media_pk)
+ self.assertEquals(formset.forms[2].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-2-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-2-url" type="text" name="generic_inline_admin-media-content_type-object_id-2-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-2-id" id="id_generic_inline_admin-media-content_type-object_id-2-id" /></p>')
+
+
+ # Works with a queryset that omits items
+ formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.filter(url__endswith=".png"))
+ self.assertEquals(len(formset.forms), 4)
+ self.assertEquals(formset.forms[0].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-0-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-0-url" type="text" name="generic_inline_admin-media-content_type-object_id-0-url" value="http://example.com/logo.png" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-0-id" value="%s" id="id_generic_inline_admin-media-content_type-object_id-0-id" /></p>' % self.png_media_pk)
+ self.assertEquals(formset.forms[1].as_p(), '<p><label for="id_generic_inline_admin-media-content_type-object_id-1-url">Url:</label> <input id="id_generic_inline_admin-media-content_type-object_id-1-url" type="text" name="generic_inline_admin-media-content_type-object_id-1-url" maxlength="200" /><input type="hidden" name="generic_inline_admin-media-content_type-object_id-1-id" id="id_generic_inline_admin-media-content_type-object_id-1-id" /></p>')
+
def testGenericInlineFormsetFactory(self):
# Regression test for #10522.
inline_formset = generic_inlineformset_factory(Media,
exclude=('url',))
+ # Regression test for #12340.
+ e = Episode.objects.get(name='This Week in Django')
+ formset = inline_formset(instance=e)
+ self.failUnless(formset.get_queryset().ordered)
+
class GenericInlineAdminParametersTest(TestCase):
fixtures = ['users.xml']
@@ -139,4 +175,4 @@ class GenericInlineAdminParametersTest(TestCase):
e = self._create_object(EpisodeExclude)
response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episodeexclude/%s/' % e.pk)
formset = response.context['inline_admin_formsets'][0].formset
- self.failIf('url' in formset.forms[0], 'The formset has excluded "url" field.') \ No newline at end of file
+ self.failIf('url' in formset.forms[0], 'The formset has excluded "url" field.')
diff --git a/tests/regressiontests/generic_inline_admin/urls.py b/tests/regressiontests/generic_inline_admin/urls.py
index 04d68cd26c..c3e8af8fe1 100644
--- a/tests/regressiontests/generic_inline_admin/urls.py
+++ b/tests/regressiontests/generic_inline_admin/urls.py
@@ -2,5 +2,5 @@ from django.conf.urls.defaults import *
from django.contrib import admin
urlpatterns = patterns('',
- (r'^admin/(.*)', admin.site.root),
+ (r'^admin/', include(admin.site.urls)),
)
diff --git a/tests/regressiontests/i18n/forms.py b/tests/regressiontests/i18n/forms.py
new file mode 100644
index 0000000000..b8244f6ca7
--- /dev/null
+++ b/tests/regressiontests/i18n/forms.py
@@ -0,0 +1,13 @@
+from django import template, forms
+from django.forms.extras import SelectDateWidget
+
+class I18nForm(forms.Form):
+ decimal_field = forms.DecimalField()
+ float_field = forms.FloatField()
+ date_field = forms.DateField()
+ datetime_field = forms.DateTimeField()
+ time_field = forms.TimeField()
+
+class SelectDateForm(forms.Form):
+ date_field = forms.DateField(widget=SelectDateWidget)
+
diff --git a/tests/regressiontests/i18n/misc.py b/tests/regressiontests/i18n/misc.py
deleted file mode 100644
index f8f35ad4e4..0000000000
--- a/tests/regressiontests/i18n/misc.py
+++ /dev/null
@@ -1,114 +0,0 @@
-import sys
-
-tests = """
->>> from django.utils.translation.trans_real import parse_accept_lang_header
->>> p = parse_accept_lang_header
-
-#
-# Testing HTTP header parsing. First, we test that we can parse the values
-# according to the spec (and that we extract all the pieces in the right order).
-#
-
-Good headers.
->>> p('de')
-[('de', 1.0)]
->>> p('en-AU')
-[('en-AU', 1.0)]
->>> p('*;q=1.00')
-[('*', 1.0)]
->>> p('en-AU;q=0.123')
-[('en-AU', 0.123)]
->>> p('en-au;q=0.1')
-[('en-au', 0.10000000000000001)]
->>> p('en-au;q=1.0')
-[('en-au', 1.0)]
->>> p('da, en-gb;q=0.25, en;q=0.5')
-[('da', 1.0), ('en', 0.5), ('en-gb', 0.25)]
->>> p('en-au-xx')
-[('en-au-xx', 1.0)]
->>> p('de,en-au;q=0.75,en-us;q=0.5,en;q=0.25,es;q=0.125,fa;q=0.125')
-[('de', 1.0), ('en-au', 0.75), ('en-us', 0.5), ('en', 0.25), ('es', 0.125), ('fa', 0.125)]
->>> p('*')
-[('*', 1.0)]
->>> p('de;q=0.')
-[('de', 1.0)]
->>> p('')
-[]
-
-Bad headers; should always return [].
->>> p('en-gb;q=1.0000')
-[]
->>> p('en;q=0.1234')
-[]
->>> p('en;q=.2')
-[]
->>> p('abcdefghi-au')
-[]
->>> p('**')
-[]
->>> p('en,,gb')
-[]
->>> p('en-au;q=0.1.0')
-[]
->>> p('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZ,en')
-[]
->>> p('da, en-gb;q=0.8, en;q=0.7,#')
-[]
->>> p('de;q=2.0')
-[]
->>> p('de;q=0.a')
-[]
->>> p('')
-[]
-
-#
-# Now test that we parse a literal HTTP header correctly.
-#
-
->>> from django.utils.translation.trans_real import get_language_from_request
->>> g = get_language_from_request
->>> from django.http import HttpRequest
->>> r = HttpRequest
->>> r.COOKIES = {}
-
-These tests assumes the es, es_AR, pt and pt_BR translations exit in the Django
-source tree.
->>> r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt-br'}
->>> g(r)
-'pt-br'
->>> r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt'}
->>> g(r)
-'pt'
->>> r.META = {'HTTP_ACCEPT_LANGUAGE': 'es,de'}
->>> g(r)
-'es'
->>> r.META = {'HTTP_ACCEPT_LANGUAGE': 'es-ar,de'}
->>> g(r)
-'es-ar'
-"""
-
-# Python 2.3 and 2.4 return slightly different results for completely bogus
-# locales, so we omit this test for that anything below 2.4. It's relatively
-# harmless in any cases (GIGO). This also means this won't be executed on
-# Jython currently, but life's like that sometimes. (On those platforms,
-# passing in a truly bogus locale will get you the default locale back.)
-if sys.version_info >= (2, 5):
- tests += """
-This test assumes there won't be a Django translation to a US variation
-of the Spanish language, a safe assumption. When the user sets it
-as the preferred language, the main 'es' translation should be selected
-instead.
->>> r.META = {'HTTP_ACCEPT_LANGUAGE': 'es-us'}
->>> g(r)
-'es'
-"""
-
-tests += """
-This tests the following scenario: there isn't a main language (zh)
-translation of Django but there is a translation to variation (zh_CN)
-the user sets zh-cn as the preferred language, it should be selected by
-Django without falling back nor ignoring it.
->>> r.META = {'HTTP_ACCEPT_LANGUAGE': 'zh-cn,de'}
->>> g(r)
-'zh-cn'
-"""
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
index 94e792cf54..42dfe91659 100644
--- a/tests/regressiontests/i18n/tests.py
+++ b/tests/regressiontests/i18n/tests.py
@@ -1,72 +1,404 @@
-# coding: utf-8
-import misc
+import sys
+import decimal
+import datetime
-regressions = ur"""
-Format string interpolation should work with *_lazy objects.
+from django.template import Template, Context
+from django.conf import settings
+from django.utils.formats import get_format, date_format, number_format, localize
+from django.utils.numberformat import format
+from django.test import TestCase, client
+from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy
->>> from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy
->>> s = ugettext_lazy('Add %(name)s')
->>> d = {'name': 'Ringo'}
->>> s % d
-u'Add Ringo'
->>> activate('de')
->>> s % d
-u'Ringo hinzuf\xfcgen'
->>> activate('pl')
->>> s % d
-u'Dodaj Ringo'
->>> deactivate()
+from forms import I18nForm, SelectDateForm, SelectDateWidget
-It should be possible to compare *_lazy objects.
+class TranslationTests(TestCase):
->>> s1 = ugettext_lazy('Add %(name)s')
->>> s == s1
-True
->>> s2 = gettext_lazy('Add %(name)s')
->>> s3 = gettext_lazy('Add %(name)s')
->>> s2 == s3
-True
->>> s == s2
-True
->>> s4 = ugettext_lazy('Some other string')
->>> s == s4
-False
+ def test_lazy_objects(self):
+ """
+ Format string interpolation should work with *_lazy objects.
+ """
+ s = ugettext_lazy('Add %(name)s')
+ d = {'name': 'Ringo'}
+ self.assertEqual(u'Add Ringo', s % d)
+ activate('de')
+ try:
+ self.assertEqual(u'Ringo hinzuf\xfcgen', s % d)
+ activate('pl')
+ self.assertEqual(u'Dodaj Ringo', s % d)
+ finally:
+ deactivate()
-unicode(string_concat(...)) should not raise a TypeError - #4796
+ # It should be possible to compare *_lazy objects.
+ s1 = ugettext_lazy('Add %(name)s')
+ self.assertEqual(True, s == s1)
+ s2 = gettext_lazy('Add %(name)s')
+ s3 = gettext_lazy('Add %(name)s')
+ self.assertEqual(True, s2 == s3)
+ self.assertEqual(True, s == s2)
+ s4 = ugettext_lazy('Some other string')
+ self.assertEqual(False, s == s4)
->>> import django.utils.translation
->>> reload(django.utils.translation)
-<module 'django.utils.translation' from ...>
->>> unicode(django.utils.translation.string_concat("dja", "ngo"))
-u'django'
+ def test_string_concat(self):
+ """
+ unicode(string_concat(...)) should not raise a TypeError - #4796
+ """
+ import django.utils.translation
+ self.assertEqual(django.utils.translation, reload(django.utils.translation))
+ self.assertEqual(u'django', unicode(django.utils.translation.string_concat("dja", "ngo")))
-Translating a string requiring no auto-escaping shouldn't change the "safe"
-status.
+ def test_safe_status(self):
+ """
+ Translating a string requiring no auto-escaping shouldn't change the "safe" status.
+ """
+ from django.utils.safestring import mark_safe, SafeString, SafeUnicode
+ s = mark_safe('Password')
+ self.assertEqual(SafeString, type(s))
+ activate('de')
+ try:
+ self.assertEqual(SafeUnicode, type(ugettext(s)))
+ finally:
+ deactivate()
+ self.assertEqual('aPassword', SafeString('a') + s)
+ self.assertEqual('Passworda', s + SafeString('a'))
+ self.assertEqual('Passworda', s + mark_safe('a'))
+ self.assertEqual('aPassword', mark_safe('a') + s)
+ self.assertEqual('as', mark_safe('a') + mark_safe('s'))
->>> from django.utils.safestring import mark_safe, SafeString
->>> s = mark_safe('Password')
->>> type(s)
-<class 'django.utils.safestring.SafeString'>
->>> activate('de')
->>> type(ugettext(s))
-<class 'django.utils.safestring.SafeUnicode'>
->>> deactivate()
+ def test_maclines(self):
+ """
+ Translations on files with mac or dos end of lines will be converted
+ to unix eof in .po catalogs, and they have to match when retrieved
+ """
+ from django.utils.translation.trans_real import translation
+ ca_translation = translation('ca')
+ ca_translation._catalog[u'Mac\nEOF\n'] = u'Catalan Mac\nEOF\n'
+ ca_translation._catalog[u'Win\nEOF\n'] = u'Catalan Win\nEOF\n'
+ activate('ca')
+ try:
+ self.assertEqual(u'Catalan Mac\nEOF\n', ugettext(u'Mac\rEOF\r'))
+ self.assertEqual(u'Catalan Win\nEOF\n', ugettext(u'Win\r\nEOF\r\n'))
+ finally:
+ deactivate()
->>> SafeString('a') + s
-'aPassword'
->>> s + SafeString('a')
-'Passworda'
->>> s + mark_safe('a')
-'Passworda'
->>> mark_safe('a') + s
-'aPassword'
->>> mark_safe('a') + mark_safe('s')
-'as'
->>> print s
-Password
-"""
+class FormattingTests(TestCase):
-__test__ = {
- 'regressions': regressions,
- 'misc': misc.tests,
-}
+ def setUp(self):
+ self._use_i18n = settings.USE_I18N
+ self._use_l10n = settings.USE_L10N
+ self._use_thousand_separator = settings.USE_THOUSAND_SEPARATOR
+ self.n = decimal.Decimal('66666.666')
+ self.f = 99999.999
+ self.d = datetime.date(2009, 12, 31)
+ self.dt = datetime.datetime(2009, 12, 31, 20, 50)
+ self.ctxt = Context({
+ 'n': self.n,
+ 'd': self.d,
+ 'dt': self.dt,
+ 'f': self.f
+ })
+
+ def tearDown(self):
+ # Restore defaults
+ settings.USE_I18N = self._use_i18n
+ settings.USE_L10N = self._use_l10n
+ settings.USE_THOUSAND_SEPARATOR = self._use_thousand_separator
+
+ def test_locale_independent(self):
+ """
+ Localization of dates and numbers
+ """
+ settings.USE_L10N = True
+ settings.USE_THOUSAND_SEPARATOR = False
+ self.assertEqual(u'66666.66', format(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
+ self.assertEqual(u'66666A6', format(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
+
+ settings.USE_THOUSAND_SEPARATOR = True
+ self.assertEqual(u'66,666.66', format(self.n, decimal_sep='.', decimal_pos=2, grouping=3, thousand_sep=','))
+ self.assertEqual(u'6B6B6B6B6A6', format(self.n, decimal_sep='A', decimal_pos=1, grouping=1, thousand_sep='B'))
+ self.assertEqual(u'-66666.6', format(-66666.666, decimal_sep='.', decimal_pos=1))
+ self.assertEqual(u'-66666.0', format(int('-66666'), decimal_sep='.', decimal_pos=1))
+
+ def test_l10n_disabled(self):
+ """
+ Catalan locale with format i18n disabled translations will be used,
+ but not formats
+ """
+ settings.USE_L10N = False
+ activate('ca')
+ try:
+ self.assertEqual('N j, Y', get_format('DATE_FORMAT'))
+ self.assertEqual(0, get_format('FIRST_DAY_OF_WEEK'))
+ self.assertEqual('.', get_format('DECIMAL_SEPARATOR'))
+ self.assertEqual(u'des. 31, 2009', date_format(self.d))
+ self.assertEqual(u'desembre 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
+ self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
+ self.assertEqual('No localizable', localize('No localizable'))
+ self.assertEqual(decimal.Decimal('66666.666'), localize(self.n))
+ self.assertEqual(99999.999, localize(self.f))
+ self.assertEqual(datetime.date(2009, 12, 31), localize(self.d))
+ self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), localize(self.dt))
+ self.assertEqual(u'66666.666', Template('{{ n }}').render(self.ctxt))
+ self.assertEqual(u'99999.999', Template('{{ f }}').render(self.ctxt))
+ self.assertEqual(u'2009-12-31', Template('{{ d }}').render(self.ctxt))
+ self.assertEqual(u'2009-12-31 20:50:00', Template('{{ dt }}').render(self.ctxt))
+ self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
+ self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
+ self.assertEqual(u'12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
+ self.assertEqual(u'12/31/2009 8:50 p.m.', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
+
+ form = I18nForm({
+ 'decimal_field': u'66666,666',
+ 'float_field': u'99999,999',
+ 'date_field': u'31/12/2009',
+ 'datetime_field': u'31/12/2009 20:50',
+ 'time_field': u'20:50'
+ })
+ self.assertEqual(False, form.is_valid())
+ self.assertEqual([u'Introdu\xefu un n\xfamero.'], form.errors['float_field'])
+ self.assertEqual([u'Introdu\xefu un n\xfamero.'], form.errors['decimal_field'])
+ self.assertEqual([u'Introdu\xefu una data v\xe0lida.'], form.errors['date_field'])
+ self.assertEqual([u'Introdu\xefu una data/hora v\xe0lides.'], form.errors['datetime_field'])
+
+ form2 = SelectDateForm({
+ 'date_field_month': u'12',
+ 'date_field_day': u'31',
+ 'date_field_year': u'2009'
+ })
+ self.assertEqual(True, form2.is_valid())
+ self.assertEqual(datetime.date(2009, 12, 31), form2.cleaned_data['date_field'])
+ self.assertEqual(
+ u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
+ SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
+ )
+ finally:
+ deactivate()
+
+ def test_l10n_enabled(self):
+ """
+ Catalan locale
+ """
+ settings.USE_L10N = True
+ activate('ca')
+ try:
+ self.assertEqual('j \de F \de Y', get_format('DATE_FORMAT'))
+ self.assertEqual(1, get_format('FIRST_DAY_OF_WEEK'))
+ self.assertEqual(',', get_format('DECIMAL_SEPARATOR'))
+ self.assertEqual(u'31 de desembre de 2009', date_format(self.d))
+ self.assertEqual(u'desembre del 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
+ self.assertEqual(u'31/12/2009 20:50', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
+ self.assertEqual('No localizable', localize('No localizable'))
+
+ settings.USE_THOUSAND_SEPARATOR = True
+ self.assertEqual(u'66.666,666', localize(self.n))
+ self.assertEqual(u'99.999,999', localize(self.f))
+
+ settings.USE_THOUSAND_SEPARATOR = False
+ self.assertEqual(u'66666,666', localize(self.n))
+ self.assertEqual(u'99999,999', localize(self.f))
+ self.assertEqual(u'31 de desembre de 2009', localize(self.d))
+ self.assertEqual(u'31 de desembre de 2009 a les 20:50', localize(self.dt))
+
+ settings.USE_THOUSAND_SEPARATOR = True
+ self.assertEqual(u'66.666,666', Template('{{ n }}').render(self.ctxt))
+ self.assertEqual(u'99.999,999', Template('{{ f }}').render(self.ctxt))
+
+ settings.USE_THOUSAND_SEPARATOR = False
+ self.assertEqual(u'66666,666', Template('{{ n }}').render(self.ctxt))
+ self.assertEqual(u'99999,999', Template('{{ f }}').render(self.ctxt))
+ self.assertEqual(u'31 de desembre de 2009', Template('{{ d }}').render(self.ctxt))
+ self.assertEqual(u'31 de desembre de 2009 a les 20:50', Template('{{ dt }}').render(self.ctxt))
+ self.assertEqual(u'66666,67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
+ self.assertEqual(u'100000,0', Template('{{ f|floatformat }}').render(self.ctxt))
+ self.assertEqual(u'31/12/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
+ self.assertEqual(u'31/12/2009 20:50', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
+
+ form3 = I18nForm({
+ 'decimal_field': u'66666,666',
+ 'float_field': u'99999,999',
+ 'date_field': u'31/12/2009',
+ 'datetime_field': u'31/12/2009 20:50',
+ 'time_field': u'20:50'
+ })
+ self.assertEqual(True, form3.is_valid())
+ self.assertEqual(decimal.Decimal('66666.666'), form3.cleaned_data['decimal_field'])
+ self.assertEqual(99999.999, form3.cleaned_data['float_field'])
+ self.assertEqual(datetime.date(2009, 12, 31), form3.cleaned_data['date_field'])
+ self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), form3.cleaned_data['datetime_field'])
+ self.assertEqual(datetime.time(20, 50), form3.cleaned_data['time_field'])
+
+ form4 = SelectDateForm({
+ 'date_field_month': u'12',
+ 'date_field_day': u'31',
+ 'date_field_year': u'2009'
+ })
+ self.assertEqual(True, form4.is_valid())
+ self.assertEqual(datetime.date(2009, 12, 31), form4.cleaned_data['date_field'])
+ self.assertEqual(
+ u'<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_month" id="id_mydate_month">\n<option value="1">gener</option>\n<option value="2">febrer</option>\n<option value="3">mar\xe7</option>\n<option value="4">abril</option>\n<option value="5">maig</option>\n<option value="6">juny</option>\n<option value="7">juliol</option>\n<option value="8">agost</option>\n<option value="9">setembre</option>\n<option value="10">octubre</option>\n<option value="11">novembre</option>\n<option value="12" selected="selected">desembre</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
+ SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
+ )
+ finally:
+ deactivate()
+
+ # English locale
+
+ settings.USE_L10N = True
+ activate('en')
+ try:
+ self.assertEqual('N j, Y', get_format('DATE_FORMAT'))
+ self.assertEqual(0, get_format('FIRST_DAY_OF_WEEK'))
+ self.assertEqual('.', get_format('DECIMAL_SEPARATOR'))
+ self.assertEqual(u'Dec. 31, 2009', date_format(self.d))
+ self.assertEqual(u'December 2009', date_format(self.d, 'YEAR_MONTH_FORMAT'))
+ self.assertEqual(u'12/31/2009 8:50 p.m.', date_format(self.dt, 'SHORT_DATETIME_FORMAT'))
+ self.assertEqual('No localizable', localize('No localizable'))
+
+ settings.USE_THOUSAND_SEPARATOR = True
+ self.assertEqual(u'66,666.666', localize(self.n))
+ self.assertEqual(u'99,999.999', localize(self.f))
+
+ settings.USE_THOUSAND_SEPARATOR = False
+ self.assertEqual(u'66666.666', localize(self.n))
+ self.assertEqual(u'99999.999', localize(self.f))
+ self.assertEqual(u'Dec. 31, 2009', localize(self.d))
+ self.assertEqual(u'Dec. 31, 2009, 8:50 p.m.', localize(self.dt))
+
+ settings.USE_THOUSAND_SEPARATOR = True
+ self.assertEqual(u'66,666.666', Template('{{ n }}').render(self.ctxt))
+ self.assertEqual(u'99,999.999', Template('{{ f }}').render(self.ctxt))
+
+ settings.USE_THOUSAND_SEPARATOR = False
+ self.assertEqual(u'66666.666', Template('{{ n }}').render(self.ctxt))
+ self.assertEqual(u'99999.999', Template('{{ f }}').render(self.ctxt))
+ self.assertEqual(u'Dec. 31, 2009', Template('{{ d }}').render(self.ctxt))
+ self.assertEqual(u'Dec. 31, 2009, 8:50 p.m.', Template('{{ dt }}').render(self.ctxt))
+ self.assertEqual(u'66666.67', Template('{{ n|floatformat:2 }}').render(self.ctxt))
+ self.assertEqual(u'100000.0', Template('{{ f|floatformat }}').render(self.ctxt))
+ self.assertEqual(u'12/31/2009', Template('{{ d|date:"SHORT_DATE_FORMAT" }}').render(self.ctxt))
+ self.assertEqual(u'12/31/2009 8:50 p.m.', Template('{{ dt|date:"SHORT_DATETIME_FORMAT" }}').render(self.ctxt))
+
+ form5 = I18nForm({
+ 'decimal_field': u'66666.666',
+ 'float_field': u'99999.999',
+ 'date_field': u'12/31/2009',
+ 'datetime_field': u'12/31/2009 20:50',
+ 'time_field': u'20:50'
+ })
+ self.assertEqual(True, form5.is_valid())
+ self.assertEqual(decimal.Decimal('66666.666'), form5.cleaned_data['decimal_field'])
+ self.assertEqual(99999.999, form5.cleaned_data['float_field'])
+ self.assertEqual(datetime.date(2009, 12, 31), form5.cleaned_data['date_field'])
+ self.assertEqual(datetime.datetime(2009, 12, 31, 20, 50), form5.cleaned_data['datetime_field'])
+ self.assertEqual(datetime.time(20, 50), form5.cleaned_data['time_field'])
+
+ form6 = SelectDateForm({
+ 'date_field_month': u'12',
+ 'date_field_day': u'31',
+ 'date_field_year': u'2009'
+ })
+ self.assertEqual(True, form6.is_valid())
+ self.assertEqual(datetime.date(2009, 12, 31), form6.cleaned_data['date_field'])
+ self.assertEqual(
+ u'<select name="mydate_month" id="id_mydate_month">\n<option value="1">January</option>\n<option value="2">February</option>\n<option value="3">March</option>\n<option value="4">April</option>\n<option value="5">May</option>\n<option value="6">June</option>\n<option value="7">July</option>\n<option value="8">August</option>\n<option value="9">September</option>\n<option value="10">October</option>\n<option value="11">November</option>\n<option value="12" selected="selected">December</option>\n</select>\n<select name="mydate_day" id="id_mydate_day">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="4">4</option>\n<option value="5">5</option>\n<option value="6">6</option>\n<option value="7">7</option>\n<option value="8">8</option>\n<option value="9">9</option>\n<option value="10">10</option>\n<option value="11">11</option>\n<option value="12">12</option>\n<option value="13">13</option>\n<option value="14">14</option>\n<option value="15">15</option>\n<option value="16">16</option>\n<option value="17">17</option>\n<option value="18">18</option>\n<option value="19">19</option>\n<option value="20">20</option>\n<option value="21">21</option>\n<option value="22">22</option>\n<option value="23">23</option>\n<option value="24">24</option>\n<option value="25">25</option>\n<option value="26">26</option>\n<option value="27">27</option>\n<option value="28">28</option>\n<option value="29">29</option>\n<option value="30">30</option>\n<option value="31" selected="selected">31</option>\n</select>\n<select name="mydate_year" id="id_mydate_year">\n<option value="2009" selected="selected">2009</option>\n<option value="2010">2010</option>\n<option value="2011">2011</option>\n<option value="2012">2012</option>\n<option value="2013">2013</option>\n<option value="2014">2014</option>\n<option value="2015">2015</option>\n<option value="2016">2016</option>\n<option value="2017">2017</option>\n<option value="2018">2018</option>\n</select>',
+ SelectDateWidget().render('mydate', datetime.date(2009, 12, 31))
+ )
+ finally:
+ deactivate()
+
+ def test_sub_locales(self):
+ """
+ Check if sublocales fall back to the main locale
+ """
+ settings.USE_L10N = True
+ activate('de-at')
+ settings.USE_THOUSAND_SEPARATOR = True
+ try:
+ self.assertEqual(u'66.666,666', Template('{{ n }}').render(self.ctxt))
+ finally:
+ deactivate()
+
+ activate('es-us')
+ try:
+ self.assertEqual(u'31 de diciembre de 2009', date_format(self.d))
+ finally:
+ deactivate()
+
+class MiscTests(TestCase):
+
+ def test_parse_spec_http_header(self):
+ """
+ Testing HTTP header parsing. First, we test that we can parse the
+ values according to the spec (and that we extract all the pieces in
+ the right order).
+ """
+ from django.utils.translation.trans_real import parse_accept_lang_header
+ p = parse_accept_lang_header
+ # Good headers.
+ self.assertEqual([('de', 1.0)], p('de'))
+ self.assertEqual([('en-AU', 1.0)], p('en-AU'))
+ self.assertEqual([('*', 1.0)], p('*;q=1.00'))
+ self.assertEqual([('en-AU', 0.123)], p('en-AU;q=0.123'))
+ self.assertEqual([('en-au', 0.10000000000000001)], p('en-au;q=0.1'))
+ self.assertEqual([('en-au', 1.0)], p('en-au;q=1.0'))
+ self.assertEqual([('da', 1.0), ('en', 0.5), ('en-gb', 0.25)], p('da, en-gb;q=0.25, en;q=0.5'))
+ self.assertEqual([('en-au-xx', 1.0)], p('en-au-xx'))
+ self.assertEqual([('de', 1.0), ('en-au', 0.75), ('en-us', 0.5), ('en', 0.25), ('es', 0.125), ('fa', 0.125)], p('de,en-au;q=0.75,en-us;q=0.5,en;q=0.25,es;q=0.125,fa;q=0.125'))
+ self.assertEqual([('*', 1.0)], p('*'))
+ self.assertEqual([('de', 1.0)], p('de;q=0.'))
+ self.assertEqual([], p(''))
+
+ # Bad headers; should always return [].
+ self.assertEqual([], p('en-gb;q=1.0000'))
+ self.assertEqual([], p('en;q=0.1234'))
+ self.assertEqual([], p('en;q=.2'))
+ self.assertEqual([], p('abcdefghi-au'))
+ self.assertEqual([], p('**'))
+ self.assertEqual([], p('en,,gb'))
+ self.assertEqual([], p('en-au;q=0.1.0'))
+ self.assertEqual([], p('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZ,en'))
+ self.assertEqual([], p('da, en-gb;q=0.8, en;q=0.7,#'))
+ self.assertEqual([], p('de;q=2.0'))
+ self.assertEqual([], p('de;q=0.a'))
+ self.assertEqual([], p(''))
+
+ def test_parse_literal_http_header(self):
+ """
+ Now test that we parse a literal HTTP header correctly.
+ """
+ from django.utils.translation.trans_real import get_language_from_request
+ g = get_language_from_request
+ from django.http import HttpRequest
+ r = HttpRequest
+ r.COOKIES = {}
+ r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt-br'}
+ self.assertEqual('pt-br', g(r))
+
+ r.META = {'HTTP_ACCEPT_LANGUAGE': 'pt'}
+ self.assertEqual('pt', g(r))
+
+ r.META = {'HTTP_ACCEPT_LANGUAGE': 'es,de'}
+ self.assertEqual('es', g(r))
+
+ r.META = {'HTTP_ACCEPT_LANGUAGE': 'es-ar,de'}
+ self.assertEqual('es-ar', g(r))
+
+ # Python 2.3 and 2.4 return slightly different results for completely
+ # bogus locales, so we omit this test for that anything below 2.4.
+ # It's relatively harmless in any cases (GIGO). This also means this
+ # won't be executed on Jython currently, but life's like that
+ # sometimes. (On those platforms, passing in a truly bogus locale
+ # will get you the default locale back.)
+ if sys.version_info >= (2, 5):
+ # This test assumes there won't be a Django translation to a US
+ # variation of the Spanish language, a safe assumption. When the
+ # user sets it as the preferred language, the main 'es'
+ # translation should be selected instead.
+ r.META = {'HTTP_ACCEPT_LANGUAGE': 'es-us'}
+ self.assertEqual(g(r), 'es')
+
+ # This tests the following scenario: there isn't a main language (zh)
+ # translation of Django but there is a translation to variation (zh_CN)
+ # the user sets zh-cn as the preferred language, it should be selected
+ # by Django without falling back nor ignoring it.
+ r.META = {'HTTP_ACCEPT_LANGUAGE': 'zh-cn,de'}
+ self.assertEqual(g(r), 'zh-cn')
diff --git a/tests/regressiontests/introspection/models.py b/tests/regressiontests/introspection/models.py
index 3227067bd8..ef485e3a3c 100644
--- a/tests/regressiontests/introspection/models.py
+++ b/tests/regressiontests/introspection/models.py
@@ -4,6 +4,7 @@ class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
+ facebook_user_id = models.BigIntegerField()
def __unicode__(self):
return u"%s %s" % (self.first_name, self.last_name)
diff --git a/tests/regressiontests/introspection/tests.py b/tests/regressiontests/introspection/tests.py
index 1454e1e3e5..10250294c5 100644
--- a/tests/regressiontests/introspection/tests.py
+++ b/tests/regressiontests/introspection/tests.py
@@ -1,5 +1,5 @@
from django.conf import settings
-from django.db import connection
+from django.db import connection, DEFAULT_DB_ALIAS
from django.test import TestCase
from django.utils import functional
@@ -77,10 +77,10 @@ class IntrospectionTests(TestCase):
cursor = connection.cursor()
desc = connection.introspection.get_table_description(cursor, Reporter._meta.db_table)
self.assertEqual([datatype(r[1], r) for r in desc],
- ['IntegerField', 'CharField', 'CharField', 'CharField'])
+ ['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField'])
# Regression test for #9991 - 'real' types in postgres
- if settings.DATABASE_ENGINE.startswith('postgresql'):
+ if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'].startswith('django.db.backends.postgresql'):
def test_postgresql_real_type(self):
cursor = connection.cursor()
cursor.execute("CREATE TABLE django_ixn_real_test_table (number REAL);")
diff --git a/tests/regressiontests/auth_backends/models.py b/tests/regressiontests/localflavor/__init__.py
index e69de29bb2..e69de29bb2 100644
--- a/tests/regressiontests/auth_backends/models.py
+++ b/tests/regressiontests/localflavor/__init__.py
diff --git a/tests/regressiontests/localflavor/forms.py b/tests/regressiontests/localflavor/forms.py
new file mode 100644
index 0000000000..49635b02fb
--- /dev/null
+++ b/tests/regressiontests/localflavor/forms.py
@@ -0,0 +1,14 @@
+from django.forms import ModelForm
+from models import Place
+
+class PlaceForm(ModelForm):
+ """docstring for PlaceForm"""
+ class Meta:
+ model = Place
+from django.forms import ModelForm
+from models import Place
+
+class PlaceForm(ModelForm):
+ """docstring for PlaceForm"""
+ class Meta:
+ model = Place
diff --git a/tests/regressiontests/localflavor/models.py b/tests/regressiontests/localflavor/models.py
new file mode 100644
index 0000000000..079c7bd982
--- /dev/null
+++ b/tests/regressiontests/localflavor/models.py
@@ -0,0 +1,16 @@
+from django.db import models
+from django.contrib.localflavor.us.models import USStateField
+
+class Place(models.Model):
+ state = USStateField(blank=True)
+ state_req = USStateField()
+ state_default = USStateField(default="CA", blank=True)
+ name = models.CharField(max_length=20)
+from django.db import models
+from django.contrib.localflavor.us.models import USStateField
+
+class Place(models.Model):
+ state = USStateField(blank=True)
+ state_req = USStateField()
+ state_default = USStateField(default="CA", blank=True)
+ name = models.CharField(max_length=20)
diff --git a/tests/regressiontests/localflavor/tests.py b/tests/regressiontests/localflavor/tests.py
new file mode 100644
index 0000000000..61e0e5b5e6
--- /dev/null
+++ b/tests/regressiontests/localflavor/tests.py
@@ -0,0 +1,166 @@
+from django.test import TestCase
+from models import Place
+from forms import PlaceForm
+
+class USLocalflavorTests(TestCase):
+ def setUp(self):
+ self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
+
+ def test_get_display_methods(self):
+ """Test that the get_*_display() methods are added to the model instances."""
+ place = self.form.save()
+ self.assertEqual(place.get_state_display(), 'Georgia')
+ self.assertEqual(place.get_state_req_display(), 'North Carolina')
+
+ def test_required(self):
+ """Test that required USStateFields throw appropriate errors."""
+ form = PlaceForm({'state':'GA', 'name':'Place in GA'})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(form.errors['state_req'], [u'This field is required.'])
+
+ def test_field_blank_option(self):
+ """Test that the empty option is there."""
+ state_select_html = """\
+<select name="state" id="id_state">
+<option value="">---------</option>
+<option value="AL">Alabama</option>
+<option value="AK">Alaska</option>
+<option value="AS">American Samoa</option>
+<option value="AZ">Arizona</option>
+<option value="AR">Arkansas</option>
+<option value="CA">California</option>
+<option value="CO">Colorado</option>
+<option value="CT">Connecticut</option>
+<option value="DE">Delaware</option>
+<option value="DC">District of Columbia</option>
+<option value="FL">Florida</option>
+<option value="GA" selected="selected">Georgia</option>
+<option value="GU">Guam</option>
+<option value="HI">Hawaii</option>
+<option value="ID">Idaho</option>
+<option value="IL">Illinois</option>
+<option value="IN">Indiana</option>
+<option value="IA">Iowa</option>
+<option value="KS">Kansas</option>
+<option value="KY">Kentucky</option>
+<option value="LA">Louisiana</option>
+<option value="ME">Maine</option>
+<option value="MD">Maryland</option>
+<option value="MA">Massachusetts</option>
+<option value="MI">Michigan</option>
+<option value="MN">Minnesota</option>
+<option value="MS">Mississippi</option>
+<option value="MO">Missouri</option>
+<option value="MT">Montana</option>
+<option value="NE">Nebraska</option>
+<option value="NV">Nevada</option>
+<option value="NH">New Hampshire</option>
+<option value="NJ">New Jersey</option>
+<option value="NM">New Mexico</option>
+<option value="NY">New York</option>
+<option value="NC">North Carolina</option>
+<option value="ND">North Dakota</option>
+<option value="MP">Northern Mariana Islands</option>
+<option value="OH">Ohio</option>
+<option value="OK">Oklahoma</option>
+<option value="OR">Oregon</option>
+<option value="PA">Pennsylvania</option>
+<option value="PR">Puerto Rico</option>
+<option value="RI">Rhode Island</option>
+<option value="SC">South Carolina</option>
+<option value="SD">South Dakota</option>
+<option value="TN">Tennessee</option>
+<option value="TX">Texas</option>
+<option value="UT">Utah</option>
+<option value="VT">Vermont</option>
+<option value="VI">Virgin Islands</option>
+<option value="VA">Virginia</option>
+<option value="WA">Washington</option>
+<option value="WV">West Virginia</option>
+<option value="WI">Wisconsin</option>
+<option value="WY">Wyoming</option>
+</select>"""
+ self.assertEqual(str(self.form['state']), state_select_html)
+from django.test import TestCase
+from models import Place
+from forms import PlaceForm
+
+class USLocalflavorTests(TestCase):
+ def setUp(self):
+ self.form = PlaceForm({'state':'GA', 'state_req':'NC', 'name':'impossible'})
+
+ def test_get_display_methods(self):
+ """Test that the get_*_display() methods are added to the model instances."""
+ place = self.form.save()
+ self.assertEqual(place.get_state_display(), 'Georgia')
+ self.assertEqual(place.get_state_req_display(), 'North Carolina')
+
+ def test_required(self):
+ """Test that required USStateFields throw appropriate errors."""
+ form = PlaceForm({'state':'GA', 'name':'Place in GA'})
+ self.assertFalse(form.is_valid())
+ self.assertEqual(form.errors['state_req'], [u'This field is required.'])
+
+ def test_field_blank_option(self):
+ """Test that the empty option is there."""
+ state_select_html = """\
+<select name="state" id="id_state">
+<option value="">---------</option>
+<option value="AL">Alabama</option>
+<option value="AK">Alaska</option>
+<option value="AS">American Samoa</option>
+<option value="AZ">Arizona</option>
+<option value="AR">Arkansas</option>
+<option value="CA">California</option>
+<option value="CO">Colorado</option>
+<option value="CT">Connecticut</option>
+<option value="DE">Delaware</option>
+<option value="DC">District of Columbia</option>
+<option value="FL">Florida</option>
+<option value="GA" selected="selected">Georgia</option>
+<option value="GU">Guam</option>
+<option value="HI">Hawaii</option>
+<option value="ID">Idaho</option>
+<option value="IL">Illinois</option>
+<option value="IN">Indiana</option>
+<option value="IA">Iowa</option>
+<option value="KS">Kansas</option>
+<option value="KY">Kentucky</option>
+<option value="LA">Louisiana</option>
+<option value="ME">Maine</option>
+<option value="MD">Maryland</option>
+<option value="MA">Massachusetts</option>
+<option value="MI">Michigan</option>
+<option value="MN">Minnesota</option>
+<option value="MS">Mississippi</option>
+<option value="MO">Missouri</option>
+<option value="MT">Montana</option>
+<option value="NE">Nebraska</option>
+<option value="NV">Nevada</option>
+<option value="NH">New Hampshire</option>
+<option value="NJ">New Jersey</option>
+<option value="NM">New Mexico</option>
+<option value="NY">New York</option>
+<option value="NC">North Carolina</option>
+<option value="ND">North Dakota</option>
+<option value="MP">Northern Mariana Islands</option>
+<option value="OH">Ohio</option>
+<option value="OK">Oklahoma</option>
+<option value="OR">Oregon</option>
+<option value="PA">Pennsylvania</option>
+<option value="PR">Puerto Rico</option>
+<option value="RI">Rhode Island</option>
+<option value="SC">South Carolina</option>
+<option value="SD">South Dakota</option>
+<option value="TN">Tennessee</option>
+<option value="TX">Texas</option>
+<option value="UT">Utah</option>
+<option value="VT">Vermont</option>
+<option value="VI">Virgin Islands</option>
+<option value="VA">Virginia</option>
+<option value="WA">Washington</option>
+<option value="WV">West Virginia</option>
+<option value="WI">Wisconsin</option>
+<option value="WY">Wyoming</option>
+</select>"""
+ self.assertEqual(str(self.form['state']), state_select_html)
diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py
index 81e6c22f09..ab841ce409 100644
--- a/tests/regressiontests/model_fields/models.py
+++ b/tests/regressiontests/model_fields/models.py
@@ -51,6 +51,9 @@ class BigD(models.Model):
class BigS(models.Model):
s = models.SlugField(max_length=255)
+class BigInt(models.Model):
+ value = models.BigIntegerField()
+ null_value = models.BigIntegerField(null = True, blank = True)
###############################################################################
# ImageField
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
index d47344d644..ea7b49ab7e 100644
--- a/tests/regressiontests/model_fields/tests.py
+++ b/tests/regressiontests/model_fields/tests.py
@@ -6,7 +6,7 @@ from django import forms
from django.db import models
from django.core.exceptions import ValidationError
-from models import Foo, Bar, Whiz, BigD, BigS, Image
+from models import Foo, Bar, Whiz, BigD, BigS, Image, BigInt
try:
from decimal import Decimal
@@ -44,8 +44,9 @@ class DecimalFieldTests(django.test.TestCase):
self.assertEqual(f._format(None), None)
def test_get_db_prep_lookup(self):
+ from django.db import connection
f = models.DecimalField(max_digits=5, decimal_places=1)
- self.assertEqual(f.get_db_prep_lookup('exact', None), [None])
+ self.assertEqual(f.get_db_prep_lookup('exact', None, connection=connection), [None])
def test_filter_with_strings(self):
"""
@@ -98,13 +99,14 @@ class DateTimeFieldTests(unittest.TestCase):
class BooleanFieldTests(unittest.TestCase):
def _test_get_db_prep_lookup(self, f):
- self.assertEqual(f.get_db_prep_lookup('exact', True), [True])
- self.assertEqual(f.get_db_prep_lookup('exact', '1'), [True])
- self.assertEqual(f.get_db_prep_lookup('exact', 1), [True])
- self.assertEqual(f.get_db_prep_lookup('exact', False), [False])
- self.assertEqual(f.get_db_prep_lookup('exact', '0'), [False])
- self.assertEqual(f.get_db_prep_lookup('exact', 0), [False])
- self.assertEqual(f.get_db_prep_lookup('exact', None), [None])
+ from django.db import connection
+ self.assertEqual(f.get_db_prep_lookup('exact', True, connection=connection), [True])
+ self.assertEqual(f.get_db_prep_lookup('exact', '1', connection=connection), [True])
+ self.assertEqual(f.get_db_prep_lookup('exact', 1, connection=connection), [True])
+ self.assertEqual(f.get_db_prep_lookup('exact', False, connection=connection), [False])
+ self.assertEqual(f.get_db_prep_lookup('exact', '0', connection=connection), [False])
+ self.assertEqual(f.get_db_prep_lookup('exact', 0, connection=connection), [False])
+ self.assertEqual(f.get_db_prep_lookup('exact', None, connection=connection), [None])
def test_booleanfield_get_db_prep_lookup(self):
self._test_get_db_prep_lookup(models.BooleanField())
@@ -145,6 +147,7 @@ class SlugFieldTests(django.test.TestCase):
bs = BigS.objects.get(pk=bs.pk)
self.assertEqual(bs.s, 'slug'*50)
+
class ValidationTest(django.test.TestCase):
def test_charfield_raises_error_on_empty_string(self):
f = models.CharField()
@@ -195,3 +198,32 @@ class ValidationTest(django.test.TestCase):
f = models.BooleanField()
self.assertRaises(ValidationError, f.clean, None, None)
+
+class BigIntegerFieldTests(django.test.TestCase):
+ def test_limits(self):
+ # Ensure that values that are right at the limits can be saved
+ # and then retrieved without corruption.
+ maxval = 9223372036854775807
+ minval = -maxval - 1
+ BigInt.objects.create(value=maxval)
+ qs = BigInt.objects.filter(value__gte=maxval)
+ self.assertEqual(qs.count(), 1)
+ self.assertEqual(qs[0].value, maxval)
+ BigInt.objects.create(value=minval)
+ qs = BigInt.objects.filter(value__lte=minval)
+ self.assertEqual(qs.count(), 1)
+ self.assertEqual(qs[0].value, minval)
+
+ def test_types(self):
+ b = BigInt(value = 0)
+ self.assertTrue(isinstance(b.value, (int, long)))
+ b.save()
+ self.assertTrue(isinstance(b.value, (int, long)))
+ b = BigInt.objects.all()[0]
+ self.assertTrue(isinstance(b.value, (int, long)))
+
+ def test_coercing(self):
+ BigInt.objects.create(value ='10')
+ b = BigInt.objects.get(value = '10')
+ self.assertEqual(b.value, 10)
+
diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py
index 85e284b639..f8b6511140 100644
--- a/tests/regressiontests/model_forms_regress/tests.py
+++ b/tests/regressiontests/model_forms_regress/tests.py
@@ -100,4 +100,16 @@ class CustomFieldSaveTests(TestCase):
# It's enough that the form saves without error -- the custom save routine will
# generate an AssertionError if it is called more than once during save.
form = CFFForm(data = {'f': None})
- form.save() \ No newline at end of file
+ form.save()
+
+class ModelChoiceIteratorTests(TestCase):
+ def test_len(self):
+ class Form(forms.ModelForm):
+ class Meta:
+ model = Article
+ fields = ["publications"]
+
+ Publication.objects.create(title="Pravda",
+ date_published=date(1991, 8, 22))
+ f = Form()
+ self.assertEqual(len(f.fields["publications"].choices), 1)
diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py
index 6a804a97c1..c669b2337a 100644
--- a/tests/regressiontests/model_inheritance_regress/models.py
+++ b/tests/regressiontests/model_inheritance_regress/models.py
@@ -314,7 +314,8 @@ DoesNotExist: ArticleWithAuthor matching query does not exist.
# likely to ocurr naturally with model inheritance, so we check it here).
# Regression test for #9390. This necessarily pokes at the SQL string for the
# query, since the duplicate problems are only apparent at that late stage.
->>> sql = ArticleWithAuthor.objects.order_by('pub_date', 'pk').query.as_sql()[0]
+>>> qs = ArticleWithAuthor.objects.order_by('pub_date', 'pk')
+>>> sql = qs.query.get_compiler(qs.db).as_sql()[0]
>>> fragment = sql[sql.find('ORDER BY'):]
>>> pos = fragment.find('pub_date')
>>> fragment.find('pub_date', pos + 1) == -1
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
index 668acd830a..420f2c262c 100644
--- a/tests/regressiontests/model_regress/models.py
+++ b/tests/regressiontests/model_regress/models.py
@@ -2,7 +2,7 @@
import datetime
from django.conf import settings
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.utils import tzinfo
CHOICES = (
@@ -149,7 +149,9 @@ datetime.datetime(2000, 1, 1, 6, 1, 1)
"""}
-if settings.DATABASE_ENGINE not in ("mysql", "oracle"):
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] not in (
+ "django.db.backends.mysql",
+ "django.db.backends.oracle"):
__test__["timezone-tests"] = """
# Saving an updating with timezone-aware datetime Python objects. Regression
# test for #10443.
@@ -167,4 +169,3 @@ if settings.DATABASE_ENGINE not in ("mysql", "oracle"):
1
"""
-
diff --git a/tests/regressiontests/multiple_database/__init__.py b/tests/regressiontests/multiple_database/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/multiple_database/__init__.py
diff --git a/tests/regressiontests/multiple_database/fixtures/multidb-common.json b/tests/regressiontests/multiple_database/fixtures/multidb-common.json
new file mode 100644
index 0000000000..33134173b9
--- /dev/null
+++ b/tests/regressiontests/multiple_database/fixtures/multidb-common.json
@@ -0,0 +1,10 @@
+[
+ {
+ "pk": 1,
+ "model": "multiple_database.book",
+ "fields": {
+ "title": "The Definitive Guide to Django",
+ "published": "2009-7-8"
+ }
+ }
+] \ No newline at end of file
diff --git a/tests/regressiontests/multiple_database/fixtures/multidb.default.json b/tests/regressiontests/multiple_database/fixtures/multidb.default.json
new file mode 100644
index 0000000000..379b18a803
--- /dev/null
+++ b/tests/regressiontests/multiple_database/fixtures/multidb.default.json
@@ -0,0 +1,26 @@
+[
+ {
+ "pk": 1,
+ "model": "multiple_database.person",
+ "fields": {
+ "name": "Marty Alchin"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "multiple_database.person",
+ "fields": {
+ "name": "George Vilches"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "multiple_database.book",
+ "fields": {
+ "title": "Pro Django",
+ "published": "2008-12-16",
+ "authors": [["Marty Alchin"]],
+ "editor": ["George Vilches"]
+ }
+ }
+]
diff --git a/tests/regressiontests/multiple_database/fixtures/multidb.other.json b/tests/regressiontests/multiple_database/fixtures/multidb.other.json
new file mode 100644
index 0000000000..c64f490201
--- /dev/null
+++ b/tests/regressiontests/multiple_database/fixtures/multidb.other.json
@@ -0,0 +1,26 @@
+[
+ {
+ "pk": 1,
+ "model": "multiple_database.person",
+ "fields": {
+ "name": "Mark Pilgrim"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "multiple_database.person",
+ "fields": {
+ "name": "Chris Mills"
+ }
+ },
+ {
+ "pk": 2,
+ "model": "multiple_database.book",
+ "fields": {
+ "title": "Dive into Python",
+ "published": "2009-5-4",
+ "authors": [["Mark Pilgrim"]],
+ "editor": ["Chris Mills"]
+ }
+ }
+] \ No newline at end of file
diff --git a/tests/regressiontests/multiple_database/models.py b/tests/regressiontests/multiple_database/models.py
new file mode 100644
index 0000000000..33cb7fb745
--- /dev/null
+++ b/tests/regressiontests/multiple_database/models.py
@@ -0,0 +1,49 @@
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes import generic
+from django.db import models, DEFAULT_DB_ALIAS
+
+class Review(models.Model):
+ source = models.CharField(max_length=100)
+ content_type = models.ForeignKey(ContentType)
+ object_id = models.PositiveIntegerField()
+ content_object = generic.GenericForeignKey()
+
+ def __unicode__(self):
+ return self.source
+
+ class Meta:
+ ordering = ('source',)
+
+class PersonManager(models.Manager):
+ def get_by_natural_key(self, name):
+ return self.get(name=name)
+
+class Person(models.Model):
+ objects = PersonManager()
+ name = models.CharField(max_length=100)
+
+ def __unicode__(self):
+ return self.name
+
+ class Meta:
+ ordering = ('name',)
+
+class Book(models.Model):
+ title = models.CharField(max_length=100)
+ published = models.DateField()
+ authors = models.ManyToManyField(Person)
+ editor = models.ForeignKey(Person, null=True, related_name='edited')
+ reviews = generic.GenericRelation(Review)
+
+ def __unicode__(self):
+ return self.title
+
+ class Meta:
+ ordering = ('title',)
+
+class UserProfile(models.Model):
+ user = models.OneToOneField(User)
+ flavor = models.CharField(max_length=100)
+
diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py
new file mode 100644
index 0000000000..300ed5e0a6
--- /dev/null
+++ b/tests/regressiontests/multiple_database/tests.py
@@ -0,0 +1,705 @@
+import datetime
+import pickle
+
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.db import connections
+from django.test import TestCase
+
+from models import Book, Person, Review, UserProfile
+
+try:
+ # we only have these models if the user is using multi-db, it's safe the
+ # run the tests without them though.
+ from models import Article, article_using
+except ImportError:
+ pass
+
+class QueryTestCase(TestCase):
+ multi_db = True
+
+ def test_default_creation(self):
+ "Objects created on the default database don't leak onto other databases"
+ # Create a book on the default database using create()
+ Book.objects.create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ # Create a book on the default database using a save
+ dive = Book()
+ dive.title="Dive into Python"
+ dive.published = datetime.date(2009, 5, 4)
+ dive.save()
+
+ # Check that book exists on the default database, but not on other database
+ try:
+ Book.objects.get(title="Pro Django")
+ Book.objects.using('default').get(title="Pro Django")
+ except Book.DoesNotExist:
+ self.fail('"Dive Into Python" should exist on default database')
+
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.using('other').get,
+ title="Pro Django"
+ )
+
+ try:
+ Book.objects.get(title="Dive into Python")
+ Book.objects.using('default').get(title="Dive into Python")
+ except Book.DoesNotExist:
+ self.fail('"Dive into Python" should exist on default database')
+
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.using('other').get,
+ title="Dive into Python"
+ )
+
+
+ def test_other_creation(self):
+ "Objects created on another database don't leak onto the default database"
+ # Create a book on the second database
+ Book.objects.using('other').create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ # Create a book on the default database using a save
+ dive = Book()
+ dive.title="Dive into Python"
+ dive.published = datetime.date(2009, 5, 4)
+ dive.save(using='other')
+
+ # Check that book exists on the default database, but not on other database
+ try:
+ Book.objects.using('other').get(title="Pro Django")
+ except Book.DoesNotExist:
+ self.fail('"Dive Into Python" should exist on other database')
+
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.get,
+ title="Pro Django"
+ )
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.using('default').get,
+ title="Pro Django"
+ )
+
+ try:
+ Book.objects.using('other').get(title="Dive into Python")
+ except Book.DoesNotExist:
+ self.fail('"Dive into Python" should exist on other database')
+
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.get,
+ title="Dive into Python"
+ )
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.using('default').get,
+ title="Dive into Python"
+ )
+
+ def test_basic_queries(self):
+ "Queries are constrained to a single database"
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ dive = Book.objects.using('other').get(published=datetime.date(2009, 5, 4))
+ self.assertEqual(dive.title, "Dive into Python")
+ self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, published=datetime.date(2009, 5, 4))
+
+ dive = Book.objects.using('other').get(title__icontains="dive")
+ self.assertEqual(dive.title, "Dive into Python")
+ self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, title__icontains="dive")
+
+ dive = Book.objects.using('other').get(title__iexact="dive INTO python")
+ self.assertEqual(dive.title, "Dive into Python")
+ self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, title__iexact="dive INTO python")
+
+ dive = Book.objects.using('other').get(published__year=2009)
+ self.assertEqual(dive.title, "Dive into Python")
+ self.assertEqual(dive.published, datetime.date(2009, 5, 4))
+ self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, published__year=2009)
+
+ years = Book.objects.using('other').dates('published', 'year')
+ self.assertEqual([o.year for o in years], [2009])
+ years = Book.objects.using('default').dates('published', 'year')
+ self.assertEqual([o.year for o in years], [])
+
+ months = Book.objects.using('other').dates('published', 'month')
+ self.assertEqual([o.month for o in months], [5])
+ months = Book.objects.using('default').dates('published', 'month')
+ self.assertEqual([o.month for o in months], [])
+
+ def test_m2m_separation(self):
+ "M2M fields are constrained to a single database"
+ # Create a book and author on the default database
+ pro = Book.objects.create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ marty = Person.objects.create(name="Marty Alchin")
+
+ # Create a book and author on the other database
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ mark = Person.objects.using('other').create(name="Mark Pilgrim")
+
+ # Save the author relations
+ pro.authors = [marty]
+ dive.authors = [mark]
+
+ # Inspect the m2m tables directly.
+ # There should be 1 entry in each database
+ self.assertEquals(Book.authors.through.objects.using('default').count(), 1)
+ self.assertEquals(Book.authors.through.objects.using('other').count(), 1)
+
+ # Check that queries work across m2m joins
+ self.assertEquals(list(Book.objects.using('default').filter(authors__name='Marty Alchin').values_list('title', flat=True)),
+ [u'Pro Django'])
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Marty Alchin').values_list('title', flat=True)),
+ [])
+
+ self.assertEquals(list(Book.objects.using('default').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)),
+ [])
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)),
+ [u'Dive into Python'])
+
+ # Reget the objects to clear caches
+ dive = Book.objects.using('other').get(title="Dive into Python")
+ mark = Person.objects.using('other').get(name="Mark Pilgrim")
+
+ # Retrive related object by descriptor. Related objects should be database-baound
+ self.assertEquals(list(dive.authors.all().values_list('name', flat=True)),
+ [u'Mark Pilgrim'])
+
+ self.assertEquals(list(mark.book_set.all().values_list('title', flat=True)),
+ [u'Dive into Python'])
+
+ def test_m2m_forward_operations(self):
+ "M2M forward manipulations are all constrained to a single DB"
+ # Create a book and author on the other database
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ mark = Person.objects.using('other').create(name="Mark Pilgrim")
+
+ # Save the author relations
+ dive.authors = [mark]
+
+ # Add a second author
+ john = Person.objects.using('other').create(name="John Smith")
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)),
+ [])
+
+
+ dive.authors.add(john)
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)),
+ [u'Dive into Python'])
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)),
+ [u'Dive into Python'])
+
+ # Remove the second author
+ dive.authors.remove(john)
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)),
+ [u'Dive into Python'])
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)),
+ [])
+
+ # Clear all authors
+ dive.authors.clear()
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)),
+ [])
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)),
+ [])
+
+ # Create an author through the m2m interface
+ dive.authors.create(name='Jane Brown')
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)),
+ [])
+ self.assertEquals(list(Book.objects.using('other').filter(authors__name='Jane Brown').values_list('title', flat=True)),
+ [u'Dive into Python'])
+
+ def test_m2m_reverse_operations(self):
+ "M2M reverse manipulations are all constrained to a single DB"
+ # Create a book and author on the other database
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ mark = Person.objects.using('other').create(name="Mark Pilgrim")
+
+ # Save the author relations
+ dive.authors = [mark]
+
+ # Create a second book on the other database
+ grease = Book.objects.using('other').create(title="Greasemonkey Hacks",
+ published=datetime.date(2005, 11, 1))
+
+ # Add a books to the m2m
+ mark.book_set.add(grease)
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)),
+ [u'Mark Pilgrim'])
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)),
+ [u'Mark Pilgrim'])
+
+ # Remove a book from the m2m
+ mark.book_set.remove(grease)
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)),
+ [u'Mark Pilgrim'])
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)),
+ [])
+
+ # Clear the books associated with mark
+ mark.book_set.clear()
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)),
+ [])
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)),
+ [])
+
+ # Create a book through the m2m interface
+ mark.book_set.create(title="Dive into HTML5", published=datetime.date(2020, 1, 1))
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)),
+ [])
+ self.assertEquals(list(Person.objects.using('other').filter(book__title='Dive into HTML5').values_list('name', flat=True)),
+ [u'Mark Pilgrim'])
+
+# def test_m2m_cross_database_protection(self):
+# "Operations that involve sharing M2M objects across databases raise an error"
+# # Create a book and author on the default database
+# pro = Book.objects.create(title="Pro Django",
+# published=datetime.date(2008, 12, 16))
+
+# marty = Person.objects.create(name="Marty Alchin")
+
+# # Create a book and author on the other database
+# dive = Book.objects.using('other').create(title="Dive into Python",
+# published=datetime.date(2009, 5, 4))
+
+# mark = Person.objects.using('other').create(name="Mark Pilgrim")
+# # Set a foreign key set with an object from a different database
+# try:
+# marty.book_set = [pro, dive]
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # Add to an m2m with an object from a different database
+# try:
+# marty.book_set.add(dive)
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # Set a m2m with an object from a different database
+# try:
+# marty.book_set = [pro, dive]
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # Add to a reverse m2m with an object from a different database
+# try:
+# dive.authors.add(marty)
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # Set a reverse m2m with an object from a different database
+# try:
+# dive.authors = [mark, marty]
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+ def test_foreign_key_separation(self):
+ "FK fields are constrained to a single database"
+ # Create a book and author on the default database
+ pro = Book.objects.create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ marty = Person.objects.create(name="Marty Alchin")
+ george = Person.objects.create(name="George Vilches")
+
+ # Create a book and author on the other database
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ mark = Person.objects.using('other').create(name="Mark Pilgrim")
+ chris = Person.objects.using('other').create(name="Chris Mills")
+
+ # Save the author's favourite books
+ pro.editor = george
+ pro.save()
+
+ dive.editor = chris
+ dive.save()
+
+ pro = Book.objects.using('default').get(title="Pro Django")
+ self.assertEquals(pro.editor.name, "George Vilches")
+
+ dive = Book.objects.using('other').get(title="Dive into Python")
+ self.assertEquals(dive.editor.name, "Chris Mills")
+
+ # Check that queries work across foreign key joins
+ self.assertEquals(list(Person.objects.using('default').filter(edited__title='Pro Django').values_list('name', flat=True)),
+ [u'George Vilches'])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Pro Django').values_list('name', flat=True)),
+ [])
+
+ self.assertEquals(list(Person.objects.using('default').filter(edited__title='Dive into Python').values_list('name', flat=True)),
+ [])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)),
+ [u'Chris Mills'])
+
+ # Reget the objects to clear caches
+ chris = Person.objects.using('other').get(name="Chris Mills")
+ dive = Book.objects.using('other').get(title="Dive into Python")
+
+ # Retrive related object by descriptor. Related objects should be database-baound
+ self.assertEquals(list(chris.edited.values_list('title', flat=True)),
+ [u'Dive into Python'])
+
+ def test_foreign_key_reverse_operations(self):
+ "FK reverse manipulations are all constrained to a single DB"
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ mark = Person.objects.using('other').create(name="Mark Pilgrim")
+ chris = Person.objects.using('other').create(name="Chris Mills")
+
+ # Save the author relations
+ dive.editor = chris
+ dive.save()
+
+ # Add a second book edited by chris
+ html5 = Book.objects.using('other').create(title="Dive into HTML5", published=datetime.date(2010, 3, 15))
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)),
+ [])
+
+ chris.edited.add(html5)
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)),
+ [u'Chris Mills'])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)),
+ [u'Chris Mills'])
+
+ # Remove the second editor
+ chris.edited.remove(html5)
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)),
+ [])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)),
+ [u'Chris Mills'])
+
+ # Clear all edited books
+ chris.edited.clear()
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)),
+ [])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)),
+ [])
+
+ # Create an author through the m2m interface
+ chris.edited.create(title='Dive into Water', published=datetime.date(2010, 3, 15))
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)),
+ [])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Water').values_list('name', flat=True)),
+ [u'Chris Mills'])
+ self.assertEquals(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)),
+ [])
+
+# def test_foreign_key_cross_database_protection(self):
+# "Operations that involve sharing FK objects across databases raise an error"
+# # Create a book and author on the default database
+# pro = Book.objects.create(title="Pro Django",
+# published=datetime.date(2008, 12, 16))
+
+# marty = Person.objects.create(name="Marty Alchin")
+
+# # Create a book and author on the other database
+# dive = Book.objects.using('other').create(title="Dive into Python",
+# published=datetime.date(2009, 5, 4))
+
+# mark = Person.objects.using('other').create(name="Mark Pilgrim")
+
+# # Set a foreign key with an object from a different database
+# try:
+# dive.editor = marty
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # Set a foreign key set with an object from a different database
+# try:
+# marty.edited = [pro, dive]
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # Add to a foreign key set with an object from a different database
+# try:
+# marty.edited.add(dive)
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # BUT! if you assign a FK object when the base object hasn't
+# # been saved yet, you implicitly assign the database for the
+# # base object.
+# chris = Person(name="Chris Mills")
+# html5 = Book(title="Dive into HTML5", published=datetime.date(2010, 3, 15))
+# # initially, no db assigned
+# self.assertEquals(chris._state.db, None)
+# self.assertEquals(html5._state.db, None)
+
+# # old object comes from 'other', so the new object is set to use 'other'...
+# dive.editor = chris
+# html5.editor = mark
+# # self.assertEquals(chris._state.db, 'other')
+# self.assertEquals(html5._state.db, 'other')
+# # ... but it isn't saved yet
+# self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)),
+# [u'Mark Pilgrim'])
+# self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),
+# [u'Dive into Python'])
+
+# # When saved (no using required), new objects goes to 'other'
+# chris.save()
+# html5.save()
+# self.assertEquals(list(Person.objects.using('default').values_list('name',flat=True)),
+# [u'Marty Alchin'])
+# self.assertEquals(list(Person.objects.using('other').values_list('name',flat=True)),
+# [u'Chris Mills', u'Mark Pilgrim'])
+# self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),
+# [u'Pro Django'])
+# self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),
+# [u'Dive into HTML5', u'Dive into Python'])
+
+# # This also works if you assign the FK in the constructor
+# water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark)
+# self.assertEquals(water._state.db, 'other')
+# # ... but it isn't saved yet
+# self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),
+# [u'Pro Django'])
+# self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),
+# [u'Dive into HTML5', u'Dive into Python'])
+
+# # When saved, the new book goes to 'other'
+# water.save()
+# self.assertEquals(list(Book.objects.using('default').values_list('title',flat=True)),
+# [u'Pro Django'])
+# self.assertEquals(list(Book.objects.using('other').values_list('title',flat=True)),
+# [u'Dive into HTML5', u'Dive into Python', u'Dive into Water'])
+
+ def test_generic_key_separation(self):
+ "Generic fields are constrained to a single database"
+ # Create a book and author on the default database
+ pro = Book.objects.create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ review1 = Review.objects.create(source="Python Monthly", content_object=pro)
+
+ # Create a book and author on the other database
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ review2 = Review.objects.using('other').create(source="Python Weekly", content_object=dive)
+
+ review1 = Review.objects.using('default').get(source="Python Monthly")
+ self.assertEquals(review1.content_object.title, "Pro Django")
+
+ review2 = Review.objects.using('other').get(source="Python Weekly")
+ self.assertEquals(review2.content_object.title, "Dive into Python")
+
+ # Reget the objects to clear caches
+ dive = Book.objects.using('other').get(title="Dive into Python")
+
+ # Retrive related object by descriptor. Related objects should be database-bound
+ self.assertEquals(list(dive.reviews.all().values_list('source', flat=True)),
+ [u'Python Weekly'])
+
+ def test_generic_key_reverse_operations(self):
+ "Generic reverse manipulations are all constrained to a single DB"
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ temp = Book.objects.using('other').create(title="Temp",
+ published=datetime.date(2009, 5, 4))
+
+ review1 = Review.objects.using('other').create(source="Python Weekly", content_object=dive)
+ review2 = Review.objects.using('other').create(source="Python Monthly", content_object=temp)
+
+ self.assertEquals(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [])
+ self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [u'Python Weekly'])
+
+ # Add a second review
+ dive.reviews.add(review2)
+ self.assertEquals(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [])
+ self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [u'Python Monthly', u'Python Weekly'])
+
+ # Remove the second author
+ dive.reviews.remove(review1)
+ self.assertEquals(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [])
+ self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [u'Python Monthly'])
+
+ # Clear all reviews
+ dive.reviews.clear()
+ self.assertEquals(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [])
+ self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [])
+
+ # Create an author through the generic interface
+ dive.reviews.create(source='Python Daily')
+ self.assertEquals(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [])
+ self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)),
+ [u'Python Daily'])
+
+# def test_generic_key_cross_database_protection(self):
+## "Operations that involve sharing FK objects across databases raise an error"
+## # Create a book and author on the default database
+## pro = Book.objects.create(title="Pro Django",
+## published=datetime.date(2008, 12, 16))
+
+## review1 = Review.objects.create(source="Python Monthly", content_object=pro)
+
+## # Create a book and author on the other database
+## dive = Book.objects.using('other').create(title="Dive into Python",
+## published=datetime.date(2009, 5, 4))
+
+## review2 = Review.objects.using('other').create(source="Python Weekly", content_object=dive)
+
+## # Set a foreign key with an object from a different database
+## try:
+## review1.content_object = dive
+## self.fail("Shouldn't be able to assign across databases")
+## except ValueError:
+## pass
+
+# # Add to a foreign key set with an object from a different database
+# try:
+# dive.reviews.add(review1)
+# self.fail("Shouldn't be able to assign across databases")
+# except ValueError:
+# pass
+
+# # BUT! if you assign a FK object when the base object hasn't
+# # been saved yet, you implicitly assign the database for the
+# # base object.
+# review3 = Review(source="Python Daily")
+# # initially, no db assigned
+# self.assertEquals(review3._state.db, None)
+
+# # Dive comes from 'other', so review3 is set to use 'other'...
+# review3.content_object = dive
+# self.assertEquals(review3._state.db, 'other')
+# # ... but it isn't saved yet
+# self.assertEquals(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)),
+# [u'Python Monthly'])
+# self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)),
+# [u'Python Weekly'])
+
+# # When saved, John goes to 'other'
+# review3.save()
+# self.assertEquals(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)),
+# [u'Python Monthly'])
+# self.assertEquals(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source',flat=True)),
+# [u'Python Daily', u'Python Weekly'])
+
+ def test_ordering(self):
+ "get_next_by_XXX commands stick to a single database"
+ pro = Book.objects.create(title="Pro Django",
+ published=datetime.date(2008, 12, 16))
+
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+
+ learn = Book.objects.using('other').create(title="Learning Python",
+ published=datetime.date(2008, 7, 16))
+
+ self.assertEquals(learn.get_next_by_published().title, "Dive into Python")
+ self.assertEquals(dive.get_previous_by_published().title, "Learning Python")
+
+ def test_raw(self):
+ "test the raw() method across databases"
+ dive = Book.objects.using('other').create(title="Dive into Python",
+ published=datetime.date(2009, 5, 4))
+ val = Book.objects.db_manager("other").raw('SELECT id FROM "multiple_database_book"')
+ self.assertEqual(map(lambda o: o.pk, val), [dive.pk])
+
+ val = Book.objects.raw('SELECT id FROM "multiple_database_book"').using('other')
+ self.assertEqual(map(lambda o: o.pk, val), [dive.pk])
+
+
+class UserProfileTestCase(TestCase):
+ def setUp(self):
+ self.old_auth_profile_module = getattr(settings, 'AUTH_PROFILE_MODULE', None)
+ settings.AUTH_PROFILE_MODULE = 'multiple_database.UserProfile'
+
+ def tearDown(self):
+ settings.AUTH_PROFILE_MODULE = self.old_auth_profile_module
+
+ def test_user_profiles(self):
+
+ alice = User.objects.create_user('alice', 'alice@example.com')
+ bob = User.objects.db_manager('other').create_user('bob', 'bob@example.com')
+
+ alice_profile = UserProfile(user=alice, flavor='chocolate')
+ alice_profile.save()
+
+ bob_profile = UserProfile(user=bob, flavor='crunchy frog')
+ bob_profile.save()
+
+ self.assertEquals(alice.get_profile().flavor, 'chocolate')
+ self.assertEquals(bob.get_profile().flavor, 'crunchy frog')
+
+class FixtureTestCase(TestCase):
+ multi_db = True
+ fixtures = ['multidb-common', 'multidb']
+
+ def test_fixture_loading(self):
+ "Multi-db fixtures are loaded correctly"
+ # Check that "Pro Django" exists on the default database, but not on other database
+ try:
+ Book.objects.get(title="Pro Django")
+ Book.objects.using('default').get(title="Pro Django")
+ except Book.DoesNotExist:
+ self.fail('"Pro Django" should exist on default database')
+
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.using('other').get,
+ title="Pro Django"
+ )
+
+ # Check that "Dive into Python" exists on the default database, but not on other database
+ try:
+ Book.objects.using('other').get(title="Dive into Python")
+ except Book.DoesNotExist:
+ self.fail('"Dive into Python" should exist on other database')
+
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.get,
+ title="Dive into Python"
+ )
+ self.assertRaises(Book.DoesNotExist,
+ Book.objects.using('default').get,
+ title="Dive into Python"
+ )
+
+ # Check that "Definitive Guide" exists on the both databases
+ try:
+ Book.objects.get(title="The Definitive Guide to Django")
+ Book.objects.using('default').get(title="The Definitive Guide to Django")
+ Book.objects.using('other').get(title="The Definitive Guide to Django")
+ except Book.DoesNotExist:
+ self.fail('"The Definitive Guide to Django" should exist on both databases')
+
+
+class PickleQuerySetTestCase(TestCase):
+ multi_db = True
+
+ def test_pickling(self):
+ for db in connections:
+ Book.objects.using(db).create(title='Dive into Python', published=datetime.date(2009, 5, 4))
+ qs = Book.objects.all()
+ self.assertEqual(qs.db, pickle.loads(pickle.dumps(qs)).db)
diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py
index ddac509ede..aababffbab 100644
--- a/tests/regressiontests/queries/models.py
+++ b/tests/regressiontests/queries/models.py
@@ -7,7 +7,7 @@ import pickle
import sys
from django.conf import settings
-from django.db import models
+from django.db import models, DEFAULT_DB_ALIAS
from django.db.models.query import Q, ITER_CHUNK_SIZE
# Python 2.3 doesn't have sorted()
@@ -822,8 +822,8 @@ We can do slicing beyond what is currently in the result cache, too.
Bug #7045 -- extra tables used to crash SQL construction on the second use.
>>> qs = Ranking.objects.extra(tables=['django_site'])
->>> s = qs.query.as_sql()
->>> s = qs.query.as_sql() # test passes if this doesn't raise an exception.
+>>> s = qs.query.get_compiler(qs.db).as_sql()
+>>> s = qs.query.get_compiler(qs.db).as_sql() # test passes if this doesn't raise an exception.
Bug #7098 -- Make sure semi-deprecated ordering by related models syntax still
works.
@@ -912,9 +912,9 @@ We should also be able to pickle things that use select_related(). The only
tricky thing here is to ensure that we do the related selections properly after
unpickling.
>>> qs = Item.objects.select_related()
->>> query = qs.query.as_sql()[0]
+>>> query = qs.query.get_compiler(qs.db).as_sql()[0]
>>> query2 = pickle.loads(pickle.dumps(qs.query))
->>> query2.as_sql()[0] == query
+>>> query2.get_compiler(qs.db).as_sql()[0] == query
True
Check pickling of deferred-loading querysets
@@ -1051,7 +1051,7 @@ sufficient that this query runs without error.
Calling order_by() with no parameters removes any existing ordering on the
model. But it should still be possible to add new ordering after that.
>>> qs = Author.objects.order_by().order_by('name')
->>> 'ORDER BY' in qs.query.as_sql()[0]
+>>> 'ORDER BY' in qs.query.get_compiler(qs.db).as_sql()[0]
True
Incorrect SQL was being generated for certain types of exclude() queries that
@@ -1085,7 +1085,8 @@ performance problems on backends like MySQL.
Nested queries should not evaluate the inner query as part of constructing the
SQL (so we should see a nested query here, indicated by two "SELECT" calls).
->>> Annotation.objects.filter(notes__in=Note.objects.filter(note="xyzzy")).query.as_sql()[0].count('SELECT')
+>>> qs = Annotation.objects.filter(notes__in=Note.objects.filter(note="xyzzy"))
+>>> qs.query.get_compiler(qs.db).as_sql()[0].count('SELECT')
2
Bug #10181 -- Avoid raising an EmptyResultSet if an inner query is provably
@@ -1222,20 +1223,20 @@ FieldError: Infinite loop caused by ordering.
# In Oracle, we expect a null CharField to return u'' instead of None.
-if settings.DATABASE_ENGINE == "oracle":
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == "django.db.backends.oracle":
__test__["API_TESTS"] = __test__["API_TESTS"].replace("<NONE_OR_EMPTY_UNICODE>", "u''")
else:
__test__["API_TESTS"] = __test__["API_TESTS"].replace("<NONE_OR_EMPTY_UNICODE>", "None")
-if settings.DATABASE_ENGINE == "mysql":
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == "django.db.backends.mysql":
__test__["API_TESTS"] += """
When grouping without specifying ordering, we add an explicit "ORDER BY NULL"
portion in MySQL to prevent unnecessary sorting.
>>> query = Tag.objects.values_list('parent_id', flat=True).order_by().query
>>> query.group_by = ['parent_id']
->>> sql = query.as_sql()[0]
+>>> sql = query.get_compiler(DEFAULT_DB_ALIAS).as_sql()[0]
>>> fragment = "ORDER BY "
>>> pos = sql.find(fragment)
>>> sql.find(fragment, pos + 1) == -1
diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py
index 313ed8fc3a..ccd850326d 100644
--- a/tests/regressiontests/serializers_regress/models.py
+++ b/tests/regressiontests/serializers_regress/models.py
@@ -43,6 +43,9 @@ class FloatData(models.Model):
class IntegerData(models.Model):
data = models.IntegerField(null=True)
+class BigIntegerData(models.Model):
+ data = models.BigIntegerField(null=True)
+
# class ImageData(models.Model):
# data = models.ImageField(null=True)
diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py
index de7ddcc9f7..11b53a5277 100644
--- a/tests/regressiontests/serializers_regress/tests.py
+++ b/tests/regressiontests/serializers_regress/tests.py
@@ -13,7 +13,7 @@ from cStringIO import StringIO
from django.utils.functional import curry
from django.core import serializers
-from django.db import transaction
+from django.db import transaction, DEFAULT_DB_ALIAS
from django.core import management
from django.conf import settings
@@ -260,19 +260,19 @@ The end."""),
(fk_obj, 452, FKDataToField, None),
(fk_obj, 460, FKDataToO2O, 300),
-
+
(im2m_obj, 470, M2MIntermediateData, None),
-
+
#testing post- and prereferences and extra fields
(im_obj, 480, Intermediate, {'right': 300, 'left': 470}),
- (im_obj, 481, Intermediate, {'right': 300, 'left': 490}),
- (im_obj, 482, Intermediate, {'right': 500, 'left': 470}),
- (im_obj, 483, Intermediate, {'right': 500, 'left': 490}),
- (im_obj, 484, Intermediate, {'right': 300, 'left': 470, 'extra': "extra"}),
- (im_obj, 485, Intermediate, {'right': 300, 'left': 490, 'extra': "extra"}),
- (im_obj, 486, Intermediate, {'right': 500, 'left': 470, 'extra': "extra"}),
- (im_obj, 487, Intermediate, {'right': 500, 'left': 490, 'extra': "extra"}),
-
+ (im_obj, 481, Intermediate, {'right': 300, 'left': 490}),
+ (im_obj, 482, Intermediate, {'right': 500, 'left': 470}),
+ (im_obj, 483, Intermediate, {'right': 500, 'left': 490}),
+ (im_obj, 484, Intermediate, {'right': 300, 'left': 470, 'extra': "extra"}),
+ (im_obj, 485, Intermediate, {'right': 300, 'left': 490, 'extra': "extra"}),
+ (im_obj, 486, Intermediate, {'right': 500, 'left': 470, 'extra': "extra"}),
+ (im_obj, 487, Intermediate, {'right': 500, 'left': 490, 'extra': "extra"}),
+
(im2m_obj, 490, M2MIntermediateData, []),
(data_obj, 500, Anchor, "Anchor 3"),
@@ -321,12 +321,17 @@ The end."""),
(inherited_obj, 900, InheritAbstractModel, {'child_data':37,'parent_data':42}),
(inherited_obj, 910, ExplicitInheritBaseModel, {'child_data':37,'parent_data':42}),
(inherited_obj, 920, InheritBaseModel, {'child_data':37,'parent_data':42}),
+
+ (data_obj, 1000, BigIntegerData, 9223372036854775807),
+ (data_obj, 1001, BigIntegerData, -9223372036854775808),
+ (data_obj, 1002, BigIntegerData, 0),
+ (data_obj, 1003, BigIntegerData, None),
]
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
# when field.empty_strings_allowed is True and the value is None; skip these
# tests.
-if settings.DATABASE_ENGINE == 'oracle':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] == 'django.db.backends.oracle':
test_data = [data for data in test_data
if not (data[0] == data_obj and
data[2]._meta.get_field('data').empty_strings_allowed and
@@ -335,7 +340,7 @@ if settings.DATABASE_ENGINE == 'oracle':
# Regression test for #8651 -- a FK to an object iwth PK of 0
# This won't work on MySQL since it won't let you create an object
# with a primary key of 0,
-if settings.DATABASE_ENGINE != 'mysql':
+if settings.DATABASES[DEFAULT_DB_ALIAS]['ENGINE'] != 'django.db.backends.mysql':
test_data.extend([
(data_obj, 0, Anchor, "Anchor 0"),
(fk_obj, 465, FKData, 0),
diff --git a/tests/regressiontests/settings_tests/__init__.py b/tests/regressiontests/settings_tests/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/settings_tests/__init__.py
diff --git a/tests/regressiontests/settings_tests/models.py b/tests/regressiontests/settings_tests/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/settings_tests/models.py
diff --git a/tests/regressiontests/settings_tests/tests.py b/tests/regressiontests/settings_tests/tests.py
new file mode 100644
index 0000000000..fa217b1d79
--- /dev/null
+++ b/tests/regressiontests/settings_tests/tests.py
@@ -0,0 +1,17 @@
+import unittest
+from django.conf import settings
+
+class SettingsTests(unittest.TestCase):
+
+ #
+ # Regression tests for #10130: deleting settings.
+ #
+
+ def test_settings_delete(self):
+ settings.TEST = 'test'
+ self.assertEqual('test', settings.TEST)
+ del settings.TEST
+ self.assertRaises(AttributeError, getattr, settings, 'TEST')
+
+ def test_settings_delete_wrapped(self):
+ self.assertRaises(TypeError, delattr, settings, '_wrapped')
diff --git a/tests/regressiontests/templates/context.py b/tests/regressiontests/templates/context.py
index d8b0f39abe..7886c8328b 100644
--- a/tests/regressiontests/templates/context.py
+++ b/tests/regressiontests/templates/context.py
@@ -10,9 +10,13 @@ context_tests = r"""
>>> c['a'] = 2
>>> c['a']
2
+>>> c.get('a')
+2
>>> c.pop()
{'a': 2}
>>> c['a']
1
+>>> c.get('foo', 42)
+42
"""
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index 2b448574f7..88266e107b 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -120,13 +120,19 @@ def get_filter_tests():
# Notice that escaping is applied *after* any filters, so the string
# formatting here only needs to deal with pre-escaped characters.
- 'filter-stringformat01': ('{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}', {"a": "a<b", "b": mark_safe("a<b")}, u". a<b. . a<b."),
- 'filter-stringformat02': ('.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.', {"a": "a<b", "b": mark_safe("a<b")}, u". a&lt;b. . a<b."),
+ 'filter-stringformat01': ('{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}',
+ {"a": "a<b", "b": mark_safe("a<b")}, u". a<b. . a<b."),
+ 'filter-stringformat02': ('.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.', {"a": "a<b", "b": mark_safe("a<b")},
+ u". a&lt;b. . a<b."),
- # XXX No test for "title" filter; needs an actual object.
+ # Test the title filter
+ 'filter-title1' : ('{{ a|title }}', {'a' : 'JOE\'S CRAB SHACK'}, u'Joe&#39;s Crab Shack'),
+ 'filter-title2' : ('{{ a|title }}', {'a' : '555 WEST 53RD STREET'}, u'555 West 53rd Street'),
- 'filter-truncatewords01': ('{% autoescape off %}{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}{% endautoescape %}', {"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha & ... alpha &amp; ..."),
- 'filter-truncatewords02': ('{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}', {"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha &amp; ... alpha &amp; ..."),
+ 'filter-truncatewords01': ('{% autoescape off %}{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}{% endautoescape %}',
+ {"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha & ... alpha &amp; ..."),
+ 'filter-truncatewords02': ('{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}',
+ {"a": "alpha & bravo", "b": mark_safe("alpha &amp; bravo")}, u"alpha &amp; ... alpha &amp; ..."),
# The "upper" filter messes up entities (which are case-sensitive),
# so it's not safe for non-escaping purposes.
diff --git a/tests/regressiontests/templates/smartif.py b/tests/regressiontests/templates/smartif.py
new file mode 100644
index 0000000000..0114753830
--- /dev/null
+++ b/tests/regressiontests/templates/smartif.py
@@ -0,0 +1,46 @@
+import unittest
+from django.template.smartif import IfParser, Literal
+
+class SmartIfTests(unittest.TestCase):
+
+ def assertCalcEqual(self, expected, tokens):
+ self.assertEqual(expected, IfParser(tokens).parse().eval({}))
+
+ # We only test things here that are difficult to test elsewhere
+ # Many other tests are found in the main tests for builtin template tags
+ # Test parsing via the printed parse tree
+ def test_not(self):
+ var = IfParser(["not", False]).parse()
+ self.assertEqual("(not (literal False))", repr(var))
+ self.assert_(var.eval({}))
+
+ self.assertFalse(IfParser(["not", True]).parse().eval({}))
+
+ def test_or(self):
+ var = IfParser([True, "or", False]).parse()
+ self.assertEqual("(or (literal True) (literal False))", repr(var))
+ self.assert_(var.eval({}))
+
+ def test_in(self):
+ list_ = [1,2,3]
+ self.assertCalcEqual(True, [1, 'in', list_])
+ self.assertCalcEqual(False, [1, 'in', None])
+ self.assertCalcEqual(False, [None, 'in', list_])
+
+ def test_precedence(self):
+ # (False and False) or True == True <- we want this one, like Python
+ # False and (False or True) == False
+ self.assertCalcEqual(True, [False, 'and', False, 'or', True])
+
+ # True or (False and False) == True <- we want this one, like Python
+ # (True or False) and False == False
+ self.assertCalcEqual(True, [True, 'or', False, 'and', False])
+
+ # (1 or 1) == 2 -> False
+ # 1 or (1 == 2) -> True <- we want this one
+ self.assertCalcEqual(True, [1, 'or', 1, '==', 2])
+
+ self.assertCalcEqual(True, [True, '==', True, 'or', True, '==', False])
+
+ self.assertEqual("(or (and (== (literal 1) (literal 2)) (literal 3)) (literal 4))",
+ repr(IfParser([1, '==', 2, 'and', 3, 'or', 4]).parse()))
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 9c01b492e3..29462086d8 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -15,7 +15,7 @@ import unittest
from django import template
from django.core import urlresolvers
from django.template import loader
-from django.template.loaders import app_directories, filesystem
+from django.template.loaders import app_directories, filesystem, cached
from django.utils.translation import activate, deactivate, ugettext as _
from django.utils.safestring import mark_safe
from django.utils.tzinfo import LocalTimezone
@@ -24,6 +24,7 @@ from context import context_tests
from custom import custom_filters
from parser import filter_parsing, variable_parsing
from unicode import unicode_tests
+from smartif import *
try:
from loaders import *
@@ -100,13 +101,15 @@ class UTF8Class:
class Templates(unittest.TestCase):
def test_loaders_security(self):
+ ad_loader = app_directories.Loader()
+ fs_loader = filesystem.Loader()
def test_template_sources(path, template_dirs, expected_sources):
if isinstance(expected_sources, list):
# Fix expected sources so they are normcased and abspathed
expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
# Test the two loaders (app_directores and filesystem).
- func1 = lambda p, t: list(app_directories.get_template_sources(p, t))
- func2 = lambda p, t: list(filesystem.get_template_sources(p, t))
+ func1 = lambda p, t: list(ad_loader.get_template_sources(p, t))
+ func2 = lambda p, t: list(fs_loader.get_template_sources(p, t))
for func in (func1, func2):
if isinstance(expected_sources, list):
self.assertEqual(func(path, template_dirs), expected_sources)
@@ -197,8 +200,11 @@ class Templates(unittest.TestCase):
except KeyError:
raise template.TemplateDoesNotExist, template_name
+ cache_loader = cached.Loader(('test_template_loader',))
+ cache_loader._cached_loaders = (test_template_loader,)
+
old_template_loaders = loader.template_source_loaders
- loader.template_source_loaders = [test_template_loader]
+ loader.template_source_loaders = [cache_loader]
failures = []
tests = template_tests.items()
@@ -231,20 +237,22 @@ class Templates(unittest.TestCase):
for invalid_str, result in [('', normal_string_result),
(expected_invalid_str, invalid_string_result)]:
settings.TEMPLATE_STRING_IF_INVALID = invalid_str
- try:
- test_template = loader.get_template(name)
- output = self.render(test_template, vals)
- except ContextStackException:
- failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (invalid_str, name))
- continue
- except Exception:
- exc_type, exc_value, exc_tb = sys.exc_info()
- if exc_type != result:
- tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
- failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s\n%s" % (invalid_str, name, exc_type, exc_value, tb))
- continue
- if output != result:
- failures.append("Template test (TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (invalid_str, name, result, output))
+ for is_cached in (False, True):
+ try:
+ test_template = loader.get_template(name)
+ output = self.render(test_template, vals)
+ except ContextStackException:
+ failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Context stack was left imbalanced" % (is_cached, invalid_str, name))
+ continue
+ except Exception:
+ exc_type, exc_value, exc_tb = sys.exc_info()
+ if exc_type != result:
+ tb = '\n'.join(traceback.format_exception(exc_type, exc_value, exc_tb))
+ failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Got %s, exception: %s\n%s" % (is_cached, invalid_str, name, exc_type, exc_value, tb))
+ continue
+ if output != result:
+ failures.append("Template test (Cached='%s', TEMPLATE_STRING_IF_INVALID='%s'): %s -- FAILED. Expected %r, got %r" % (is_cached, invalid_str, name, result, output))
+ cache_loader.reset()
if 'LANGUAGE_CODE' in vals[1]:
deactivate()
@@ -534,6 +542,27 @@ class Templates(unittest.TestCase):
'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"),
'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"),
+ # Filters
+ 'if-tag-filter01': ("{% if foo|length == 5 %}yes{% else %}no{% endif %}", {'foo': 'abcde'}, "yes"),
+ 'if-tag-filter02': ("{% if foo|upper == 'ABC' %}yes{% else %}no{% endif %}", {}, "no"),
+
+ # Equality
+ 'if-tag-eq01': ("{% if foo == bar %}yes{% else %}no{% endif %}", {}, "yes"),
+ 'if-tag-eq02': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1}, "no"),
+ 'if-tag-eq03': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 1}, "yes"),
+ 'if-tag-eq04': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 2}, "no"),
+ 'if-tag-eq05': ("{% if foo == '' %}yes{% else %}no{% endif %}", {}, "no"),
+
+ # Comparison
+ 'if-tag-gt-01': ("{% if 2 > 1 %}yes{% else %}no{% endif %}", {}, "yes"),
+ 'if-tag-gt-02': ("{% if 1 > 1 %}yes{% else %}no{% endif %}", {}, "no"),
+ 'if-tag-gte-01': ("{% if 1 >= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
+ 'if-tag-gte-02': ("{% if 1 >= 2 %}yes{% else %}no{% endif %}", {}, "no"),
+ 'if-tag-lt-01': ("{% if 1 < 2 %}yes{% else %}no{% endif %}", {}, "yes"),
+ 'if-tag-lt-02': ("{% if 1 < 1 %}yes{% else %}no{% endif %}", {}, "no"),
+ 'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"),
+ 'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"),
+
# AND
'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'),
'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'),
@@ -554,14 +583,13 @@ class Templates(unittest.TestCase):
'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'),
'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'),
- # TODO: multiple ORs
+ # multiple ORs
+ 'if-tag-or09': ("{% if foo or bar or baz %}yes{% else %}no{% endif %}", {'baz': True}, 'yes'),
# NOT
'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'),
- 'if-tag-not02': ("{% if not %}yes{% else %}no{% endif %}", {'foo': True}, 'no'),
- 'if-tag-not03': ("{% if not %}yes{% else %}no{% endif %}", {'not': True}, 'yes'),
- 'if-tag-not04': ("{% if not not %}no{% else %}yes{% endif %}", {'not': True}, 'yes'),
- 'if-tag-not05': ("{% if not not %}no{% else %}yes{% endif %}", {}, 'no'),
+ 'if-tag-not02': ("{% if not not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'no'),
+ # not03 to not05 removed, now TemplateSyntaxErrors
'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'),
'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'),
@@ -599,12 +627,21 @@ class Templates(unittest.TestCase):
'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'),
'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'),
- # AND and OR raises a TemplateSyntaxError
- 'if-tag-error01': ("{% if foo or bar and baz %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, template.TemplateSyntaxError),
+ # Various syntax errors
+ 'if-tag-error01': ("{% if %}yes{% endif %}", {}, template.TemplateSyntaxError),
'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError),
+ 'if-tag-error06': ("{% if abc def %}yes{% endif %}", {}, template.TemplateSyntaxError),
+ 'if-tag-error07': ("{% if not %}yes{% endif %}", {}, template.TemplateSyntaxError),
+ 'if-tag-error08': ("{% if and %}yes{% endif %}", {}, template.TemplateSyntaxError),
+ 'if-tag-error09': ("{% if or %}yes{% endif %}", {}, template.TemplateSyntaxError),
+ 'if-tag-error10': ("{% if == %}yes{% endif %}", {}, template.TemplateSyntaxError),
+ 'if-tag-error11': ("{% if 1 == %}yes{% endif %}", {}, template.TemplateSyntaxError),
+ 'if-tag-error12': ("{% if a not b %}yes{% endif %}", {}, template.TemplateSyntaxError),
+
+ # Additional, more precise parsing tests are in SmartIfTests
### IFCHANGED TAG #########################################################
'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1,2,3)}, '123'),
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index 58693cc395..e532c90afc 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -10,6 +10,7 @@ from django.test.utils import ContextList
from django.core.urlresolvers import reverse
from django.core.exceptions import SuspiciousOperation
from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context
+from django.template import loader
class AssertContainsTests(TestCase):
def setUp(self):
@@ -436,6 +437,11 @@ class ExceptionTests(TestCase):
class TemplateExceptionTests(TestCase):
def setUp(self):
+ # Reset the loaders so they don't try to render cached templates.
+ if loader.template_source_loaders is not None:
+ for template_loader in loader.template_source_loaders:
+ if hasattr(template_loader, 'reset'):
+ template_loader.reset()
self.old_templates = settings.TEMPLATE_DIRS
settings.TEMPLATE_DIRS = ()
diff --git a/tests/regressiontests/test_runner/__init__.py b/tests/regressiontests/test_runner/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/test_runner/__init__.py
diff --git a/tests/regressiontests/test_runner/models.py b/tests/regressiontests/test_runner/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/test_runner/models.py
diff --git a/tests/regressiontests/test_runner/tests.py b/tests/regressiontests/test_runner/tests.py
new file mode 100644
index 0000000000..d8db7baf94
--- /dev/null
+++ b/tests/regressiontests/test_runner/tests.py
@@ -0,0 +1,29 @@
+"""
+Tests for django test runner
+"""
+import StringIO
+import unittest
+import django
+from django.test import simple
+
+class DjangoTestRunnerTests(unittest.TestCase):
+
+ def test_failfast(self):
+ class MockTestOne(unittest.TestCase):
+ def runTest(self):
+ assert False
+ class MockTestTwo(unittest.TestCase):
+ def runTest(self):
+ assert False
+
+ suite = unittest.TestSuite([MockTestOne(), MockTestTwo()])
+ mock_stream = StringIO.StringIO()
+ dtr = simple.DjangoTestRunner(verbosity=0, failfast=False, stream=mock_stream)
+ result = dtr.run(suite)
+ self.assertEqual(2, result.testsRun)
+ self.assertEqual(2, len(result.failures))
+
+ dtr = simple.DjangoTestRunner(verbosity=0, failfast=True, stream=mock_stream)
+ result = dtr.run(suite)
+ self.assertEqual(1, result.testsRun)
+ self.assertEqual(1, len(result.failures))
diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py
index 34c9db25d3..1dc85b0424 100644
--- a/tests/regressiontests/urlpatterns_reverse/tests.py
+++ b/tests/regressiontests/urlpatterns_reverse/tests.py
@@ -244,7 +244,6 @@ class NamespaceTests(TestCase):
self.assertEquals('/other1/inner/37/42/', reverse('nodefault:urlobject-view', args=[37,42], current_app='other-ns1'))
self.assertEquals('/other1/inner/42/37/', reverse('nodefault:urlobject-view', kwargs={'arg1':42, 'arg2':37}, current_app='other-ns1'))
-
class RequestURLconfTests(TestCase):
def setUp(self):
self.root_urlconf = settings.ROOT_URLCONF
@@ -276,3 +275,25 @@ class RequestURLconfTests(TestCase):
response = self.client.get('/second_test/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, 'outer:,inner:/second_test/')
+
+class ErrorHandlerResolutionTests(TestCase):
+ """Tests for handler404 and handler500"""
+
+ def setUp(self):
+ from django.core.urlresolvers import RegexURLResolver
+ urlconf = 'regressiontests.urlpatterns_reverse.urls_error_handlers'
+ urlconf_callables = 'regressiontests.urlpatterns_reverse.urls_error_handlers_callables'
+ self.resolver = RegexURLResolver(r'^$', urlconf)
+ self.callable_resolver = RegexURLResolver(r'^$', urlconf_callables)
+
+ def test_named_handlers(self):
+ from views import empty_view
+ handler = (empty_view, {})
+ self.assertEqual(self.resolver.resolve404(), handler)
+ self.assertEqual(self.resolver.resolve500(), handler)
+
+ def test_callable_handers(self):
+ from views import empty_view
+ handler = (empty_view, {})
+ self.assertEqual(self.callable_resolver.resolve404(), handler)
+ self.assertEqual(self.callable_resolver.resolve500(), handler)
diff --git a/tests/regressiontests/urlpatterns_reverse/urls_error_handlers.py b/tests/regressiontests/urlpatterns_reverse/urls_error_handlers.py
new file mode 100644
index 0000000000..c2e0d32498
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/urls_error_handlers.py
@@ -0,0 +1,8 @@
+# Used by the ErrorHandlerResolutionTests test case.
+
+from django.conf.urls.defaults import patterns
+
+urlpatterns = patterns('')
+
+handler404 = 'regressiontests.urlpatterns_reverse.views.empty_view'
+handler500 = 'regressiontests.urlpatterns_reverse.views.empty_view'
diff --git a/tests/regressiontests/urlpatterns_reverse/urls_error_handlers_callables.py b/tests/regressiontests/urlpatterns_reverse/urls_error_handlers_callables.py
new file mode 100644
index 0000000000..00f25a7236
--- /dev/null
+++ b/tests/regressiontests/urlpatterns_reverse/urls_error_handlers_callables.py
@@ -0,0 +1,9 @@
+# Used by the ErrorHandlerResolutionTests test case.
+
+from django.conf.urls.defaults import patterns
+from views import empty_view
+
+urlpatterns = patterns('')
+
+handler404 = empty_view
+handler500 = empty_view
diff --git a/tests/regressiontests/utils/termcolors.py b/tests/regressiontests/utils/termcolors.py
new file mode 100644
index 0000000000..7c8fe0d63b
--- /dev/null
+++ b/tests/regressiontests/utils/termcolors.py
@@ -0,0 +1,149 @@
+from unittest import TestCase
+
+from django.utils.termcolors import parse_color_setting, PALETTES, DEFAULT_PALETTE, LIGHT_PALETTE, DARK_PALETTE, NOCOLOR_PALETTE
+
+class TermColorTests(TestCase):
+
+ def test_empty_string(self):
+ self.assertEquals(parse_color_setting(''), PALETTES[DEFAULT_PALETTE])
+
+ def test_simple_palette(self):
+ self.assertEquals(parse_color_setting('light'), PALETTES[LIGHT_PALETTE])
+ self.assertEquals(parse_color_setting('dark'), PALETTES[DARK_PALETTE])
+ self.assertEquals(parse_color_setting('nocolor'), None)
+
+ def test_fg(self):
+ self.assertEquals(parse_color_setting('error=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+
+ def test_fg_bg(self):
+ self.assertEquals(parse_color_setting('error=green/blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue'}))
+
+ def test_fg_opts(self):
+ self.assertEquals(parse_color_setting('error=green,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+ self.assertEquals(parse_color_setting('error=green,bold,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink','bold')}))
+
+ def test_fg_bg_opts(self):
+ self.assertEquals(parse_color_setting('error=green/blue,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue', 'opts': ('blink',)}))
+ self.assertEquals(parse_color_setting('error=green/blue,bold,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue', 'opts': ('blink','bold')}))
+
+ def test_override_palette(self):
+ self.assertEquals(parse_color_setting('light;error=green'),
+ dict(PALETTES[LIGHT_PALETTE],
+ ERROR={'fg':'green'}))
+
+ def test_override_nocolor(self):
+ self.assertEquals(parse_color_setting('nocolor;error=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg': 'green'}))
+
+ def test_reverse_override(self):
+ self.assertEquals(parse_color_setting('error=green;light'), PALETTES[LIGHT_PALETTE])
+
+ def test_multiple_roles(self):
+ self.assertEquals(parse_color_setting('error=green;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'},
+ SQL_FIELD={'fg':'blue'}))
+
+ def test_override_with_multiple_roles(self):
+ self.assertEquals(parse_color_setting('light;error=green;sql_field=blue'),
+ dict(PALETTES[LIGHT_PALETTE],
+ ERROR={'fg':'green'},
+ SQL_FIELD={'fg':'blue'}))
+
+ def test_empty_definition(self):
+ self.assertEquals(parse_color_setting(';'), None)
+ self.assertEquals(parse_color_setting('light;'), PALETTES[LIGHT_PALETTE])
+ self.assertEquals(parse_color_setting(';;;'), None)
+
+ def test_empty_options(self):
+ self.assertEquals(parse_color_setting('error=green,'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('error=green,,,'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('error=green,,blink,,'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ def test_bad_palette(self):
+ self.assertEquals(parse_color_setting('unknown'), None)
+
+ def test_bad_role(self):
+ self.assertEquals(parse_color_setting('unknown='), None)
+ self.assertEquals(parse_color_setting('unknown=green'), None)
+ self.assertEquals(parse_color_setting('unknown=green;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ SQL_FIELD={'fg':'blue'}))
+
+ def test_bad_color(self):
+ self.assertEquals(parse_color_setting('error='), None)
+ self.assertEquals(parse_color_setting('error=;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ SQL_FIELD={'fg':'blue'}))
+ self.assertEquals(parse_color_setting('error=unknown'), None)
+ self.assertEquals(parse_color_setting('error=unknown;sql_field=blue'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ SQL_FIELD={'fg':'blue'}))
+ self.assertEquals(parse_color_setting('error=green/unknown'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('error=green/blue/something'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg': 'blue'}))
+ self.assertEquals(parse_color_setting('error=green/blue/something,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg': 'blue', 'opts': ('blink',)}))
+
+ def test_bad_option(self):
+ self.assertEquals(parse_color_setting('error=green,unknown'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('error=green,unknown,blink'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ def test_role_case(self):
+ self.assertEquals(parse_color_setting('ERROR=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('eRrOr=green'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+
+ def test_color_case(self):
+ self.assertEquals(parse_color_setting('error=GREEN'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('error=GREEN/BLUE'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue'}))
+
+ self.assertEquals(parse_color_setting('error=gReEn'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green'}))
+ self.assertEquals(parse_color_setting('error=gReEn/bLuE'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'bg':'blue'}))
+
+ def test_opts_case(self):
+ self.assertEquals(parse_color_setting('error=green,BLINK'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
+
+ self.assertEquals(parse_color_setting('error=green,bLiNk'),
+ dict(PALETTES[NOCOLOR_PALETTE],
+ ERROR={'fg':'green', 'opts': ('blink',)}))
diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py
index a7a6e4c3a1..fe5f463110 100644
--- a/tests/regressiontests/utils/tests.py
+++ b/tests/regressiontests/utils/tests.py
@@ -9,8 +9,8 @@ from django.utils.functional import SimpleLazyObject
import timesince
import datastructures
-import dateformat
import itercompat
+
from decorators import DecoratorFromMiddlewareTests
# We need this because "datastructures" uses sorted() and the tests are run in
@@ -28,6 +28,7 @@ __test__ = {
}
from dateformat import *
+from termcolors import *
class TestUtilsHtml(TestCase):
@@ -220,7 +221,7 @@ class TestUtilsSimpleLazyObject(TestCase):
self.assertEqual(_ComplexObject, SimpleLazyObject(complex_object).__class__)
def test_deepcopy(self):
- import copy
+ import django.utils.copycompat as copy
# Check that we *can* do deep copy, and that it returns the right
# objects.
diff --git a/tests/regressiontests/views/tests/defaults.py b/tests/regressiontests/views/tests/defaults.py
index bf490d7cf0..1d1b3c3501 100644
--- a/tests/regressiontests/views/tests/defaults.py
+++ b/tests/regressiontests/views/tests/defaults.py
@@ -10,8 +10,8 @@ class DefaultsTests(TestCase):
"""Test django views in django/views/defaults.py"""
fixtures = ['testdata.json']
- def test_shorcut_with_absolute_url(self):
- "Can view a shortcut an Author object that has with a get_absolute_url method"
+ def test_shortcut_with_absolute_url(self):
+ "Can view a shortcut for an Author object that has a get_absolute_url method"
for obj in Author.objects.all():
short_url = '/views/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, obj.pk)
response = self.client.get(short_url)
@@ -19,12 +19,34 @@ class DefaultsTests(TestCase):
status_code=302, target_status_code=404)
def test_shortcut_no_absolute_url(self):
- "Shortcuts for an object that has with a get_absolute_url method raises 404"
+ "Shortcuts for an object that has no get_absolute_url method raises 404"
for obj in Article.objects.all():
short_url = '/views/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Article).id, obj.pk)
response = self.client.get(short_url)
self.assertEquals(response.status_code, 404)
+ def test_wrong_type_pk(self):
+ short_url = '/views/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, 'nobody/expects')
+ response = self.client.get(short_url)
+ self.assertEquals(response.status_code, 404)
+
+ def test_shortcut_bad_pk(self):
+ short_url = '/views/shortcut/%s/%s/' % (ContentType.objects.get_for_model(Author).id, '4242424242')
+ response = self.client.get(short_url)
+ self.assertEquals(response.status_code, 404)
+
+ def test_nonint_content_type(self):
+ an_author = Author.objects.all()[0]
+ short_url = '/views/shortcut/%s/%s/' % ('spam', an_author.pk)
+ response = self.client.get(short_url)
+ self.assertEquals(response.status_code, 404)
+
+ def test_bad_content_type(self):
+ an_author = Author.objects.all()[0]
+ short_url = '/views/shortcut/%s/%s/' % (4242424242, an_author.pk)
+ response = self.client.get(short_url)
+ self.assertEquals(response.status_code, 404)
+
def test_page_not_found(self):
"A 404 status is returned by the page_not_found view"
non_existing_urls = ['/views/non_existing_url/', # this is in urls.py
diff --git a/tests/runtests.py b/tests/runtests.py
index f556246c90..1cd95b9fef 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -28,6 +28,7 @@ ALWAYS_INSTALLED_APPS = [
'django.contrib.flatpages',
'django.contrib.redirects',
'django.contrib.sessions',
+ 'django.contrib.messages',
'django.contrib.comments',
'django.contrib.admin',
]
@@ -85,11 +86,10 @@ class InvalidModelTestCase(unittest.TestCase):
self.assert_(not unexpected, "Unexpected Errors: " + '\n'.join(unexpected))
self.assert_(not missing, "Missing Errors: " + '\n'.join(missing))
-def django_tests(verbosity, interactive, test_labels):
+def django_tests(verbosity, interactive, failfast, test_labels):
from django.conf import settings
old_installed_apps = settings.INSTALLED_APPS
- old_test_database_name = settings.TEST_DATABASE_NAME
old_root_urlconf = getattr(settings, "ROOT_URLCONF", "")
old_template_dirs = settings.TEMPLATE_DIRS
old_use_i18n = settings.USE_I18N
@@ -107,6 +107,7 @@ def django_tests(verbosity, interactive, test_labels):
settings.MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.common.CommonMiddleware',
)
settings.SITE_ID = 1
@@ -158,7 +159,8 @@ def django_tests(verbosity, interactive, test_labels):
settings.TEST_RUNNER = 'django.test.simple.run_tests'
test_runner = get_runner(settings)
- failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
+ failures = test_runner(test_labels, verbosity=verbosity, interactive=interactive, failfast=failfast,
+ extra_tests=extra_tests)
if failures:
sys.exit(failures)
@@ -180,6 +182,8 @@ if __name__ == "__main__":
help='Verbosity level; 0=minimal output, 1=normal output, 2=all output')
parser.add_option('--noinput', action='store_false', dest='interactive', default=True,
help='Tells Django to NOT prompt the user for input of any kind.')
+ parser.add_option('--failfast', action='store_true', dest='failfast', default=False,
+ help='Tells Django to stop running the test suite after first failed test.')
parser.add_option('--settings',
help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.')
options, args = parser.parse_args()
@@ -188,4 +192,4 @@ if __name__ == "__main__":
elif "DJANGO_SETTINGS_MODULE" not in os.environ:
parser.error("DJANGO_SETTINGS_MODULE is not set in the environment. "
"Set it or use --settings.")
- django_tests(int(options.verbosity), options.interactive, args)
+ django_tests(int(options.verbosity), options.interactive, options.failfast, args)