summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJustin Bronn <jbronn@gmail.com>2007-08-26 01:10:53 +0000
committerJustin Bronn <jbronn@gmail.com>2007-08-26 01:10:53 +0000
commit2052b508eb92c62fc0678efd4936c5ec1e0e735b (patch)
treee510109b74b28c8ccef5f6955727cb9dce3da655 /tests
parenta7297a255f4bb86f608ea251e00253d18c31d9d4 (diff)
gis: Made necessary modifications for unicode, manage refactor, backend refactor and merged 5584-6000 via svnmerge from [repos:django/trunk trunk].
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6018 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests')
-rw-r--r--tests/modeltests/basic/models.py26
-rw-r--r--tests/modeltests/choices/models.py10
-rw-r--r--tests/modeltests/custom_columns/models.py24
-rw-r--r--tests/modeltests/custom_managers/models.py18
-rw-r--r--tests/modeltests/custom_methods/models.py4
-rw-r--r--tests/modeltests/custom_pk/models.py18
-rw-r--r--tests/modeltests/field_defaults/models.py4
-rw-r--r--tests/modeltests/fixtures/models.py40
-rw-r--r--tests/modeltests/generic_relations/models.py24
-rw-r--r--tests/modeltests/get_latest/models.py8
-rw-r--r--tests/modeltests/get_object_or_404/models.py35
-rw-r--r--tests/modeltests/get_or_create/models.py8
-rw-r--r--tests/modeltests/invalid_models/models.py44
-rw-r--r--tests/modeltests/lookup/models.py56
-rw-r--r--tests/modeltests/m2m_and_m2o/models.py6
-rw-r--r--tests/modeltests/m2m_intermediary/models.py18
-rw-r--r--tests/modeltests/m2m_multiple/models.py8
-rw-r--r--tests/modeltests/m2m_recursive/models.py4
-rw-r--r--tests/modeltests/m2o_recursive/models.py4
-rw-r--r--tests/modeltests/m2o_recursive2/models.py4
-rw-r--r--tests/modeltests/manipulators/models.py22
-rw-r--r--tests/modeltests/many_to_many/models.py8
-rw-r--r--tests/modeltests/many_to_one/models.py21
-rw-r--r--tests/modeltests/many_to_one_null/models.py8
-rw-r--r--tests/modeltests/model_forms/models.py48
-rw-r--r--tests/modeltests/model_inheritance/models.py16
-rw-r--r--tests/modeltests/mutually_referential/models.py4
-rw-r--r--tests/modeltests/one_to_one/models.py24
-rw-r--r--tests/modeltests/or_lookups/models.py6
-rw-r--r--tests/modeltests/ordering/models.py4
-rw-r--r--tests/modeltests/pagination/models.py4
-rw-r--r--tests/modeltests/properties/models.py4
-rw-r--r--tests/modeltests/reserved_names/models.py18
-rw-r--r--tests/modeltests/reverse_lookup/models.py12
-rw-r--r--tests/modeltests/save_delete_hooks/models.py8
-rw-r--r--tests/modeltests/select_related/models.py32
-rw-r--r--tests/modeltests/serializers/models.py16
-rw-r--r--tests/modeltests/str/models.py37
-rw-r--r--tests/modeltests/test_client/fixtures/testdata.json18
-rw-r--r--tests/modeltests/test_client/models.py45
-rw-r--r--tests/modeltests/test_client/tests.py20
-rw-r--r--tests/modeltests/test_client/views.py5
-rw-r--r--tests/modeltests/transactions/models.py10
-rw-r--r--tests/modeltests/validation/models.py16
-rw-r--r--tests/regressiontests/bug639/models.py2
-rw-r--r--tests/regressiontests/cache/tests.py41
-rw-r--r--tests/regressiontests/datastructures/tests.py9
-rw-r--r--tests/regressiontests/datatypes/models.py2
-rw-r--r--tests/regressiontests/dateformat/tests.py46
-rw-r--r--tests/regressiontests/defaultfilters/tests.py446
-rw-r--r--tests/regressiontests/fixtures_regress/fixtures/pretty.xml11
-rw-r--r--tests/regressiontests/fixtures_regress/models.py31
-rw-r--r--tests/regressiontests/forms/localflavor.py118
-rw-r--r--tests/regressiontests/forms/regressions.py49
-rw-r--r--tests/regressiontests/forms/tests.py154
-rw-r--r--tests/regressiontests/httpwrappers/tests.py76
-rw-r--r--tests/regressiontests/humanize/tests.py32
-rw-r--r--tests/regressiontests/i18n/__init__.py0
-rw-r--r--tests/regressiontests/i18n/models.py0
-rw-r--r--tests/regressiontests/i18n/tests.py33
-rw-r--r--tests/regressiontests/initial_sql_regress/models.py2
-rw-r--r--tests/regressiontests/invalid_admin_options/models.py74
-rw-r--r--tests/regressiontests/many_to_one_regress/models.py6
-rw-r--r--tests/regressiontests/maxlength/__init__.py0
-rw-r--r--tests/regressiontests/maxlength/models.py0
-rw-r--r--tests/regressiontests/maxlength/tests.py160
-rw-r--r--tests/regressiontests/model_fields/__init__.py0
-rw-r--r--tests/regressiontests/model_fields/models.py0
-rw-r--r--tests/regressiontests/model_fields/tests.py18
-rw-r--r--tests/regressiontests/model_regress/__init__.py0
-rw-r--r--tests/regressiontests/model_regress/models.py40
-rw-r--r--tests/regressiontests/null_queries/models.py12
-rw-r--r--tests/regressiontests/one_to_one_regress/models.py18
-rw-r--r--tests/regressiontests/serializers_regress/models.py37
-rw-r--r--tests/regressiontests/serializers_regress/tests.py39
-rw-r--r--tests/regressiontests/string_lookup/models.py62
-rw-r--r--tests/regressiontests/templates/tests.py72
-rw-r--r--tests/regressiontests/templates/unicode.py33
-rw-r--r--tests/regressiontests/templates/urls.py4
-rw-r--r--tests/regressiontests/test_client_regress/models.py59
-rw-r--r--tests/regressiontests/test_client_regress/urls.py2
-rw-r--r--tests/regressiontests/test_client_regress/views.py16
-rw-r--r--tests/regressiontests/text/tests.py31
-rw-r--r--tests/regressiontests/utils/__init__.py0
-rw-r--r--tests/regressiontests/utils/models.py1
-rw-r--r--tests/regressiontests/utils/tests.py115
-rwxr-xr-xtests/runtests.py25
87 files changed, 1872 insertions, 773 deletions
diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py
index 9af13c0e3e..0a09579761 100644
--- a/tests/modeltests/basic/models.py
+++ b/tests/modeltests/basic/models.py
@@ -1,3 +1,4 @@
+# coding: utf-8
"""
1. Bare-bones model
@@ -7,13 +8,13 @@ This is a basic model with only two non-primary-key fields.
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=100, default='Default headline')
+ headline = models.CharField(max_length=100, default='Default headline')
pub_date = models.DateTimeField()
class Meta:
ordering = ('pub_date','headline')
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS': """
@@ -246,6 +247,19 @@ datetime.datetime(2005, 7, 28, 0, 0)
>>> (s1 | s2 | s3)[::2]
[<Article: Area woman programs in Python>, <Article: Third article>]
+# Slicing works with longs.
+>>> Article.objects.all()[0L]
+<Article: Area woman programs in Python>
+>>> Article.objects.all()[1L:3L]
+[<Article: Second article>, <Article: Third article>]
+>>> s3 = Article.objects.filter(id__exact=3)
+>>> (s1 | s2 | s3)[::2L]
+[<Article: Area woman programs in Python>, <Article: Third article>]
+
+# And can be mixed with ints.
+>>> Article.objects.all()[1:3L]
+[<Article: Second article>, <Article: Third article>]
+
# Slices (without step) are lazy:
>>> Article.objects.all()[0:5].filter()
[<Article: Area woman programs in Python>, <Article: Second article>, <Article: Third article>, <Article: Article 6>, <Article: Default headline>]
@@ -351,7 +365,7 @@ __test__['API_TESTS'] += """
>>> a101.save()
>>> a101 = Article.objects.get(pk=101)
>>> a101.headline
-'Article 101'
+u'Article 101'
# You can create saved objects in a single step
>>> a10 = Article.objects.create(headline="Article 10", pub_date=datetime(2005, 7, 31, 12, 30, 45))
@@ -364,4 +378,10 @@ year, including Jan. 1 and Dec. 31.
>>> a12 = Article.objects.create(headline='Article 12', pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999))
>>> Article.objects.filter(pub_date__year=2008)
[<Article: Article 11>, <Article: Article 12>]
+
+# Unicode data works, too.
+>>> a = Article(headline=u'\u6797\u539f \u3081\u3050\u307f', pub_date=datetime(2005, 7, 28))
+>>> a.save()
+>>> Article.objects.get(pk=a.id).headline
+u'\u6797\u539f \u3081\u3050\u307f'
"""
diff --git a/tests/modeltests/choices/models.py b/tests/modeltests/choices/models.py
index 37d36fe1d8..550e655e46 100644
--- a/tests/modeltests/choices/models.py
+++ b/tests/modeltests/choices/models.py
@@ -17,10 +17,10 @@ GENDER_CHOICES = (
)
class Person(models.Model):
- name = models.CharField(maxlength=20)
- gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
+ name = models.CharField(max_length=20)
+ gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
@@ -33,7 +33,7 @@ __test__ = {'API_TESTS':"""
>>> s.gender
'F'
>>> a.get_gender_display()
-'Male'
+u'Male'
>>> s.get_gender_display()
-'Female'
+u'Female'
"""}
diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py
index 1283da07cf..e1d0bc6e94 100644
--- a/tests/modeltests/custom_columns/models.py
+++ b/tests/modeltests/custom_columns/models.py
@@ -6,11 +6,11 @@ If your database column name is different than your model attribute, use the
name, in API usage.
If your database table name is different than your model name, use the
-``db_table`` Meta attribute. This has no effect on the API used to
+``db_table`` Meta attribute. This has no effect on the API used to
query the database.
-If you need to use a table name for a many-to-many relationship that differs
-from the default generated name, use the ``db_table`` parameter on the
+If you need to use a table name for a many-to-many relationship that differs
+from the default generated name, use the ``db_table`` parameter on the
ManyToMany field. This has no effect on the API for querying the database.
"""
@@ -18,26 +18,26 @@ ManyToMany field. This has no effect on the API for querying the database.
from django.db import models
class Author(models.Model):
- first_name = models.CharField(maxlength=30, db_column='firstname')
- last_name = models.CharField(maxlength=30, db_column='last')
+ first_name = models.CharField(max_length=30, db_column='firstname')
+ last_name = models.CharField(max_length=30, db_column='last')
- def __str__(self):
- return '%s %s' % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u'%s %s' % (self.first_name, self.last_name)
class Meta:
db_table = 'my_author_table'
ordering = ('last_name','first_name')
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
authors = models.ManyToManyField(Author, db_table='my_m2m_table')
- def __str__(self):
+ def __unicode__(self):
return self.headline
class Meta:
ordering = ('headline',)
-
+
__test__ = {'API_TESTS':"""
# Create a Author.
>>> a = Author(first_name='John', last_name='Smith')
@@ -75,9 +75,9 @@ TypeError: Cannot resolve keyword 'firstname' into field. Choices are: article,
>>> a = Author.objects.get(last_name__exact='Smith')
>>> a.first_name
-'John'
+u'John'
>>> a.last_name
-'Smith'
+u'Smith'
>>> a.firstname
Traceback (most recent call last):
...
diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py
index 99df875275..40bf77e273 100644
--- a/tests/modeltests/custom_managers/models.py
+++ b/tests/modeltests/custom_managers/models.py
@@ -18,13 +18,13 @@ class PersonManager(models.Manager):
return self.filter(fun=True)
class Person(models.Model):
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
fun = models.BooleanField()
objects = PersonManager()
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
# An example of a custom manager that sets get_query_set().
@@ -33,13 +33,13 @@ class PublishedBookManager(models.Manager):
return super(PublishedBookManager, self).get_query_set().filter(is_published=True)
class Book(models.Model):
- title = models.CharField(maxlength=50)
- author = models.CharField(maxlength=30)
+ title = models.CharField(max_length=50)
+ author = models.CharField(max_length=30)
is_published = models.BooleanField()
published_objects = PublishedBookManager()
authors = models.ManyToManyField(Person, related_name='books')
- def __str__(self):
+ def __unicode__(self):
return self.title
# An example of providing multiple custom managers.
@@ -49,13 +49,13 @@ class FastCarManager(models.Manager):
return super(FastCarManager, self).get_query_set().filter(top_speed__gt=150)
class Car(models.Model):
- name = models.CharField(maxlength=10)
+ name = models.CharField(max_length=10)
mileage = models.IntegerField()
top_speed = models.IntegerField(help_text="In miles per hour.")
cars = models.Manager()
fast_cars = FastCarManager()
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/custom_methods/models.py b/tests/modeltests/custom_methods/models.py
index e8fb751d54..b0ca4131a5 100644
--- a/tests/modeltests/custom_methods/models.py
+++ b/tests/modeltests/custom_methods/models.py
@@ -8,10 +8,10 @@ from django.db import models
import datetime
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateField()
- def __str__(self):
+ def __unicode__(self):
return self.headline
def was_published_today(self):
diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py
index fd0901da3c..53bbadbfd4 100644
--- a/tests/modeltests/custom_pk/models.py
+++ b/tests/modeltests/custom_pk/models.py
@@ -8,23 +8,23 @@ this behavior by explicitly adding ``primary_key=True`` to a field.
from django.db import models
class Employee(models.Model):
- employee_code = models.CharField(maxlength=10, primary_key=True,
+ employee_code = models.CharField(max_length=10, primary_key=True,
db_column = 'code')
- first_name = models.CharField(maxlength=20)
- last_name = models.CharField(maxlength=20)
+ first_name = models.CharField(max_length=20)
+ last_name = models.CharField(max_length=20)
class Meta:
ordering = ('last_name', 'first_name')
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
class Business(models.Model):
- name = models.CharField(maxlength=20, primary_key=True)
+ name = models.CharField(max_length=20, primary_key=True)
employees = models.ManyToManyField(Employee)
class Meta:
verbose_name_plural = 'businesses'
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
@@ -62,7 +62,7 @@ DoesNotExist: Employee matching query does not exist.
>>> Employee.objects.filter(last_name__exact='Jones')
[<Employee: Dan Jones>, <Employee: Fran Jones>]
>>> Employee.objects.in_bulk(['ABC123', 'XYZ456'])
-{'XYZ456': <Employee: Fran Jones>, 'ABC123': <Employee: Dan Jones>}
+{u'XYZ456': <Employee: Fran Jones>, u'ABC123': <Employee: Dan Jones>}
>>> b = Business(name='Sears')
>>> b.save()
@@ -72,7 +72,7 @@ DoesNotExist: Employee matching query does not exist.
>>> fran.business_set.all()
[<Business: Sears>]
>>> Business.objects.in_bulk(['Sears'])
-{'Sears': <Business: Sears>}
+{u'Sears': <Business: Sears>}
>>> Business.objects.filter(name__exact='Sears')
[<Business: Sears>]
diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py
index 8e803d00d8..23c4733b8f 100644
--- a/tests/modeltests/field_defaults/models.py
+++ b/tests/modeltests/field_defaults/models.py
@@ -13,10 +13,10 @@ from django.db import models
from datetime import datetime
class Article(models.Model):
- headline = models.CharField(maxlength=100, default='Default headline')
+ headline = models.CharField(max_length=100, default='Default headline')
pub_date = models.DateTimeField(default=datetime.now)
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py
index c75e6723fd..b59c388bbd 100644
--- a/tests/modeltests/fixtures/models.py
+++ b/tests/modeltests/fixtures/models.py
@@ -1,79 +1,79 @@
"""
37. Fixtures.
-Fixtures are a way of loading data into the database in bulk. Fixure data
-can be stored in any serializable format (including JSON and XML). Fixtures
+Fixtures are a way of loading data into the database in bulk. Fixure data
+can be stored in any serializable format (including JSON and XML). Fixtures
are identified by name, and are stored in either a directory named 'fixtures'
-in the application directory, on in one of the directories named in the
+in the application directory, on in one of the directories named in the
FIXTURE_DIRS setting.
"""
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=100, default='Default headline')
+ headline = models.CharField(max_length=100, default='Default headline')
pub_date = models.DateTimeField()
- def __str__(self):
+ def __unicode__(self):
return self.headline
-
+
class Meta:
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.
+# Reset the database representation of this app.
# This will return the database to a clean initial state.
->>> management.flush(verbosity=0, interactive=False)
+>>> 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.load_data(['fixture1.json'], verbosity=0)
+>>> 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.load_data(['fixture2.json'], verbosity=0)
+>>> management.call_command('loaddata', 'fixture2.json', verbosity=0)
>>> Article.objects.all()
[<Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
-# Load fixture 3, XML format.
->>> management.load_data(['fixture3.xml'], verbosity=0)
+# Load fixture 3, XML format.
+>>> management.call_command('loaddata', 'fixture3.xml', verbosity=0)
>>> Article.objects.all()
[<Article: XML identified as leading cause of cancer>, <Article: Django conquers world!>, <Article: Copyright is fine the way it is>, <Article: Poker on TV is great!>, <Article: Python program becomes self aware>]
# Load a fixture that doesn't exist
->>> management.load_data(['unknown.json'], verbosity=0)
+>>> 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>]
# Reset the database representation of this app. This will delete all data.
->>> management.flush(verbosity=0, interactive=False)
+>>> management.call_command('flush', verbosity=0, interactive=False)
>>> Article.objects.all()
[<Article: Python program becomes self aware>]
# Load fixture 1 again, using format discovery
->>> management.load_data(['fixture1'], verbosity=0)
+>>> management.call_command('loaddata', 'fixture1', verbosity=0)
>>> Article.objects.all()
[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
# Try to load fixture 2 using format discovery; this will fail
-# because there are two fixture2's in the fixtures directory
->>> management.load_data(['fixture2'], verbosity=0) # doctest: +ELLIPSIS
+# because there are two fixture2's in the fixtures directory
+>>> management.call_command('loaddata', 'fixture2', verbosity=0) # doctest: +ELLIPSIS
Multiple fixtures named 'fixture2' in '...fixtures'. Aborting.
>>> Article.objects.all()
[<Article: Time to reform copyright>, <Article: Poker has no place on ESPN>, <Article: Python program becomes self aware>]
# Dump the current contents of the database as a JSON fixture
->>> print management.dump_data(['fixtures'], format='json')
+>>> management.call_command('dumpdata', 'fixtures', format='json')
[{"pk": "3", "model": "fixtures.article", "fields": {"headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00"}}, {"pk": "2", "model": "fixtures.article", "fields": {"headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00"}}, {"pk": "1", "model": "fixtures.article", "fields": {"headline": "Python program becomes self aware", "pub_date": "2006-06-16 11:00:00"}}]
"""}
@@ -81,7 +81,7 @@ 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)
diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py
index 195f67db8f..ce1d824ca8 100644
--- a/tests/modeltests/generic_relations/models.py
+++ b/tests/modeltests/generic_relations/models.py
@@ -24,34 +24,34 @@ class TaggedItem(models.Model):
class Meta:
ordering = ["tag"]
- def __str__(self):
+ def __unicode__(self):
return self.tag
class Animal(models.Model):
- common_name = models.CharField(maxlength=150)
- latin_name = models.CharField(maxlength=150)
+ common_name = models.CharField(max_length=150)
+ latin_name = models.CharField(max_length=150)
tags = generic.GenericRelation(TaggedItem)
- def __str__(self):
+ def __unicode__(self):
return self.common_name
class Vegetable(models.Model):
- name = models.CharField(maxlength=150)
+ name = models.CharField(max_length=150)
is_yucky = models.BooleanField(default=True)
tags = generic.GenericRelation(TaggedItem)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Mineral(models.Model):
- name = models.CharField(maxlength=150)
+ name = models.CharField(max_length=150)
hardness = models.PositiveSmallIntegerField()
# note the lack of an explicit GenericRelation here...
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
@@ -111,17 +111,17 @@ __test__ = {'API_TESTS':"""
# objects are deleted when the source object is deleted.
# Original list of tags:
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
-[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('hairy', <ContentType: animal>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2), ('yellow', <ContentType: animal>, 1)]
+[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'hairy', <ContentType: animal>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2), (u'yellow', <ContentType: animal>, 1)]
>>> lion.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
-[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
# If Generic Relation is not explicitly defined, any related objects
# remain after deletion of the source object.
>>> quartz.delete()
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
-[('clearish', <ContentType: mineral>, 1), ('fatty', <ContentType: vegetable>, 2), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+[(u'clearish', <ContentType: mineral>, 1), (u'fatty', <ContentType: vegetable>, 2), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
# If you delete a tag, the objects using the tag are unaffected
# (other than losing a tag)
@@ -130,6 +130,6 @@ __test__ = {'API_TESTS':"""
>>> bacon.tags.all()
[<TaggedItem: salty>]
>>> [(t.tag, t.content_type, t.object_id) for t in TaggedItem.objects.all()]
-[('clearish', <ContentType: mineral>, 1), ('salty', <ContentType: vegetable>, 2), ('shiny', <ContentType: animal>, 2)]
+[(u'clearish', <ContentType: mineral>, 1), (u'salty', <ContentType: vegetable>, 2), (u'shiny', <ContentType: animal>, 2)]
"""}
diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py
index 84c6273818..099f1e28a6 100644
--- a/tests/modeltests/get_latest/models.py
+++ b/tests/modeltests/get_latest/models.py
@@ -11,22 +11,22 @@ into the future."
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateField()
expire_date = models.DateField()
class Meta:
get_latest_by = 'pub_date'
- def __str__(self):
+ def __unicode__(self):
return self.headline
class Person(models.Model):
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
birthday = models.DateField()
# Note that this model doesn't have "get_latest_by" set.
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py
index 2ad459669f..bd800317d3 100644
--- a/tests/modeltests/get_object_or_404/models.py
+++ b/tests/modeltests/get_object_or_404/models.py
@@ -3,11 +3,11 @@
get_object_or_404 is a shortcut function to be used in view functions for
performing a get() lookup and raising a Http404 exception if a DoesNotExist
-exception was rasied during the get() call.
+exception was raised during the get() call.
get_list_or_404 is a shortcut function to be used in view functions for
performing a filter() lookup and raising a Http404 exception if a DoesNotExist
-exception was rasied during the filter() call.
+exception was raised during the filter() call.
"""
from django.db import models
@@ -15,9 +15,9 @@ from django.http import Http404
from django.shortcuts import get_object_or_404, get_list_or_404
class Author(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
- def __str__(self):
+ def __unicode__(self):
return self.name
class ArticleManager(models.Manager):
@@ -26,11 +26,11 @@ class ArticleManager(models.Manager):
class Article(models.Model):
authors = models.ManyToManyField(Author)
- title = models.CharField(maxlength=50)
+ title = models.CharField(max_length=50)
objects = models.Manager()
by_a_sir = ArticleManager()
- def __str__(self):
+ def __unicode__(self):
return self.title
__test__ = {'API_TESTS':"""
@@ -69,11 +69,28 @@ Http404: No Article matches the given query.
>>> get_object_or_404(Article.by_a_sir, title="Run away!")
<Article: Run away!>
+# QuerySets can be used too.
+>>> get_object_or_404(Article.objects.all(), title__contains="Run")
+<Article: Run away!>
+
+# Just as when using a get() lookup, you will get an error if more than one
+# object is returned.
+>>> get_object_or_404(Author.objects.all())
+Traceback (most recent call last):
+...
+AssertionError: get() returned more than one Author -- it returned ...! Lookup parameters were {}
+
+# Using an EmptyQuerySet raises a Http404 error.
+>>> get_object_or_404(Article.objects.none(), title__contains="Run")
+Traceback (most recent call last):
+...
+Http404: No Article matches the given query.
+
# get_list_or_404 can be used to get lists of objects
>>> get_list_or_404(a.article_set, title__icontains='Run')
[<Article: Run away!>]
-# Http404 is returned if the list is empty
+# Http404 is returned if the list is empty.
>>> get_list_or_404(a.article_set, title__icontains='Shrubbery')
Traceback (most recent call last):
...
@@ -83,4 +100,8 @@ Http404: No Article matches the given query.
>>> get_list_or_404(Article.by_a_sir, title__icontains="Run")
[<Article: Run away!>]
+# QuerySets can be used too.
+>>> get_list_or_404(Article.objects.all(), title__icontains="Run")
+[<Article: Run away!>]
+
"""}
diff --git a/tests/modeltests/get_or_create/models.py b/tests/modeltests/get_or_create/models.py
index f974a82dee..9f025dc582 100644
--- a/tests/modeltests/get_or_create/models.py
+++ b/tests/modeltests/get_or_create/models.py
@@ -8,12 +8,12 @@ parameters. If an object isn't found, it creates one with the given parameters.
from django.db import models
class Person(models.Model):
- first_name = models.CharField(maxlength=100)
- last_name = models.CharField(maxlength=100)
+ first_name = models.CharField(max_length=100)
+ last_name = models.CharField(max_length=100)
birthday = models.DateField()
- def __str__(self):
- return '%s %s' % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u'%s %s' % (self.first_name, self.last_name)
__test__ = {'API_TESTS':"""
# Acting as a divine being, create an Person.
diff --git a/tests/modeltests/invalid_models/models.py b/tests/modeltests/invalid_models/models.py
index 90f2f54632..1afe660dd0 100644
--- a/tests/modeltests/invalid_models/models.py
+++ b/tests/modeltests/invalid_models/models.py
@@ -10,26 +10,26 @@ class FieldErrors(models.Model):
charfield = models.CharField()
decimalfield = models.DecimalField()
filefield = models.FileField()
- prepopulate = models.CharField(maxlength=10, prepopulate_from='bad')
- choices = models.CharField(maxlength=10, choices='bad')
- choices2 = models.CharField(maxlength=10, choices=[(1,2,3),(1,2,3)])
- index = models.CharField(maxlength=10, db_index='bad')
+ prepopulate = models.CharField(max_length=10, prepopulate_from='bad')
+ choices = models.CharField(max_length=10, choices='bad')
+ choices2 = models.CharField(max_length=10, choices=[(1,2,3),(1,2,3)])
+ index = models.CharField(max_length=10, db_index='bad')
class Target(models.Model):
- tgt_safe = models.CharField(maxlength=10)
- clash1 = models.CharField(maxlength=10)
- clash2 = models.CharField(maxlength=10)
+ tgt_safe = models.CharField(max_length=10)
+ clash1 = models.CharField(max_length=10)
+ clash2 = models.CharField(max_length=10)
- clash1_set = models.CharField(maxlength=10)
+ clash1_set = models.CharField(max_length=10)
class Clash1(models.Model):
- src_safe = models.CharField(maxlength=10, core=True)
+ src_safe = models.CharField(max_length=10, core=True)
foreign = models.ForeignKey(Target)
m2m = models.ManyToManyField(Target)
class Clash2(models.Model):
- src_safe = models.CharField(maxlength=10, core=True)
+ src_safe = models.CharField(max_length=10, core=True)
foreign_1 = models.ForeignKey(Target, related_name='id')
foreign_2 = models.ForeignKey(Target, related_name='src_safe')
@@ -38,7 +38,7 @@ class Clash2(models.Model):
m2m_2 = models.ManyToManyField(Target, related_name='src_safe')
class Target2(models.Model):
- clash3 = models.CharField(maxlength=10)
+ clash3 = models.CharField(max_length=10)
foreign_tgt = models.ForeignKey(Target)
clashforeign_set = models.ForeignKey(Target)
@@ -46,7 +46,7 @@ class Target2(models.Model):
clashm2m_set = models.ManyToManyField(Target)
class Clash3(models.Model):
- src_safe = models.CharField(maxlength=10, core=True)
+ src_safe = models.CharField(max_length=10, core=True)
foreign_1 = models.ForeignKey(Target2, related_name='foreign_tgt')
foreign_2 = models.ForeignKey(Target2, related_name='m2m_tgt')
@@ -61,16 +61,16 @@ class ClashM2M(models.Model):
m2m = models.ManyToManyField(Target2)
class SelfClashForeign(models.Model):
- src_safe = models.CharField(maxlength=10, core=True)
- selfclashforeign = models.CharField(maxlength=10)
+ src_safe = models.CharField(max_length=10, core=True)
+ selfclashforeign = models.CharField(max_length=10)
selfclashforeign_set = models.ForeignKey("SelfClashForeign")
foreign_1 = models.ForeignKey("SelfClashForeign", related_name='id')
foreign_2 = models.ForeignKey("SelfClashForeign", related_name='src_safe')
class ValidM2M(models.Model):
- src_safe = models.CharField(maxlength=10)
- validm2m = models.CharField(maxlength=10)
+ src_safe = models.CharField(max_length=10)
+ validm2m = models.CharField(max_length=10)
# M2M fields are symmetrical by default. Symmetrical M2M fields
# on self don't require a related accessor, so many potential
@@ -84,8 +84,8 @@ class ValidM2M(models.Model):
m2m_4 = models.ManyToManyField('self')
class SelfClashM2M(models.Model):
- src_safe = models.CharField(maxlength=10)
- selfclashm2m = models.CharField(maxlength=10)
+ src_safe = models.CharField(max_length=10)
+ selfclashm2m = models.CharField(max_length=10)
# Non-symmetrical M2M fields _do_ have related accessors, so
# there is potential for clashes.
@@ -100,14 +100,14 @@ class SelfClashM2M(models.Model):
class Model(models.Model):
"But it's valid to call a model Model."
year = models.PositiveIntegerField() #1960
- make = models.CharField(maxlength=10) #Aston Martin
- name = models.CharField(maxlength=10) #DB 4 GT
+ make = models.CharField(max_length=10) #Aston Martin
+ name = models.CharField(max_length=10) #DB 4 GT
class Car(models.Model):
- colour = models.CharField(maxlength=5)
+ colour = models.CharField(max_length=5)
model = models.ForeignKey(Model)
-model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "maxlength" attribute.
+model_errors = """invalid_models.fielderrors: "charfield": CharFields require a "max_length" attribute.
invalid_models.fielderrors: "decimalfield": DecimalFields require a "decimal_places" attribute.
invalid_models.fielderrors: "decimalfield": DecimalFields require a "max_digits" attribute.
invalid_models.fielderrors: "filefield": FileFields require an "upload_to" attribute.
diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py
index 9d3e8ca4b4..aa90d1e5ec 100644
--- a/tests/modeltests/lookup/models.py
+++ b/tests/modeltests/lookup/models.py
@@ -8,12 +8,12 @@ from django.db import models
from django.conf import settings
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateTimeField()
class Meta:
ordering = ('-pub_date', 'headline')
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':r"""
@@ -100,7 +100,7 @@ TypeError: in_bulk() got an unexpected keyword argument 'headline__startswith'
# values() returns a list of dictionaries instead of object instances -- and
# you can specify which fields you want to retrieve.
>>> Article.objects.values('headline')
-[{'headline': 'Article 5'}, {'headline': 'Article 6'}, {'headline': 'Article 4'}, {'headline': 'Article 2'}, {'headline': 'Article 3'}, {'headline': 'Article 7'}, {'headline': 'Article 1'}]
+[{'headline': u'Article 5'}, {'headline': u'Article 6'}, {'headline': u'Article 4'}, {'headline': u'Article 2'}, {'headline': u'Article 3'}, {'headline': u'Article 7'}, {'headline': u'Article 1'}]
>>> Article.objects.filter(pub_date__exact=datetime(2005, 7, 27)).values('id')
[{'id': 2}, {'id': 3}, {'id': 7}]
>>> list(Article.objects.values('id', 'headline')) == [{'id': 5, 'headline': 'Article 5'}, {'id': 6, 'headline': 'Article 6'}, {'id': 4, 'headline': 'Article 4'}, {'id': 2, 'headline': 'Article 2'}, {'id': 3, 'headline': 'Article 3'}, {'id': 7, 'headline': 'Article 7'}, {'id': 1, 'headline': 'Article 1'}]
@@ -110,13 +110,13 @@ True
... i = d.items()
... i.sort()
... i
-[('headline', 'Article 5'), ('id', 5)]
-[('headline', 'Article 6'), ('id', 6)]
-[('headline', 'Article 4'), ('id', 4)]
-[('headline', 'Article 2'), ('id', 2)]
-[('headline', 'Article 3'), ('id', 3)]
-[('headline', 'Article 7'), ('id', 7)]
-[('headline', 'Article 1'), ('id', 1)]
+[('headline', u'Article 5'), ('id', 5)]
+[('headline', u'Article 6'), ('id', 6)]
+[('headline', u'Article 4'), ('id', 4)]
+[('headline', u'Article 2'), ('id', 2)]
+[('headline', u'Article 3'), ('id', 3)]
+[('headline', u'Article 7'), ('id', 7)]
+[('headline', u'Article 1'), ('id', 1)]
# You can use values() with iterator() for memory savings, because iterator()
# uses database-level iteration.
@@ -124,17 +124,16 @@ True
... i = d.items()
... i.sort()
... i
-[('headline', 'Article 5'), ('id', 5)]
-[('headline', 'Article 6'), ('id', 6)]
-[('headline', 'Article 4'), ('id', 4)]
-[('headline', 'Article 2'), ('id', 2)]
-[('headline', 'Article 3'), ('id', 3)]
-[('headline', 'Article 7'), ('id', 7)]
-[('headline', 'Article 1'), ('id', 1)]
+[('headline', u'Article 5'), ('id', 5)]
+[('headline', u'Article 6'), ('id', 6)]
+[('headline', u'Article 4'), ('id', 4)]
+[('headline', u'Article 2'), ('id', 2)]
+[('headline', u'Article 3'), ('id', 3)]
+[('headline', u'Article 7'), ('id', 7)]
+[('headline', u'Article 1'), ('id', 1)]
-
-# you can use values() even on extra fields
->>> for d in Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_one'):
+# The values() method works with "extra" fields specified in extra(select).
+>>> for d in Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_one'):
... i = d.items()
... i.sort()
... i
@@ -145,15 +144,24 @@ True
[('id', 3), ('id_plus_one', 4)]
[('id', 7), ('id_plus_one', 8)]
[('id', 1), ('id_plus_one', 2)]
+>>> data = {'id_plus_one': 'id+1', 'id_plus_two': 'id+2', 'id_plus_three': 'id+3',
+... 'id_plus_four': 'id+4', 'id_plus_five': 'id+5', 'id_plus_six': 'id+6',
+... 'id_plus_seven': 'id+7', 'id_plus_eight': 'id+8'}
+>>> result = list(Article.objects.filter(id=1).extra(select=data).values(*data.keys()))[0]
+>>> result = result.items()
+>>> result.sort()
+>>> result
+[('id_plus_eight', 9), ('id_plus_five', 6), ('id_plus_four', 5), ('id_plus_one', 2), ('id_plus_seven', 8), ('id_plus_six', 7), ('id_plus_three', 4), ('id_plus_two', 3)]
-# however, an exception FieldDoesNotExist will still be thrown
-# if you try to access non-existent field (field that is neither on the model nor extra)
->>> Article.objects.extra( select={'id_plus_one' : 'id + 1'} ).values('id', 'id_plus_two')
+# However, an exception FieldDoesNotExist will be thrown if you specify a
+# non-existent field name in values() (a field that is neither in the model
+# nor in extra(select)).
+>>> Article.objects.extra(select={'id_plus_one': 'id + 1'}).values('id', 'id_plus_two')
Traceback (most recent call last):
...
FieldDoesNotExist: Article has no field named 'id_plus_two'
-# if you don't specify which fields, all are returned
+# If you don't specify field names to values(), all are returned.
>>> list(Article.objects.filter(id=5).values()) == [{'id': 5, 'headline': 'Article 5', 'pub_date': datetime(2005, 8, 1, 9, 0)}]
True
diff --git a/tests/modeltests/m2m_and_m2o/models.py b/tests/modeltests/m2m_and_m2o/models.py
index 09affb002f..0ab7a72d57 100644
--- a/tests/modeltests/m2m_and_m2o/models.py
+++ b/tests/modeltests/m2m_and_m2o/models.py
@@ -7,15 +7,15 @@ Make sure to set ``related_name`` if you use relationships to the same table.
from django.db import models
class User(models.Model):
- username = models.CharField(maxlength=20)
+ username = models.CharField(max_length=20)
class Issue(models.Model):
num = models.IntegerField()
cc = models.ManyToManyField(User, blank=True, related_name='test_issue_cc')
client = models.ForeignKey(User, related_name='test_issue_client')
- def __str__(self):
- return str(self.num)
+ def __unicode__(self):
+ return unicode(self.num)
class Meta:
ordering = ('num',)
diff --git a/tests/modeltests/m2m_intermediary/models.py b/tests/modeltests/m2m_intermediary/models.py
index b917db6189..0f93d5a154 100644
--- a/tests/modeltests/m2m_intermediary/models.py
+++ b/tests/modeltests/m2m_intermediary/models.py
@@ -13,26 +13,26 @@ writer").
from django.db import models
class Reporter(models.Model):
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateField()
- def __str__(self):
+ def __unicode__(self):
return self.headline
class Writer(models.Model):
reporter = models.ForeignKey(Reporter)
article = models.ForeignKey(Article)
- position = models.CharField(maxlength=100)
+ position = models.CharField(max_length=100)
- def __str__(self):
- return '%s (%s)' % (self.reporter, self.position)
+ def __unicode__(self):
+ return u'%s (%s)' % (self.reporter, self.position)
__test__ = {'API_TESTS':"""
# Create a few Reporters.
diff --git a/tests/modeltests/m2m_multiple/models.py b/tests/modeltests/m2m_multiple/models.py
index 5a1aa122a9..26a0a2e798 100644
--- a/tests/modeltests/m2m_multiple/models.py
+++ b/tests/modeltests/m2m_multiple/models.py
@@ -10,22 +10,22 @@ Set ``related_name`` to designate what the reverse relationship is called.
from django.db import models
class Category(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
class Meta:
ordering = ('name',)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Article(models.Model):
- headline = models.CharField(maxlength=50)
+ headline = models.CharField(max_length=50)
pub_date = models.DateTimeField()
primary_categories = models.ManyToManyField(Category, related_name='primary_article_set')
secondary_categories = models.ManyToManyField(Category, related_name='secondary_article_set')
class Meta:
ordering = ('pub_date',)
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/m2m_recursive/models.py b/tests/modeltests/m2m_recursive/models.py
index 15c713a759..98f5ce526a 100644
--- a/tests/modeltests/m2m_recursive/models.py
+++ b/tests/modeltests/m2m_recursive/models.py
@@ -15,11 +15,11 @@ there will be a clash, and tests that symmetry is preserved where appropriate.
from django.db import models
class Person(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
friends = models.ManyToManyField('self')
idols = models.ManyToManyField('self', symmetrical=False, related_name='stalkers')
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/m2o_recursive/models.py b/tests/modeltests/m2o_recursive/models.py
index 0b528faf9e..c38200a25b 100644
--- a/tests/modeltests/m2o_recursive/models.py
+++ b/tests/modeltests/m2o_recursive/models.py
@@ -13,10 +13,10 @@ Set ``related_name`` to designate what the reverse relationship is called.
from django.db import models
class Category(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
parent = models.ForeignKey('self', null=True, related_name='child_set')
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/m2o_recursive2/models.py b/tests/modeltests/m2o_recursive2/models.py
index 5b7c5447ad..47af18ba0e 100644
--- a/tests/modeltests/m2o_recursive2/models.py
+++ b/tests/modeltests/m2o_recursive2/models.py
@@ -10,11 +10,11 @@ Set ``related_name`` to designate what the reverse relationship is called.
from django.db import models
class Person(models.Model):
- full_name = models.CharField(maxlength=20)
+ full_name = models.CharField(max_length=20)
mother = models.ForeignKey('self', null=True, related_name='mothers_child_set')
father = models.ForeignKey('self', null=True, related_name='fathers_child_set')
- def __str__(self):
+ def __unicode__(self):
return self.full_name
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/manipulators/models.py b/tests/modeltests/manipulators/models.py
index 1a44cfe7f4..2ee81f62b3 100644
--- a/tests/modeltests/manipulators/models.py
+++ b/tests/modeltests/manipulators/models.py
@@ -7,18 +7,18 @@ Each model gets an AddManipulator and ChangeManipulator by default.
from django.db import models
class Musician(models.Model):
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
class Album(models.Model):
- name = models.CharField(maxlength=100)
+ name = models.CharField(max_length=100)
musician = models.ForeignKey(Musician)
release_date = models.DateField(blank=True, null=True)
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
@@ -41,24 +41,24 @@ True
# Attempt to add a Musician without a first_name.
>>> man.get_validation_errors(MultiValueDict({'last_name': ['Blakey']}))
-{'first_name': ['This field is required.']}
+{'first_name': [u'This field is required.']}
# Attempt to add a Musician without a first_name and last_name.
>>> man.get_validation_errors(MultiValueDict({}))
-{'first_name': ['This field is required.'], 'last_name': ['This field is required.']}
+{'first_name': [u'This field is required.'], 'last_name': [u'This field is required.']}
# Attempt to create an Album without a name or musician.
>>> man = Album.AddManipulator()
>>> man.get_validation_errors(MultiValueDict({}))
-{'musician': ['This field is required.'], 'name': ['This field is required.']}
+{'musician': [u'This field is required.'], 'name': [u'This field is required.']}
# Attempt to create an Album with an invalid musician.
>>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['foo']}))
-{'musician': ["Select a valid choice; 'foo' is not in ['', '1']."]}
+{'musician': [u"Select a valid choice; 'foo' is not in [u'', u'1']."]}
# Attempt to create an Album with an invalid release_date.
>>> man.get_validation_errors(MultiValueDict({'name': ['Sallies Fforth'], 'musician': ['1'], 'release_date': 'today'}))
-{'release_date': ['Enter a valid date in YYYY-MM-DD format.']}
+{'release_date': [u'Enter a valid date in YYYY-MM-DD format.']}
# Create an Album without a release_date (because it's optional).
>>> data = MultiValueDict({'name': ['Ella and Basie'], 'musician': ['1']})
diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py
index 5e46ad428d..198c95c4d5 100644
--- a/tests/modeltests/many_to_many/models.py
+++ b/tests/modeltests/many_to_many/models.py
@@ -10,19 +10,19 @@ and a publication has multiple articles.
from django.db import models
class Publication(models.Model):
- title = models.CharField(maxlength=30)
+ title = models.CharField(max_length=30)
- def __str__(self):
+ def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
- def __str__(self):
+ def __unicode__(self):
return self.headline
class Meta:
diff --git a/tests/modeltests/many_to_one/models.py b/tests/modeltests/many_to_one/models.py
index 02f7bf1066..d5d07a1e21 100644
--- a/tests/modeltests/many_to_one/models.py
+++ b/tests/modeltests/many_to_one/models.py
@@ -7,19 +7,19 @@ To define a many-to-one relationship, use ``ForeignKey()`` .
from django.db import models
class Reporter(models.Model):
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
email = models.EmailField()
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter)
- def __str__(self):
+ def __unicode__(self):
return self.headline
class Meta:
@@ -47,7 +47,7 @@ __test__ = {'API_TESTS':"""
# Article objects have access to their related Reporter objects.
>>> r = a.reporter
>>> r.first_name, r.last_name
-('John', 'Smith')
+(u'John', u'Smith')
# Create an Article via the Reporter object.
>>> new_article = r.article_set.create(headline="John's second story", pub_date=datetime(2005, 7, 29))
@@ -154,8 +154,13 @@ False
>>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_article__reporter.last_name='Smith'"])
[<Article: John's second story>, <Article: This is a test>]
+# And should work fine with the unicode that comes out of
+# newforms.Form.cleaned_data
+>>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_article__reporter.last_name='%s'" % u'Smith'])
+[<Article: John's second story>, <Article: This is a test>]
+
# Find all Articles for the Reporter whose ID is 1.
-# Use direct ID check, pk check, and object comparison
+# Use direct ID check, pk check, and object comparison
>>> Article.objects.filter(reporter__id__exact=1)
[<Article: John's second story>, <Article: This is a test>]
>>> Article.objects.filter(reporter__pk=1)
diff --git a/tests/modeltests/many_to_one_null/models.py b/tests/modeltests/many_to_one_null/models.py
index fb0f6ac3b7..60c5888371 100644
--- a/tests/modeltests/many_to_one_null/models.py
+++ b/tests/modeltests/many_to_one_null/models.py
@@ -8,19 +8,19 @@ To define a many-to-one relationship that can have a null foreign key, use
from django.db import models
class Reporter(models.Model):
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
reporter = models.ForeignKey(Reporter, null=True)
class Meta:
ordering = ('headline',)
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py
index 6ffd4d1bce..d27f0b0e54 100644
--- a/tests/modeltests/model_forms/models.py
+++ b/tests/modeltests/model_forms/models.py
@@ -31,20 +31,20 @@ ARTICLE_STATUS = (
)
class Category(models.Model):
- name = models.CharField(maxlength=20)
- url = models.CharField('The URL', maxlength=40)
+ name = models.CharField(max_length=20)
+ url = models.CharField('The URL', max_length=40)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Writer(models.Model):
- name = models.CharField(maxlength=50, help_text='Use both first and last names.')
+ name = models.CharField(max_length=50, help_text='Use both first and last names.')
- def __str__(self):
+ def __unicode__(self):
return self.name
class Article(models.Model):
- headline = models.CharField(maxlength=50)
+ headline = models.CharField(max_length=50)
pub_date = models.DateField()
created = models.DateField(editable=False)
writer = models.ForeignKey(Writer)
@@ -58,14 +58,14 @@ class Article(models.Model):
self.created = datetime.date.today()
return super(Article, self).save()
- def __str__(self):
+ def __unicode__(self):
return self.headline
class PhoneNumber(models.Model):
phone = models.PhoneNumberField()
- description = models.CharField(maxlength=20)
+ description = models.CharField(max_length=20)
- def __str__(self):
+ def __unicode__(self):
return self.phone
__test__ = {'API_TESTS': """
@@ -244,10 +244,10 @@ True
1
>>> test_art = Article.objects.get(id=1)
>>> test_art.headline
-'Test headline'
+u'Test headline'
-You can create a form over a subset of the available fields
-by specifying a 'fields' argument to form_for_instance.
+You can create a form over a subset of the available fields
+by specifying a 'fields' argument to form_for_instance.
>>> PartialArticleForm = form_for_instance(art, fields=('headline','pub_date'))
>>> f = PartialArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04'}, auto_id=False)
>>> print f.as_ul()
@@ -260,7 +260,7 @@ True
1
>>> new_art = Article.objects.get(id=1)
>>> new_art.headline
-'New headline'
+u'New headline'
Add some categories and test the many-to-many form output.
>>> new_art.categories.all()
@@ -332,6 +332,28 @@ Create a new article, with no categories, via the form.
>>> new_art.categories.all()
[]
+Create a new article, with categories, via the form, but use commit=False.
+The m2m data won't be saved until save_m2m() is invoked on the form.
+>>> ArticleForm = form_for_model(Article)
+>>> f = ArticleForm({'headline': u'The walrus was Paul', 'pub_date': u'1967-11-01',
+... 'writer': u'1', 'article': u'Test.', 'categories': [u'1', u'2']})
+>>> new_art = f.save(commit=False)
+
+# Manually save the instance
+>>> new_art.save()
+>>> new_art.id
+4
+
+# The instance doesn't have m2m data yet
+>>> new_art = Article.objects.get(id=4)
+>>> new_art.categories.all()
+[]
+
+# Save the m2m data on the form
+>>> f.save_m2m()
+>>> new_art.categories.all()
+[<Category: Entertainment>, <Category: It's a test>]
+
Here, we define a custom Form. Because it happens to have the same fields as
the Category model, we can use save_instance() to apply its changes to an
existing Category instance.
diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
index babef73e0a..ca00e96418 100644
--- a/tests/modeltests/model_inheritance/models.py
+++ b/tests/modeltests/model_inheritance/models.py
@@ -7,24 +7,24 @@ Model inheritance isn't yet supported.
from django.db import models
class Place(models.Model):
- name = models.CharField(maxlength=50)
- address = models.CharField(maxlength=80)
+ name = models.CharField(max_length=50)
+ address = models.CharField(max_length=80)
- def __str__(self):
- return "%s the place" % self.name
+ def __unicode__(self):
+ return u"%s the place" % self.name
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
- def __str__(self):
- return "%s the restaurant" % self.name
+ def __unicode__(self):
+ return u"%s the restaurant" % self.name
class ItalianRestaurant(Restaurant):
serves_gnocchi = models.BooleanField()
- def __str__(self):
- return "%s the italian restaurant" % self.name
+ def __unicode__(self):
+ return u"%s the italian restaurant" % self.name
__test__ = {'API_TESTS':"""
# Make sure Restaurant has the right fields in the right order.
diff --git a/tests/modeltests/mutually_referential/models.py b/tests/modeltests/mutually_referential/models.py
index 5a3897d21a..7cf7bf8bb2 100644
--- a/tests/modeltests/mutually_referential/models.py
+++ b/tests/modeltests/mutually_referential/models.py
@@ -7,11 +7,11 @@ To define a many-to-one relationship, use ``ForeignKey()`` .
from django.db.models import *
class Parent(Model):
- name = CharField(maxlength=100, core=True)
+ name = CharField(max_length=100, core=True)
bestchild = ForeignKey("Child", null=True, related_name="favoured_by")
class Child(Model):
- name = CharField(maxlength=100)
+ name = CharField(max_length=100)
parent = ForeignKey(Parent)
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/one_to_one/models.py b/tests/modeltests/one_to_one/models.py
index 7488204ff1..2f3f22e628 100644
--- a/tests/modeltests/one_to_one/models.py
+++ b/tests/modeltests/one_to_one/models.py
@@ -9,34 +9,34 @@ In this example, a ``Place`` optionally can be a ``Restaurant``.
from django.db import models
class Place(models.Model):
- name = models.CharField(maxlength=50)
- address = models.CharField(maxlength=80)
+ name = models.CharField(max_length=50)
+ address = models.CharField(max_length=80)
- def __str__(self):
- return "%s the place" % self.name
+ def __unicode__(self):
+ return u"%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
- def __str__(self):
- return "%s the restaurant" % self.place.name
+ def __unicode__(self):
+ return u"%s the restaurant" % self.place.name
class Waiter(models.Model):
restaurant = models.ForeignKey(Restaurant)
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
- def __str__(self):
- return "%s the waiter at %s" % (self.name, self.restaurant)
+ def __unicode__(self):
+ return u"%s the waiter at %s" % (self.name, self.restaurant)
class ManualPrimaryKey(models.Model):
- primary_key = models.CharField(maxlength=10, primary_key=True)
- name = models.CharField(maxlength = 50)
+ primary_key = models.CharField(max_length=10, primary_key=True)
+ name = models.CharField(max_length = 50)
class RelatedModel(models.Model):
link = models.OneToOneField(ManualPrimaryKey)
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
__test__ = {'API_TESTS':"""
# Create a couple of Places.
diff --git a/tests/modeltests/or_lookups/models.py b/tests/modeltests/or_lookups/models.py
index 9f926a7373..ee2cfd2b95 100644
--- a/tests/modeltests/or_lookups/models.py
+++ b/tests/modeltests/or_lookups/models.py
@@ -14,13 +14,13 @@ a get_sql method).
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=50)
+ headline = models.CharField(max_length=50)
pub_date = models.DateTimeField()
class Meta:
ordering = ('pub_date',)
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':"""
@@ -100,7 +100,7 @@ __test__ = {'API_TESTS':"""
3
>>> list(Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')).values())
-[{'headline': 'Hello and goodbye', 'pub_date': datetime.datetime(2005, 11, 29, 0, 0), 'id': 3}]
+[{'headline': u'Hello and goodbye', 'pub_date': datetime.datetime(2005, 11, 29, 0, 0), 'id': 3}]
>>> Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([1,2])
{1: <Article: Hello>}
diff --git a/tests/modeltests/ordering/models.py b/tests/modeltests/ordering/models.py
index 110ae3d7fc..3e651d4ee7 100644
--- a/tests/modeltests/ordering/models.py
+++ b/tests/modeltests/ordering/models.py
@@ -16,12 +16,12 @@ undefined -- not random, just undefined.
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateTimeField()
class Meta:
ordering = ('-pub_date', 'headline')
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py
index 94deb885f5..a7af2a7089 100644
--- a/tests/modeltests/pagination/models.py
+++ b/tests/modeltests/pagination/models.py
@@ -9,10 +9,10 @@ objects into easily readable pages.
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=100, default='Default headline')
+ headline = models.CharField(max_length=100, default='Default headline')
pub_date = models.DateTimeField()
- def __str__(self):
+ def __unicode__(self):
return self.headline
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/properties/models.py b/tests/modeltests/properties/models.py
index 4ba6b1a808..5326e4ec5f 100644
--- a/tests/modeltests/properties/models.py
+++ b/tests/modeltests/properties/models.py
@@ -7,8 +7,8 @@ Use properties on models just like on any other Python object.
from django.db import models
class Person(models.Model):
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
def _get_full_name(self):
return "%s %s" % (self.first_name, self.last_name)
diff --git a/tests/modeltests/reserved_names/models.py b/tests/modeltests/reserved_names/models.py
index affe3f649d..a11b8d9f88 100644
--- a/tests/modeltests/reserved_names/models.py
+++ b/tests/modeltests/reserved_names/models.py
@@ -10,18 +10,18 @@ reserved-name usage.
from django.db import models
class Thing(models.Model):
- when = models.CharField(maxlength=1, primary_key=True)
- join = models.CharField(maxlength=1)
- like = models.CharField(maxlength=1)
- drop = models.CharField(maxlength=1)
- alter = models.CharField(maxlength=1)
- having = models.CharField(maxlength=1)
- where = models.DateField(maxlength=1)
- has_hyphen = models.CharField(maxlength=1, db_column='has-hyphen')
+ when = models.CharField(max_length=1, primary_key=True)
+ join = models.CharField(max_length=1)
+ like = models.CharField(max_length=1)
+ drop = models.CharField(max_length=1)
+ alter = models.CharField(max_length=1)
+ having = models.CharField(max_length=1)
+ where = models.DateField(max_length=1)
+ has_hyphen = models.CharField(max_length=1, db_column='has-hyphen')
class Meta:
db_table = 'select'
- def __str__(self):
+ def __unicode__(self):
return self.when
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/reverse_lookup/models.py b/tests/modeltests/reverse_lookup/models.py
index 4d6591551a..80408ad761 100644
--- a/tests/modeltests/reverse_lookup/models.py
+++ b/tests/modeltests/reverse_lookup/models.py
@@ -7,24 +7,24 @@ This demonstrates the reverse lookup features of the database API.
from django.db import models
class User(models.Model):
- name = models.CharField(maxlength=200)
+ name = models.CharField(max_length=200)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Poll(models.Model):
- question = models.CharField(maxlength=200)
+ question = models.CharField(max_length=200)
creator = models.ForeignKey(User)
- def __str__(self):
+ def __unicode__(self):
return self.question
class Choice(models.Model):
- name = models.CharField(maxlength=100)
+ name = models.CharField(max_length=100)
poll = models.ForeignKey(Poll, related_name="poll_choice")
related_poll = models.ForeignKey(Poll, related_name="related_choice")
- def __str(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
diff --git a/tests/modeltests/save_delete_hooks/models.py b/tests/modeltests/save_delete_hooks/models.py
index 6e24c308ba..c1b1d8f08b 100644
--- a/tests/modeltests/save_delete_hooks/models.py
+++ b/tests/modeltests/save_delete_hooks/models.py
@@ -8,11 +8,11 @@ the methods.
from django.db import models
class Person(models.Model):
- first_name = models.CharField(maxlength=20)
- last_name = models.CharField(maxlength=20)
+ first_name = models.CharField(max_length=20)
+ last_name = models.CharField(max_length=20)
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
def save(self):
print "Before save"
diff --git a/tests/modeltests/select_related/models.py b/tests/modeltests/select_related/models.py
index cd34bd1d84..43efab3a7d 100644
--- a/tests/modeltests/select_related/models.py
+++ b/tests/modeltests/select_related/models.py
@@ -12,50 +12,50 @@ from django.db import models
# Who remembers high school biology?
class Domain(models.Model):
- name = models.CharField(maxlength=50)
- def __str__(self):
+ name = models.CharField(max_length=50)
+ def __unicode__(self):
return self.name
class Kingdom(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
domain = models.ForeignKey(Domain)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Phylum(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
kingdom = models.ForeignKey(Kingdom)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Klass(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
phylum = models.ForeignKey(Phylum)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Order(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
klass = models.ForeignKey(Klass)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Family(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
order = models.ForeignKey(Order)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Genus(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
family = models.ForeignKey(Family)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Species(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
genus = models.ForeignKey(Genus)
- def __str__(self):
+ def __unicode__(self):
return self.name
def create_tree(stringtree):
diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py
index 8d44d5eae7..2ee6cfce67 100644
--- a/tests/modeltests/serializers/models.py
+++ b/tests/modeltests/serializers/models.py
@@ -8,41 +8,41 @@ to and from "flat" data (i.e. strings).
from django.db import models
class Category(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
class Meta:
ordering = ('name',)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Author(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
class Meta:
ordering = ('name',)
- def __str__(self):
+ def __unicode__(self):
return self.name
class Article(models.Model):
author = models.ForeignKey(Author)
- headline = models.CharField(maxlength=50)
+ headline = models.CharField(max_length=50)
pub_date = models.DateTimeField()
categories = models.ManyToManyField(Category)
class Meta:
ordering = ('pub_date',)
- def __str__(self):
+ def __unicode__(self):
return self.headline
class AuthorProfile(models.Model):
author = models.OneToOneField(Author)
date_of_birth = models.DateField()
- def __str__(self):
- return "Profile of %s" % self.author
+ def __unicode__(self):
+ return u"Profile of %s" % self.author
__test__ = {'API_TESTS':"""
# Create some data:
diff --git a/tests/modeltests/str/models.py b/tests/modeltests/str/models.py
index 81230d538c..644c6025ab 100644
--- a/tests/modeltests/str/models.py
+++ b/tests/modeltests/str/models.py
@@ -1,23 +1,38 @@
+# -*- coding: utf-8 -*-
"""
-2. Adding __str__() to models
+2. Adding __str__() or __unicode__() to models
-Although it's not a strict requirement, each model should have a ``__str__()``
-method to return a "human-readable" representation of the object. Do this not
-only for your own sanity when dealing with the interactive prompt, but also
-because objects' representations are used throughout Django's
-automatically-generated admin.
+Although it's not a strict requirement, each model should have a
+``_str__()`` or ``__unicode__()`` method to return a "human-readable"
+representation of the object. Do this not only for your own sanity when dealing
+with the interactive prompt, but also because objects' representations are used
+throughout Django's automatically-generated admin.
+
+Normally, you should write ``__unicode__()`` method, since this will work for
+all field types (and Django will automatically provide an appropriate
+``__str__()`` method). However, you can write a ``__str__()`` method directly,
+if you prefer. You must be careful to encode the results correctly, though.
"""
from django.db import models
class Article(models.Model):
- headline = models.CharField(maxlength=100)
+ headline = models.CharField(max_length=100)
pub_date = models.DateTimeField()
def __str__(self):
+ # Caution: this is only safe if you are certain that headline will be
+ # in ASCII.
return self.headline
-__test__ = {'API_TESTS':"""
+class InternationalArticle(models.Model):
+ headline = models.CharField(max_length=100)
+ pub_date = models.DateTimeField()
+
+ def __unicode__(self):
+ return self.headline
+
+__test__ = {'API_TESTS':ur"""
# Create an Article.
>>> from datetime import datetime
>>> a = Article(headline='Area man programs in Python', pub_date=datetime(2005, 7, 28))
@@ -28,4 +43,10 @@ __test__ = {'API_TESTS':"""
>>> a
<Article: Area man programs in Python>
+
+>>> a1 = InternationalArticle(headline=u'Girl wins €12.500 in lottery', pub_date=datetime(2005, 7, 28))
+
+# The default str() output will be the UTF-8 encoded output of __unicode__().
+>>> str(a1)
+'Girl wins \xe2\x82\xac12.500 in lottery'
"""}
diff --git a/tests/modeltests/test_client/fixtures/testdata.json b/tests/modeltests/test_client/fixtures/testdata.json
index 5c9e415240..e9d3ebe9a8 100644
--- a/tests/modeltests/test_client/fixtures/testdata.json
+++ b/tests/modeltests/test_client/fixtures/testdata.json
@@ -16,5 +16,23 @@
"email": "testclient@example.com",
"date_joined": "2006-12-17 07:03:31"
}
+ },
+ {
+ "pk": "2",
+ "model": "auth.user",
+ "fields": {
+ "username": "inactive",
+ "first_name": "Inactive",
+ "last_name": "User",
+ "is_active": false,
+ "is_superuser": false,
+ "is_staff": false,
+ "last_login": "2006-12-17 07:03:31",
+ "groups": [],
+ "user_permissions": [],
+ "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161",
+ "email": "testclient@example.com",
+ "date_joined": "2006-12-17 07:03:31"
+ }
}
] \ No newline at end of file
diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py
index 5ebf29678c..e1f3987847 100644
--- a/tests/modeltests/test_client/models.py
+++ b/tests/modeltests/test_client/models.py
@@ -1,3 +1,4 @@
+# coding: utf-8
"""
38. Testing using the Test Client
@@ -27,11 +28,14 @@ class ClientTest(TestCase):
def test_get_view(self):
"GET a view"
- response = self.client.get('/test_client/get_view/')
+ # The data is ignored, but let's check it doesn't crash the system
+ # anyway.
+ data = {'var': u'\xf2'}
+ response = self.client.get('/test_client/get_view/', data)
# Check some response details
self.assertContains(response, 'This is a test')
- self.assertEqual(response.context['var'], 42)
+ self.assertEqual(response.context['var'], u'\xf2')
self.assertEqual(response.template.name, 'GET Template')
def test_get_post_view(self):
@@ -118,6 +122,18 @@ class ClientTest(TestCase):
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "Valid POST Template")
+ def test_valid_form_with_hints(self):
+ "GET a form, providing hints in the GET data"
+ hints = {
+ 'text': 'Hello World',
+ 'multi': ('b','c','e')
+ }
+ response = self.client.get('/test_client/form_view/', data=hints)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "Form GET Template")
+ # Check that the multi-value data has been rolled out ok
+ self.assertContains(response, 'Select a valid choice.', 0)
+
def test_incomplete_data_form(self):
"POST incomplete data to a form"
post_data = {
@@ -224,6 +240,29 @@ class ClientTest(TestCase):
login = self.client.login(username='otheruser', password='nopassword')
self.failIf(login)
+ def test_view_with_inactive_login(self):
+ "Request a page that is protected with @login, but use an inactive login"
+
+ login = self.client.login(username='inactive', password='password')
+ self.failIf(login)
+
+ def test_logout(self):
+ "Request a logout after logging in"
+ # Log in
+ self.client.login(username='testclient', password='password')
+
+ # Request a page that requires a login
+ response = self.client.get('/test_client/login_protected_view/')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.context['user'].username, 'testclient')
+
+ # Log out
+ self.client.logout()
+
+ # Request a page that requires a login
+ response = self.client.get('/test_client/login_protected_view/')
+ self.assertRedirects(response, '/accounts/login/')
+
def test_session_modifying_view(self):
"Request a page that modifies the session"
# Session value isn't set initially
@@ -281,4 +320,4 @@ class ClientTest(TestCase):
self.assertEqual(mail.outbox[1].from_email, 'from@example.com')
self.assertEqual(mail.outbox[1].to[0], 'second@example.com')
self.assertEqual(mail.outbox[1].to[1], 'third@example.com')
- \ No newline at end of file
+
diff --git a/tests/modeltests/test_client/tests.py b/tests/modeltests/test_client/tests.py
new file mode 100644
index 0000000000..09f292e037
--- /dev/null
+++ b/tests/modeltests/test_client/tests.py
@@ -0,0 +1,20 @@
+# Validate that you can override the default test suite
+
+import unittest
+
+def suite():
+ """
+ Define a suite that deliberately ignores a test defined in
+ this module.
+ """
+
+ testSuite = unittest.TestSuite()
+ testSuite.addTest(SampleTests('testGoodStuff'))
+ return testSuite
+
+class SampleTests(unittest.TestCase):
+ def testGoodStuff(self):
+ pass
+
+ def testBadStuff(self):
+ self.fail("This test shouldn't run")
diff --git a/tests/modeltests/test_client/views.py b/tests/modeltests/test_client/views.py
index 9bdf213b35..81b4a2f283 100644
--- a/tests/modeltests/test_client/views.py
+++ b/tests/modeltests/test_client/views.py
@@ -10,7 +10,7 @@ from django.shortcuts import render_to_response
def get_view(request):
"A simple view that expects a GET request, and returns a rendered template"
t = Template('This is a test. {{ var }} is the value.', name='GET Template')
- c = Context({'var': 42})
+ c = Context({'var': request.GET.get('var', 42)})
return HttpResponse(t.render(c))
@@ -84,7 +84,7 @@ def form_view(request):
t = Template('Invalid POST data. {{ form.errors }}', name='Invalid POST Template')
c = Context({'form': form})
else:
- form = TestForm()
+ form = TestForm(request.GET)
t = Template('Viewing base form. {{ form }}.', name='Form GET Template')
c = Context({'form': form})
@@ -107,7 +107,6 @@ def form_view_with_template(request):
'message': message
}
)
-
def login_protected_view(request):
"A simple view that is login protected."
diff --git a/tests/modeltests/transactions/models.py b/tests/modeltests/transactions/models.py
index e1fad8063e..06d21bbdd4 100644
--- a/tests/modeltests/transactions/models.py
+++ b/tests/modeltests/transactions/models.py
@@ -10,12 +10,12 @@ manually.
from django.db import models
class Reporter(models.Model):
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
email = models.EmailField()
- def __str__(self):
- return "%s %s" % (self.first_name, self.last_name)
+ def __unicode__(self):
+ return u"%s %s" % (self.first_name, self.last_name)
__test__ = {'API_TESTS':"""
>>> from django.db import connection, transaction
@@ -96,4 +96,4 @@ Exception: I meant to do that
Traceback (most recent call last):
...
TransactionManagementError: Transaction managed block ended with pending COMMIT/ROLLBACK
-""" \ No newline at end of file
+"""
diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py
index b31f981aac..aacd041678 100644
--- a/tests/modeltests/validation/models.py
+++ b/tests/modeltests/validation/models.py
@@ -12,12 +12,12 @@ from django.db import models
class Person(models.Model):
is_child = models.BooleanField()
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
birthdate = models.DateField()
favorite_moment = models.DateTimeField()
email = models.EmailField()
- def __str__(self):
+ def __unicode__(self):
return self.name
__test__ = {'API_TESTS':"""
@@ -42,7 +42,7 @@ __test__ = {'API_TESTS':"""
>>> p = Person(**dict(valid_params, id='foo'))
>>> p.validate()
-{'id': ['This value must be an integer.']}
+{'id': [u'This value must be an integer.']}
>>> p = Person(**dict(valid_params, id=None))
>>> p.validate()
@@ -76,7 +76,7 @@ False
>>> p = Person(**dict(valid_params, is_child='foo'))
>>> p.validate()
-{'is_child': ['This value must be either True or False.']}
+{'is_child': [u'This value must be either True or False.']}
>>> p = Person(**dict(valid_params, name=u'Jose'))
>>> p.validate()
@@ -88,7 +88,7 @@ u'Jose'
>>> p.validate()
{}
>>> p.name
-'227'
+u'227'
>>> p = Person(**dict(valid_params, birthdate=datetime.date(2000, 5, 3)))
>>> p.validate()
@@ -116,7 +116,7 @@ datetime.date(2000, 5, 3)
>>> p = Person(**dict(valid_params, birthdate='foo'))
>>> p.validate()
-{'birthdate': ['Enter a valid date in YYYY-MM-DD format.']}
+{'birthdate': [u'Enter a valid date in YYYY-MM-DD format.']}
>>> p = Person(**dict(valid_params, favorite_moment=datetime.datetime(2002, 4, 3, 13, 23)))
>>> p.validate()
@@ -144,10 +144,10 @@ u'john@example.com'
>>> p = Person(**dict(valid_params, email=22))
>>> p.validate()
-{'email': ['Enter a valid e-mail address.']}
+{'email': [u'Enter a valid e-mail address.']}
# Make sure that Date and DateTime return validation errors and don't raise Python errors.
>>> Person(name='John Doe', is_child=True, email='abc@def.com').validate()
-{'favorite_moment': ['This field is required.'], 'birthdate': ['This field is required.']}
+{'favorite_moment': [u'This field is required.'], 'birthdate': [u'This field is required.']}
"""}
diff --git a/tests/regressiontests/bug639/models.py b/tests/regressiontests/bug639/models.py
index 7cfdfc82ef..fc241aba8c 100644
--- a/tests/regressiontests/bug639/models.py
+++ b/tests/regressiontests/bug639/models.py
@@ -2,7 +2,7 @@ import tempfile
from django.db import models
class Photo(models.Model):
- title = models.CharField(maxlength=30)
+ title = models.CharField(max_length=30)
image = models.FileField(upload_to=tempfile.gettempdir())
# Support code for the tests; this keeps track of how many times save() gets
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 9dc7161c03..752083bd2f 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -1,10 +1,12 @@
+# -*- coding: utf-8 -*-
+
# Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file.
from django.core.cache import cache
import time, unittest
-# functions/classes for complex data type tests
+# functions/classes for complex data type tests
def f():
return 42
class C:
@@ -19,8 +21,8 @@ class Cache(unittest.TestCase):
def test_non_existent(self):
# get with non-existent keys
- self.assertEqual(cache.get("does not exist"), None)
- self.assertEqual(cache.get("does not exist", "bang!"), "bang!")
+ self.assertEqual(cache.get("does_not_exist"), None)
+ self.assertEqual(cache.get("does_not_exist", "bang!"), "bang!")
def test_get_many(self):
# get_many
@@ -42,17 +44,16 @@ class Cache(unittest.TestCase):
def test_has_key(self):
# has_key
- cache.set("hello", "goodbye")
- self.assertEqual(cache.has_key("hello"), True)
- self.assertEqual(cache.has_key("goodbye"), False)
+ cache.set("hello1", "goodbye1")
+ self.assertEqual(cache.has_key("hello1"), True)
+ self.assertEqual(cache.has_key("goodbye1"), False)
- def test_in(self):
- cache.set("hello", "goodbye")
- self.assertEqual("hello" in cache, True)
- self.assertEqual("goodbye" in cache, False)
+ def test_in(self):
+ cache.set("hello2", "goodbye2")
+ self.assertEqual("hello2" in cache, True)
+ self.assertEqual("goodbye2" in cache, False)
def test_data_types(self):
- # test data types
stuff = {
'string' : 'this is a string',
'int' : 42,
@@ -62,15 +63,23 @@ class Cache(unittest.TestCase):
'function' : f,
'class' : C,
}
- for (key, value) in stuff.items():
- cache.set(key, value)
- self.assertEqual(cache.get(key), value)
-
+
def test_expiration(self):
# expiration
cache.set('expire', 'very quickly', 1)
time.sleep(2)
self.assertEqual(cache.get("expire"), None)
+ def test_unicode(self):
+ stuff = {
+ u'ascii': u'ascii_value',
+ u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
+ u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2',
+ u'ascii': {u'x' : 1 }
+ }
+ for (key, value) in stuff.items():
+ cache.set(key, value)
+ self.assertEqual(cache.get(key), value)
+
if __name__ == '__main__':
- unittest.main() \ No newline at end of file
+ unittest.main()
diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py
index 18eb4fcccd..3920e1ca40 100644
--- a/tests/regressiontests/datastructures/tests.py
+++ b/tests/regressiontests/datastructures/tests.py
@@ -64,4 +64,13 @@ True
['Holovaty']
>>> d['person']['2']['firstname']
['Adrian']
+
+### FileDict ################################################################
+
+>>> d = FileDict({'content': 'once upon a time...'})
+>>> repr(d)
+"{'content': '<omitted>'}"
+>>> d = FileDict({'other-key': 'once upon a time...'})
+>>> repr(d)
+"{'other-key': 'once upon a time...'}"
"""
diff --git a/tests/regressiontests/datatypes/models.py b/tests/regressiontests/datatypes/models.py
index 8c5c8c285d..ff9666bc0c 100644
--- a/tests/regressiontests/datatypes/models.py
+++ b/tests/regressiontests/datatypes/models.py
@@ -7,7 +7,7 @@ from django.db import models
from django.conf import settings
class Donut(models.Model):
- name = models.CharField(maxlength=100)
+ name = models.CharField(max_length=100)
is_frosted = models.BooleanField(default=False)
has_sprinkles = models.NullBooleanField()
baked_date = models.DateField(null=True)
diff --git a/tests/regressiontests/dateformat/tests.py b/tests/regressiontests/dateformat/tests.py
index f9f84145c5..30c9a4e6dd 100644
--- a/tests/regressiontests/dateformat/tests.py
+++ b/tests/regressiontests/dateformat/tests.py
@@ -1,54 +1,54 @@
r"""
>>> format(my_birthday, '')
-''
+u''
>>> format(my_birthday, 'a')
-'p.m.'
+u'p.m.'
>>> format(my_birthday, 'A')
-'PM'
+u'PM'
>>> format(my_birthday, 'd')
-'08'
+u'08'
>>> format(my_birthday, 'j')
-'8'
+u'8'
>>> format(my_birthday, 'l')
-'Sunday'
+u'Sunday'
>>> format(my_birthday, 'L')
-'False'
+u'False'
>>> format(my_birthday, 'm')
-'07'
+u'07'
>>> format(my_birthday, 'M')
-'Jul'
+u'Jul'
>>> format(my_birthday, 'b')
-'jul'
+u'jul'
>>> format(my_birthday, 'n')
-'7'
+u'7'
>>> format(my_birthday, 'N')
-'July'
+u'July'
>>> no_tz or format(my_birthday, 'O') == '+0100'
True
>>> format(my_birthday, 'P')
-'10 p.m.'
+u'10 p.m.'
>>> no_tz or format(my_birthday, 'r') == 'Sun, 8 Jul 1979 22:00:00 +0100'
True
>>> format(my_birthday, 's')
-'00'
+u'00'
>>> format(my_birthday, 'S')
-'th'
+u'th'
>>> format(my_birthday, 't')
-'31'
+u'31'
>>> no_tz or format(my_birthday, 'T') == 'CET'
True
>>> no_tz or format(my_birthday, 'U') == '300531600'
True
>>> format(my_birthday, 'w')
-'0'
+u'0'
>>> format(my_birthday, 'W')
-'27'
+u'27'
>>> format(my_birthday, 'y')
-'79'
+u'79'
>>> format(my_birthday, 'Y')
-'1979'
+u'1979'
>>> format(my_birthday, 'z')
-'189'
+u'189'
>>> no_tz or format(my_birthday, 'Z') == '3600'
True
@@ -62,10 +62,10 @@ True
True
>>> format(my_birthday, r'Y z \C\E\T')
-'1979 189 CET'
+u'1979 189 CET'
>>> format(my_birthday, r'jS o\f F')
-'8th of July'
+u'8th of July'
"""
from django.utils import dateformat, translation
diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py
index b35636aba6..a1efae66f6 100644
--- a/tests/regressiontests/defaultfilters/tests.py
+++ b/tests/regressiontests/defaultfilters/tests.py
@@ -2,197 +2,211 @@
r"""
>>> floatformat(7.7)
-'7.7'
+u'7.7'
>>> floatformat(7.0)
-'7'
+u'7'
>>> floatformat(0.7)
-'0.7'
+u'0.7'
>>> floatformat(0.07)
-'0.1'
+u'0.1'
>>> floatformat(0.007)
-'0.0'
+u'0.0'
>>> floatformat(0.0)
-'0'
+u'0'
>>> floatformat(7.7,3)
-'7.700'
+u'7.700'
>>> floatformat(6.000000,3)
-'6.000'
+u'6.000'
>>> floatformat(13.1031,-3)
-'13.103'
+u'13.103'
>>> floatformat(11.1197, -2)
-'11.12'
+u'11.12'
>>> floatformat(11.0000, -2)
-'11'
+u'11'
>>> floatformat(11.000001, -2)
-'11.00'
+u'11.00'
>>> floatformat(8.2798, 3)
-'8.280'
->>> floatformat('foo')
-''
->>> floatformat(13.1031, 'bar')
-'13.1031'
->>> floatformat('foo', 'bar')
-''
+u'8.280'
+>>> floatformat(u'foo')
+u''
+>>> floatformat(13.1031, u'bar')
+u'13.1031'
+>>> floatformat(u'foo', u'bar')
+u''
->>> addslashes('"double quotes" and \'single quotes\'')
-'\\"double quotes\\" and \\\'single quotes\\\''
+>>> addslashes(u'"double quotes" and \'single quotes\'')
+u'\\"double quotes\\" and \\\'single quotes\\\''
->>> addslashes(r'\ : backslashes, too')
-'\\\\ : backslashes, too'
+>>> addslashes(ur'\ : backslashes, too')
+u'\\\\ : backslashes, too'
->>> capfirst('hello world')
-'Hello world'
+>>> capfirst(u'hello world')
+u'Hello world'
->>> fix_ampersands('Jack & Jill & Jeroboam')
-'Jack &amp; Jill &amp; Jeroboam'
+>>> fix_ampersands(u'Jack & Jill & Jeroboam')
+u'Jack &amp; Jill &amp; Jeroboam'
->>> linenumbers('line 1\nline 2')
-'1. line 1\n2. line 2'
+>>> linenumbers(u'line 1\nline 2')
+u'1. line 1\n2. line 2'
->>> linenumbers('\n'.join(['x'] * 10))
-'01. x\n02. x\n03. x\n04. x\n05. x\n06. x\n07. x\n08. x\n09. x\n10. x'
+>>> linenumbers(u'\n'.join([u'x'] * 10))
+u'01. x\n02. x\n03. x\n04. x\n05. x\n06. x\n07. x\n08. x\n09. x\n10. x'
>>> lower('TEST')
-'test'
+u'test'
>>> lower(u'\xcb') # uppercase E umlaut
u'\xeb'
>>> make_list('abc')
-['a', 'b', 'c']
+[u'a', u'b', u'c']
>>> make_list(1234)
-['1', '2', '3', '4']
+[u'1', u'2', u'3', u'4']
>>> slugify(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/')
-'jack-jill-like-numbers-123-and-4-and-silly-characters'
+u'jack-jill-like-numbers-123-and-4-and-silly-characters'
->>> stringformat(1, '03d')
-'001'
+>>> slugify(u"Un \xe9l\xe9phant \xe0 l'or\xe9e du bois")
+u'un-elephant-a-loree-du-bois'
->>> stringformat(1, 'z')
-''
+>>> stringformat(1, u'03d')
+u'001'
+
+>>> stringformat(1, u'z')
+u''
>>> title('a nice title, isn\'t it?')
-"A Nice Title, Isn't It?"
+u"A Nice Title, Isn't It?"
+
+>>> title(u'discoth\xe8que')
+u'Discoth\xe8que'
+>>> truncatewords(u'A sentence with a few words in it', 1)
+u'A ...'
->>> truncatewords('A sentence with a few words in it', 1)
-'A ...'
+>>> truncatewords(u'A sentence with a few words in it', 5)
+u'A sentence with a few ...'
->>> truncatewords('A sentence with a few words in it', 5)
-'A sentence with a few ...'
+>>> truncatewords(u'A sentence with a few words in it', 100)
+u'A sentence with a few words in it'
->>> truncatewords('A sentence with a few words in it', 100)
-'A sentence with a few words in it'
+>>> truncatewords(u'A sentence with a few words in it', 'not a number')
+u'A sentence with a few words in it'
->>> truncatewords('A sentence with a few words in it', 'not a number')
-'A sentence with a few words in it'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 0)
+u''
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 0)
-''
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 2)
+u'<p>one <a href="#">two ...</a></p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 2)
-'<p>one <a href="#">two ...</a></p>'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 4)
+u'<p>one <a href="#">two - three <br>four ...</a></p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 4)
-'<p>one <a href="#">two - three <br>four ...</a></p>'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 5)
+u'<p>one <a href="#">two - three <br>four</a> five</p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 5)
-'<p>one <a href="#">two - three <br>four</a> five</p>'
+>>> truncatewords_html(u'<p>one <a href="#">two - three <br>four</a> five</p>', 100)
+u'<p>one <a href="#">two - three <br>four</a> five</p>'
->>> truncatewords_html('<p>one <a href="#">two - three <br>four</a> five</p>', 100)
-'<p>one <a href="#">two - three <br>four</a> five</p>'
+>>> truncatewords_html(u'\xc5ngstr\xf6m was here', 1)
+u'\xc5ngstr\xf6m ...'
->>> upper('Mixed case input')
-'MIXED CASE INPUT'
+>>> upper(u'Mixed case input')
+u'MIXED CASE INPUT'
>>> upper(u'\xeb') # lowercase e umlaut
u'\xcb'
->>> urlencode('jack & jill')
-'jack%20%26%20jill'
+>>> urlencode(u'fran\xe7ois & jill')
+u'fran%C3%A7ois%20%26%20jill'
>>> urlencode(1)
-'1'
+u'1'
+>>> iriencode(u'S\xf8r-Tr\xf8ndelag')
+u'S%C3%B8r-Tr%C3%B8ndelag'
+>>> iriencode(urlencode(u'fran\xe7ois & jill'))
+u'fran%C3%A7ois%20%26%20jill'
+>>> urlizetrunc(u'http://short.com/', 20)
+u'<a href="http://short.com/" rel="nofollow">http://short.com/</a>'
->>> urlizetrunc('http://short.com/', 20)
-'<a href="http://short.com/" rel="nofollow">http://short.com/</a>'
+>>> urlizetrunc(u'http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20)
+u'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google....</a>'
>>> urlizetrunc('http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20)
-'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google...</a>'
+u'<a href="http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=" rel="nofollow">http://www.google...</a>'
# Check truncating of URIs which are the exact length
>>> uri = 'http://31characteruri.com/test/'
>>> len(uri)
31
>>> urlizetrunc(uri, 31)
-'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/test/</a>'
+u'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/test/</a>'
>>> urlizetrunc(uri, 30)
-'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/t...</a>'
+u'<a href="http://31characteruri.com/test/" rel="nofollow">http://31characteruri.com/t...</a>'
>>> urlizetrunc(uri, 2)
-'<a href="http://31characteruri.com/test/" rel="nofollow">...</a>'
+u'<a href="http://31characteruri.com/test/" rel="nofollow">...</a>'
>>> wordcount('')
0
->>> wordcount('oneword')
+>>> wordcount(u'oneword')
1
->>> wordcount('lots of words')
+>>> wordcount(u'lots of words')
3
->>> wordwrap('this is a long paragraph of text that really needs to be wrapped I\'m afraid', 14)
-"this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI'm afraid"
+>>> wordwrap(u'this is a long paragraph of text that really needs to be wrapped I\'m afraid', 14)
+u"this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI'm afraid"
->>> wordwrap('this is a short paragraph of text.\n But this line should be indented',14)
-'this is a\nshort\nparagraph of\ntext.\n But this\nline should be\nindented'
+>>> wordwrap(u'this is a short paragraph of text.\n But this line should be indented',14)
+u'this is a\nshort\nparagraph of\ntext.\n But this\nline should be\nindented'
->>> wordwrap('this is a short paragraph of text.\n But this line should be indented',15)
-'this is a short\nparagraph of\ntext.\n But this line\nshould be\nindented'
+>>> wordwrap(u'this is a short paragraph of text.\n But this line should be indented',15)
+u'this is a short\nparagraph of\ntext.\n But this line\nshould be\nindented'
->>> ljust('test', 10)
-'test '
+>>> ljust(u'test', 10)
+u'test '
->>> ljust('test', 3)
-'test'
+>>> ljust(u'test', 3)
+u'test'
->>> rjust('test', 10)
-' test'
+>>> rjust(u'test', 10)
+u' test'
->>> rjust('test', 3)
-'test'
+>>> rjust(u'test', 3)
+u'test'
->>> center('test', 6)
-' test '
+>>> center(u'test', 6)
+u' test '
->>> cut('a string to be mangled', 'a')
-' string to be mngled'
+>>> cut(u'a string to be mangled', 'a')
+u' string to be mngled'
->>> cut('a string to be mangled', 'ng')
-'a stri to be maled'
+>>> cut(u'a string to be mangled', 'ng')
+u'a stri to be maled'
->>> cut('a string to be mangled', 'strings')
-'a string to be mangled'
+>>> cut(u'a string to be mangled', 'strings')
+u'a string to be mangled'
->>> escape('<some html & special characters > here')
-'&lt;some html &amp; special characters &gt; here'
+>>> escape(u'<some html & special characters > here')
+u'&lt;some html &amp; special characters &gt; here'
>>> escape(u'<some html & special characters > here ĐÅ€£')
u'&lt;some html &amp; special characters &gt; here \xc4\x90\xc3\x85\xe2\x82\xac\xc2\xa3'
->>> linebreaks('line 1')
-'<p>line 1</p>'
+>>> linebreaks(u'line 1')
+u'<p>line 1</p>'
->>> linebreaks('line 1\nline 2')
-'<p>line 1<br />line 2</p>'
+>>> linebreaks(u'line 1\nline 2')
+u'<p>line 1<br />line 2</p>'
->>> removetags('some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags', 'script img')
-'some <b>html</b> with alert("You smell") disallowed tags'
+>>> removetags(u'some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags', 'script img')
+u'some <b>html</b> with alert("You smell") disallowed tags'
->>> striptags('some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags')
-'some html with alert("You smell") disallowed tags'
+>>> striptags(u'some <b>html</b> with <script>alert("You smell")</script> disallowed <img /> tags')
+u'some html with alert("You smell") disallowed tags'
>>> dictsort([{'age': 23, 'name': 'Barbara-Ann'},
... {'age': 63, 'name': 'Ra Ra Rasputin'},
@@ -207,16 +221,16 @@ u'&lt;some html &amp; special characters &gt; here \xc4\x90\xc3\x85\xe2\x82\xac\
>>> first([0,1,2])
0
->>> first('')
-''
+>>> first(u'')
+u''
->>> first('test')
-'t'
+>>> first(u'test')
+u't'
->>> join([0,1,2], 'glue')
-'0glue1glue2'
+>>> join([0,1,2], u'glue')
+u'0glue1glue2'
->>> length('1234')
+>>> length(u'1234')
4
>>> length([1,2,3,4])
@@ -231,37 +245,37 @@ False
>>> length_is('a', 1)
True
->>> length_is('a', 10)
+>>> length_is(u'a', 10)
False
->>> slice_('abcdefg', '0')
-''
+>>> slice_(u'abcdefg', u'0')
+u''
->>> slice_('abcdefg', '1')
-'a'
+>>> slice_(u'abcdefg', u'1')
+u'a'
->>> slice_('abcdefg', '-1')
-'abcdef'
+>>> slice_(u'abcdefg', u'-1')
+u'abcdef'
->>> slice_('abcdefg', '1:2')
-'b'
+>>> slice_(u'abcdefg', u'1:2')
+u'b'
->>> slice_('abcdefg', '1:3')
-'bc'
+>>> slice_(u'abcdefg', u'1:3')
+u'bc'
->>> slice_('abcdefg', '0::2')
-'aceg'
+>>> slice_(u'abcdefg', u'0::2')
+u'aceg'
->>> unordered_list(['item 1', []])
-'\t<li>item 1</li>'
+>>> unordered_list([u'item 1', []])
+u'\t<li>item 1</li>'
->>> unordered_list(['item 1', [['item 1.1', []]]])
-'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>'
+>>> unordered_list([u'item 1', [[u'item 1.1', []]]])
+u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t</ul>\n\t</li>'
->>> unordered_list(['item 1', [['item 1.1', []], ['item 1.2', []]]])
-'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item 1.2</li>\n\t</ul>\n\t</li>'
+>>> unordered_list([u'item 1', [[u'item 1.1', []], [u'item 1.2', []]]])
+u'\t<li>item 1\n\t<ul>\n\t\t<li>item 1.1</li>\n\t\t<li>item 1.2</li>\n\t</ul>\n\t</li>'
->>> add('1', '2')
+>>> add(u'1', u'2')
3
>>> get_digit(123, 1)
@@ -279,43 +293,43 @@ False
>>> get_digit(123, 0)
123
->>> get_digit('xyz', 0)
-'xyz'
+>>> get_digit(u'xyz', 0)
+u'xyz'
# real testing of date() is in dateformat.py
->>> date(datetime.datetime(2005, 12, 29), "d F Y")
-'29 December 2005'
->>> date(datetime.datetime(2005, 12, 29), r'jS o\f F')
-'29th of December'
+>>> date(datetime.datetime(2005, 12, 29), u"d F Y")
+u'29 December 2005'
+>>> date(datetime.datetime(2005, 12, 29), ur'jS o\f F')
+u'29th of December'
# real testing of time() is done in dateformat.py
->>> time(datetime.time(13), "h")
-'01'
+>>> time(datetime.time(13), u"h")
+u'01'
->>> time(datetime.time(0), "h")
-'12'
+>>> time(datetime.time(0), u"h")
+u'12'
# real testing is done in timesince.py, where we can provide our own 'now'
>>> timesince(datetime.datetime.now() - datetime.timedelta(1))
-'1 day'
+u'1 day'
->>> default("val", "default")
-'val'
+>>> default(u"val", u"default")
+u'val'
->>> default(None, "default")
-'default'
+>>> default(None, u"default")
+u'default'
->>> default('', "default")
-'default'
+>>> default(u'', u"default")
+u'default'
->>> default_if_none("val", "default")
-'val'
+>>> default_if_none(u"val", u"default")
+u'val'
->>> default_if_none(None, "default")
-'default'
+>>> default_if_none(None, u"default")
+u'default'
->>> default_if_none('', "default")
-''
+>>> default_if_none(u'', u"default")
+u''
>>> divisibleby(4, 2)
True
@@ -324,139 +338,139 @@ True
False
>>> yesno(True)
-'yes'
+u'yes'
>>> yesno(False)
-'no'
+u'no'
>>> yesno(None)
-'maybe'
+u'maybe'
->>> yesno(True, 'certainly,get out of town,perhaps')
-'certainly'
+>>> yesno(True, u'certainly,get out of town,perhaps')
+u'certainly'
->>> yesno(False, 'certainly,get out of town,perhaps')
-'get out of town'
+>>> yesno(False, u'certainly,get out of town,perhaps')
+u'get out of town'
->>> yesno(None, 'certainly,get out of town,perhaps')
-'perhaps'
+>>> yesno(None, u'certainly,get out of town,perhaps')
+u'perhaps'
->>> yesno(None, 'certainly,get out of town')
-'get out of town'
+>>> yesno(None, u'certainly,get out of town')
+u'get out of town'
>>> filesizeformat(1023)
-'1023 bytes'
+u'1023 bytes'
>>> filesizeformat(1024)
-'1.0 KB'
+u'1.0 KB'
>>> filesizeformat(10*1024)
-'10.0 KB'
+u'10.0 KB'
>>> filesizeformat(1024*1024-1)
-'1024.0 KB'
+u'1024.0 KB'
>>> filesizeformat(1024*1024)
-'1.0 MB'
+u'1.0 MB'
>>> filesizeformat(1024*1024*50)
-'50.0 MB'
+u'50.0 MB'
>>> filesizeformat(1024*1024*1024-1)
-'1024.0 MB'
+u'1024.0 MB'
>>> filesizeformat(1024*1024*1024)
-'1.0 GB'
+u'1.0 GB'
>>> pluralize(1)
-''
+u''
>>> pluralize(0)
-'s'
+u's'
>>> pluralize(2)
-'s'
+u's'
>>> pluralize([1])
-''
+u''
>>> pluralize([])
-'s'
+u's'
>>> pluralize([1,2,3])
-'s'
+u's'
->>> pluralize(1,'es')
-''
+>>> pluralize(1,u'es')
+u''
->>> pluralize(0,'es')
-'es'
+>>> pluralize(0,u'es')
+u'es'
->>> pluralize(2,'es')
-'es'
+>>> pluralize(2,u'es')
+u'es'
->>> pluralize(1,'y,ies')
-'y'
+>>> pluralize(1,u'y,ies')
+u'y'
->>> pluralize(0,'y,ies')
-'ies'
+>>> pluralize(0,u'y,ies')
+u'ies'
->>> pluralize(2,'y,ies')
-'ies'
+>>> pluralize(2,u'y,ies')
+u'ies'
->>> pluralize(0,'y,ies,error')
-''
+>>> pluralize(0,u'y,ies,error')
+u''
->>> phone2numeric('0800 flowers')
-'0800 3569377'
+>>> phone2numeric(u'0800 flowers')
+u'0800 3569377'
# Filters shouldn't break if passed non-strings
>>> addslashes(123)
-'123'
+u'123'
>>> linenumbers(123)
-'1. 123'
+u'1. 123'
>>> lower(123)
-'123'
+u'123'
>>> make_list(123)
-['1', '2', '3']
+[u'1', u'2', u'3']
>>> slugify(123)
-'123'
+u'123'
>>> title(123)
-'123'
+u'123'
>>> truncatewords(123, 2)
-'123'
+u'123'
>>> upper(123)
-'123'
+u'123'
>>> urlencode(123)
-'123'
+u'123'
>>> urlize(123)
-'123'
+u'123'
>>> urlizetrunc(123, 1)
-'123'
+u'123'
>>> wordcount(123)
1
>>> wordwrap(123, 2)
-'123'
+u'123'
>>> ljust('123', 4)
-'123 '
+u'123 '
>>> rjust('123', 4)
-' 123'
+u' 123'
>>> center('123', 5)
-' 123 '
+u' 123 '
>>> center('123', 6)
-' 123 '
+u' 123 '
>>> cut(123, '2')
-'13'
+u'13'
>>> escape(123)
-'123'
+u'123'
>>> linebreaks(123)
-'<p>123</p>'
+u'<p>123</p>'
>>> linebreaksbr(123)
-'123'
+u'123'
>>> removetags(123, 'a')
-'123'
+u'123'
>>> striptags(123)
-'123'
+u'123'
"""
diff --git a/tests/regressiontests/fixtures_regress/fixtures/pretty.xml b/tests/regressiontests/fixtures_regress/fixtures/pretty.xml
new file mode 100644
index 0000000000..68e5710c6a
--- /dev/null
+++ b/tests/regressiontests/fixtures_regress/fixtures/pretty.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+ <object pk="1" model="fixtures_regress.stuff">
+ <field type="CharField" name="name">
+ <None/>
+ </field>
+ <field to="auth.user" name="owner" rel="ManyToOneRel">
+ <None/>
+ </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 dd407df353..c6a50f73ce 100644
--- a/tests/regressiontests/fixtures_regress/models.py
+++ b/tests/regressiontests/fixtures_regress/models.py
@@ -1,24 +1,32 @@
from django.db import models
+from django.contrib.auth.models import User
class Animal(models.Model):
- name = models.CharField(maxlength=150)
- latin_name = models.CharField(maxlength=150)
+ name = models.CharField(max_length=150)
+ latin_name = models.CharField(max_length=150)
- def __str__(self):
- return self.common_name
+ def __unicode__(self):
+ return self.common_name
class Plant(models.Model):
- name = models.CharField(maxlength=150)
+ name = models.CharField(max_length=150)
class Meta:
# For testing when upper case letter in app name; regression for #4057
db_table = "Fixtures_regress_plant"
+class Stuff(models.Model):
+ name = models.CharField(max_length=20, null=True)
+ owner = models.ForeignKey(User, null=True)
+
+ def __unicode__(self):
+ return unicode(self.name) + u' is owned by ' + unicode(self.owner)
+
__test__ = {'API_TESTS':"""
>>> from django.core import management
# Load a fixture that uses PK=1
->>> management.load_data(['sequence'], verbosity=0)
+>>> management.call_command('loaddata', 'sequence', verbosity=0)
# Create a new animal. Without a sequence reset, this new object
# will take a PK of 1 (on Postgres), and the save will fail.
@@ -26,4 +34,13 @@ __test__ = {'API_TESTS':"""
>>> animal = Animal(name='Platypus', latin_name='Ornithorhynchus anatinus')
>>> animal.save()
-"""} \ No newline at end of file
+###############################################
+# Regression test for ticket #4558 -- pretty printing of XML fixtures
+# doesn't affect parsing of None values.
+
+# Load a pretty-printed XML fixture with Nulls.
+>>> management.call_command('loaddata', 'pretty.xml', verbosity=0)
+>>> Stuff.objects.all()
+[<Stuff: None is owned by None>]
+
+"""}
diff --git a/tests/regressiontests/forms/localflavor.py b/tests/regressiontests/forms/localflavor.py
index ede89de2a0..a896013b0d 100644
--- a/tests/regressiontests/forms/localflavor.py
+++ b/tests/regressiontests/forms/localflavor.py
@@ -913,11 +913,11 @@ ValidationError: [u'This field requires only numbers.']
>>> f.clean('375.788.573-000')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 14 characters.']
+ValidationError: [u'Ensure this value has at most 14 characters (it has 15).']
>>> f.clean('123.456.78')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 11 characters.']
+ValidationError: [u'Ensure this value has at least 11 characters (it has 10).']
>>> f.clean('123456789555')
Traceback (most recent call last):
...
@@ -1208,11 +1208,11 @@ u'230880-3449'
>>> f.clean('230880343')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 9).']
>>> f.clean('230880343234')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 11 characters.']
+ValidationError: [u'Ensure this value has at most 11 characters (it has 12).']
>>> f.clean('abcdefghijk')
Traceback (most recent call last):
...
@@ -1254,18 +1254,18 @@ ValidationError: [u'Enter a valid value.']
>>> f.clean('123456')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 7 characters.']
+ValidationError: [u'Ensure this value has at least 7 characters (it has 6).']
>>> f.clean('123456555')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 8 characters.']
+ValidationError: [u'Ensure this value has at most 8 characters (it has 9).']
>>> f.clean('abcdefg')
Traceback (most recent call last):
ValidationError: [u'Enter a valid value.']
>>> f.clean(' 1234567 ')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 8 characters.']
+ValidationError: [u'Ensure this value has at most 8 characters (it has 9).']
>>> f.clean(' 12367 ')
Traceback (most recent call last):
...
@@ -1303,9 +1303,9 @@ strict.
>>> rut = CLRutField()
>>> rut.clean('11-6')
-'11-6'
+u'11-6'
>>> rut.clean('116')
-'11-6'
+u'11-6'
# valid format, bad verifier.
>>> rut.clean('11.111.111-0')
@@ -1318,13 +1318,13 @@ Traceback (most recent call last):
ValidationError: [u'The Chilean RUT is not valid.']
>>> rut.clean('767484100')
-'76.748.410-0'
+u'76.748.410-0'
>>> rut.clean('78.412.790-7')
-'78.412.790-7'
+u'78.412.790-7'
>>> rut.clean('8.334.6043')
-'8.334.604-3'
+u'8.334.604-3'
>>> rut.clean('76793310-K')
-'76.793.310-K'
+u'76.793.310-K'
Strict RUT usage (does not allow imposible values)
>>> rut = CLRutField(strict=True)
@@ -1346,7 +1346,7 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
>>> rut.clean('78.412.790-7')
-'78.412.790-7'
+u'78.412.790-7'
>>> rut.clean('8.334.6043')
Traceback (most recent call last):
...
@@ -1356,4 +1356,94 @@ Traceback (most recent call last):
...
ValidationError: [u'Enter valid a Chilean RUT. The format is XX.XXX.XXX-X.']
+## CLRegionSelect #########################################################
+>>> from django.contrib.localflavor.cl.forms import CLRegionSelect
+>>> f = CLRegionSelect()
+
+>>> f.render('foo', 'bar')
+u'<select name="foo">\n<option value="RM">Regi\xf3n Metropolitana de Santiago</option>\n<option value="I">Regi\xf3n de Tarapac\xe1</option>\n<option value="II">Regi\xf3n de Antofagasta</option>\n<option value="III">Regi\xf3n de Atacama</option>\n<option value="IV">Regi\xf3n de Coquimbo</option>\n<option value="V">Regi\xf3n de Valpara\xedso</option>\n<option value="VI">Regi\xf3n del Libertador Bernardo O&#39;Higgins</option>\n<option value="VII">Regi\xf3n del Maule</option>\n<option value="VIII">Regi\xf3n del B\xedo B\xedo</option>\n<option value="IX">Regi\xf3n de la Araucan\xeda</option>\n<option value="X">Regi\xf3n de los Lagos</option>\n<option value="XI">Regi\xf3n de Ays\xe9n del General Carlos Ib\xe1\xf1ez del Campo</option>\n<option value="XII">Regi\xf3n de Magallanes y la Ant\xe1rtica Chilena</option>\n<option value="XIV">Regi\xf3n de Los R\xedos</option>\n<option value="XV">Regi\xf3n de Arica-Parinacota</option>\n</select>'
+
+# SKPostalCodeField #########################################################
+
+>>> from django.contrib.localflavor.sk.forms import SKPostalCodeField
+>>> f = SKPostalCodeField()
+>>> f.clean('84545x')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XXXXX or XXX XX.']
+>>> f.clean('91909')
+u'91909'
+>>> f.clean('917 01')
+u'91701'
+
+# SKRegionSelect ############################################################
+
+>>> from django.contrib.localflavor.sk.forms import SKRegionSelect
+>>> w = SKRegionSelect()
+>>> w.render('regions', 'TT')
+u'<select name="regions">\n<option value="BB">Banska Bystrica region</option>\n<option value="BA">Bratislava region</option>\n<option value="KE">Kosice region</option>\n<option value="NR">Nitra region</option>\n<option value="PO">Presov region</option>\n<option value="TN">Trencin region</option>\n<option value="TT" selected="selected">Trnava region</option>\n<option value="ZA">Zilina region</option>\n</select>'
+
+# SKDistrictSelect ##########################################################
+
+>>> from django.contrib.localflavor.sk.forms import SKDistrictSelect
+>>> w = SKDistrictSelect()
+>>> w.render('Districts', 'RK')
+u'<select name="Districts">\n<option value="BB">Banska Bystrica</option>\n<option value="BS">Banska Stiavnica</option>\n<option value="BJ">Bardejov</option>\n<option value="BN">Banovce nad Bebravou</option>\n<option value="BR">Brezno</option>\n<option value="BA1">Bratislava I</option>\n<option value="BA2">Bratislava II</option>\n<option value="BA3">Bratislava III</option>\n<option value="BA4">Bratislava IV</option>\n<option value="BA5">Bratislava V</option>\n<option value="BY">Bytca</option>\n<option value="CA">Cadca</option>\n<option value="DT">Detva</option>\n<option value="DK">Dolny Kubin</option>\n<option value="DS">Dunajska Streda</option>\n<option value="GA">Galanta</option>\n<option value="GL">Gelnica</option>\n<option value="HC">Hlohovec</option>\n<option value="HE">Humenne</option>\n<option value="IL">Ilava</option>\n<option value="KK">Kezmarok</option>\n<option value="KN">Komarno</option>\n<option value="KE1">Kosice I</option>\n<option value="KE2">Kosice II</option>\n<option value="KE3">Kosice III</option>\n<option value="KE4">Kosice IV</option>\n<option value="KEO">Kosice - okolie</option>\n<option value="KA">Krupina</option>\n<option value="KM">Kysucke Nove Mesto</option>\n<option value="LV">Levice</option>\n<option value="LE">Levoca</option>\n<option value="LM">Liptovsky Mikulas</option>\n<option value="LC">Lucenec</option>\n<option value="MA">Malacky</option>\n<option value="MT">Martin</option>\n<option value="ML">Medzilaborce</option>\n<option value="MI">Michalovce</option>\n<option value="MY">Myjava</option>\n<option value="NO">Namestovo</option>\n<option value="NR">Nitra</option>\n<option value="NM">Nove Mesto nad Vahom</option>\n<option value="NZ">Nove Zamky</option>\n<option value="PE">Partizanske</option>\n<option value="PK">Pezinok</option>\n<option value="PN">Piestany</option>\n<option value="PT">Poltar</option>\n<option value="PP">Poprad</option>\n<option value="PB">Povazska Bystrica</option>\n<option value="PO">Presov</option>\n<option value="PD">Prievidza</option>\n<option value="PU">Puchov</option>\n<option value="RA">Revuca</option>\n<option value="RS">Rimavska Sobota</option>\n<option value="RV">Roznava</option>\n<option value="RK" selected="selected">Ruzomberok</option>\n<option value="SB">Sabinov</option>\n<option value="SC">Senec</option>\n<option value="SE">Senica</option>\n<option value="SI">Skalica</option>\n<option value="SV">Snina</option>\n<option value="SO">Sobrance</option>\n<option value="SN">Spisska Nova Ves</option>\n<option value="SL">Stara Lubovna</option>\n<option value="SP">Stropkov</option>\n<option value="SK">Svidnik</option>\n<option value="SA">Sala</option>\n<option value="TO">Topolcany</option>\n<option value="TV">Trebisov</option>\n<option value="TN">Trencin</option>\n<option value="TT">Trnava</option>\n<option value="TR">Turcianske Teplice</option>\n<option value="TS">Tvrdosin</option>\n<option value="VK">Velky Krtis</option>\n<option value="VT">Vranov nad Toplou</option>\n<option value="ZM">Zlate Moravce</option>\n<option value="ZV">Zvolen</option>\n<option value="ZC">Zarnovica</option>\n<option value="ZH">Ziar nad Hronom</option>\n<option value="ZA">Zilina</option>\n</select>'
+
+# PLVoivodeshipSelect ##########################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLVoivodeshipSelect
+>>> f = PLVoivodeshipSelect()
+>>> f.render('voivodeships','pomerania')
+u'<select name="voivodeships">\n<option value="lower_silesia">Lower Silesia</option>\n<option value="kuyavia-pomerania">Kuyavia-Pomerania</option>\n<option value="lublin">Lublin</option>\n<option value="lubusz">Lubusz</option>\n<option value="lodz">Lodz</option>\n<option value="lesser_poland">Lesser Poland</option>\n<option value="masovia">Masovia</option>\n<option value="opole">Opole</option>\n<option value="subcarpatia">Subcarpatia</option>\n<option value="podlasie">Podlasie</option>\n<option value="pomerania" selected="selected">Pomerania</option>\n<option value="silesia">Silesia</option>\n<option value="swietokrzyskie">Swietokrzyskie</option>\n<option value="warmia-masuria">Warmia-Masuria</option>\n<option value="greater_poland">Greater Poland</option>\n<option value="west_pomerania">West Pomerania</option>\n</select>'
+
+# PLAdministrativeUnitSelect ##########################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLAdministrativeUnitSelect
+>>> f = PLAdministrativeUnitSelect()
+>>> f.render('administrativeunit','katowice')
+u'<select name="administrativeunit">\n<option value="wroclaw">Wroc\u0142aw</option>\n<option value="jeleniagora">Jelenia G\xf3ra</option>\n<option value="legnica">Legnica</option>\n<option value="boleslawiecki">boles\u0142awiecki</option>\n<option value="dzierzoniowski">dzier\u017coniowski</option>\n<option value="glogowski">g\u0142ogowski</option>\n<option value="gorowski">g\xf3rowski</option>\n<option value="jaworski">jaworski</option>\n<option value="jeleniogorski">jeleniog\xf3rski</option>\n<option value="kamiennogorski">kamiennog\xf3rski</option>\n<option value="klodzki">k\u0142odzki</option>\n<option value="legnicki">legnicki</option>\n<option value="lubanski">luba\u0144ski</option>\n<option value="lubinski">lubi\u0144ski</option>\n<option value="lwowecki">lw\xf3wecki</option>\n<option value="milicki">milicki</option>\n<option value="olesnicki">ole\u015bnicki</option>\n<option value="olawski">o\u0142awski</option>\n<option value="polkowicki">polkowicki</option>\n<option value="strzelinski">strzeli\u0144ski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="trzebnicki">trzebnicki</option>\n<option value="walbrzyski">wa\u0142brzyski</option>\n<option value="wolowski">wo\u0142owski</option>\n<option value="wroclawski">wroc\u0142awski</option>\n<option value="zabkowicki">z\u0105bkowicki</option>\n<option value="zgorzelecki">zgorzelecki</option>\n<option value="zlotoryjski">z\u0142otoryjski</option>\n<option value="bydgoszcz">Bydgoszcz</option>\n<option value="torun">Toru\u0144</option>\n<option value="wloclawek">W\u0142oc\u0142awek</option>\n<option value="grudziadz">Grudzi\u0105dz</option>\n<option value="aleksandrowski">aleksandrowski</option>\n<option value="brodnicki">brodnicki</option>\n<option value="bydgoski">bydgoski</option>\n<option value="chelminski">che\u0142mi\u0144ski</option>\n<option value="golubsko-dobrzynski">golubsko-dobrzy\u0144ski</option>\n<option value="grudziadzki">grudzi\u0105dzki</option>\n<option value="inowroclawski">inowroc\u0142awski</option>\n<option value="lipnowski">lipnowski</option>\n<option value="mogilenski">mogile\u0144ski</option>\n<option value="nakielski">nakielski</option>\n<option value="radziejowski">radziejowski</option>\n<option value="rypinski">rypi\u0144ski</option>\n<option value="sepolenski">s\u0119pole\u0144ski</option>\n<option value="swiecki">\u015bwiecki</option>\n<option value="torunski">toru\u0144ski</option>\n<option value="tucholski">tucholski</option>\n<option value="wabrzeski">w\u0105brzeski</option>\n<option value="wloclawski">wroc\u0142awski</option>\n<option value="zninski">\u017ani\u0144ski</option>\n<option value="lublin">Lublin</option>\n<option value="biala-podlaska">Bia\u0142a Podlaska</option>\n<option value="chelm">Che\u0142m</option>\n<option value="zamosc">Zamo\u015b\u0107</option>\n<option value="bialski">bialski</option>\n<option value="bilgorajski">bi\u0142gorajski</option>\n<option value="chelmski">che\u0142mski</option>\n<option value="hrubieszowski">hrubieszowski</option>\n<option value="janowski">janowski</option>\n<option value="krasnostawski">krasnostawski</option>\n<option value="krasnicki">kra\u015bnicki</option>\n<option value="lubartowski">lubartowski</option>\n<option value="lubelski">lubelski</option>\n<option value="leczynski">\u0142\u0119czy\u0144ski</option>\n<option value="lukowski">\u0142ukowski</option>\n<option value="opolski">opolski</option>\n<option value="parczewski">parczewski</option>\n<option value="pulawski">pu\u0142awski</option>\n<option value="radzynski">radzy\u0144ski</option>\n<option value="rycki">rycki</option>\n<option value="swidnicki">\u015bwidnicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wlodawski">w\u0142odawski</option>\n<option value="zamojski">zamojski</option>\n<option value="gorzow-wielkopolski">Gorz\xf3w Wielkopolski</option>\n<option value="zielona-gora">Zielona G\xf3ra</option>\n<option value="gorzowski">gorzowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="miedzyrzecki">mi\u0119dzyrzecki</option>\n<option value="nowosolski">nowosolski</option>\n<option value="slubicki">s\u0142ubicki</option>\n<option value="strzelecko-drezdenecki">strzelecko-drezdenecki</option>\n<option value="sulecinski">sule\u0144ci\u0144ski</option>\n<option value="swiebodzinski">\u015bwiebodzi\u0144ski</option>\n<option value="wschowski">wschowski</option>\n<option value="zielonogorski">zielonog\xf3rski</option>\n<option value="zaganski">\u017caga\u0144ski</option>\n<option value="zarski">\u017carski</option>\n<option value="lodz">\u0141\xf3d\u017a</option>\n<option value="piotrkow-trybunalski">Piotrk\xf3w Trybunalski</option>\n<option value="skierniewice">Skierniewice</option>\n<option value="belchatowski">be\u0142chatowski</option>\n<option value="brzezinski">brzezi\u0144ski</option>\n<option value="kutnowski">kutnowski</option>\n<option value="laski">\u0142aski</option>\n<option value="leczycki">\u0142\u0119czycki</option>\n<option value="lowicki">\u0142owicki</option>\n<option value="lodzki wschodni">\u0142\xf3dzki wschodni</option>\n<option value="opoczynski">opoczy\u0144ski</option>\n<option value="pabianicki">pabianicki</option>\n<option value="pajeczanski">paj\u0119cza\u0144ski</option>\n<option value="piotrkowski">piotrkowski</option>\n<option value="poddebicki">podd\u0119bicki</option>\n<option value="radomszczanski">radomszcza\u0144ski</option>\n<option value="rawski">rawski</option>\n<option value="sieradzki">sieradzki</option>\n<option value="skierniewicki">skierniewicki</option>\n<option value="tomaszowski">tomaszowski</option>\n<option value="wielunski">wielu\u0144ski</option>\n<option value="wieruszowski">wieruszowski</option>\n<option value="zdunskowolski">zdu\u0144skowolski</option>\n<option value="zgierski">zgierski</option>\n<option value="krakow">Krak\xf3w</option>\n<option value="tarnow">Tarn\xf3w</option>\n<option value="nowy-sacz">Nowy S\u0105cz</option>\n<option value="bochenski">boche\u0144ski</option>\n<option value="brzeski">brzeski</option>\n<option value="chrzanowski">chrzanowski</option>\n<option value="dabrowski">d\u0105browski</option>\n<option value="gorlicki">gorlicki</option>\n<option value="krakowski">krakowski</option>\n<option value="limanowski">limanowski</option>\n<option value="miechowski">miechowski</option>\n<option value="myslenicki">my\u015blenicki</option>\n<option value="nowosadecki">nowos\u0105decki</option>\n<option value="nowotarski">nowotarski</option>\n<option value="olkuski">olkuski</option>\n<option value="oswiecimski">o\u015bwi\u0119cimski</option>\n<option value="proszowicki">proszowicki</option>\n<option value="suski">suski</option>\n<option value="tarnowski">tarnowski</option>\n<option value="tatrzanski">tatrza\u0144ski</option>\n<option value="wadowicki">wadowicki</option>\n<option value="wielicki">wielicki</option>\n<option value="warszawa">Warszawa</option>\n<option value="ostroleka">Ostro\u0142\u0119ka</option>\n<option value="plock">P\u0142ock</option>\n<option value="radom">Radom</option>\n<option value="siedlce">Siedlce</option>\n<option value="bialobrzeski">bia\u0142obrzeski</option>\n<option value="ciechanowski">ciechanowski</option>\n<option value="garwolinski">garwoli\u0144ski</option>\n<option value="gostyninski">gostyni\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="grojecki">gr\xf3jecki</option>\n<option value="kozienicki">kozenicki</option>\n<option value="legionowski">legionowski</option>\n<option value="lipski">lipski</option>\n<option value="losicki">\u0142osicki</option>\n<option value="makowski">makowski</option>\n<option value="minski">mi\u0144ski</option>\n<option value="mlawski">m\u0142awski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="ostrolecki">ostro\u0142\u0119cki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="otwocki">otwocki</option>\n<option value="piaseczynski">piaseczy\u0144ski</option>\n<option value="plocki">p\u0142ocki</option>\n<option value="plonski">p\u0142o\u0144ski</option>\n<option value="pruszkowski">pruszkowski</option>\n<option value="przasnyski">przasnyski</option>\n<option value="przysuski">przysuski</option>\n<option value="pultuski">pu\u0142tuski</option>\n<option value="radomski">radomski</option>\n<option value="siedlecki">siedlecki</option>\n<option value="sierpecki">sierpecki</option>\n<option value="sochaczewski">sochaczewski</option>\n<option value="sokolowski">soko\u0142owski</option>\n<option value="szydlowiecki">szyd\u0142owiecki</option>\n<option value="warszawski-zachodni">warszawski zachodni</option>\n<option value="wegrowski">w\u0119growski</option>\n<option value="wolominski">wo\u0142omi\u0144ski</option>\n<option value="wyszkowski">wyszkowski</option>\n<option value="zwolenski">zwole\u0144ski</option>\n<option value="zurominski">\u017curomi\u0144ski</option>\n<option value="zyrardowski">\u017cyrardowski</option>\n<option value="opole">Opole</option>\n<option value="brzeski">brzeski</option>\n<option value="glubczycki">g\u0142ubczyski</option>\n<option value="kedzierzynsko-kozielski">k\u0119dzierzy\u0144ski-kozielski</option>\n<option value="kluczborski">kluczborski</option>\n<option value="krapkowicki">krapkowicki</option>\n<option value="namyslowski">namys\u0142owski</option>\n<option value="nyski">nyski</option>\n<option value="oleski">oleski</option>\n<option value="opolski">opolski</option>\n<option value="prudnicki">prudnicki</option>\n<option value="strzelecki">strzelecki</option>\n<option value="rzeszow">Rzesz\xf3w</option>\n<option value="krosno">Krosno</option>\n<option value="przemysl">Przemy\u015bl</option>\n<option value="tarnobrzeg">Tarnobrzeg</option>\n<option value="bieszczadzki">bieszczadzki</option>\n<option value="brzozowski">brzozowski</option>\n<option value="debicki">d\u0119bicki</option>\n<option value="jaroslawski">jaros\u0142awski</option>\n<option value="jasielski">jasielski</option>\n<option value="kolbuszowski">kolbuszowski</option>\n<option value="krosnienski">kro\u015bnie\u0144ski</option>\n<option value="leski">leski</option>\n<option value="lezajski">le\u017cajski</option>\n<option value="lubaczowski">lubaczowski</option>\n<option value="lancucki">\u0142a\u0144cucki</option>\n<option value="mielecki">mielecki</option>\n<option value="nizanski">ni\u017ca\u0144ski</option>\n<option value="przemyski">przemyski</option>\n<option value="przeworski">przeworski</option>\n<option value="ropczycko-sedziszowski">ropczycko-s\u0119dziszowski</option>\n<option value="rzeszowski">rzeszowski</option>\n<option value="sanocki">sanocki</option>\n<option value="stalowowolski">stalowowolski</option>\n<option value="strzyzowski">strzy\u017cowski</option>\n<option value="tarnobrzeski">tarnobrzeski</option>\n<option value="bialystok">Bia\u0142ystok</option>\n<option value="lomza">\u0141om\u017ca</option>\n<option value="suwalki">Suwa\u0142ki</option>\n<option value="augustowski">augustowski</option>\n<option value="bialostocki">bia\u0142ostocki</option>\n<option value="bielski">bielski</option>\n<option value="grajewski">grajewski</option>\n<option value="hajnowski">hajnowski</option>\n<option value="kolnenski">kolne\u0144ski</option>\n<option value="\u0142omzynski">\u0142om\u017cy\u0144ski</option>\n<option value="moniecki">moniecki</option>\n<option value="sejnenski">sejne\u0144ski</option>\n<option value="siemiatycki">siematycki</option>\n<option value="sokolski">sok\xf3lski</option>\n<option value="suwalski">suwalski</option>\n<option value="wysokomazowiecki">wysokomazowiecki</option>\n<option value="zambrowski">zambrowski</option>\n<option value="gdansk">Gda\u0144sk</option>\n<option value="gdynia">Gdynia</option>\n<option value="slupsk">S\u0142upsk</option>\n<option value="sopot">Sopot</option>\n<option value="bytowski">bytowski</option>\n<option value="chojnicki">chojnicki</option>\n<option value="czluchowski">cz\u0142uchowski</option>\n<option value="kartuski">kartuski</option>\n<option value="koscierski">ko\u015bcierski</option>\n<option value="kwidzynski">kwidzy\u0144ski</option>\n<option value="leborski">l\u0119borski</option>\n<option value="malborski">malborski</option>\n<option value="nowodworski">nowodworski</option>\n<option value="gdanski">gda\u0144ski</option>\n<option value="pucki">pucki</option>\n<option value="slupski">s\u0142upski</option>\n<option value="starogardzki">starogardzki</option>\n<option value="sztumski">sztumski</option>\n<option value="tczewski">tczewski</option>\n<option value="wejherowski">wejcherowski</option>\n<option value="katowice" selected="selected">Katowice</option>\n<option value="bielsko-biala">Bielsko-Bia\u0142a</option>\n<option value="bytom">Bytom</option>\n<option value="chorzow">Chorz\xf3w</option>\n<option value="czestochowa">Cz\u0119stochowa</option>\n<option value="dabrowa-gornicza">D\u0105browa G\xf3rnicza</option>\n<option value="gliwice">Gliwice</option>\n<option value="jastrzebie-zdroj">Jastrz\u0119bie Zdr\xf3j</option>\n<option value="jaworzno">Jaworzno</option>\n<option value="myslowice">Mys\u0142owice</option>\n<option value="piekary-slaskie">Piekary \u015al\u0105skie</option>\n<option value="ruda-slaska">Ruda \u015al\u0105ska</option>\n<option value="rybnik">Rybnik</option>\n<option value="siemianowice-slaskie">Siemianowice \u015al\u0105skie</option>\n<option value="sosnowiec">Sosnowiec</option>\n<option value="swietochlowice">\u015awi\u0119toch\u0142owice</option>\n<option value="tychy">Tychy</option>\n<option value="zabrze">Zabrze</option>\n<option value="zory">\u017bory</option>\n<option value="bedzinski">b\u0119dzi\u0144ski</option>\n<option value="bielski">bielski</option>\n<option value="bierunsko-ledzinski">bieru\u0144sko-l\u0119dzi\u0144ski</option>\n<option value="cieszynski">cieszy\u0144ski</option>\n<option value="czestochowski">cz\u0119stochowski</option>\n<option value="gliwicki">gliwicki</option>\n<option value="klobucki">k\u0142obucki</option>\n<option value="lubliniecki">lubliniecki</option>\n<option value="mikolowski">miko\u0142owski</option>\n<option value="myszkowski">myszkowski</option>\n<option value="pszczynski">pszczy\u0144ski</option>\n<option value="raciborski">raciborski</option>\n<option value="rybnicki">rybnicki</option>\n<option value="tarnogorski">tarnog\xf3rski</option>\n<option value="wodzislawski">wodzis\u0142awski</option>\n<option value="zawiercianski">zawiercia\u0144ski</option>\n<option value="zywiecki">\u017cywiecki</option>\n<option value="kielce">Kielce</option>\n<option value="buski">buski</option>\n<option value="jedrzejowski">j\u0119drzejowski</option>\n<option value="kazimierski">kazimierski</option>\n<option value="kielecki">kielecki</option>\n<option value="konecki">konecki</option>\n<option value="opatowski">opatowski</option>\n<option value="ostrowiecki">ostrowiecki</option>\n<option value="pinczowski">pi\u0144czowski</option>\n<option value="sandomierski">sandomierski</option>\n<option value="skarzyski">skar\u017cyski</option>\n<option value="starachowicki">starachowicki</option>\n<option value="staszowski">staszowski</option>\n<option value="wloszczowski">w\u0142oszczowski</option>\n<option value="olsztyn">Olsztyn</option>\n<option value="elblag">Elbl\u0105g</option>\n<option value="bartoszycki">bartoszycki</option>\n<option value="braniewski">braniewski</option>\n<option value="dzialdowski">dzia\u0142dowski</option>\n<option value="elblaski">elbl\u0105ski</option>\n<option value="elcki">e\u0142cki</option>\n<option value="gizycki">gi\u017cycki</option>\n<option value="goldapski">go\u0142dapski</option>\n<option value="ilawski">i\u0142awski</option>\n<option value="ketrzynski">k\u0119trzy\u0144ski</option>\n<option value="lidzbarski">lidzbarski</option>\n<option value="mragowski">mr\u0105gowski</option>\n<option value="nidzicki">nidzicki</option>\n<option value="nowomiejski">nowomiejski</option>\n<option value="olecki">olecki</option>\n<option value="olsztynski">olszty\u0144ski</option>\n<option value="ostrodzki">ostr\xf3dzki</option>\n<option value="piski">piski</option>\n<option value="szczycienski">szczycie\u0144ski</option>\n<option value="wegorzewski">w\u0119gorzewski</option>\n<option value="poznan">Pozna\u0144</option>\n<option value="kalisz">Kalisz</option>\n<option value="konin">Konin</option>\n<option value="leszno">Leszno</option>\n<option value="chodzieski">chodziejski</option>\n<option value="czarnkowsko-trzcianecki">czarnkowsko-trzcianecki</option>\n<option value="gnieznienski">gnie\u017anie\u0144ski</option>\n<option value="gostynski">gosty\u0144ski</option>\n<option value="grodziski">grodziski</option>\n<option value="jarocinski">jaroci\u0144ski</option>\n<option value="kaliski">kaliski</option>\n<option value="kepinski">k\u0119pi\u0144ski</option>\n<option value="kolski">kolski</option>\n<option value="koninski">koni\u0144ski</option>\n<option value="koscianski">ko\u015bcia\u0144ski</option>\n<option value="krotoszynski">krotoszy\u0144ski</option>\n<option value="leszczynski">leszczy\u0144ski</option>\n<option value="miedzychodzki">mi\u0119dzychodzki</option>\n<option value="nowotomyski">nowotomyski</option>\n<option value="obornicki">obornicki</option>\n<option value="ostrowski">ostrowski</option>\n<option value="ostrzeszowski">ostrzeszowski</option>\n<option value="pilski">pilski</option>\n<option value="pleszewski">pleszewski</option>\n<option value="poznanski">pozna\u0144ski</option>\n<option value="rawicki">rawicki</option>\n<option value="slupecki">s\u0142upecki</option>\n<option value="szamotulski">szamotulski</option>\n<option value="sredzki">\u015bredzki</option>\n<option value="sremski">\u015bremski</option>\n<option value="turecki">turecki</option>\n<option value="wagrowiecki">w\u0105growiecki</option>\n<option value="wolsztynski">wolszty\u0144ski</option>\n<option value="wrzesinski">wrzesi\u0144ski</option>\n<option value="zlotowski">z\u0142otowski</option>\n<option value="bialogardzki">bia\u0142ogardzki</option>\n<option value="choszczenski">choszcze\u0144ski</option>\n<option value="drawski">drawski</option>\n<option value="goleniowski">goleniowski</option>\n<option value="gryficki">gryficki</option>\n<option value="gryfinski">gryfi\u0144ski</option>\n<option value="kamienski">kamie\u0144ski</option>\n<option value="kolobrzeski">ko\u0142obrzeski</option>\n<option value="koszalinski">koszali\u0144ski</option>\n<option value="lobeski">\u0142obeski</option>\n<option value="mysliborski">my\u015bliborski</option>\n<option value="policki">policki</option>\n<option value="pyrzycki">pyrzycki</option>\n<option value="slawienski">s\u0142awie\u0144ski</option>\n<option value="stargardzki">stargardzki</option>\n<option value="szczecinecki">szczecinecki</option>\n<option value="swidwinski">\u015bwidwi\u0144ski</option>\n<option value="walecki">wa\u0142ecki</option>\n</select>'
+
+# PLPostalCodeField ##############################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLPostalCodeField
+>>> f = PLPostalCodeField()
+>>> f.clean('43--434')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a postal code in the format XX-XXX.']
+>>> f.clean('41-403')
+u'41-403'
+
+# PLTaxNumberField ###############################################################
+
+>>> from django.contrib.localflavor.pl.forms import PLTaxNumberField
+>>> f = PLTaxNumberField()
+>>> f.clean('43-343-234-323')
+Traceback (most recent call last):
+...
+ValidationError: [u'Enter a tax number field (NIP) in the format XXX-XXX-XX-XX or XX-XX-XXX-XXX.']
+>>> f.clean('43-34-234-323')
+u'43-34-234-323'
+>>> f.clean('433-344-24-23')
+u'433-344-24-23'
+
+# PLNationalIdentificationNumberField ############################################
+
+>>> from django.contrib.localflavor.pl.forms import PLNationalIdentificationNumberField
+>>> f = PLNationalIdentificationNumberField()
+>>> f.clean('80071610614')
+u'80071610614'
+>>> f.clean('80071610610')
+Traceback (most recent call last):
+...
+ValidationError: [u'Wrong checksum for the National Identification Number.']
+>>> f.clean('80')
+Traceback (most recent call last):
+...
+ValidationError: [u'National Identification Number consists of 11 digits.']
+>>> f.clean('800716106AA')
+Traceback (most recent call last):
+...
+ValidationError: [u'National Identification Number consists of 11 digits.']
"""
diff --git a/tests/regressiontests/forms/regressions.py b/tests/regressiontests/forms/regressions.py
index 5fe057b5d8..784ef49902 100644
--- a/tests/regressiontests/forms/regressions.py
+++ b/tests/regressiontests/forms/regressions.py
@@ -16,24 +16,63 @@ u'<p>F1: <input type="text" class="special" name="f1" maxlength="10" /></p>\n<p>
#######################
There were some problems with form translations in #3600
->>> from django.utils.translation import gettext_lazy, activate, deactivate
+>>> from django.utils.translation import ugettext_lazy, activate, deactivate
>>> class SomeForm(Form):
-... username = CharField(max_length=10, label=gettext_lazy('Username'))
+... username = CharField(max_length=10, label=ugettext_lazy('Username'))
>>> f = SomeForm()
>>> print f.as_p()
<p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
+
+Translations are done at rendering time, so multi-lingual apps can define forms
+early and still send back the right translation.
+
+# XFAIL
>>> activate('de')
>>> print f.as_p()
<p><label for="id_username">Benutzername:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>
+>>> activate('pl')
+>>> f.as_p()
+u'<p><label for="id_username">Nazwa u\u017cytkownika:</label> <input id="id_username" type="text" name="username" maxlength="10" /></p>'
>>> deactivate()
Unicode decoding problems...
->>> GENDERS = (('0', u'En tied\xe4'), ('1', u'Mies'), ('2', u'Nainen'))
+>>> GENDERS = ((u'\xc5', u'En tied\xe4'), (u'\xf8', u'Mies'), (u'\xdf', u'Nainen'))
>>> class SomeForm(Form):
-... somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect())
+... somechoice = ChoiceField(choices=GENDERS, widget=RadioSelect(), label=u'\xc5\xf8\xdf')
>>> f = SomeForm()
>>> f.as_p()
-u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>'
+u'<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
+
+Testing choice validation with UTF-8 bytestrings as input (these are the
+Russian abbreviations "мес." and "шт.".
+
+>>> UNITS = (('\xd0\xbc\xd0\xb5\xd1\x81.', '\xd0\xbc\xd0\xb5\xd1\x81.'), ('\xd1\x88\xd1\x82.', '\xd1\x88\xd1\x82.'))
+>>> f = ChoiceField(choices=UNITS)
+>>> f.clean(u'\u0448\u0442.')
+u'\u0448\u0442.'
+>>> f.clean('\xd1\x88\xd1\x82.')
+u'\u0448\u0442.'
+
+Translated error messages used to be buggy.
+>>> activate('ru')
+>>> f = SomeForm({})
+>>> f.as_p()
+u'<ul class="errorlist"><li>\u041e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u043f\u043e\u043b\u0435.</li></ul>\n<p><label for="id_somechoice_0">\xc5\xf8\xdf:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="\xc5" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="\xf8" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="\xdf" name="somechoice" /> Nainen</label></li>\n</ul></p>'
+>>> deactivate()
+
+#######################
+# Miscellaneous Tests #
+#######################
+
+There once was a problem with Form fields called "data". Let's make sure that
+doesn't come back.
+>>> class DataForm(Form):
+... data = CharField(max_length=10)
+>>> f = DataForm({'data': 'xyzzy'})
+>>> f.is_valid()
+True
+>>> f.cleaned_data
+{'data': u'xyzzy'}
#######################
# Miscellaneous Tests #
diff --git a/tests/regressiontests/forms/tests.py b/tests/regressiontests/forms/tests.py
index 0808ebe97e..78442677eb 100644
--- a/tests/regressiontests/forms/tests.py
+++ b/tests/regressiontests/forms/tests.py
@@ -4,6 +4,7 @@ from regressions import regression_tests
form_tests = r"""
>>> from django.newforms import *
+>>> from django.newforms.widgets import RadioFieldRenderer
>>> import datetime
>>> import time
>>> import re
@@ -172,27 +173,29 @@ u'<input type="hidden" class="special" value="foo@example.com" name="email" />'
# FileInput Widget ############################################################
+FileInput widgets don't ever show the value, because the old value is of no use
+if you are updating the form or if the provided file generated an error.
>>> w = FileInput()
>>> w.render('email', '')
u'<input type="file" name="email" />'
>>> w.render('email', None)
u'<input type="file" name="email" />'
>>> w.render('email', 'test@example.com')
-u'<input type="file" name="email" value="test@example.com" />'
+u'<input type="file" name="email" />'
>>> w.render('email', 'some "quoted" & ampersanded value')
-u'<input type="file" name="email" value="some &quot;quoted&quot; &amp; ampersanded value" />'
+u'<input type="file" name="email" />'
>>> w.render('email', 'test@example.com', attrs={'class': 'fun'})
-u'<input type="file" name="email" value="test@example.com" class="fun" />'
+u'<input type="file" name="email" class="fun" />'
You can also pass 'attrs' to the constructor:
>>> w = FileInput(attrs={'class': 'fun'})
>>> w.render('email', '')
u'<input type="file" class="fun" name="email" />'
>>> w.render('email', 'foo@example.com')
-u'<input type="file" class="fun" value="foo@example.com" name="email" />'
+u'<input type="file" class="fun" name="email" />'
>>> w.render('email', 'ŠĐĆŽćžšđ', attrs={'class': 'fun'})
-u'<input type="file" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" name="email" />'
+u'<input type="file" class="fun" name="email" />'
# Textarea Widget #############################################################
@@ -614,11 +617,11 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<li><label><input type="radio" name="num" value="5" /> 5</label></li>
</ul>
-The render() method returns a RadioFieldRenderer object, whose str() is a <ul>.
+RadioSelect uses a RadioFieldRenderer to render the individual radio inputs.
You can manipulate that object directly to customize the way the RadioSelect
is rendered.
>>> w = RadioSelect()
->>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> for inp in r:
... print inp
<label><input checked="checked" type="radio" name="beatle" value="J" /> John</label>
@@ -644,10 +647,21 @@ beatle J P Paul False
beatle J G George False
beatle J R Ringo False
+You can create your own custom renderers for RadioSelect to use.
+>>> class MyRenderer(RadioFieldRenderer):
+... def render(self):
+... return u'<br />\n'.join([unicode(choice) for choice in self])
+>>> w = RadioSelect(renderer=MyRenderer)
+>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<label><input type="radio" name="beatle" value="J" /> John</label><br />
+<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
+<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
+<label><input type="radio" name="beatle" value="R" /> Ringo</label>
+
A RadioFieldRenderer object also allows index access to individual RadioInput
objects.
>>> w = RadioSelect()
->>> r = w.render('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+>>> r = w.get_renderer('beatle', 'J', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
>>> print r[1]
<label><input type="radio" name="beatle" value="P" /> Paul</label>
>>> print r[0]
@@ -896,7 +910,7 @@ u'1234567890'
>>> f.clean('1234567890a')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 10 characters.']
+ValidationError: [u'Ensure this value has at most 10 characters (it has 11).']
CharField accepts an optional min_length parameter:
>>> f = CharField(min_length=10, required=False)
@@ -905,7 +919,7 @@ u''
>>> f.clean('12345')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 5).']
>>> f.clean('1234567890')
u'1234567890'
>>> f.clean('1234567890a')
@@ -919,7 +933,7 @@ ValidationError: [u'This field is required.']
>>> f.clean('12345')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 5).']
>>> f.clean('1234567890')
u'1234567890'
>>> f.clean('1234567890a')
@@ -1176,6 +1190,10 @@ ValidationError: [u'Ensure this value is greater than or equal to 0.5.']
Decimal("1.5")
>>> f.clean('0.5')
Decimal("0.5")
+>>> f.clean('.5')
+Decimal("0.5")
+>>> f.clean('00.50')
+Decimal("0.50")
# DateField ###################################################################
@@ -1439,11 +1457,11 @@ RegexField also access min_length and max_length parameters, for convenience.
>>> f.clean('123')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 5 characters.']
+ValidationError: [u'Ensure this value has at least 5 characters (it has 3).']
>>> f.clean('abc')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 5 characters.']
+ValidationError: [u'Ensure this value has at least 5 characters (it has 3).']
>>> f.clean('12345')
u'12345'
>>> f.clean('1234567890')
@@ -1451,7 +1469,7 @@ u'1234567890'
>>> f.clean('12345678901')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 10 characters.']
+ValidationError: [u'Ensure this value has at most 10 characters (it has 11).']
>>> f.clean('12345a')
Traceback (most recent call last):
...
@@ -1508,13 +1526,49 @@ EmailField also access min_length and max_length parameters, for convenience.
>>> f.clean('a@foo.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 10 characters.']
+ValidationError: [u'Ensure this value has at least 10 characters (it has 9).']
>>> f.clean('alf@foo.com')
u'alf@foo.com'
>>> f.clean('alf123456788@foo.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 15 characters.']
+ValidationError: [u'Ensure this value has at most 15 characters (it has 20).']
+
+# FileField ##################################################################
+
+>>> f = FileField()
+>>> 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.']
+
+>>> f.clean({})
+Traceback (most recent call last):
+...
+ValidationError: [u'No file was submitted.']
+
+>>> f.clean('some content that is not a file')
+Traceback (most recent call last):
+...
+ValidationError: [u'No file was submitted. Check the encoding type on the form.']
+
+>>> f.clean({'filename': 'name', 'content':None})
+Traceback (most recent call last):
+...
+ValidationError: [u'The submitted file is empty.']
+
+>>> f.clean({'filename': 'name', 'content':''})
+Traceback (most recent call last):
+...
+ValidationError: [u'The submitted file is empty.']
+
+>>> type(f.clean({'filename': 'name', 'content':'Some File Content'}))
+<class 'django.newforms.fields.UploadedFile'>
# URLField ##################################################################
@@ -1618,13 +1672,13 @@ URLField also access min_length and max_length parameters, for convenience.
>>> f.clean('http://f.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at least 15 characters.']
+ValidationError: [u'Ensure this value has at least 15 characters (it has 12).']
>>> f.clean('http://example.com')
u'http://example.com'
>>> f.clean('http://abcdefghijklmnopqrstuvwxyz.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 20 characters.']
+ValidationError: [u'Ensure this value has at most 20 characters (it has 37).']
# BooleanField ################################################################
@@ -1796,7 +1850,7 @@ u'test@example.com'
>>> f.clean('longemailaddress@example.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 20 characters.']
+ValidationError: [u'Ensure this value has at most 20 characters (it has 28).']
>>> f.clean('not an e-mail')
Traceback (most recent call last):
...
@@ -1816,7 +1870,7 @@ u'test@example.com'
>>> f.clean('longemailaddress@example.com')
Traceback (most recent call last):
...
-ValidationError: [u'Ensure this value has at most 20 characters.']
+ValidationError: [u'Ensure this value has at most 20 characters (it has 28).']
>>> f.clean('not an e-mail')
Traceback (most recent call last):
...
@@ -2557,7 +2611,7 @@ Instances of a dynamic Form do not persist fields from one Form instance to
the next.
>>> class MyForm(Form):
... def __init__(self, data=None, auto_id=False, field_list=[]):
-... Form.__init__(self, data, auto_id)
+... Form.__init__(self, data, auto_id=auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
@@ -2575,7 +2629,7 @@ the next.
... default_field_1 = CharField()
... default_field_2 = CharField()
... def __init__(self, data=None, auto_id=False, field_list=[]):
-... Form.__init__(self, data, auto_id)
+... Form.__init__(self, data, auto_id=auto_id)
... for field in field_list:
... self.fields[field[0]] = field[1]
>>> field_list = [('field1', CharField()), ('field2', CharField())]
@@ -3230,6 +3284,35 @@ is different than its data. This is handled transparently, though.
<option value="3" selected="selected">No</option>
</select>
+# Forms with FileFields ################################################
+
+FileFields are a special case because they take their data from the request.FILES,
+not request.POST.
+
+>>> class FileForm(Form):
+... file1 = FileField()
+>>> f = FileForm(auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><ul class="errorlist"><li>This field is required.</li></ul><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':''}}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><ul class="errorlist"><li>The submitted file is empty.</li></ul><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={'file1': 'something that is not a file'}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><ul class="errorlist"><li>No file was submitted. Check the encoding type on the form.</li></ul><input type="file" name="file1" /></td></tr>
+
+>>> f = FileForm(data={}, files={'file1': {'filename': 'name', 'content':'some content'}}, auto_id=False)
+>>> print f
+<tr><th>File1:</th><td><input type="file" name="file1" /></td></tr>
+>>> f.is_valid()
+True
+
# Basic form processing in a view #############################################
>>> from django.template import Template, Context
@@ -3267,7 +3350,7 @@ Case 2: POST with erroneous data (a redisplayed form, with errors).
<form action="" method="post">
<table>
<tr><td colspan="2"><ul class="errorlist"><li>Please make sure your passwords match.</li></ul></td></tr>
-<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters.</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
+<tr><th>Username:</th><td><ul class="errorlist"><li>Ensure this value has at most 10 characters (it has 23).</li></ul><input type="text" name="username" value="this-is-a-long-username" maxlength="10" /></td></tr>
<tr><th>Password1:</th><td><input type="password" name="password1" value="foo" /></td></tr>
<tr><th>Password2:</th><td><input type="password" name="password2" value="bar" /></td></tr>
</table>
@@ -3374,7 +3457,7 @@ does not have help text, nothing will be output.
<input type="submit" />
</form>
>>> Template('{{ form.password1.help_text }}').render(Context({'form': UserRegistration(auto_id=False)}))
-''
+u''
The label_tag() method takes an optional attrs argument: a dictionary of HTML
attributes to add to the <label> tag.
@@ -3552,6 +3635,29 @@ True
<option value="2016">2016</option>
</select>
+Using a SelectDateWidget in a form:
+
+>>> class GetDate(Form):
+... mydate = DateField(widget=SelectDateWidget)
+>>> a = GetDate({'mydate_month':'4', 'mydate_day':'1', 'mydate_year':'2008'})
+>>> print a.is_valid()
+True
+>>> print a.cleaned_data['mydate']
+2008-04-01
+
+As with any widget that implements get_value_from_datadict,
+we must be prepared to accept the input from the "as_hidden"
+rendering as well.
+
+>>> print a['mydate'].as_hidden()
+<input type="hidden" name="mydate" value="2008-4-1" id="id_mydate" />
+>>> b=GetDate({'mydate':'2008-4-1'})
+>>> print b.is_valid()
+True
+>>> print b.cleaned_data['mydate']
+2008-04-01
+
+
# MultiWidget and MultiValueField #############################################
# MultiWidgets are widgets composed of other widgets. They are usually
# combined with MultiValueFields - a field that is composed of other fields.
diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py
index e7245104e9..9d5ca2006a 100644
--- a/tests/regressiontests/httpwrappers/tests.py
+++ b/tests/regressiontests/httpwrappers/tests.py
@@ -94,7 +94,7 @@ MultiValueDictKeyError: "Key 'foo' not found in <MultiValueDict: {}>"
>>> q['name'] = 'john'
>>> q['name']
-'john'
+u'john'
>>> del q['name']
>>> 'name' in q
@@ -106,10 +106,10 @@ False
'default'
>>> q.get('name', 'default')
-'john'
+u'john'
>>> q.getlist('name')
-['john']
+[u'john']
>>> q.getlist('foo')
[]
@@ -117,18 +117,18 @@ False
>>> q.setlist('foo', ['bar', 'baz'])
>>> q.get('foo', 'default')
-'baz'
+u'baz'
>>> q.getlist('foo')
-['bar', 'baz']
+[u'bar', u'baz']
>>> q.appendlist('foo', 'another')
>>> q.getlist('foo')
-['bar', 'baz', 'another']
+[u'bar', u'baz', u'another']
>>> q['foo']
-'another'
+u'another'
>>> q.has_key('foo')
True
@@ -137,16 +137,16 @@ True
True
>>> q.items()
-[('foo', 'another'), ('name', 'john')]
+[(u'foo', u'another'), (u'name', u'john')]
>>> q.lists()
-[('foo', ['bar', 'baz', 'another']), ('name', ['john'])]
+[(u'foo', [u'bar', u'baz', u'another']), (u'name', [u'john'])]
>>> q.keys()
-['foo', 'name']
+[u'foo', u'name']
>>> q.values()
-['another', 'john']
+[u'another', u'john']
>>> len(q)
2
@@ -155,16 +155,16 @@ True
# Displays last value
>>> q['foo']
-'hello'
+u'hello'
>>> q.get('foo', 'not available')
-'hello'
+u'hello'
>>> q.getlist('foo')
-['bar', 'baz', 'another', 'hello']
+[u'bar', u'baz', u'another', u'hello']
>>> q.pop('foo')
-['bar', 'baz', 'another', 'hello']
+[u'bar', u'baz', u'another', u'hello']
>>> q.pop('foo', 'not there')
'not there'
@@ -173,13 +173,13 @@ True
'not there'
>>> q.setdefault('foo', 'bar')
-'bar'
+u'bar'
>>> q['foo']
-'bar'
+u'bar'
>>> q.getlist('foo')
-['bar']
+[u'bar']
>>> q.urlencode()
'foo=bar&name=john'
@@ -196,12 +196,12 @@ True
>>> q = QueryDict('foo=bar')
>>> q['foo']
-'bar'
+u'bar'
>>> q['bar']
Traceback (most recent call last):
...
-MultiValueDictKeyError: "Key 'bar' not found in <MultiValueDict: {'foo': ['bar']}>"
+MultiValueDictKeyError: "Key 'bar' not found in <MultiValueDict: {u'foo': [u'bar']}>"
>>> q['something'] = 'bar'
Traceback (most recent call last):
@@ -209,13 +209,13 @@ Traceback (most recent call last):
AttributeError: This QueryDict instance is immutable
>>> q.get('foo', 'default')
-'bar'
+u'bar'
>>> q.get('bar', 'default')
'default'
>>> q.getlist('foo')
-['bar']
+[u'bar']
>>> q.getlist('bar')
[]
@@ -243,16 +243,16 @@ False
False
>>> q.items()
-[('foo', 'bar')]
+[(u'foo', u'bar')]
>>> q.lists()
-[('foo', ['bar'])]
+[(u'foo', [u'bar'])]
>>> q.keys()
-['foo']
+[u'foo']
>>> q.values()
-['bar']
+[u'bar']
>>> len(q)
1
@@ -292,7 +292,7 @@ AttributeError: This QueryDict instance is immutable
>>> q = QueryDict('vote=yes&vote=no')
>>> q['vote']
-'no'
+u'no'
>>> q['something'] = 'bar'
Traceback (most recent call last):
@@ -300,13 +300,13 @@ Traceback (most recent call last):
AttributeError: This QueryDict instance is immutable
>>> q.get('vote', 'default')
-'no'
+u'no'
>>> q.get('foo', 'default')
'default'
>>> q.getlist('vote')
-['yes', 'no']
+[u'yes', u'no']
>>> q.getlist('foo')
[]
@@ -334,16 +334,16 @@ False
False
>>> q.items()
-[('vote', 'no')]
+[(u'vote', u'no')]
>>> q.lists()
-[('vote', ['yes', 'no'])]
+[(u'vote', [u'yes', u'no'])]
>>> q.keys()
-['vote']
+[u'vote']
>>> q.values()
-['no']
+[u'no']
>>> len(q)
1
@@ -381,6 +381,16 @@ Traceback (most recent call last):
...
AttributeError: This QueryDict instance is immutable
+# QueryDicts must be able to handle invalid input encoding (in this case, bad
+# UTF-8 encoding).
+>>> q = QueryDict('foo=bar&foo=\xff')
+
+>>> q['foo']
+u'\ufffd'
+
+>>> q.getlist('foo')
+[u'bar', u'\ufffd']
+
"""
from django.http import QueryDict
diff --git a/tests/regressiontests/humanize/tests.py b/tests/regressiontests/humanize/tests.py
index eca65f7575..196488ba6e 100644
--- a/tests/regressiontests/humanize/tests.py
+++ b/tests/regressiontests/humanize/tests.py
@@ -1,5 +1,8 @@
import unittest
+from datetime import timedelta, date
from django.template import Template, Context, add_to_builtins
+from django.utils.dateformat import DateFormat
+from django.utils.translation import ugettext as _
add_to_builtins('django.contrib.humanize.templatetags.humanize')
@@ -8,14 +11,13 @@ class HumanizeTests(unittest.TestCase):
def humanize_tester(self, test_list, result_list, method):
# Using max below ensures we go through both lists
# However, if the lists are not equal length, this raises an exception
- for index in xrange(len(max(test_list,result_list))):
+ for index in xrange(max(len(test_list), len(result_list))):
test_content = test_list[index]
t = Template('{{ test_content|%s }}' % method)
rendered = t.render(Context(locals())).strip()
self.assertEqual(rendered, result_list[index],
- msg="""%s test failed, produced %s,
-should've produced %s""" % (method, rendered, result_list[index]))
-
+ msg="%s test failed, produced %s, should've produced %s" % (method, rendered, result_list[index]))
+
def test_ordinal(self):
test_list = ('1','2','3','4','11','12',
'13','101','102','103','111',
@@ -43,12 +45,26 @@ should've produced %s""" % (method, rendered, result_list[index]))
self.humanize_tester(test_list, result_list, 'intword')
def test_apnumber(self):
- test_list = [str(x) for x in xrange(1,11)]
- result_list = ('one', 'two', 'three', 'four', 'five', 'six',
- 'seven', 'eight', 'nine', '10')
+ test_list = [str(x) for x in range(1, 11)]
+ result_list = (u'one', u'two', u'three', u'four', u'five', u'six',
+ u'seven', u'eight', u'nine', u'10')
self.humanize_tester(test_list, result_list, 'apnumber')
+ def test_naturalday(self):
+ from django.template import defaultfilters
+ today = date.today()
+ yesterday = today - timedelta(days=1)
+ tomorrow = today + timedelta(days=1)
+ someday = today - timedelta(days=10)
+ notdate = u"I'm not a date value"
+
+ test_list = (today, yesterday, tomorrow, someday, notdate)
+ someday_result = defaultfilters.date(someday)
+ result_list = (_(u'today'), _(u'yesterday'), _(u'tomorrow'),
+ someday_result, u"I'm not a date value")
+ self.humanize_tester(test_list, result_list, 'naturalday')
+
if __name__ == '__main__':
unittest.main()
-
+
diff --git a/tests/regressiontests/i18n/__init__.py b/tests/regressiontests/i18n/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/i18n/__init__.py
diff --git a/tests/regressiontests/i18n/models.py b/tests/regressiontests/i18n/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/i18n/models.py
diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py
new file mode 100644
index 0000000000..8a7d2bee3e
--- /dev/null
+++ b/tests/regressiontests/i18n/tests.py
@@ -0,0 +1,33 @@
+# coding: utf-8
+
+ur"""
+Format string interpolation should work with *_lazy objects.
+
+>>> from django.utils.translation import 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()
+
+It should be possible to compare *_lazy objects.
+
+>>> 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
+"""
diff --git a/tests/regressiontests/initial_sql_regress/models.py b/tests/regressiontests/initial_sql_regress/models.py
index dedbba8e5c..7cf725991a 100644
--- a/tests/regressiontests/initial_sql_regress/models.py
+++ b/tests/regressiontests/initial_sql_regress/models.py
@@ -5,7 +5,7 @@ Regression tests for initial SQL insertion.
from django.db import models
class Simple(models.Model):
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
__test__ = {'API_TESTS':""}
diff --git a/tests/regressiontests/invalid_admin_options/models.py b/tests/regressiontests/invalid_admin_options/models.py
index 43bcc533ba..14db463735 100644
--- a/tests/regressiontests/invalid_admin_options/models.py
+++ b/tests/regressiontests/invalid_admin_options/models.py
@@ -12,7 +12,7 @@ model_errors = ""
##This should fail gracefully but is causing a metaclass error
#class BadAdminOption(models.Model):
# "Test nonexistent admin option"
-# name = models.CharField(maxlength=30)
+# name = models.CharField(max_length=30)
#
# class Admin:
# nonexistent = 'option'
@@ -22,7 +22,7 @@ model_errors = ""
class ListDisplayBadOne(models.Model):
"Test list_display, list_display must be a list or tuple"
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
list_display = 'first_name'
@@ -32,7 +32,7 @@ model_errors += """invalid_admin_options.listdisplaybadone: "admin.list_display"
class ListDisplayBadTwo(models.Model):
"Test list_display, list_display items must be attributes, methods or properties."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
list_display = ['first_name','nonexistent']
@@ -41,7 +41,7 @@ model_errors += """invalid_admin_options.listdisplaybadtwo: "admin.list_display"
"""
class ListDisplayBadThree(models.Model):
"Test list_display, list_display items can not be a ManyToManyField."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
nick_names = models.ManyToManyField('ListDisplayGood')
class Admin:
@@ -52,7 +52,7 @@ model_errors += """invalid_admin_options.listdisplaybadthree: "admin.list_displa
class ListDisplayGood(models.Model):
"Test list_display, Admin list_display can be a attribute, method or property."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -66,8 +66,8 @@ class ListDisplayGood(models.Model):
class ListDisplayLinksBadOne(models.Model):
"Test list_display_links, item must be included in list_display."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
list_display = ['last_name']
@@ -78,8 +78,8 @@ model_errors += """invalid_admin_options.listdisplaylinksbadone: "admin.list_dis
class ListDisplayLinksBadTwo(models.Model):
"Test list_display_links, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
list_display = ['first_name','last_name']
@@ -92,8 +92,8 @@ model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "admin.list_dis
## This is failing but the validation which should fail is not.
#class ListDisplayLinksBadThree(models.Model):
# "Test list_display_links, must define list_display to use list_display_links."
-# first_name = models.CharField(maxlength=30)
-# last_name = models.CharField(maxlength=30)
+# first_name = models.CharField(max_length=30)
+# last_name = models.CharField(max_length=30)
#
# class Admin:
# list_display_links = ('first_name',)
@@ -103,7 +103,7 @@ model_errors += """invalid_admin_options.listdisplaylinksbadtwo: "admin.list_dis
class ListDisplayLinksGood(models.Model):
"Test list_display_links, Admin list_display_list can be a attribute, method or property."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -118,7 +118,7 @@ class ListDisplayLinksGood(models.Model):
class ListFilterBadOne(models.Model):
"Test list_filter, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
list_filter = 'first_name'
@@ -128,7 +128,7 @@ model_errors += """invalid_admin_options.listfilterbadone: "admin.list_filter",
class ListFilterBadTwo(models.Model):
"Test list_filter, must be a field not a property or method."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -146,7 +146,7 @@ invalid_admin_options.listfilterbadtwo: "admin.list_filter" refers to 'full_name
class DateHierarchyBadOne(models.Model):
"Test date_hierarchy, must be a date or datetime field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -158,7 +158,7 @@ class DateHierarchyBadOne(models.Model):
class DateHierarchyBadTwo(models.Model):
"Test date_hieracrhy, must be a field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -169,7 +169,7 @@ model_errors += """invalid_admin_options.datehierarchybadtwo: "admin.date_hierar
class DateHierarchyGood(models.Model):
"Test date_hieracrhy, must be a field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -177,7 +177,7 @@ class DateHierarchyGood(models.Model):
class SearchFieldsBadOne(models.Model):
"Test search_fields, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
class Admin:
search_fields = ('nonexistent')
@@ -188,7 +188,7 @@ class SearchFieldsBadOne(models.Model):
class SearchFieldsBadTwo(models.Model):
"Test search_fields, must be a field."
- first_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
def _last_name(self):
return self.first_name
@@ -203,8 +203,8 @@ class SearchFieldsBadTwo(models.Model):
class SearchFieldsGood(models.Model):
"Test search_fields, must be a list or tuple."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
search_fields = ['first_name','last_name']
@@ -212,7 +212,7 @@ class SearchFieldsGood(models.Model):
class JsBadOne(models.Model):
"Test js, must be a list or tuple"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
js = 'test.js'
@@ -223,7 +223,7 @@ class JsBadOne(models.Model):
class SaveAsBad(models.Model):
"Test save_as, should be True or False"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
save_as = 'not True or False'
@@ -234,7 +234,7 @@ class SaveAsBad(models.Model):
class SaveOnTopBad(models.Model):
"Test save_on_top, should be True or False"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
save_on_top = 'not True or False'
@@ -245,7 +245,7 @@ class SaveOnTopBad(models.Model):
class ListSelectRelatedBad(models.Model):
"Test list_select_related, should be True or False"
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
list_select_related = 'not True or False'
@@ -256,7 +256,7 @@ class ListSelectRelatedBad(models.Model):
class ListPerPageBad(models.Model):
"Test list_per_page, should be a positive integer value."
- name = models.CharField(maxlength=30)
+ name = models.CharField(max_length=30)
class Admin:
list_per_page = 89.3
@@ -267,8 +267,8 @@ class ListPerPageBad(models.Model):
class FieldsBadOne(models.Model):
"Test fields, should be a tuple"
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
fields = 'not a tuple'
@@ -279,8 +279,8 @@ class FieldsBadOne(models.Model):
class FieldsBadTwo(models.Model):
"""Test fields, 'fields' dict option is required."""
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
fields = ('Name', {'description': 'this fieldset needs fields'})
@@ -291,8 +291,8 @@ class FieldsBadTwo(models.Model):
class FieldsBadThree(models.Model):
"""Test fields, 'classes' and 'description' are the only allowable extra dict options."""
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
fields = ('Name', {'fields': ('first_name','last_name'),'badoption': 'verybadoption'})
@@ -303,8 +303,8 @@ class FieldsBadThree(models.Model):
class FieldsGood(models.Model):
"Test fields, working example"
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
birth_day = models.DateField()
class Admin:
@@ -315,8 +315,8 @@ class FieldsGood(models.Model):
class OrderingBad(models.Model):
"Test ordering, must be a field."
- first_name = models.CharField(maxlength=30)
- last_name = models.CharField(maxlength=30)
+ first_name = models.CharField(max_length=30)
+ last_name = models.CharField(max_length=30)
class Admin:
ordering = 'nonexistent'
@@ -328,7 +328,7 @@ class OrderingBad(models.Model):
## TODO: Add a manager validator, this should fail gracefully.
#class ManagerBad(models.Model):
# "Test manager, must be a manager object."
-# first_name = models.CharField(maxlength=30)
+# first_name = models.CharField(max_length=30)
#
# class Admin:
# manager = 'nonexistent'
diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py
index 8ddec98da4..57bbcd8489 100644
--- a/tests/regressiontests/many_to_one_regress/models.py
+++ b/tests/regressiontests/many_to_one_regress/models.py
@@ -12,15 +12,15 @@ class Second(models.Model):
# Protect against repetition of #1839, #2415 and #2536.
class Third(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
third = models.ForeignKey('self', null=True, related_name='child_set')
class Parent(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
bestchild = models.ForeignKey('Child', null=True, related_name='favored_by')
class Child(models.Model):
- name = models.CharField(maxlength=20)
+ name = models.CharField(max_length=20)
parent = models.ForeignKey(Parent)
diff --git a/tests/regressiontests/maxlength/__init__.py b/tests/regressiontests/maxlength/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/maxlength/__init__.py
diff --git a/tests/regressiontests/maxlength/models.py b/tests/regressiontests/maxlength/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/maxlength/models.py
diff --git a/tests/regressiontests/maxlength/tests.py b/tests/regressiontests/maxlength/tests.py
new file mode 100644
index 0000000000..8a5f874c78
--- /dev/null
+++ b/tests/regressiontests/maxlength/tests.py
@@ -0,0 +1,160 @@
+# Test access to max_length while still providing full backwards compatibility
+# with legacy maxlength attribute.
+"""
+
+Don't print out the deprecation warnings during testing.
+>>> from warnings import filterwarnings
+>>> filterwarnings("ignore")
+
+# legacy_maxlength function
+
+>>> from django.utils.maxlength import legacy_maxlength
+
+>>> legacy_maxlength(None, None)
+
+
+>>> legacy_maxlength(10, None)
+10
+
+>>> legacy_maxlength(None, 10)
+10
+
+>>> legacy_maxlength(10, 12)
+Traceback (most recent call last):
+...
+TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+
+>>> legacy_maxlength(0, 10)
+Traceback (most recent call last):
+...
+TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+
+>>> legacy_maxlength(0, None)
+0
+
+>>> legacy_maxlength(None, 0)
+0
+
+#===============================================================================
+# Fields
+#===============================================================================
+
+# Set up fields
+>>> from django.db.models import fields
+>>> new = fields.Field(max_length=15)
+>>> old = fields.Field(maxlength=10)
+
+# Ensure both max_length and legacy maxlength are not able to both be specified
+>>> fields.Field(maxlength=10, max_length=15)
+Traceback (most recent call last):
+ ...
+TypeError: field can not take both the max_length argument and the legacy maxlength argument.
+
+# Test max_length
+>>> new.max_length
+15
+>>> old.max_length
+10
+
+# Test accessing maxlength
+>>> new.maxlength
+15
+>>> old.maxlength
+10
+
+# Test setting maxlength
+>>> new.maxlength += 1
+>>> old.maxlength += 1
+>>> new.max_length
+16
+>>> old.max_length
+11
+
+# SlugField __init__ passes through max_length so test that too
+>>> fields.SlugField('new', max_length=15).max_length
+15
+>>> fields.SlugField('empty').max_length
+50
+>>> fields.SlugField('old', maxlength=10).max_length
+10
+
+#===============================================================================
+# (old)forms
+#===============================================================================
+
+>>> from django import oldforms
+
+# Test max_length attribute
+
+>>> oldforms.TextField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vTextField" name="new" size="30" value="" maxlength="15" />'
+
+>>> oldforms.IntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vIntegerField" name="new" size="10" value="" maxlength="15" />'
+
+>>> oldforms.SmallIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vSmallIntegerField" name="new" size="5" value="" maxlength="15" />'
+
+>>> oldforms.PositiveIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vPositiveIntegerField" name="new" size="10" value="" maxlength="15" />'
+
+>>> oldforms.PositiveSmallIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vPositiveSmallIntegerField" name="new" size="5" value="" maxlength="15" />'
+
+>>> oldforms.DatetimeField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vDatetimeField" name="new" size="30" value="" maxlength="15" />'
+
+>>> oldforms.EmailField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vEmailField" name="new" size="50" value="" maxlength="15" />'
+>>> oldforms.EmailField('new').render('')
+u'<input type="text" id="id_new" class="vEmailField" name="new" size="50" value="" maxlength="75" />'
+
+>>> oldforms.URLField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vURLField" name="new" size="50" value="" maxlength="15" />'
+>>> oldforms.URLField('new').render('')
+u'<input type="text" id="id_new" class="vURLField" name="new" size="50" value="" maxlength="200" />'
+
+>>> oldforms.IPAddressField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vIPAddressField" name="new" size="15" value="" maxlength="15" />'
+>>> oldforms.IPAddressField('new').render('')
+u'<input type="text" id="id_new" class="vIPAddressField" name="new" size="15" value="" maxlength="15" />'
+
+>>> oldforms.CommaSeparatedIntegerField('new', max_length=15).render('')
+u'<input type="text" id="id_new" class="vCommaSeparatedIntegerField" name="new" size="20" value="" maxlength="15" />'
+
+
+# Test legacy maxlength attribute
+
+>>> oldforms.TextField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vTextField" name="old" size="30" value="" maxlength="10" />'
+
+>>> oldforms.IntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vIntegerField" name="old" size="10" value="" maxlength="10" />'
+
+>>> oldforms.SmallIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vSmallIntegerField" name="old" size="5" value="" maxlength="10" />'
+
+>>> oldforms.PositiveIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vPositiveIntegerField" name="old" size="10" value="" maxlength="10" />'
+
+>>> oldforms.PositiveSmallIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vPositiveSmallIntegerField" name="old" size="5" value="" maxlength="10" />'
+
+>>> oldforms.DatetimeField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vDatetimeField" name="old" size="30" value="" maxlength="10" />'
+
+>>> oldforms.EmailField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vEmailField" name="old" size="50" value="" maxlength="10" />'
+
+>>> oldforms.URLField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vURLField" name="old" size="50" value="" maxlength="10" />'
+
+>>> oldforms.IPAddressField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vIPAddressField" name="old" size="15" value="" maxlength="10" />'
+
+>>> oldforms.CommaSeparatedIntegerField('old', maxlength=10).render('')
+u'<input type="text" id="id_old" class="vCommaSeparatedIntegerField" name="old" size="20" value="" maxlength="10" />'
+"""
+if __name__ == "__main__":
+ import doctest
+ doctest.testmod()
diff --git a/tests/regressiontests/model_fields/__init__.py b/tests/regressiontests/model_fields/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/model_fields/__init__.py
diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/model_fields/models.py
diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py
new file mode 100644
index 0000000000..e279a0669f
--- /dev/null
+++ b/tests/regressiontests/model_fields/tests.py
@@ -0,0 +1,18 @@
+"""
+>>> from django.db.models.fields import *
+
+# DecimalField
+
+>>> f = DecimalField()
+
+>>> f.to_python(3)
+Decimal("3")
+
+>>> f.to_python("3.14")
+Decimal("3.14")
+
+>>> f.to_python("abc")
+Traceback (most recent call last):
+...
+ValidationError: [u'This value must be a decimal number.']
+"""
diff --git a/tests/regressiontests/model_regress/__init__.py b/tests/regressiontests/model_regress/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/model_regress/__init__.py
diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py
new file mode 100644
index 0000000000..7aa9e2a7c4
--- /dev/null
+++ b/tests/regressiontests/model_regress/models.py
@@ -0,0 +1,40 @@
+# coding: utf-8
+from django.db import models
+
+CHOICES = (
+ (1, 'first'),
+ (2, 'second'),
+)
+
+class Article(models.Model):
+ headline = models.CharField(max_length=100, default='Default headline')
+ pub_date = models.DateTimeField()
+ status = models.IntegerField(blank=True, null=True, choices=CHOICES)
+ misc_data = models.CharField(max_length=100, blank=True)
+
+ class Meta:
+ ordering = ('pub_date','headline')
+ # A utf-8 verbose name (Ångström's Articles) to test they are valid.
+ verbose_name = "\xc3\x85ngstr\xc3\xb6m's Articles"
+
+ def __unicode__(self):
+ return self.headline
+
+__test__ = {'API_TESTS': """
+(NOTE: Part of the regression test here is merely parsing the model
+declaration. The verbose_name, in particular, did not always work.)
+
+An empty choice field should return None for the display name.
+
+>>> from datetime import datetime
+>>> a = Article(headline="Look at me!", pub_date=datetime.now())
+>>> a.save()
+>>> a.get_status_display() is None
+True
+
+Empty strings should be returned as Unicode
+>>> a2 = Article.objects.get(pk=a.id)
+>>> a2.misc_data
+u''
+"""
+}
diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py
index 21944d9e7a..2aa36b2c1a 100644
--- a/tests/regressiontests/null_queries/models.py
+++ b/tests/regressiontests/null_queries/models.py
@@ -1,17 +1,17 @@
from django.db import models
class Poll(models.Model):
- question = models.CharField(maxlength=200)
+ question = models.CharField(max_length=200)
- def __str__(self):
- return "Q: %s " % self.question
+ def __unicode__(self):
+ return u"Q: %s " % self.question
class Choice(models.Model):
poll = models.ForeignKey(Poll)
- choice = models.CharField(maxlength=200)
+ choice = models.CharField(max_length=200)
- def __str__(self):
- return "Choice: %s in poll %s" % (self.choice, self.poll)
+ def __unicode__(self):
+ return u"Choice: %s in poll %s" % (self.choice, self.poll)
__test__ = {'API_TESTS':"""
# Regression test for the use of None as a query value. None is interpreted as
diff --git a/tests/regressiontests/one_to_one_regress/models.py b/tests/regressiontests/one_to_one_regress/models.py
index b81f4266e1..c5ffd3fb3c 100644
--- a/tests/regressiontests/one_to_one_regress/models.py
+++ b/tests/regressiontests/one_to_one_regress/models.py
@@ -1,26 +1,26 @@
from django.db import models
class Place(models.Model):
- name = models.CharField(maxlength=50)
- address = models.CharField(maxlength=80)
+ name = models.CharField(max_length=50)
+ address = models.CharField(max_length=80)
- def __str__(self):
- return "%s the place" % self.name
+ def __unicode__(self):
+ return u"%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(Place)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
- def __str__(self):
- return "%s the restaurant" % self.place.name
+ def __unicode__(self):
+ return u"%s the restaurant" % self.place.name
class Favorites(models.Model):
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
restaurants = models.ManyToManyField(Restaurant)
- def __str__(self):
- return "Favorites for %s" % self.name
+ def __unicode__(self):
+ return u"Favorites for %s" % self.name
__test__ = {'API_TESTS':"""
# Regression test for #1064 and #1506: Check that we create models via the m2m
diff --git a/tests/regressiontests/serializers_regress/models.py b/tests/regressiontests/serializers_regress/models.py
index b441885f10..e9df508822 100644
--- a/tests/regressiontests/serializers_regress/models.py
+++ b/tests/regressiontests/serializers_regress/models.py
@@ -16,7 +16,7 @@ class BooleanData(models.Model):
data = models.BooleanField(null=True)
class CharData(models.Model):
- data = models.CharField(maxlength=30, null=True)
+ data = models.CharField(max_length=30, null=True)
class DateData(models.Model):
data = models.DateField(null=True)
@@ -90,7 +90,7 @@ class Tag(models.Model):
ordering = ["data"]
class GenericData(models.Model):
- data = models.CharField(maxlength=30)
+ data = models.CharField(max_length=30)
tags = generic.GenericRelation(Tag)
@@ -102,13 +102,13 @@ class Anchor(models.Model):
"""This is a model that can be used as
something for other models to point at"""
- data = models.CharField(maxlength=30)
+ data = models.CharField(max_length=30)
class UniqueAnchor(models.Model):
"""This is a model that can be used as
something for other models to point at"""
- data = models.CharField(unique=True, maxlength=30)
+ data = models.CharField(unique=True, max_length=30)
class FKData(models.Model):
data = models.ForeignKey(Anchor, null=True)
@@ -117,7 +117,8 @@ class M2MData(models.Model):
data = models.ManyToManyField(Anchor, null=True)
class O2OData(models.Model):
- data = models.OneToOneField(Anchor, null=True)
+ # One to one field can't be null, since it is a PK.
+ data = models.OneToOneField(Anchor)
class FKSelfData(models.Model):
data = models.ForeignKey('self', null=True)
@@ -143,7 +144,7 @@ class BooleanPKData(models.Model):
data = models.BooleanField(primary_key=True)
class CharPKData(models.Model):
- data = models.CharField(maxlength=30, primary_key=True)
+ data = models.CharField(max_length=30, primary_key=True)
# class DatePKData(models.Model):
# data = models.DateField(primary_key=True)
@@ -175,8 +176,9 @@ class IntegerPKData(models.Model):
class IPAddressPKData(models.Model):
data = models.IPAddressField(primary_key=True)
-class NullBooleanPKData(models.Model):
- data = models.NullBooleanField(primary_key=True)
+# This is just a Boolean field with null=True, and we can't test a PK value of NULL.
+# class NullBooleanPKData(models.Model):
+# data = models.NullBooleanField(primary_key=True)
class PhonePKData(models.Model):
data = models.PhoneNumberField(primary_key=True)
@@ -206,6 +208,19 @@ class USStatePKData(models.Model):
# data = models.XMLField(primary_key=True)
class ComplexModel(models.Model):
- field1 = models.CharField(maxlength=10)
- field2 = models.CharField(maxlength=10)
- field3 = models.CharField(maxlength=10)
+ field1 = models.CharField(max_length=10)
+ field2 = models.CharField(max_length=10)
+ field3 = models.CharField(max_length=10)
+
+# Tests for handling fields with pre_save functions, or
+# models with save functions that modify data
+class AutoNowDateTimeData(models.Model):
+ data = models.DateTimeField(null=True, auto_now=True)
+
+class ModifyingSaveData(models.Model):
+ data = models.IntegerField(null=True)
+
+ def save(self):
+ "A save method that modifies the data in the object"
+ self.data = 666
+ super(ModifyingSaveData, self).save(raw)
diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py
index febcfa822e..24111308d7 100644
--- a/tests/regressiontests/serializers_regress/tests.py
+++ b/tests/regressiontests/serializers_regress/tests.py
@@ -24,17 +24,20 @@ except ImportError:
from django.utils import _decimal as decimal
# A set of functions that can be used to recreate
-# test data objects of various kinds
+# test data objects of various kinds.
+# The save method is a raw base model save, to make
+# sure that the data in the database matches the
+# exact test case.
def data_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
def generic_create(pk, klass, data):
instance = klass(id=pk)
instance.data = data[0]
- instance.save()
+ models.Model.save(instance, raw=True)
for tag in data[1:]:
instance.tags.create(data=tag)
return instance
@@ -42,25 +45,25 @@ def generic_create(pk, klass, data):
def fk_create(pk, klass, data):
instance = klass(id=pk)
setattr(instance, 'data_id', data)
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
def m2m_create(pk, klass, data):
instance = klass(id=pk)
- instance.save()
+ models.Model.save(instance, raw=True)
instance.data = data
return instance
def o2o_create(pk, klass, data):
instance = klass()
instance.data_id = data
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
def pk_create(pk, klass, data):
instance = klass()
instance.data = data
- instance.save()
+ models.Model.save(instance, raw=True)
return instance
# A set of functions that can be used to compare
@@ -111,6 +114,9 @@ test_data = [
(data_obj, 13, CharData, "null"),
(data_obj, 14, CharData, "NULL"),
(data_obj, 15, CharData, None),
+ # (We use something that will fit into a latin1 database encoding here,
+ # because that is still the default used on many system setups.)
+ (data_obj, 16, CharData, u'\xa5'),
(data_obj, 20, DateData, datetime.date(2006,6,16)),
(data_obj, 21, DateData, None),
(data_obj, 30, DateTimeData, datetime.datetime(2006,6,16,10,42,37)),
@@ -230,8 +236,8 @@ The end."""),
(pk_obj, 682, IntegerPKData, 0),
# (XX, ImagePKData
(pk_obj, 690, IPAddressPKData, "127.0.0.1"),
- (pk_obj, 700, NullBooleanPKData, True),
- (pk_obj, 701, NullBooleanPKData, False),
+ # (pk_obj, 700, NullBooleanPKData, True),
+ # (pk_obj, 701, NullBooleanPKData, False),
(pk_obj, 710, PhonePKData, "212-634-5789"),
(pk_obj, 720, PositiveIntegerPKData, 123456789),
(pk_obj, 730, PositiveSmallIntegerPKData, 12),
@@ -246,6 +252,9 @@ The end."""),
# (pk_obj, 770, TimePKData, datetime.time(10,42,37)),
(pk_obj, 780, USStatePKData, "MA"),
# (pk_obj, 790, XMLPKData, "<foo></foo>"),
+
+ (data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006,6,16,10,42,37)),
+ (data_obj, 810, ModifyingSaveData, 42),
]
# Because Oracle treats the empty string as NULL, Oracle is expected to fail
@@ -264,7 +273,7 @@ class SerializerTests(unittest.TestCase):
def serializerTest(format, self):
# Clear the database first
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
# Create all the objects defined in the test data
objects = []
@@ -282,7 +291,7 @@ def serializerTest(format, self):
serialized_data = serializers.serialize(format, objects, indent=2)
# Flush the database and recreate from the serialized data
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
transaction.enter_transaction_management()
transaction.managed(True)
for obj in serializers.deserialize(format, serialized_data):
@@ -297,10 +306,10 @@ def serializerTest(format, self):
def fieldsTest(format, self):
# Clear the database first
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
- obj.save()
+ obj.save(raw=True)
# Serialize then deserialize the test database
serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3'))
@@ -313,10 +322,10 @@ def fieldsTest(format, self):
def streamTest(format, self):
# Clear the database first
- management.flush(verbosity=0, interactive=False)
+ management.call_command('flush', verbosity=0, interactive=False)
obj = ComplexModel(field1='first',field2='second',field3='third')
- obj.save()
+ obj.save(raw=True)
# Serialize the test database to a stream
stream = StringIO()
diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py
index 441bb3f8a3..12ebd0cf07 100644
--- a/tests/regressiontests/string_lookup/models.py
+++ b/tests/regressiontests/string_lookup/models.py
@@ -1,46 +1,55 @@
+# -*- coding: utf-8 -*-
from django.db import models
class Foo(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
+ friend = models.CharField(max_length=50, blank=True)
- def __str__(self):
+ def __unicode__(self):
return "Foo %s" % self.name
class Bar(models.Model):
- name = models.CharField(maxlength=50)
+ name = models.CharField(max_length=50)
normal = models.ForeignKey(Foo, related_name='normal_foo')
fwd = models.ForeignKey("Whiz")
back = models.ForeignKey("Foo")
- def __str__(self):
+ def __unicode__(self):
return "Bar %s" % self.place.name
class Whiz(models.Model):
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
- def __str__(self):
+ def __unicode__(self):
return "Whiz %s" % self.name
class Child(models.Model):
parent = models.OneToOneField('Base')
- name = models.CharField(maxlength = 50)
+ name = models.CharField(max_length = 50)
- def __str__(self):
+ def __unicode__(self):
return "Child %s" % self.name
-
+
class Base(models.Model):
+ name = models.CharField(max_length = 50)
+
+ def __unicode__(self):
+ return "Base %s" % self.name
+
+class Article(models.Model):
name = models.CharField(maxlength = 50)
+ text = models.TextField()
def __str__(self):
- return "Base %s" % self.name
+ return "Article %s" % self.name
-__test__ = {'API_TESTS':"""
-# Regression test for #1661 and #1662: Check that string form referencing of models works,
-# both as pre and post reference, on all RelatedField types.
+__test__ = {'API_TESTS': ur"""
+# Regression test for #1661 and #1662: Check that string form referencing of
+# models works, both as pre and post reference, on all RelatedField types.
>>> f1 = Foo(name="Foo1")
>>> f1.save()
->>> f2 = Foo(name="Foo1")
+>>> f2 = Foo(name="Foo2")
>>> f2.save()
>>> w1 = Whiz(name="Whiz1")
@@ -56,7 +65,7 @@ __test__ = {'API_TESTS':"""
<Whiz: Whiz Whiz1>
>>> b1.back
-<Foo: Foo Foo1>
+<Foo: Foo Foo2>
>>> base1 = Base(name="Base1")
>>> base1.save()
@@ -66,4 +75,27 @@ __test__ = {'API_TESTS':"""
>>> child1.parent
<Base: Base Base1>
+
+# Regression tests for #3937: make sure we can use unicode characters in
+# queries.
+# BUG: These tests fail on MySQL, but it's a problem with the test setup. A
+# properly configured UTF-8 database can handle this.
+
+>>> fx = Foo(name='Bjorn', friend=u'François')
+>>> fx.save()
+>>> Foo.objects.get(friend__contains=u'\xe7')
+<Foo: Foo Bjorn>
+
+# We can also do the above query using UTF-8 strings.
+>>> Foo.objects.get(friend__contains='\xc3\xa7')
+<Foo: Foo Bjorn>
+
+# Regression tests for #5087: make sure we can perform queries on TextFields.
+>>> a = Article(name='Test', text='The quick brown fox jumps over the lazy dog.')
+>>> a.save()
+>>> Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.')
+<Article: Article Test>
+
+>>> Article.objects.get(text__contains='quick brown fox')
+<Article: Article Test>
"""}
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 8801100bcc..0f7ac2f352 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -6,12 +6,22 @@ if __name__ == '__main__':
# before importing 'template'.
settings.configure()
+import os
+import unittest
+from datetime import datetime, timedelta
+
from django import template
from django.template import loader
-from django.utils.translation import activate, deactivate, install
+from django.template.loaders import app_directories, filesystem
+from django.utils.translation import activate, deactivate, install, ugettext as _
from django.utils.tzinfo import LocalTimezone
-from datetime import datetime, timedelta
-import unittest
+
+from unicode import unicode_tests
+
+# Some other tests we would like to run
+__test__ = {
+ 'unicode': unicode_tests,
+}
#################################
# Custom template tag for tests #
@@ -63,12 +73,52 @@ class OtherClass:
def method(self):
return "OtherClass.method"
-class UnicodeInStrClass:
- "Class whose __str__ returns a Unicode object."
+class UTF8Class:
+ "Class whose __str__ returns non-ASCII data"
def __str__(self):
- return u'ŠĐĆŽćžšđ'
+ return u'ŠĐĆŽćžšđ'.encode('utf-8')
class Templates(unittest.TestCase):
+ def test_loaders_security(self):
+ def test_template_sources(path, template_dirs, expected_sources):
+ # Fix expected sources so they are normcased and abspathed
+ expected_sources = [os.path.normcase(os.path.abspath(s)) for s in expected_sources]
+ # Test app_directories loader
+ sources = app_directories.get_template_sources(path, template_dirs)
+ self.assertEqual(list(sources), expected_sources)
+ # Test filesystem loader
+ sources = filesystem.get_template_sources(path, template_dirs)
+ self.assertEqual(list(sources), expected_sources)
+
+ template_dirs = ['/dir1', '/dir2']
+ test_template_sources('index.html', template_dirs,
+ ['/dir1/index.html', '/dir2/index.html'])
+ test_template_sources('/etc/passwd', template_dirs,
+ [])
+ test_template_sources('etc/passwd', template_dirs,
+ ['/dir1/etc/passwd', '/dir2/etc/passwd'])
+ test_template_sources('../etc/passwd', template_dirs,
+ [])
+ test_template_sources('../../../etc/passwd', template_dirs,
+ [])
+ test_template_sources('/dir1/index.html', template_dirs,
+ ['/dir1/index.html'])
+ test_template_sources('../dir2/index.html', template_dirs,
+ ['/dir2/index.html'])
+ test_template_sources('/dir1blah', template_dirs,
+ [])
+ test_template_sources('../dir1blah', template_dirs,
+ [])
+
+ # Case insensitive tests (for win32). Not run unless we're on
+ # a case insensitive operating system.
+ if os.path.normcase('/TEST') == os.path.normpath('/test'):
+ template_dirs = ['/dir1', '/DIR2']
+ test_template_sources('index.html', template_dirs,
+ ['/dir1/index.html', '/dir2/index.html'])
+ test_template_sources('/DIR1/index.HTML', template_dirs,
+ ['/dir1/index.html'])
+
def test_templates(self):
# NOW and NOW_tz are used by timesince tag tests.
NOW = datetime.now()
@@ -215,9 +265,9 @@ class Templates(unittest.TestCase):
# Empty strings can be passed as arguments to filters
'filter-syntax17': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'),
- # If a variable has a __str__() that returns a Unicode object, the
- # value will be converted to a bytestring.
- 'filter-syntax18': (r'{{ var }}', {'var': UnicodeInStrClass()}, '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91'),
+ # Make sure that any unicode strings are converted to bytestrings
+ # in the final output.
+ 'filter-syntax18': (r'{{ var }}', {'var': UTF8Class()}, u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'),
# Numbers as filter arguments should work
'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."),
@@ -596,7 +646,7 @@ class Templates(unittest.TestCase):
# translation of a constant string
'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'),
- ### HANDLING OF TEMPLATE_TAG_IF_INVALID ###################################
+ ### HANDLING OF TEMPLATE_STRING_IF_INVALID ###################################
'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo','INVALID')),
'invalidstr02': ('{{ var|default_if_none:"Foo" }}', {}, ('','INVALID')),
@@ -729,6 +779,8 @@ class Templates(unittest.TestCase):
'url02' : ('{% url regressiontests.templates.views.client_action client.id, action="update" %}', {'client': {'id': 1}}, '/url_tag/client/1/update/'),
'url03' : ('{% url regressiontests.templates.views.index %}', {}, '/url_tag/'),
'url04' : ('{% url named.client client.id %}', {'client': {'id': 1}}, '/url_tag/named-client/1/'),
+ 'url05' : (u'{% url метка_оператора v %}', {'v': u'Ω'},
+ '/url_tag/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'),
# Failures
'url-fail01' : ('{% url %}', {}, template.TemplateSyntaxError),
diff --git a/tests/regressiontests/templates/unicode.py b/tests/regressiontests/templates/unicode.py
new file mode 100644
index 0000000000..efda11c2da
--- /dev/null
+++ b/tests/regressiontests/templates/unicode.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+unicode_tests = ur"""
+Templates can be created from unicode strings.
+>>> from django.template import *
+>>> t1 = Template(u'ŠĐĆŽćžšđ {{ var }}')
+
+Templates can also be created from bytestrings. These are assumed by encoded
+using UTF-8.
+
+>>> s = '\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91 {{ var }}'
+>>> t2 = Template(s)
+>>> s = '\x80\xc5\xc0'
+>>> Template(s)
+Traceback (most recent call last):
+ ...
+TemplateEncodingError: Templates can only be constructed from unicode or UTF-8 strings.
+
+Contexts can be constructed from unicode or UTF-8 bytestrings.
+
+>>> c1 = Context({'var': 'foo'})
+>>> c2 = Context({u'var': 'foo'})
+>>> c3 = Context({'var': u'Đđ'})
+>>> c4 = Context({u'var': '\xc4\x90\xc4\x91'})
+
+Since both templates and all four contexts represent the same thing, they all
+render the same (and are returned as unicode objects).
+
+>>> t1.render(c3) == t2.render(c3)
+True
+>>> type(t1.render(c3))
+<type 'unicode'>
+"""
diff --git a/tests/regressiontests/templates/urls.py b/tests/regressiontests/templates/urls.py
index 5fbade5c58..d79f38e0a7 100644
--- a/tests/regressiontests/templates/urls.py
+++ b/tests/regressiontests/templates/urls.py
@@ -1,3 +1,4 @@
+# coding: utf-8
from django.conf.urls.defaults import *
from regressiontests.templates import views
@@ -8,4 +9,7 @@ urlpatterns = patterns('',
(r'^client/(\d+)/$', views.client),
(r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action),
url(r'^named-client/(\d+)/$', views.client, name="named.client"),
+
+ # Unicode strings are permitted everywhere.
+ url(ur'^Юникод/(\w+)/$', views.client, name=u"метка_оператора"),
)
diff --git a/tests/regressiontests/test_client_regress/models.py b/tests/regressiontests/test_client_regress/models.py
index 40d022a47a..e17770cd03 100644
--- a/tests/regressiontests/test_client_regress/models.py
+++ b/tests/regressiontests/test_client_regress/models.py
@@ -4,7 +4,49 @@ Regression tests for the Test Client, especially the customized assertions.
"""
from django.test import Client, TestCase
from django.core import mail
+import os
+class AssertContainsTests(TestCase):
+ def test_contains(self):
+ "Responses can be inspected for content, including counting repeated substrings"
+ response = self.client.get('/test_client_regress/no_template_view/')
+
+ self.assertContains(response, 'never', 0)
+ self.assertContains(response, 'once')
+ self.assertContains(response, 'once', 1)
+ self.assertContains(response, 'twice')
+ self.assertContains(response, 'twice', 2)
+
+ try:
+ self.assertContains(response, 'never', 1)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 0 instances of 'never' in response (expected 1)")
+
+ try:
+ self.assertContains(response, 'once', 0)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 0)")
+
+ try:
+ self.assertContains(response, 'once', 2)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 1 instances of 'once' in response (expected 2)")
+
+ try:
+ self.assertContains(response, 'twice', 1)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 2 instances of 'twice' in response (expected 1)")
+
+ try:
+ self.assertContains(response, 'thrice')
+ except AssertionError, e:
+ self.assertEquals(str(e), "Couldn't find 'thrice' in response")
+
+ try:
+ self.assertContains(response, 'thrice', 3)
+ except AssertionError, e:
+ self.assertEquals(str(e), "Found 0 instances of 'thrice' in response (expected 3)")
+
class AssertTemplateUsedTests(TestCase):
fixtures = ['testdata.json']
@@ -59,7 +101,7 @@ class AssertTemplateUsedTests(TestCase):
try:
self.assertTemplateUsed(response, "Valid POST Template")
except AssertionError, e:
- self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']")
+ self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: form_view.html, base.html")
class AssertRedirectsTests(TestCase):
def test_redirect_page(self):
@@ -69,7 +111,7 @@ class AssertRedirectsTests(TestCase):
try:
self.assertRedirects(response, '/test_client/get_view/')
except AssertionError, e:
- self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
+ self.assertEquals(str(e), "Response didn't redirect as expected: Response code was 301 (expected 302)")
def test_incorrect_target(self):
"An assertion is raised if the response redirects to another target"
@@ -78,10 +120,10 @@ class AssertRedirectsTests(TestCase):
# Should redirect to get_view
self.assertRedirects(response, '/test_client/some_view/')
except AssertionError, e:
- self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)")
+ self.assertEquals(str(e), "Response didn't redirect as expected: Response code was 301 (expected 302)")
def test_target_page(self):
- "An assertion is raised if the reponse redirect target cannot be retrieved as expected"
+ "An assertion is raised if the response redirect target cannot be retrieved as expected"
response = self.client.get('/test_client/double_redirect_view/')
try:
# The redirect target responds with a 301 code, not 200
@@ -162,3 +204,12 @@ class AssertFormErrorTests(TestCase):
except AssertionError, e:
self.assertEqual(str(e), "The field 'email' on form 'form' in context 0 does not contain the error 'Some error.' (actual errors: [u'Enter a valid e-mail address.'])")
+class AssertFileUploadTests(TestCase):
+ def test_simple_upload(self):
+ fd = open(os.path.join(os.path.dirname(__file__), "views.py"))
+ post_data = {
+ 'name': 'Ringo',
+ 'file_field': fd,
+ }
+ response = self.client.post('/test_client_regress/file_upload/', post_data)
+ self.assertEqual(response.status_code, 200)
diff --git a/tests/regressiontests/test_client_regress/urls.py b/tests/regressiontests/test_client_regress/urls.py
index e9cd0aea15..160f9f992d 100644
--- a/tests/regressiontests/test_client_regress/urls.py
+++ b/tests/regressiontests/test_client_regress/urls.py
@@ -1,7 +1,7 @@
from django.conf.urls.defaults import *
-from django.views.generic.simple import redirect_to
import views
urlpatterns = patterns('',
(r'^no_template_view/$', views.no_template_view),
+ (r'^file_upload/$', views.file_upload_view),
)
diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py
index d8dd2b349c..d3fbcf8448 100644
--- a/tests/regressiontests/test_client_regress/views.py
+++ b/tests/regressiontests/test_client_regress/views.py
@@ -1,8 +1,20 @@
from django.core.mail import EmailMessage, SMTPConnection
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseServerError
from django.shortcuts import render_to_response
def no_template_view(request):
"A simple view that expects a GET request, and returns a rendered template"
- return HttpResponse("No template used")
+ return HttpResponse("No template used. Sample content: twice once twice. Content ends.")
+
+def file_upload_view(request):
+ """
+ Check that a file upload can be updated into the POST dictionary without
+ going pear-shaped.
+ """
+ form_data = request.POST.copy()
+ form_data.update(request.FILES)
+ if isinstance(form_data['file_field'], dict) and isinstance(form_data['name'], unicode):
+ return HttpResponse('')
+ else:
+ return HttpResponseServerError()
diff --git a/tests/regressiontests/text/tests.py b/tests/regressiontests/text/tests.py
index f758ecaf90..0fd22b58b0 100644
--- a/tests/regressiontests/text/tests.py
+++ b/tests/regressiontests/text/tests.py
@@ -1,17 +1,38 @@
-"""
-# Tests for stuff in django.utils.text.
+# coding: utf-8
+r"""
+# Tests for stuff in django.utils.text and other text munging util functions.
>>> from django.utils.text import *
### smart_split ###########################################################
>>> list(smart_split(r'''This is "a person" test.'''))
-['This', 'is', '"a person"', 'test.']
+[u'This', u'is', u'"a person"', u'test.']
>>> print list(smart_split(r'''This is "a person's" test.'''))[2]
"a person's"
->>> print list(smart_split(r'''This is "a person\\"s" test.'''))[2]
+>>> print list(smart_split(r'''This is "a person\"s" test.'''))[2]
"a person"s"
>>> list(smart_split('''"a 'one'''))
-['"a', "'one"]
+[u'"a', u"'one"]
>>> print list(smart_split(r'''all friends' tests'''))[1]
friends'
+
+### urlquote #############################################################
+>>> from django.utils.http import urlquote, urlquote_plus
+>>> urlquote(u'Paris & Orl\xe9ans')
+u'Paris%20%26%20Orl%C3%A9ans'
+>>> urlquote_plus(u'Paris & Orl\xe9ans')
+u'Paris+%26+Orl%C3%A9ans'
+
+### iri_to_uri ###########################################################
+>>> from django.utils.encoding import iri_to_uri
+>>> iri_to_uri(u'red%09ros\xe9#red')
+'red%09ros%C3%A9#red'
+>>> iri_to_uri(u'/blog/for/J\xfcrgen M\xfcnster/')
+'/blog/for/J%C3%BCrgen%20M%C3%BCnster/'
+>>> iri_to_uri(u'locations/%s' % urlquote_plus(u'Paris & Orl\xe9ans'))
+'locations/Paris+%26+Orl%C3%A9ans'
+
+iri_to_uri() is idempotent:
+>>> iri_to_uri(iri_to_uri(u'red%09ros\xe9#red'))
+'red%09ros%C3%A9#red'
"""
diff --git a/tests/regressiontests/utils/__init__.py b/tests/regressiontests/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/regressiontests/utils/__init__.py
diff --git a/tests/regressiontests/utils/models.py b/tests/regressiontests/utils/models.py
new file mode 100644
index 0000000000..97a72bab14
--- /dev/null
+++ b/tests/regressiontests/utils/models.py
@@ -0,0 +1 @@
+# Test runner needs a models.py file.
diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py
new file mode 100644
index 0000000000..258aea697e
--- /dev/null
+++ b/tests/regressiontests/utils/tests.py
@@ -0,0 +1,115 @@
+"""
+Tests for django.utils.
+"""
+
+from unittest import TestCase
+
+from django.utils import html
+
+class TestUtilsHtml(TestCase):
+
+ def check_output(self, function, value, output=None):
+ """
+ Check that function(value) equals output. If output is None,
+ check that function(value) equals value.
+ """
+ if output is None:
+ output = value
+ self.assertEqual(function(value), output)
+
+ def test_escape(self):
+ f = html.escape
+ items = (
+ ('&','&amp;'),
+ ('<', '&lt;'),
+ ('>', '&gt;'),
+ ('"', '&quot;'),
+ ("'", '&#39;'),
+ )
+ # Substitution patterns for testing the above items.
+ patterns = ("%s", "asdf%sfdsa", "%s1", "1%sb")
+ for value, output in items:
+ for pattern in patterns:
+ self.check_output(f, pattern % value, pattern % output)
+ # Check repeated values.
+ self.check_output(f, value * 2, output * 2)
+ # Verify it doesn't double replace &.
+ self.check_output(f, '<&', '&lt;&amp;')
+
+ def test_linebreaks(self):
+ f = html.linebreaks
+ items = (
+ ("para1\n\npara2\r\rpara3", "<p>para1</p>\n\n<p>para2</p>\n\n<p>para3</p>"),
+ ("para1\nsub1\rsub2\n\npara2", "<p>para1<br />sub1<br />sub2</p>\n\n<p>para2</p>"),
+ ("para1\r\n\r\npara2\rsub1\r\rpara4", "<p>para1</p>\n\n<p>para2<br />sub1</p>\n\n<p>para4</p>"),
+ ("para1\tmore\n\npara2", "<p>para1\tmore</p>\n\n<p>para2</p>"),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_tags(self):
+ f = html.strip_tags
+ items = (
+ ('<adf>a', 'a'),
+ ('</adf>a', 'a'),
+ ('<asdf><asdf>e', 'e'),
+ ('<f', '<f'),
+ ('</fe', '</fe'),
+ ('<x>b<y>', 'b'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_spaces_between_tags(self):
+ f = html.strip_spaces_between_tags
+ # Strings that should come out untouched.
+ items = (' <adf>', '<adf> ', ' </adf> ', ' <f> x</f>')
+ for value in items:
+ self.check_output(f, value)
+ # Strings that have spaces to strip.
+ items = (
+ ('<d> </d>', '<d></d>'),
+ ('<p>hello </p>\n<p> world</p>', '<p>hello </p><p> world</p>'),
+ ('\n<p>\t</p>\n<p> </p>\n', '\n<p></p><p></p>\n'),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
+
+ def test_strip_entities(self):
+ f = html.strip_entities
+ # Strings that should come out untouched.
+ values = ("&", "&a", "&a", "a&#a")
+ for value in values:
+ self.check_output(f, value)
+ # Valid entities that should be stripped from the patterns.
+ entities = ("&#1;", "&#12;", "&a;", "&fdasdfasdfasdf;")
+ patterns = (
+ ("asdf %(entity)s ", "asdf "),
+ ("%(entity)s%(entity)s", ""),
+ ("&%(entity)s%(entity)s", "&"),
+ ("%(entity)s3", "3"),
+ )
+ for entity in entities:
+ for in_pattern, output in patterns:
+ self.check_output(f, in_pattern % {'entity': entity}, output)
+
+ def test_fix_ampersands(self):
+ f = html.fix_ampersands
+ # Strings without ampersands or with ampersands already encoded.
+ values = ("a&#1;", "b", "&a;", "&amp; &x; ", "asdf")
+ patterns = (
+ ("%s", "%s"),
+ ("&%s", "&amp;%s"),
+ ("&%s&", "&amp;%s&amp;"),
+ )
+ for value in values:
+ for in_pattern, out_pattern in patterns:
+ self.check_output(f, in_pattern % value, out_pattern % value)
+ # Strings with ampersands that need encoding.
+ items = (
+ ("&#;", "&amp;#;"),
+ ("&#875 ;", "&amp;#875 ;"),
+ ("&#4abc;", "&amp;#4abc;"),
+ )
+ for value, output in items:
+ self.check_output(f, value, output)
diff --git a/tests/runtests.py b/tests/runtests.py
index 7d1ee1e29c..85aea50180 100755
--- a/tests/runtests.py
+++ b/tests/runtests.py
@@ -51,7 +51,7 @@ class InvalidModelTestCase(unittest.TestCase):
self.model_label = model_label
def runTest(self):
- from django.core import management
+ from django.core.management.validation import get_validation_errors
from django.db.models.loading import load_app
from cStringIO import StringIO
@@ -60,8 +60,14 @@ class InvalidModelTestCase(unittest.TestCase):
except Exception, e:
self.fail('Unable to load invalid model module')
+ # Make sure sys.stdout is not a tty so that we get errors without
+ # coloring attached (makes matching the results easier). We restore
+ # sys.stderr afterwards.
+ orig_stdout = sys.stdout
s = StringIO()
- count = management.get_validation_errors(s, module)
+ sys.stdout = s
+ count = get_validation_errors(s, module)
+ sys.stdout = orig_stdout
s.seek(0)
error_log = s.read()
actual = error_log.split('\n')
@@ -73,7 +79,7 @@ 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, tests_to_run):
+def django_tests(verbosity, interactive, test_labels):
from django.conf import settings
old_installed_apps = settings.INSTALLED_APPS
@@ -94,6 +100,8 @@ def django_tests(verbosity, tests_to_run):
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.middleware.common.CommonMiddleware',
)
+ if not hasattr(settings, 'SITE_ID'):
+ settings.SITE_ID = 1
# Load all the ALWAYS_INSTALLED_APPS.
# (This import statement is intentionally delayed until after we
@@ -109,14 +117,13 @@ def django_tests(verbosity, tests_to_run):
# if the model was named on the command line, or
# no models were named (i.e., run all), import
# this model and add it to the list to test.
- if not tests_to_run or model_name in tests_to_run:
+ if not test_labels or model_name in set([label.split('.')[0] for label in test_labels]):
if verbosity >= 1:
print "Importing model %s" % model_name
mod = load_app(model_label)
if mod:
if model_label not in settings.INSTALLED_APPS:
settings.INSTALLED_APPS.append(model_label)
- test_models.append(mod)
except Exception, e:
sys.stderr.write("Error while importing %s:" % model_name + ''.join(traceback.format_exception(*sys.exc_info())[1:]))
continue
@@ -125,12 +132,12 @@ def django_tests(verbosity, tests_to_run):
extra_tests = []
for model_dir, model_name in get_invalid_models():
model_label = '.'.join([model_dir, model_name])
- if not tests_to_run or model_name in tests_to_run:
+ if not test_labels or model_name in test_labels:
extra_tests.append(InvalidModelTestCase(model_label))
# Run the test suite, including the extra validation tests.
from django.test.simple import run_tests
- failures = run_tests(test_models, verbosity, extra_tests=extra_tests)
+ failures = run_tests(test_labels, verbosity=verbosity, interactive=interactive, extra_tests=extra_tests)
if failures:
sys.exit(failures)
@@ -149,6 +156,8 @@ if __name__ == "__main__":
parser.add_option('-v','--verbosity', action='store', dest='verbosity', default='0',
type='choice', choices=['0', '1', '2'],
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('--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()
@@ -157,4 +166,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), args)
+ django_tests(int(options.verbosity), options.interactive, args)