diff options
| author | Justin Bronn <jbronn@gmail.com> | 2007-08-26 01:10:53 +0000 |
|---|---|---|
| committer | Justin Bronn <jbronn@gmail.com> | 2007-08-26 01:10:53 +0000 |
| commit | 2052b508eb92c62fc0678efd4936c5ec1e0e735b (patch) | |
| tree | e510109b74b28c8ccef5f6955727cb9dce3da655 /tests | |
| parent | a7297a255f4bb86f608ea251e00253d18c31d9d4 (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')
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 & Jill & Jeroboam' +>>> fix_ampersands(u'Jack & Jill & Jeroboam') +u'Jack & Jill & 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') -'<some html & special characters > here' +>>> escape(u'<some html & special characters > here') +u'<some html & special characters > here' >>> escape(u'<some html & special characters > here ĐÅ€£') u'<some html & special characters > 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'<some html & special characters > 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'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 "quoted" & 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 = ( + ('&','&'), + ('<', '<'), + ('>', '>'), + ('"', '"'), + ("'", '''), + ) + # 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, '<&', '<&') + + 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 = ("", "", "&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", "b", "&a;", "& &x; ", "asdf") + patterns = ( + ("%s", "%s"), + ("&%s", "&%s"), + ("&%s&", "&%s&"), + ) + 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 = ( + ("&#;", "&#;"), + ("ͫ ;", "&#875 ;"), + ("abc;", "&#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) |
