diff options
| author | Andrew Godwin <andrew@aeracode.org> | 2012-09-05 09:39:03 -0400 |
|---|---|---|
| committer | Andrew Godwin <andrew@aeracode.org> | 2012-09-05 09:39:03 -0400 |
| commit | b546e7eb633022ee1962570387f22fb2bcea46ed (patch) | |
| tree | f87f4a2d68fb66afae39148fa35489930710b623 /tests | |
| parent | cd583d6dbd222ae61331a6965b0e1fc86c974c50 (diff) | |
| parent | cff911f4ba3b3e6393c58da5131ce8b188a68f0c (diff) | |
Merge branch 'master' into schema-alteration
Diffstat (limited to 'tests')
193 files changed, 2381 insertions, 1142 deletions
diff --git a/tests/modeltests/aggregation/models.py b/tests/modeltests/aggregation/models.py index ccc12898b7..b4f797ee03 100644 --- a/tests/modeltests/aggregation/models.py +++ b/tests/modeltests/aggregation/models.py @@ -1,22 +1,26 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() friends = models.ManyToManyField('self', blank=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Publisher(models.Model): name = models.CharField(max_length=255) num_awards = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): isbn = models.CharField(max_length=9) name = models.CharField(max_length=255) @@ -28,15 +32,16 @@ class Book(models.Model): publisher = models.ForeignKey(Publisher) pubdate = models.DateField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Store(models.Model): name = models.CharField(max_length=255) books = models.ManyToManyField(Book) original_opening = models.DateTimeField() friday_night_closing = models.TimeField() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/basic/models.py b/tests/modeltests/basic/models.py index 06aa9cf3c3..660beddf49 100644 --- a/tests/modeltests/basic/models.py +++ b/tests/modeltests/basic/models.py @@ -5,8 +5,10 @@ This is a basic model with only two non-primary-key fields. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() @@ -14,5 +16,5 @@ class Article(models.Model): class Meta: ordering = ('pub_date','headline') - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/choices/models.py b/tests/modeltests/choices/models.py index ee01911573..2fa33a9680 100644 --- a/tests/modeltests/choices/models.py +++ b/tests/modeltests/choices/models.py @@ -10,6 +10,7 @@ field. This method returns the "human-readable" value of the field. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible GENDER_CHOICES = ( @@ -17,9 +18,10 @@ GENDER_CHOICES = ( ('F', 'Female'), ) +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/custom_columns/models.py b/tests/modeltests/custom_columns/models.py index 39f1274a8f..16f0563d6b 100644 --- a/tests/modeltests/custom_columns/models.py +++ b/tests/modeltests/custom_columns/models.py @@ -18,24 +18,27 @@ from the default generated name, use the ``db_table`` parameter on the from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): first_name = models.CharField(max_length=30, db_column='firstname') last_name = models.CharField(max_length=30, db_column='last') - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) class Meta: db_table = 'my_author_table' ordering = ('last_name','first_name') +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) authors = models.ManyToManyField(Author, db_table='my_m2m_table') - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index a9845ad414..de7c1772ed 100644 --- a/tests/modeltests/custom_managers/models.py +++ b/tests/modeltests/custom_managers/models.py @@ -12,6 +12,7 @@ returns. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # An example of a custom manager called "objects". @@ -19,13 +20,14 @@ class PersonManager(models.Manager): def get_fun_people(self): return self.filter(fun=True) +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) fun = models.BooleanField() objects = PersonManager() - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) # An example of a custom manager that sets get_query_set(). @@ -34,6 +36,7 @@ class PublishedBookManager(models.Manager): def get_query_set(self): return super(PublishedBookManager, self).get_query_set().filter(is_published=True) +@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=50) author = models.CharField(max_length=30) @@ -41,7 +44,7 @@ class Book(models.Model): published_objects = PublishedBookManager() authors = models.ManyToManyField(Person, related_name='books') - def __unicode__(self): + def __str__(self): return self.title # An example of providing multiple custom managers. @@ -50,6 +53,7 @@ class FastCarManager(models.Manager): def get_query_set(self): return super(FastCarManager, self).get_query_set().filter(top_speed__gt=150) +@python_2_unicode_compatible class Car(models.Model): name = models.CharField(max_length=10) mileage = models.IntegerField() @@ -57,5 +61,5 @@ class Car(models.Model): cars = models.Manager() fast_cars = FastCarManager() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/custom_methods/models.py b/tests/modeltests/custom_methods/models.py index 4e3da58851..cef3fd722b 100644 --- a/tests/modeltests/custom_methods/models.py +++ b/tests/modeltests/custom_methods/models.py @@ -7,13 +7,15 @@ Any method you add to a model will be available to instances. import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() - def __unicode__(self): + def __str__(self): return self.headline def was_published_today(self): diff --git a/tests/modeltests/custom_pk/fields.py b/tests/modeltests/custom_pk/fields.py index 68fb9dcd16..92096a0ff7 100644 --- a/tests/modeltests/custom_pk/fields.py +++ b/tests/modeltests/custom_pk/fields.py @@ -3,8 +3,10 @@ import string from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class MyWrapper(object): def __init__(self, value): self.value = value @@ -12,7 +14,7 @@ class MyWrapper(object): def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.value) - def __unicode__(self): + def __str__(self): return self.value def __eq__(self, other): @@ -20,8 +22,7 @@ class MyWrapper(object): return self.value == other.value return self.value == other -class MyAutoField(models.CharField): - __metaclass__ = models.SubfieldBase +class MyAutoField(six.with_metaclass(models.SubfieldBase, models.CharField)): def __init__(self, *args, **kwargs): kwargs['max_length'] = 10 @@ -30,7 +31,7 @@ class MyAutoField(models.CharField): def pre_save(self, instance, add): value = getattr(instance, self.attname, None) if not value: - value = MyWrapper(''.join(random.sample(string.lowercase, 10))) + value = MyWrapper(''.join(random.sample(string.ascii_lowercase, 10))) setattr(instance, self.attname, value) return value diff --git a/tests/modeltests/custom_pk/models.py b/tests/modeltests/custom_pk/models.py index 8199b05a1a..5ef9b69f0c 100644 --- a/tests/modeltests/custom_pk/models.py +++ b/tests/modeltests/custom_pk/models.py @@ -11,8 +11,10 @@ from __future__ import absolute_import, unicode_literals from django.db import models from .fields import MyAutoField +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Employee(models.Model): employee_code = models.IntegerField(primary_key=True, db_column = 'code') first_name = models.CharField(max_length=20) @@ -20,22 +22,24 @@ class Employee(models.Model): class Meta: ordering = ('last_name', 'first_name') - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Business(models.Model): name = models.CharField(max_length=20, primary_key=True) employees = models.ManyToManyField(Employee) class Meta: verbose_name_plural = 'businesses' - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Bar(models.Model): id = MyAutoField(primary_key=True, db_index=True) - def __unicode__(self): + def __str__(self): return repr(self.pk) diff --git a/tests/modeltests/defer/models.py b/tests/modeltests/defer/models.py index c64becf972..0688cbc984 100644 --- a/tests/modeltests/defer/models.py +++ b/tests/modeltests/defer/models.py @@ -3,18 +3,20 @@ Tests for defer() and only(). """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Secondary(models.Model): first = models.CharField(max_length=50) second = models.CharField(max_length=50) +@python_2_unicode_compatible class Primary(models.Model): name = models.CharField(max_length=50) value = models.CharField(max_length=50) related = models.ForeignKey(Secondary) - def __unicode__(self): + def __str__(self): return self.name class Child(Primary): diff --git a/tests/modeltests/delete/models.py b/tests/modeltests/delete/models.py index f8b78eb7b7..e0cec426ea 100644 --- a/tests/modeltests/delete/models.py +++ b/tests/modeltests/delete/models.py @@ -1,6 +1,10 @@ +from __future__ import unicode_literals + from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class R(models.Model): is_default = models.BooleanField(default=False) diff --git a/tests/modeltests/distinct_on_fields/models.py b/tests/modeltests/distinct_on_fields/models.py index 33665e9624..7982f435d0 100644 --- a/tests/modeltests/distinct_on_fields/models.py +++ b/tests/modeltests/distinct_on_fields/models.py @@ -1,7 +1,9 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, @@ -10,19 +12,21 @@ class Tag(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Celebrity(models.Model): name = models.CharField("Name", max_length=20) greatest_fan = models.ForeignKey("Fan", null=True, unique=True) - def __unicode__(self): + def __str__(self): return self.name class Fan(models.Model): fan_of = models.ForeignKey(Celebrity) +@python_2_unicode_compatible class Staff(models.Model): id = models.IntegerField(primary_key=True) name = models.CharField(max_length=50) @@ -30,12 +34,13 @@ class Staff(models.Model): tags = models.ManyToManyField(Tag, through='StaffTag') coworkers = models.ManyToManyField('self') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class StaffTag(models.Model): staff = models.ForeignKey(Staff) tag = models.ForeignKey(Tag) - def __unicode__(self): + def __str__(self): return "%s -> %s" % (self.tag, self.staff) diff --git a/tests/modeltests/expressions/models.py b/tests/modeltests/expressions/models.py index 018a0cf795..f592a0eb13 100644 --- a/tests/modeltests/expressions/models.py +++ b/tests/modeltests/expressions/models.py @@ -4,15 +4,18 @@ Tests for F() query expression syntax. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Employee(models.Model): firstname = models.CharField(max_length=50) lastname = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.firstname, self.lastname) +@python_2_unicode_compatible class Company(models.Model): name = models.CharField(max_length=100) num_employees = models.PositiveIntegerField() @@ -25,5 +28,5 @@ class Company(models.Model): related_name='company_point_of_contact_set', null=True) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/field_defaults/models.py b/tests/modeltests/field_defaults/models.py index 18840c8b4b..c99d4871cd 100644 --- a/tests/modeltests/field_defaults/models.py +++ b/tests/modeltests/field_defaults/models.py @@ -13,11 +13,13 @@ field. from datetime import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField(default=datetime.now) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/field_subclassing/fields.py b/tests/modeltests/field_subclassing/fields.py index 0d4ff98aa7..a3867e3671 100644 --- a/tests/modeltests/field_subclassing/fields.py +++ b/tests/modeltests/field_subclassing/fields.py @@ -5,8 +5,10 @@ import json from django.db import models from django.utils.encoding import force_text from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Small(object): """ A simple class to show that non-trivial Python objects can be used as @@ -15,19 +17,15 @@ class Small(object): def __init__(self, first, second): self.first, self.second = first, second - def __unicode__(self): - return '%s%s' % (force_text(self.first), force_text(self.second)) - def __str__(self): - return six.text_type(self).encode('utf-8') + return '%s%s' % (force_text(self.first), force_text(self.second)) -class SmallField(models.Field): +class SmallField(six.with_metaclass(models.SubfieldBase, models.Field)): """ Turns the "Small" class into a Django field. Because of the similarities with normal character fields and the fact that Small.__unicode__ does something sensible, we don't need to implement a lot here. """ - __metaclass__ = models.SubfieldBase def __init__(self, *args, **kwargs): kwargs['max_length'] = 2 @@ -57,8 +55,7 @@ class SmallerField(SmallField): pass -class JSONField(models.TextField): - __metaclass__ = models.SubfieldBase +class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)): description = ("JSONField automatically serializes and desializes values to " "and from JSON.") diff --git a/tests/modeltests/field_subclassing/models.py b/tests/modeltests/field_subclassing/models.py index 2df9664cdc..642573cc83 100644 --- a/tests/modeltests/field_subclassing/models.py +++ b/tests/modeltests/field_subclassing/models.py @@ -8,13 +8,15 @@ from django.db import models from django.utils.encoding import force_text from .fields import SmallField, SmallerField, JSONField +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class MyModel(models.Model): name = models.CharField(max_length=10) data = SmallField('small field') - def __unicode__(self): + def __str__(self): return force_text(self.name) class OtherModel(models.Model): diff --git a/tests/modeltests/files/tests.py b/tests/modeltests/files/tests.py index 565d4c8942..71ca103205 100644 --- a/tests/modeltests/files/tests.py +++ b/tests/modeltests/files/tests.py @@ -20,7 +20,7 @@ class FileStorageTests(TestCase): shutil.rmtree(temp_storage_location) def test_files(self): - temp_storage.save('tests/default.txt', ContentFile(b'default content')) + temp_storage.save('tests/default.txt', ContentFile('default content')) # Attempting to access a FileField from the class raises a descriptive # error self.assertRaises(AttributeError, lambda: Storage.normal) @@ -31,7 +31,7 @@ class FileStorageTests(TestCase): self.assertRaises(ValueError, lambda: obj1.normal.size) # Saving a file enables full functionality. - obj1.normal.save("django_test.txt", ContentFile(b"content")) + obj1.normal.save("django_test.txt", ContentFile("content")) self.assertEqual(obj1.normal.name, "tests/django_test.txt") self.assertEqual(obj1.normal.size, 7) self.assertEqual(obj1.normal.read(), b"content") @@ -59,7 +59,7 @@ class FileStorageTests(TestCase): # Save another file with the same name. obj2 = Storage() - obj2.normal.save("django_test.txt", ContentFile(b"more content")) + obj2.normal.save("django_test.txt", ContentFile("more content")) self.assertEqual(obj2.normal.name, "tests/django_test_1.txt") self.assertEqual(obj2.normal.size, 12) @@ -70,13 +70,13 @@ class FileStorageTests(TestCase): # Deleting an object does not delete the file it uses. obj2.delete() - obj2.normal.save("django_test.txt", ContentFile(b"more content")) + obj2.normal.save("django_test.txt", ContentFile("more content")) self.assertEqual(obj2.normal.name, "tests/django_test_2.txt") # Multiple files with the same name get _N appended to them. objs = [Storage() for i in range(3)] for o in objs: - o.normal.save("multiple_files.txt", ContentFile(b"Same Content")) + o.normal.save("multiple_files.txt", ContentFile("Same Content")) self.assertEqual( [o.normal.name for o in objs], ["tests/multiple_files.txt", "tests/multiple_files_1.txt", "tests/multiple_files_2.txt"] @@ -100,7 +100,7 @@ class FileStorageTests(TestCase): # Verify the fix for #5655, making sure the directory is only # determined once. obj4 = Storage() - obj4.random.save("random_file", ContentFile(b"random content")) + obj4.random.save("random_file", ContentFile("random content")) self.assertTrue(obj4.random.name.endswith("/random_file")) def test_max_length(self): @@ -135,6 +135,6 @@ class FileTests(unittest.TestCase): def test_file_mode(self): # Should not set mode to None if it is not present. # See #14681, stdlib gzip module crashes if mode is set to None - file = SimpleUploadedFile("mode_test.txt", "content") + file = SimpleUploadedFile("mode_test.txt", b"content") self.assertFalse(hasattr(file, 'mode')) g = gzip.GzipFile(fileobj=file) diff --git a/tests/modeltests/fixtures/models.py b/tests/modeltests/fixtures/models.py index 586f65e60f..8bd3501926 100644 --- a/tests/modeltests/fixtures/models.py +++ b/tests/modeltests/fixtures/models.py @@ -12,38 +12,43 @@ from django.contrib.auth.models import Permission from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): title = models.CharField(max_length=100) description = models.TextField() - def __unicode__(self): + def __str__(self): return self.title class Meta: ordering = ('title',) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline class Meta: ordering = ('-pub_date', 'headline') +@python_2_unicode_compatible class Blog(models.Model): name = models.CharField(max_length=100) featured = models.ForeignKey(Article, related_name='fixtures_featured_set') articles = models.ManyToManyField(Article, blank=True, related_name='fixtures_articles_set') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=100) tagged_type = models.ForeignKey(ContentType, related_name="fixtures_tag_set") @@ -51,7 +56,7 @@ class Tag(models.Model): tagged = generic.GenericForeignKey(ct_field='tagged_type', fk_field='tagged_id') - def __unicode__(self): + def __str__(self): return '<%s: %s> tagged "%s"' % (self.tagged.__class__.__name__, self.tagged, self.name) @@ -59,10 +64,11 @@ class PersonManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) +@python_2_unicode_compatible class Person(models.Model): objects = PersonManager() name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -79,19 +85,21 @@ class Spy(Person): objects = SpyManager() cover_blown = models.BooleanField(default=False) +@python_2_unicode_compatible class Visa(models.Model): person = models.ForeignKey(Person) permissions = models.ManyToManyField(Permission, blank=True) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.person.name, ', '.join(p.name for p in self.permissions.all())) +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Person) - def __unicode__(self): + def __str__(self): authors = ' and '.join(a.name for a in self.authors.all()) return '%s by %s' % (self.name, authors) if authors else self.name diff --git a/tests/modeltests/fixtures_model_package/models/__init__.py b/tests/modeltests/fixtures_model_package/models/__init__.py index d309165e9c..deeba48aa9 100644 --- a/tests/modeltests/fixtures_model_package/models/__init__.py +++ b/tests/modeltests/fixtures_model_package/models/__init__.py @@ -1,11 +1,13 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/generic_relations/models.py b/tests/modeltests/generic_relations/models.py index f2dcf7db24..2f025e660b 100644 --- a/tests/modeltests/generic_relations/models.py +++ b/tests/modeltests/generic_relations/models.py @@ -14,8 +14,10 @@ from __future__ import unicode_literals from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class TaggedItem(models.Model): """A tag on an item.""" tag = models.SlugField() @@ -27,12 +29,13 @@ class TaggedItem(models.Model): class Meta: ordering = ["tag", "content_type__name"] - def __unicode__(self): + def __str__(self): return self.tag class ValuableTaggedItem(TaggedItem): value = models.PositiveIntegerField() +@python_2_unicode_compatible class Comparison(models.Model): """ A model that tests having multiple GenericForeignKeys @@ -48,9 +51,10 @@ class Comparison(models.Model): first_obj = generic.GenericForeignKey(ct_field="content_type1", fk_field="object_id1") other_obj = generic.GenericForeignKey(ct_field="content_type2", fk_field="object_id2") - def __unicode__(self): + def __str__(self): return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj) +@python_2_unicode_compatible class Animal(models.Model): common_name = models.CharField(max_length=150) latin_name = models.CharField(max_length=150) @@ -60,25 +64,27 @@ class Animal(models.Model): object_id_field="object_id1", content_type_field="content_type1") - def __unicode__(self): + def __str__(self): return self.common_name +@python_2_unicode_compatible class Vegetable(models.Model): name = models.CharField(max_length=150) is_yucky = models.BooleanField(default=True) tags = generic.GenericRelation(TaggedItem) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Mineral(models.Model): name = models.CharField(max_length=150) hardness = models.PositiveSmallIntegerField() # note the lack of an explicit GenericRelation here... - def __unicode__(self): + def __str__(self): return self.name class GeckoManager(models.Manager): diff --git a/tests/modeltests/get_latest/models.py b/tests/modeltests/get_latest/models.py index d8a690f48c..fe594dd802 100644 --- a/tests/modeltests/get_latest/models.py +++ b/tests/modeltests/get_latest/models.py @@ -9,8 +9,10 @@ farthest into the future." """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() @@ -18,14 +20,15 @@ class Article(models.Model): class Meta: get_latest_by = 'pub_date' - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=30) birthday = models.DateField() # Note that this model doesn't have "get_latest_by" set. - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/get_object_or_404/models.py b/tests/modeltests/get_object_or_404/models.py index f0c73ed94a..bda060569e 100644 --- a/tests/modeltests/get_object_or_404/models.py +++ b/tests/modeltests/get_object_or_404/models.py @@ -11,23 +11,26 @@ performing a ``filter()`` lookup and raising a ``Http404`` exception if a """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name class ArticleManager(models.Manager): def get_query_set(self): return super(ArticleManager, self).get_query_set().filter(authors__name__icontains='sir') +@python_2_unicode_compatible class Article(models.Model): authors = models.ManyToManyField(Author) title = models.CharField(max_length=50) objects = models.Manager() by_a_sir = ArticleManager() - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/modeltests/get_or_create/models.py b/tests/modeltests/get_or_create/models.py index 78b92f09df..678f5a401c 100644 --- a/tests/modeltests/get_or_create/models.py +++ b/tests/modeltests/get_or_create/models.py @@ -9,14 +9,16 @@ parameters. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) birthday = models.DateField() - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) class ManualPrimaryKeyTest(models.Model): diff --git a/tests/modeltests/invalid_models/tests.py b/tests/modeltests/invalid_models/tests.py index 9d7adb045c..e1fc68743e 100644 --- a/tests/modeltests/invalid_models/tests.py +++ b/tests/modeltests/invalid_models/tests.py @@ -1,11 +1,11 @@ import copy import sys -from io import BytesIO from django.core.management.validation import get_validation_errors from django.db.models.loading import cache, load_app from django.utils import unittest +from django.utils.six import StringIO class InvalidModelTestCase(unittest.TestCase): @@ -16,7 +16,7 @@ class InvalidModelTestCase(unittest.TestCase): # coloring attached (makes matching the results easier). We restore # sys.stderr afterwards. self.old_stdout = sys.stdout - self.stdout = BytesIO() + self.stdout = StringIO() sys.stdout = self.stdout # This test adds dummy applications to the app cache. These diff --git a/tests/modeltests/lookup/models.py b/tests/modeltests/lookup/models.py index b685750347..f388ddf403 100644 --- a/tests/modeltests/lookup/models.py +++ b/tests/modeltests/lookup/models.py @@ -8,6 +8,7 @@ from __future__ import unicode_literals from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class Author(models.Model): @@ -15,6 +16,7 @@ class Author(models.Model): class Meta: ordering = ('name', ) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() @@ -22,7 +24,7 @@ class Article(models.Model): class Meta: ordering = ('-pub_date', 'headline') - def __unicode__(self): + def __str__(self): return self.headline class Tag(models.Model): @@ -31,24 +33,27 @@ class Tag(models.Model): class Meta: ordering = ('name', ) +@python_2_unicode_compatible class Season(models.Model): year = models.PositiveSmallIntegerField() gt = models.IntegerField(null=True, blank=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.year) +@python_2_unicode_compatible class Game(models.Model): season = models.ForeignKey(Season, related_name='games') home = models.CharField(max_length=100) away = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return "%s at %s" % (self.away, self.home) +@python_2_unicode_compatible class Player(models.Model): name = models.CharField(max_length=100) games = models.ManyToManyField(Game, related_name='players') - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/m2m_and_m2o/models.py b/tests/modeltests/m2m_and_m2o/models.py index 92ed3fbcd9..99c7b01017 100644 --- a/tests/modeltests/m2m_and_m2o/models.py +++ b/tests/modeltests/m2m_and_m2o/models.py @@ -7,17 +7,19 @@ from __future__ import unicode_literals from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class User(models.Model): username = models.CharField(max_length=20) +@python_2_unicode_compatible 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 __unicode__(self): + def __str__(self): return six.text_type(self.num) class Meta: diff --git a/tests/modeltests/m2m_intermediary/models.py b/tests/modeltests/m2m_intermediary/models.py index 85786e8458..e9ae422afb 100644 --- a/tests/modeltests/m2m_intermediary/models.py +++ b/tests/modeltests/m2m_intermediary/models.py @@ -12,27 +12,31 @@ field, which specifies the ``Reporter``'s position for the given article from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class Writer(models.Model): reporter = models.ForeignKey(Reporter) article = models.ForeignKey(Article) position = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return '%s (%s)' % (self.reporter, self.position) diff --git a/tests/modeltests/m2m_multiple/models.py b/tests/modeltests/m2m_multiple/models.py index 3efe7108b9..c2e3b030a4 100644 --- a/tests/modeltests/m2m_multiple/models.py +++ b/tests/modeltests/m2m_multiple/models.py @@ -8,16 +8,19 @@ Set ``related_name`` to designate what the reverse relationship is called. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) pub_date = models.DateTimeField() @@ -26,6 +29,6 @@ class Article(models.Model): class Meta: ordering = ('pub_date',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/m2m_recursive/models.py b/tests/modeltests/m2m_recursive/models.py index 83c943ae60..b69930208c 100644 --- a/tests/modeltests/m2m_recursive/models.py +++ b/tests/modeltests/m2m_recursive/models.py @@ -17,12 +17,14 @@ appropriate. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) friends = models.ManyToManyField('self') idols = models.ManyToManyField('self', symmetrical=False, related_name='stalkers') - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/m2m_signals/models.py b/tests/modeltests/m2m_signals/models.py index 526c4a782e..e997d87f21 100644 --- a/tests/modeltests/m2m_signals/models.py +++ b/tests/modeltests/m2m_signals/models.py @@ -1,15 +1,18 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Part(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Car(models.Model): name = models.CharField(max_length=20) default_parts = models.ManyToManyField(Part) @@ -18,12 +21,13 @@ class Car(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class SportsCar(Car): price = models.IntegerField() +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) fans = models.ManyToManyField('self', related_name='idols', symmetrical=False) @@ -32,5 +36,5 @@ class Person(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/m2m_through/models.py b/tests/modeltests/m2m_through/models.py index aa71a049cb..a896f6d9ff 100644 --- a/tests/modeltests/m2m_through/models.py +++ b/tests/modeltests/m2m_through/models.py @@ -1,18 +1,21 @@ from datetime import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible # M2M described on one of the models +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=128) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') @@ -22,9 +25,10 @@ class Group(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Membership(models.Model): person = models.ForeignKey(Person) group = models.ForeignKey(Group) @@ -34,16 +38,17 @@ class Membership(models.Model): class Meta: ordering = ('date_joined', 'invite_reason', 'group') - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) +@python_2_unicode_compatible class CustomMembership(models.Model): person = models.ForeignKey(Person, db_column="custom_person_column", related_name="custom_person_related_name") group = models.ForeignKey(Group) weird_fk = models.ForeignKey(Membership, null=True) date_joined = models.DateTimeField(default=datetime.now) - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) class Meta: @@ -54,11 +59,12 @@ class TestNoDefaultsOrNulls(models.Model): group = models.ForeignKey(Group) nodefaultnonull = models.CharField(max_length=5) +@python_2_unicode_compatible class PersonSelfRefM2M(models.Model): name = models.CharField(max_length=5) friends = models.ManyToManyField('self', through="Friendship", symmetrical=False) - def __unicode__(self): + def __str__(self): return self.name class Friendship(models.Model): diff --git a/tests/modeltests/m2o_recursive/models.py b/tests/modeltests/m2o_recursive/models.py index c0a4bdeec2..2775d713dd 100644 --- a/tests/modeltests/m2o_recursive/models.py +++ b/tests/modeltests/m2o_recursive/models.py @@ -11,19 +11,22 @@ Set ``related_name`` to designate what the reverse relationship is called. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) parent = models.ForeignKey('self', blank=True, null=True, related_name='child_set') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Person(models.Model): 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 __unicode__(self): + def __str__(self): return self.full_name diff --git a/tests/modeltests/many_to_many/models.py b/tests/modeltests/many_to_many/models.py index 5076e35653..a196c85092 100644 --- a/tests/modeltests/many_to_many/models.py +++ b/tests/modeltests/many_to_many/models.py @@ -8,22 +8,25 @@ objects, and a ``Publication`` has multiple ``Article`` objects. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Publication(models.Model): title = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return self.title class Meta: ordering = ('title',) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyField(Publication) - def __unicode__(self): + def __str__(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 0d2688e8a4..4e2ed67eea 100644 --- a/tests/modeltests/many_to_one/models.py +++ b/tests/modeltests/many_to_one/models.py @@ -6,22 +6,25 @@ To define a many-to-one relationship, use ``ForeignKey()``. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) email = models.EmailField() - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/modeltests/many_to_one_null/models.py b/tests/modeltests/many_to_one_null/models.py index be7e650c65..e00ca85928 100644 --- a/tests/modeltests/many_to_one_null/models.py +++ b/tests/modeltests/many_to_one_null/models.py @@ -6,14 +6,17 @@ To define a many-to-one relationship that can have a null foreign key, use """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) reporter = models.ForeignKey(Reporter, null=True) @@ -21,5 +24,5 @@ class Article(models.Model): class Meta: ordering = ('headline',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index 8942b21f73..90b019b611 100644 --- a/tests/modeltests/model_forms/models.py +++ b/tests/modeltests/model_forms/models.py @@ -14,6 +14,7 @@ import tempfile from django.core.files.storage import FileSystemStorage from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible temp_storage_dir = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) @@ -31,23 +32,29 @@ ARTICLE_STATUS_CHAR = ( ('l', 'Live'), ) +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) slug = models.SlugField(max_length=20) url = models.CharField('The URL', max_length=40) - def __unicode__(self): + def __str__(self): return self.name + def __repr__(self): + return self.__str__() + +@python_2_unicode_compatible class Writer(models.Model): name = models.CharField(max_length=50, help_text='Use both first and last names.') class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) slug = models.SlugField() @@ -64,7 +71,7 @@ class Article(models.Model): self.created = datetime.date.today() return super(Article, self).save() - def __unicode__(self): + def __str__(self): return self.headline class ImprovedArticle(models.Model): @@ -76,26 +83,29 @@ class ImprovedArticleWithParentLink(models.Model): class BetterWriter(Writer): score = models.IntegerField() +@python_2_unicode_compatible class WriterProfile(models.Model): writer = models.OneToOneField(Writer, primary_key=True) age = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s is %s" % (self.writer, self.age) from django.contrib.localflavor.us.models import PhoneNumberField +@python_2_unicode_compatible class PhoneNumber(models.Model): phone = PhoneNumberField() description = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.phone +@python_2_unicode_compatible class TextFile(models.Model): description = models.CharField(max_length=20) file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15) - def __unicode__(self): + def __str__(self): return self.description try: @@ -110,6 +120,7 @@ try: test_images = True + @python_2_unicode_compatible class ImageFile(models.Model): def custom_upload_path(self, filename): path = self.path or 'tests' @@ -125,9 +136,10 @@ try: width_field='width', height_field='height') path = models.CharField(max_length=16, blank=True, default='') - def __unicode__(self): + def __str__(self): return self.description + @python_2_unicode_compatible class OptionalImageFile(models.Model): def custom_upload_path(self, filename): path = self.path or 'tests' @@ -141,28 +153,31 @@ try: height = models.IntegerField(editable=False, null=True) path = models.CharField(max_length=16, blank=True, default='') - def __unicode__(self): + def __str__(self): return self.description except ImportError: test_images = False +@python_2_unicode_compatible class CommaSeparatedInteger(models.Model): field = models.CommaSeparatedIntegerField(max_length=20) - def __unicode__(self): + def __str__(self): return self.field +@python_2_unicode_compatible class Product(models.Model): slug = models.SlugField(unique=True) - def __unicode__(self): + def __str__(self): return self.slug +@python_2_unicode_compatible class Price(models.Model): price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s for %s" % (self.quantity, self.price) class Meta: @@ -171,6 +186,7 @@ class Price(models.Model): class ArticleStatus(models.Model): status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True) +@python_2_unicode_compatible class Inventory(models.Model): barcode = models.PositiveIntegerField(unique=True) parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True) @@ -179,9 +195,12 @@ class Inventory(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name + def __repr__(self): + return self.__str__() + class Book(models.Model): title = models.CharField(max_length=40) author = models.ForeignKey(Writer, blank=True, null=True) @@ -202,31 +221,34 @@ class BookXtra(models.Model): class DerivedBook(Book, BookXtra): pass +@python_2_unicode_compatible class ExplicitPK(models.Model): key = models.CharField(max_length=20, primary_key=True) desc = models.CharField(max_length=20, blank=True, unique=True) class Meta: unique_together = ('key', 'desc') - def __unicode__(self): + def __str__(self): return self.key +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=50, unique_for_date='posted', blank=True) slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) posted = models.DateField() - def __unicode__(self): + def __str__(self): return self.name class DerivedPost(Post): pass +@python_2_unicode_compatible class BigInt(models.Model): biggie = models.BigIntegerField() - def __unicode__(self): + def __str__(self): return six.text_type(self.biggie) class MarkupField(models.CharField): diff --git a/tests/modeltests/model_forms/tests.py b/tests/modeltests/model_forms/tests.py index 1da7f583be..038ce32287 100644 --- a/tests/modeltests/model_forms/tests.py +++ b/tests/modeltests/model_forms/tests.py @@ -179,14 +179,14 @@ class PriceFormWithoutQuantity(forms.ModelForm): class ModelFormBaseTest(TestCase): def test_base_form(self): - self.assertEqual(BaseCategoryForm.base_fields.keys(), + self.assertEqual(list(BaseCategoryForm.base_fields), ['name', 'slug', 'url']) def test_extra_fields(self): class ExtraFields(BaseCategoryForm): some_extra_field = forms.BooleanField() - self.assertEqual(ExtraFields.base_fields.keys(), + self.assertEqual(list(ExtraFields.base_fields), ['name', 'slug', 'url', 'some_extra_field']) def test_replace_field(self): @@ -215,7 +215,7 @@ class ModelFormBaseTest(TestCase): model = Category fields = ['url'] - self.assertEqual(LimitFields.base_fields.keys(), + self.assertEqual(list(LimitFields.base_fields), ['url']) def test_exclude_fields(self): @@ -224,7 +224,7 @@ class ModelFormBaseTest(TestCase): model = Category exclude = ['url'] - self.assertEqual(ExcludeFields.base_fields.keys(), + self.assertEqual(list(ExcludeFields.base_fields), ['name', 'slug']) def test_confused_form(self): @@ -237,7 +237,7 @@ class ModelFormBaseTest(TestCase): fields = ['name', 'url'] exclude = ['url'] - self.assertEqual(ConfusedForm.base_fields.keys(), + self.assertEqual(list(ConfusedForm.base_fields), ['name']) def test_mixmodel_form(self): @@ -254,13 +254,13 @@ class ModelFormBaseTest(TestCase): # overrides BaseCategoryForm.Meta. self.assertEqual( - MixModelForm.base_fields.keys(), + list(MixModelForm.base_fields), ['headline', 'slug', 'pub_date', 'writer', 'article', 'categories', 'status'] ) def test_article_form(self): self.assertEqual( - ArticleForm.base_fields.keys(), + list(ArticleForm.base_fields), ['headline', 'slug', 'pub_date', 'writer', 'article', 'categories', 'status'] ) @@ -270,7 +270,7 @@ class ModelFormBaseTest(TestCase): pass self.assertEqual( - BadForm.base_fields.keys(), + list(BadForm.base_fields), ['headline', 'slug', 'pub_date', 'writer', 'article', 'categories', 'status'] ) @@ -282,7 +282,7 @@ class ModelFormBaseTest(TestCase): """ pass - self.assertEqual(SubCategoryForm.base_fields.keys(), + self.assertEqual(list(SubCategoryForm.base_fields), ['name', 'slug', 'url']) def test_subclassmeta_form(self): @@ -312,7 +312,7 @@ class ModelFormBaseTest(TestCase): model = Category fields = ['url', 'name'] - self.assertEqual(OrderFields.base_fields.keys(), + self.assertEqual(list(OrderFields.base_fields), ['url', 'name']) self.assertHTMLEqual( str(OrderFields()), @@ -327,7 +327,7 @@ class ModelFormBaseTest(TestCase): fields = ['slug', 'url', 'name'] exclude = ['url'] - self.assertEqual(OrderFields2.base_fields.keys(), + self.assertEqual(list(OrderFields2.base_fields), ['slug', 'name']) @@ -751,9 +751,9 @@ class OldFormForXTests(TestCase): self.assertEqual(new_art.headline, 'New headline') # Add some categories and test the many-to-many form output. - self.assertEqual(map(lambda o: o.name, new_art.categories.all()), []) + self.assertQuerysetEqual(new_art.categories.all(), []) new_art.categories.add(Category.objects.get(name='Entertainment')) - self.assertEqual(map(lambda o: o.name, new_art.categories.all()), ["Entertainment"]) + self.assertQuerysetEqual(new_art.categories.all(), ["Entertainment"]) f = TestArticleForm(auto_id=False, instance=new_art) self.assertHTMLEqual(f.as_ul(), '''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" /></li> @@ -815,7 +815,7 @@ class OldFormForXTests(TestCase): new_art = f.save() self.assertEqual(new_art.id == art_id_1, True) new_art = Article.objects.get(id=art_id_1) - self.assertEqual(map(lambda o: o.name, new_art.categories.order_by('name')), + self.assertQuerysetEqual(new_art.categories.order_by('name'), ["Entertainment", "It's a test"]) # Now, submit form data with no categories. This deletes the existing categories. @@ -824,7 +824,7 @@ class OldFormForXTests(TestCase): new_art = f.save() self.assertEqual(new_art.id == art_id_1, True) new_art = Article.objects.get(id=art_id_1) - self.assertEqual(map(lambda o: o.name, new_art.categories.all()), []) + self.assertQuerysetEqual(new_art.categories.all(), []) # Create a new article, with categories, via the form. f = ArticleForm({'headline': 'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': '1967-11-01', @@ -833,7 +833,7 @@ class OldFormForXTests(TestCase): art_id_2 = new_art.id self.assertEqual(art_id_2 not in (None, art_id_1), True) new_art = Article.objects.get(id=art_id_2) - self.assertEqual(map(lambda o: o.name, new_art.categories.order_by('name')), ["Entertainment", "It's a test"]) + self.assertQuerysetEqual(new_art.categories.order_by('name'), ["Entertainment", "It's a test"]) # Create a new article, with no categories, via the form. f = ArticleForm({'headline': 'The walrus was Paul', 'slug': 'walrus-was-paul', 'pub_date': '1967-11-01', @@ -842,7 +842,7 @@ class OldFormForXTests(TestCase): art_id_3 = new_art.id self.assertEqual(art_id_3 not in (None, art_id_1, art_id_2), True) new_art = Article.objects.get(id=art_id_3) - self.assertEqual(map(lambda o: o.name, new_art.categories.all()), []) + self.assertQuerysetEqual(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. @@ -857,11 +857,11 @@ class OldFormForXTests(TestCase): # The instance doesn't have m2m data yet new_art = Article.objects.get(id=art_id_4) - self.assertEqual(map(lambda o: o.name, new_art.categories.all()), []) + self.assertQuerysetEqual(new_art.categories.all(), []) # Save the m2m data on the form f.save_m2m() - self.assertEqual(map(lambda o: o.name, new_art.categories.order_by('name')), ["Entertainment", "It's a test"]) + self.assertQuerysetEqual(new_art.categories.order_by('name'), ["Entertainment", "It's a test"]) # Here, we define a custom ModelForm. Because it happens to have the same fields as # the Category model, we can just call the form's save() to apply its changes to an @@ -1007,12 +1007,12 @@ class OldFormForXTests(TestCase): f.clean(None) with self.assertRaises(ValidationError): f.clean([]) - self.assertEqual(map(lambda o: o.name, f.clean([c1.id])), ["Entertainment"]) - self.assertEqual(map(lambda o: o.name, f.clean([c2.id])), ["It's a test"]) - self.assertEqual(map(lambda o: o.name, f.clean([str(c1.id)])), ["Entertainment"]) - self.assertEqual(map(lambda o: o.name, f.clean([str(c1.id), str(c2.id)])), ["Entertainment", "It's a test"]) - self.assertEqual(map(lambda o: o.name, f.clean([c1.id, str(c2.id)])), ["Entertainment", "It's a test"]) - self.assertEqual(map(lambda o: o.name, f.clean((c1.id, str(c2.id)))), ["Entertainment", "It's a test"]) + self.assertQuerysetEqual(f.clean([c1.id]), ["Entertainment"]) + self.assertQuerysetEqual(f.clean([c2.id]), ["It's a test"]) + self.assertQuerysetEqual(f.clean([str(c1.id)]), ["Entertainment"]) + self.assertQuerysetEqual(f.clean([str(c1.id), str(c2.id)]), ["Entertainment", "It's a test"]) + self.assertQuerysetEqual(f.clean([c1.id, str(c2.id)]), ["Entertainment", "It's a test"]) + self.assertQuerysetEqual(f.clean((c1.id, str(c2.id))), ["Entertainment", "It's a test"]) with self.assertRaises(ValidationError): f.clean(['100']) with self.assertRaises(ValidationError): @@ -1025,7 +1025,7 @@ class OldFormForXTests(TestCase): # than caching it at time of instantiation. c6 = Category.objects.create(id=6, name='Sixth', url='6th') self.assertEqual(c6.name, 'Sixth') - self.assertEqual(map(lambda o: o.name, f.clean([c6.id])), ["Sixth"]) + self.assertQuerysetEqual(f.clean([c6.id]), ["Sixth"]) # Delete a Category object *after* the ModelMultipleChoiceField has already been # instantiated. This proves clean() checks the database during clean() rather @@ -1050,7 +1050,7 @@ class OldFormForXTests(TestCase): (c1.pk, 'Entertainment'), (c2.pk, "It's a test"), (c3.pk, 'Third')]) - self.assertEqual(map(lambda o: o.name, f.clean([c3.id])), ["Third"]) + self.assertQuerysetEqual(f.clean([c3.id]), ["Third"]) with self.assertRaises(ValidationError): f.clean([c4.id]) with self.assertRaises(ValidationError): @@ -1066,13 +1066,13 @@ class OldFormForXTests(TestCase): # OneToOneField ############################################################### - self.assertEqual(ImprovedArticleForm.base_fields.keys(), ['article']) + self.assertEqual(list(ImprovedArticleForm.base_fields), ['article']) - self.assertEqual(ImprovedArticleWithParentLinkForm.base_fields.keys(), []) + self.assertEqual(list(ImprovedArticleWithParentLinkForm.base_fields), []) bw = BetterWriter(name='Joe Better', score=10) bw.save() - self.assertEqual(sorted(model_to_dict(bw).keys()), + self.assertEqual(sorted(model_to_dict(bw)), ['id', 'name', 'score', 'writer_ptr']) form = BetterWriterForm({'name': 'Some Name', 'score': 12}) @@ -1463,7 +1463,7 @@ class OldFormForXTests(TestCase): model = Category fields = ['description', 'url'] - self.assertEqual(CategoryForm.base_fields.keys(), + self.assertEqual(list(CategoryForm.base_fields), ['description', 'url']) self.assertHTMLEqual(six.text_type(CategoryForm()), '''<tr><th><label for="id_description">Description:</label></th><td><input type="text" name="description" id="id_description" /></td></tr> @@ -1472,14 +1472,15 @@ class OldFormForXTests(TestCase): field = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode') self.assertEqual(tuple(field.choices), ((86, 'Apple'), (87, 'Core'), (22, 'Pear'))) - self.assertEqual(map(lambda o: o.name, field.clean([86])), ['Apple']) + self.assertQuerysetEqual(field.clean([86]), ['Apple']) form = SelectInventoryForm({'items': [87, 22]}) self.assertEqual(form.is_valid(), True) self.assertEqual(len(form.cleaned_data), 1) - self.assertEqual(map(lambda o: o.name, form.cleaned_data['items']), ['Core', 'Pear']) + self.assertQuerysetEqual(form.cleaned_data['items'], ['Core', 'Pear']) def test_model_field_that_returns_none_to_exclude_itself_with_explicit_fields(self): - self.assertEqual(CustomFieldForExclusionForm.base_fields.keys(), ['name']) + self.assertEqual(list(CustomFieldForExclusionForm.base_fields), + ['name']) self.assertHTMLEqual(six.text_type(CustomFieldForExclusionForm()), '''<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" maxlength="10" /></td></tr>''') diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index a01a2da0fa..ae152448ab 100644 --- a/tests/modeltests/model_formsets/models.py +++ b/tests/modeltests/model_formsets/models.py @@ -4,20 +4,23 @@ import datetime from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class BetterAuthor(Author): write_speed = models.IntegerField() +@python_2_unicode_compatible class Book(models.Model): author = models.ForeignKey(Author) title = models.CharField(max_length=100) @@ -28,20 +31,22 @@ class Book(models.Model): ) ordering = ['id'] - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class BookWithCustomPK(models.Model): my_pk = models.DecimalField(max_digits=5, decimal_places=0, primary_key=True) author = models.ForeignKey(Author) title = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return '%s: %s' % (self.my_pk, self.title) class Editor(models.Model): name = models.CharField(max_length=100) +@python_2_unicode_compatible class BookWithOptionalAltEditor(models.Model): author = models.ForeignKey(Author) # Optional secondary author @@ -53,21 +58,23 @@ class BookWithOptionalAltEditor(models.Model): ('author', 'title', 'alt_editor'), ) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class AlternateBook(Book): notes = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return '%s - %s' % (self.title, self.notes) +@python_2_unicode_compatible class AuthorMeeting(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) created = models.DateField(editable=False) - def __unicode__(self): + def __str__(self): return self.name class CustomPrimaryKey(models.Model): @@ -77,19 +84,21 @@ class CustomPrimaryKey(models.Model): # models for inheritance tests. +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) city = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Owner(models.Model): auto_id = models.AutoField(primary_key=True) name = models.CharField(max_length=100) place = models.ForeignKey(Place) - def __unicode__(self): + def __str__(self): return "%s at %s" % (self.name, self.place) class Location(models.Model): @@ -98,30 +107,34 @@ class Location(models.Model): lat = models.CharField(max_length=100) lon = models.CharField(max_length=100) +@python_2_unicode_compatible class OwnerProfile(models.Model): owner = models.OneToOneField(Owner, primary_key=True) age = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s is %d" % (self.owner.name, self.age) +@python_2_unicode_compatible class Restaurant(Place): serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Product(models.Model): slug = models.SlugField(unique=True) - def __unicode__(self): + def __str__(self): return self.slug +@python_2_unicode_compatible class Price(models.Model): price = models.DecimalField(max_digits=10, decimal_places=2) quantity = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s for %s" % (self.quantity, self.price) class Meta: @@ -136,12 +149,14 @@ class ClassyMexicanRestaurant(MexicanRestaurant): # models for testing unique_together validation when a fk is involved and # using inlineformset_factory. +@python_2_unicode_compatible class Repository(models.Model): name = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Revision(models.Model): repository = models.ForeignKey(Repository) revision = models.CharField(max_length=40) @@ -149,7 +164,7 @@ class Revision(models.Model): class Meta: unique_together = (("repository", "revision"),) - def __unicode__(self): + def __str__(self): return "%s (%s)" % (self.revision, six.text_type(self.repository)) # models for testing callable defaults (see bug #7975). If you define a model @@ -167,32 +182,36 @@ class Membership(models.Model): class Team(models.Model): name = models.CharField(max_length=100) +@python_2_unicode_compatible class Player(models.Model): team = models.ForeignKey(Team, null=True) name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name # Models for testing custom ModelForm save methods in formsets and inline formsets +@python_2_unicode_compatible class Poet(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Poem(models.Model): poet = models.ForeignKey(Poet) name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=50, unique_for_date='posted', blank=True) slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) posted = models.DateField() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py index 37ca603021..2101f394f7 100644 --- a/tests/modeltests/model_inheritance/models.py +++ b/tests/modeltests/model_inheritance/models.py @@ -14,11 +14,13 @@ Both styles are demonstrated here. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # # Abstract base classes # +@python_2_unicode_compatible class CommonInfo(models.Model): name = models.CharField(max_length=50) age = models.PositiveIntegerField() @@ -27,7 +29,7 @@ class CommonInfo(models.Model): abstract = True ordering = ['name'] - def __unicode__(self): + def __str__(self): return '%s %s' % (self.__class__.__name__, self.name) class Worker(CommonInfo): @@ -49,6 +51,7 @@ class StudentWorker(Student, Worker): class Post(models.Model): title = models.CharField(max_length=50) +@python_2_unicode_compatible class Attachment(models.Model): post = models.ForeignKey(Post, related_name='attached_%(class)s_set') content = models.TextField() @@ -56,7 +59,7 @@ class Attachment(models.Model): class Meta: abstract = True - def __unicode__(self): + def __str__(self): return self.content class Comment(Attachment): @@ -69,17 +72,19 @@ class Link(Attachment): # Multi-table inheritance # +@python_2_unicode_compatible class Chef(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "%s the chef" % self.name +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name class Rating(models.Model): @@ -89,6 +94,7 @@ class Rating(models.Model): abstract = True ordering = ['-rating'] +@python_2_unicode_compatible class Restaurant(Place, Rating): serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() @@ -97,27 +103,30 @@ class Restaurant(Place, Rating): class Meta(Rating.Meta): db_table = 'my_restaurant' - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.name +@python_2_unicode_compatible class ItalianRestaurant(Restaurant): serves_gnocchi = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the italian restaurant" % self.name +@python_2_unicode_compatible class Supplier(Place): customers = models.ManyToManyField(Restaurant, related_name='provider') - def __unicode__(self): + def __str__(self): return "%s the supplier" % self.name +@python_2_unicode_compatible class ParkingLot(Place): # An explicit link to the parent (we can control the attribute name). parent = models.OneToOneField(Place, primary_key=True, parent_link=True) main_site = models.ForeignKey(Place, related_name='lot') - def __unicode__(self): + def __str__(self): return "%s the parking lot" % self.name # @@ -139,10 +148,11 @@ class NamedURL(models.Model): class Meta: abstract = True +@python_2_unicode_compatible class Copy(NamedURL): content = models.TextField() - def __unicode__(self): + def __str__(self): return self.content class Mixin(object): diff --git a/tests/modeltests/model_inheritance_same_model_name/models.py b/tests/modeltests/model_inheritance_same_model_name/models.py index de7541694a..801724df18 100644 --- a/tests/modeltests/model_inheritance_same_model_name/models.py +++ b/tests/modeltests/model_inheritance_same_model_name/models.py @@ -11,12 +11,14 @@ from __future__ import absolute_import from django.db import models from ..model_inheritance.models import NamedURL +from django.utils.encoding import python_2_unicode_compatible # # Abstract base classes with related models # +@python_2_unicode_compatible class Copy(NamedURL): content = models.TextField() - def __unicode__(self): + def __str__(self): return self.content diff --git a/tests/modeltests/one_to_one/models.py b/tests/modeltests/one_to_one/models.py index 8c6d56a795..9599496cb7 100644 --- a/tests/modeltests/one_to_one/models.py +++ b/tests/modeltests/one_to_one/models.py @@ -8,28 +8,32 @@ In this example, a ``Place`` optionally can be a ``Restaurant``. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True) serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.place.name +@python_2_unicode_compatible class Waiter(models.Model): restaurant = models.ForeignKey(Restaurant) name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "%s the waiter at %s" % (self.name, self.restaurant) class ManualPrimaryKey(models.Model): @@ -40,10 +44,11 @@ class RelatedModel(models.Model): link = models.OneToOneField(ManualPrimaryKey) name = models.CharField(max_length = 50) +@python_2_unicode_compatible class MultiModel(models.Model): link1 = models.OneToOneField(Place) link2 = models.OneToOneField(ManualPrimaryKey) name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Multimodel %s" % self.name diff --git a/tests/modeltests/or_lookups/models.py b/tests/modeltests/or_lookups/models.py index 0037b41afb..f146b2e72d 100644 --- a/tests/modeltests/or_lookups/models.py +++ b/tests/modeltests/or_lookups/models.py @@ -10,8 +10,10 @@ clauses using the variable ``django.db.models.Q`` (or any object with an """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) pub_date = models.DateTimeField() @@ -19,5 +21,5 @@ class Article(models.Model): class Meta: ordering = ('pub_date',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/order_with_respect_to/models.py b/tests/modeltests/order_with_respect_to/models.py index a4e20c2fe0..06bb56b141 100644 --- a/tests/modeltests/order_with_respect_to/models.py +++ b/tests/modeltests/order_with_respect_to/models.py @@ -4,11 +4,13 @@ Tests for the order_with_respect_to Meta attribute. from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class Question(models.Model): text = models.CharField(max_length=200) +@python_2_unicode_compatible class Answer(models.Model): text = models.CharField(max_length=200) question = models.ForeignKey(Question) @@ -16,9 +18,10 @@ class Answer(models.Model): class Meta: order_with_respect_to = 'question' - def __unicode__(self): + def __str__(self): return six.text_type(self.text) +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=200) parent = models.ForeignKey("self", related_name="children", null=True) @@ -26,5 +29,5 @@ class Post(models.Model): class Meta: order_with_respect_to = "parent" - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/modeltests/ordering/models.py b/tests/modeltests/ordering/models.py index bfb4b97107..67126e1bda 100644 --- a/tests/modeltests/ordering/models.py +++ b/tests/modeltests/ordering/models.py @@ -14,22 +14,25 @@ undefined -- not random, just undefined. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pub_date', 'headline') - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class ArticlePKOrdering(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pk',) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/pagination/models.py b/tests/modeltests/pagination/models.py index 48484dd59b..779d3029ba 100644 --- a/tests/modeltests/pagination/models.py +++ b/tests/modeltests/pagination/models.py @@ -7,11 +7,13 @@ objects into easily readable pages. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/modeltests/pagination/tests.py b/tests/modeltests/pagination/tests.py index cba8825ec4..12ce0e3ecb 100644 --- a/tests/modeltests/pagination/tests.py +++ b/tests/modeltests/pagination/tests.py @@ -28,7 +28,7 @@ class PaginationTests(TestCase): paginator = Paginator(Article.objects.all(), 5) self.assertEqual(9, paginator.count) self.assertEqual(2, paginator.num_pages) - self.assertEqual([1, 2], paginator.page_range) + self.assertEqual([1, 2], list(paginator.page_range)) def test_first_page(self): paginator = Paginator(Article.objects.all(), 5) @@ -78,13 +78,13 @@ class PaginationTests(TestCase): paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=True) self.assertEqual(0, paginator.count) self.assertEqual(1, paginator.num_pages) - self.assertEqual([1], paginator.page_range) + self.assertEqual([1], list(paginator.page_range)) # Empty paginators with allow_empty_first_page=False. paginator = Paginator(Article.objects.filter(id=0), 5, allow_empty_first_page=False) self.assertEqual(0, paginator.count) self.assertEqual(0, paginator.num_pages) - self.assertEqual([], paginator.page_range) + self.assertEqual([], list(paginator.page_range)) def test_invalid_page(self): paginator = Paginator(Article.objects.all(), 5) @@ -108,7 +108,7 @@ class PaginationTests(TestCase): paginator = Paginator([1, 2, 3, 4, 5, 6, 7, 8, 9], 3) self.assertEqual(9, paginator.count) self.assertEqual(3, paginator.num_pages) - self.assertEqual([1, 2, 3], paginator.page_range) + self.assertEqual([1, 2, 3], list(paginator.page_range)) p = paginator.page(2) self.assertEqual("<Page 2 of 3>", six.text_type(p)) self.assertEqual([4, 5, 6], p.object_list) @@ -125,10 +125,10 @@ class PaginationTests(TestCase): paginator = Paginator(CountContainer(), 10) self.assertEqual(42, paginator.count) self.assertEqual(5, paginator.num_pages) - self.assertEqual([1, 2, 3, 4, 5], paginator.page_range) + self.assertEqual([1, 2, 3, 4, 5], list(paginator.page_range)) # Paginator can be passed other objects that implement __len__. paginator = Paginator(LenContainer(), 10) self.assertEqual(42, paginator.count) self.assertEqual(5, paginator.num_pages) - self.assertEqual([1, 2, 3, 4, 5], paginator.page_range) + self.assertEqual([1, 2, 3, 4, 5], list(paginator.page_range)) diff --git a/tests/modeltests/prefetch_related/models.py b/tests/modeltests/prefetch_related/models.py index 589f78c7d3..85488f0879 100644 --- a/tests/modeltests/prefetch_related/models.py +++ b/tests/modeltests/prefetch_related/models.py @@ -1,16 +1,18 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible ## Basic tests +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=50, unique=True) first_book = models.ForeignKey('Book', related_name='first_time_authors') favorite_authors = models.ManyToManyField( 'self', through='FavoriteAuthors', symmetrical=False, related_name='favors_me') - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -30,6 +32,7 @@ class FavoriteAuthors(models.Model): ordering = ['id'] +@python_2_unicode_compatible class AuthorAddress(models.Model): author = models.ForeignKey(Author, to_field='name', related_name='addresses') address = models.TextField() @@ -37,15 +40,16 @@ class AuthorAddress(models.Model): class Meta: ordering = ['id'] - def __unicode__(self): + def __str__(self): return self.address +@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=255) authors = models.ManyToManyField(Author, related_name='books') - def __unicode__(self): + def __str__(self): return self.title class Meta: @@ -58,11 +62,12 @@ class BookWithYear(Book): AuthorWithAge, related_name='books_with_year') +@python_2_unicode_compatible class Reader(models.Model): name = models.CharField(max_length=50) books_read = models.ManyToManyField(Book, related_name='read_by') - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -86,13 +91,14 @@ class TeacherManager(models.Manager): return super(TeacherManager, self).get_query_set().prefetch_related('qualifications') +@python_2_unicode_compatible class Teacher(models.Model): name = models.CharField(max_length=50) qualifications = models.ManyToManyField(Qualification) objects = TeacherManager() - def __unicode__(self): + def __str__(self): return "%s (%s)" % (self.name, ", ".join(q.name for q in self.qualifications.all())) class Meta: @@ -109,6 +115,7 @@ class Department(models.Model): ## GenericRelation/GenericForeignKey tests +@python_2_unicode_compatible class TaggedItem(models.Model): tag = models.SlugField() content_type = models.ForeignKey(ContentType, related_name="taggeditem_set2") @@ -119,7 +126,7 @@ class TaggedItem(models.Model): created_by_fkey = models.PositiveIntegerField(null=True) created_by = generic.GenericForeignKey('created_by_ct', 'created_by_fkey',) - def __unicode__(self): + def __str__(self): return self.tag @@ -169,12 +176,13 @@ class Person(models.Model): ## Models for nullable FK tests +@python_2_unicode_compatible class Employee(models.Model): name = models.CharField(max_length=50) boss = models.ForeignKey('self', null=True, related_name='serfs') - def __unicode__(self): + def __str__(self): return self.name class Meta: diff --git a/tests/modeltests/prefetch_related/tests.py b/tests/modeltests/prefetch_related/tests.py index 8ae0a1e298..614a5fc1d6 100644 --- a/tests/modeltests/prefetch_related/tests.py +++ b/tests/modeltests/prefetch_related/tests.py @@ -352,7 +352,7 @@ class MultiTableInheritanceTest(TestCase): with self.assertNumQueries(2): qs = BookReview.objects.prefetch_related('book') titles = [obj.book.title for obj in qs] - self.assertEquals(titles, ["Poems", "More poems"]) + self.assertEqual(titles, ["Poems", "More poems"]) def test_m2m_to_inheriting_model(self): qs = AuthorWithAge.objects.prefetch_related('books_with_year') diff --git a/tests/modeltests/proxy_model_inheritance/tests.py b/tests/modeltests/proxy_model_inheritance/tests.py index 061cdc7e91..39fee7ee6d 100644 --- a/tests/modeltests/proxy_model_inheritance/tests.py +++ b/tests/modeltests/proxy_model_inheritance/tests.py @@ -24,7 +24,8 @@ class ProxyModelInheritanceTests(TransactionTestCase): def setUp(self): self.old_sys_path = sys.path[:] sys.path.append(os.path.dirname(os.path.abspath(__file__))) - map(load_app, settings.INSTALLED_APPS) + for app in settings.INSTALLED_APPS: + load_app(app) def tearDown(self): sys.path = self.old_sys_path diff --git a/tests/modeltests/proxy_models/models.py b/tests/modeltests/proxy_models/models.py index 49fd87deff..6c962aadc8 100644 --- a/tests/modeltests/proxy_models/models.py +++ b/tests/modeltests/proxy_models/models.py @@ -5,6 +5,7 @@ than using a new table of their own. This allows them to act as simple proxies, providing a modified interface to the data from the base class. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible # A couple of managers for testing managing overriding in proxy model cases. @@ -16,6 +17,7 @@ class SubManager(models.Manager): def get_query_set(self): return super(SubManager, self).get_query_set().exclude(name="wilma") +@python_2_unicode_compatible class Person(models.Model): """ A simple concrete base class. @@ -24,7 +26,7 @@ class Person(models.Model): objects = PersonManager() - def __unicode__(self): + def __str__(self): return self.name class Abstract(models.Model): @@ -82,10 +84,11 @@ class MyPersonProxy(MyPerson): class LowerStatusPerson(MyPersonProxy): status = models.CharField(max_length=80) +@python_2_unicode_compatible class User(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name class UserProxy(User): @@ -100,11 +103,12 @@ class UserProxyProxy(UserProxy): class Country(models.Model): name = models.CharField(max_length=50) +@python_2_unicode_compatible class State(models.Model): name = models.CharField(max_length=50) country = models.ForeignKey(Country) - def __unicode__(self): + def __str__(self): return self.name class StateProxy(State): @@ -124,11 +128,12 @@ class ProxyTrackerUser(TrackerUser): proxy = True +@python_2_unicode_compatible class Issue(models.Model): summary = models.CharField(max_length=255) assignee = models.ForeignKey(TrackerUser) - def __unicode__(self): + def __str__(self): return ':'.join((self.__class__.__name__,self.summary,)) class Bug(Issue): diff --git a/tests/modeltests/reserved_names/models.py b/tests/modeltests/reserved_names/models.py index 010649e681..8e942b20e8 100644 --- a/tests/modeltests/reserved_names/models.py +++ b/tests/modeltests/reserved_names/models.py @@ -8,8 +8,10 @@ reserved-name usage. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Thing(models.Model): when = models.CharField(max_length=1, primary_key=True) join = models.CharField(max_length=1) @@ -22,5 +24,5 @@ class Thing(models.Model): class Meta: db_table = 'select' - def __unicode__(self): - return self.when
\ No newline at end of file + def __str__(self): + return self.when diff --git a/tests/modeltests/reverse_lookup/models.py b/tests/modeltests/reverse_lookup/models.py index bb7a163327..ed58177770 100644 --- a/tests/modeltests/reverse_lookup/models.py +++ b/tests/modeltests/reverse_lookup/models.py @@ -5,25 +5,29 @@ This demonstrates the reverse lookup features of the database API. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class User(models.Model): name = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Poll(models.Model): question = models.CharField(max_length=200) creator = models.ForeignKey(User) - def __unicode__(self): + def __str__(self): return self.question +@python_2_unicode_compatible class Choice(models.Model): name = models.CharField(max_length=100) poll = models.ForeignKey(Poll, related_name="poll_choice") related_poll = models.ForeignKey(Poll, related_name="related_choice") - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/save_delete_hooks/models.py b/tests/modeltests/save_delete_hooks/models.py index bae60e4b86..a6e1abfb77 100644 --- a/tests/modeltests/save_delete_hooks/models.py +++ b/tests/modeltests/save_delete_hooks/models.py @@ -7,8 +7,10 @@ the methods. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) @@ -17,7 +19,7 @@ class Person(models.Model): super(Person, self).__init__(*args, **kwargs) self.data = [] - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) def save(self, *args, **kwargs): diff --git a/tests/modeltests/select_related/models.py b/tests/modeltests/select_related/models.py index 3c2e7721fd..ec41957adf 100644 --- a/tests/modeltests/select_related/models.py +++ b/tests/modeltests/select_related/models.py @@ -8,52 +8,61 @@ the select-related behavior will traverse. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible # Who remembers high school biology? +@python_2_unicode_compatible class Domain(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Kingdom(models.Model): name = models.CharField(max_length=50) domain = models.ForeignKey(Domain) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Phylum(models.Model): name = models.CharField(max_length=50) kingdom = models.ForeignKey(Kingdom) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Klass(models.Model): name = models.CharField(max_length=50) phylum = models.ForeignKey(Phylum) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Order(models.Model): name = models.CharField(max_length=50) klass = models.ForeignKey(Klass) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Family(models.Model): name = models.CharField(max_length=50) order = models.ForeignKey(Order) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Genus(models.Model): name = models.CharField(max_length=50) family = models.ForeignKey(Family) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Species(models.Model): name = models.CharField(max_length=50) genus = models.ForeignKey(Genus) - def __unicode__(self): - return self.name
\ No newline at end of file + def __str__(self): + return self.name diff --git a/tests/modeltests/serializers/models.py b/tests/modeltests/serializers/models.py index 9da099c027..569f2cec49 100644 --- a/tests/modeltests/serializers/models.py +++ b/tests/modeltests/serializers/models.py @@ -11,28 +11,32 @@ from decimal import Decimal from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=20) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Article(models.Model): author = models.ForeignKey(Author) headline = models.CharField(max_length=50) @@ -42,28 +46,31 @@ class Article(models.Model): class Meta: ordering = ('pub_date',) - def __unicode__(self): + def __str__(self): return self.headline +@python_2_unicode_compatible class AuthorProfile(models.Model): author = models.OneToOneField(Author, primary_key=True) date_of_birth = models.DateField() - def __unicode__(self): + def __str__(self): return "Profile of %s" % self.author +@python_2_unicode_compatible class Actor(models.Model): name = models.CharField(max_length=20, primary_key=True) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Movie(models.Model): actor = models.ForeignKey(Actor) title = models.CharField(max_length=50) @@ -72,7 +79,7 @@ class Movie(models.Model): class Meta: ordering = ('title',) - def __unicode__(self): + def __str__(self): return self.title @@ -80,13 +87,11 @@ class Score(models.Model): score = models.FloatField() +@python_2_unicode_compatible class Team(object): def __init__(self, title): self.title = title - def __unicode__(self): - raise NotImplementedError("Not so simple") - def __str__(self): raise NotImplementedError("Not so simple") @@ -94,8 +99,7 @@ class Team(object): return "%s" % self.title -class TeamField(models.CharField): - __metaclass__ = models.SubfieldBase +class TeamField(six.with_metaclass(models.SubfieldBase, models.CharField)): def __init__(self): super(TeamField, self).__init__(max_length=100) @@ -112,10 +116,11 @@ class TeamField(models.CharField): return self._get_val_from_obj(obj).to_string() +@python_2_unicode_compatible class Player(models.Model): name = models.CharField(max_length=50) rank = models.IntegerField() team = TeamField() - def __unicode__(self): + def __str__(self): return '%s (%d) playing for %s' % (self.name, self.rank, self.team.to_string()) diff --git a/tests/modeltests/serializers/tests.py b/tests/modeltests/serializers/tests.py index ec3cc132db..e39f17b68a 100644 --- a/tests/modeltests/serializers/tests.py +++ b/tests/modeltests/serializers/tests.py @@ -114,8 +114,8 @@ class SerializersTestBase(object): Tests the ability to create new objects by modifying serialized content. """ - old_headline = b"Poker has no place on ESPN" - new_headline = b"Poker has no place on television" + old_headline = "Poker has no place on ESPN" + new_headline = "Poker has no place on television" serial_str = serializers.serialize(self.serializer_name, Article.objects.all()) serial_str = serial_str.replace(old_headline, new_headline) @@ -285,7 +285,7 @@ class SerializersTransactionTestBase(object): class XmlSerializerTestCase(SerializersTestBase, TestCase): serializer_name = "xml" - pkless_str = b"""<?xml version="1.0" encoding="utf-8"?> + pkless_str = """<?xml version="1.0" encoding="utf-8"?> <django-objects version="1.0"> <object model="serializers.category"> <field type="CharField" name="name">Reference</field> @@ -331,7 +331,7 @@ class XmlSerializerTestCase(SerializersTestBase, TestCase): class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): serializer_name = "xml" - fwd_ref_str = b"""<?xml version="1.0" encoding="utf-8"?> + fwd_ref_str = """<?xml version="1.0" encoding="utf-8"?> <django-objects version="1.0"> <object pk="1" model="serializers.article"> <field to="serializers.author" name="author" rel="ManyToOneRel">1</field> @@ -351,7 +351,7 @@ class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, Transacti class JsonSerializerTestCase(SerializersTestBase, TestCase): serializer_name = "json" - pkless_str = b"""[{"pk": null, "model": "serializers.category", "fields": {"name": "Reference"}}]""" + pkless_str = """[{"pk": null, "model": "serializers.category", "fields": {"name": "Reference"}}]""" @staticmethod def _validate_output(serial_str): @@ -381,7 +381,7 @@ class JsonSerializerTestCase(SerializersTestBase, TestCase): class JsonSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): serializer_name = "json" - fwd_ref_str = b"""[ + fwd_ref_str = """[ { "pk": 1, "model": "serializers.article", @@ -414,7 +414,7 @@ except ImportError: else: class YamlSerializerTestCase(SerializersTestBase, TestCase): serializer_name = "yaml" - fwd_ref_str = b"""- fields: + fwd_ref_str = """- fields: headline: Forward references pose no problem pub_date: 2006-06-16 15:00:00 categories: [1] @@ -430,7 +430,7 @@ else: pk: 1 model: serializers.author""" - pkless_str = b"""- fields: + pkless_str = """- fields: name: Reference pk: null model: serializers.category""" @@ -470,7 +470,7 @@ else: class YamlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): serializer_name = "yaml" - fwd_ref_str = b"""- fields: + fwd_ref_str = """- fields: headline: Forward references pose no problem pub_date: 2006-06-16 15:00:00 categories: [1] diff --git a/tests/modeltests/signals/models.py b/tests/modeltests/signals/models.py index 18ca467655..e54a22fce9 100644 --- a/tests/modeltests/signals/models.py +++ b/tests/modeltests/signals/models.py @@ -4,18 +4,21 @@ Testing signals before/after saving and deleting. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Car(models.Model): make = models.CharField(max_length=20) model = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.make, self.model) diff --git a/tests/modeltests/str/models.py b/tests/modeltests/str/models.py index 6f6b877c88..488012e861 100644 --- a/tests/modeltests/str/models.py +++ b/tests/modeltests/str/models.py @@ -15,6 +15,7 @@ if you prefer. You must be careful to encode the results correctly, though. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Article(models.Model): @@ -26,9 +27,10 @@ class Article(models.Model): # in ASCII. return self.headline +@python_2_unicode_compatible class InternationalArticle(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() - def __unicode__(self): - return self.headline
\ No newline at end of file + def __str__(self): + return self.headline diff --git a/tests/modeltests/str/tests.py b/tests/modeltests/str/tests.py index bed9ea719b..2c11ac8c78 100644 --- a/tests/modeltests/str/tests.py +++ b/tests/modeltests/str/tests.py @@ -4,23 +4,32 @@ from __future__ import absolute_import, unicode_literals import datetime from django.test import TestCase +from django.utils import six +from django.utils.unittest import skipIf from .models import Article, InternationalArticle class SimpleTests(TestCase): + + @skipIf(six.PY3, "tests a __str__ method returning unicode under Python 2") def test_basic(self): a = Article.objects.create( headline=b'Area man programs in Python', pub_date=datetime.datetime(2005, 7, 28) ) - self.assertEqual(str(a), b'Area man programs in Python') - self.assertEqual(repr(a), b'<Article: Area man programs in Python>') + self.assertEqual(str(a), str('Area man programs in Python')) + self.assertEqual(repr(a), str('<Article: Area man programs in Python>')) def test_international(self): a = InternationalArticle.objects.create( headline='Girl wins €12.500 in lottery', pub_date=datetime.datetime(2005, 7, 28) ) - # The default str() output will be the UTF-8 encoded output of __unicode__(). - self.assertEqual(str(a), b'Girl wins \xe2\x82\xac12.500 in lottery') + if six.PY3: + self.assertEqual(str(a), 'Girl wins €12.500 in lottery') + else: + # On Python 2, the default str() output will be the UTF-8 encoded + # output of __unicode__() -- or __str__() when the + # python_2_unicode_compatible decorator is used. + self.assertEqual(str(a), b'Girl wins \xe2\x82\xac12.500 in lottery') diff --git a/tests/modeltests/test_client/models.py b/tests/modeltests/test_client/models.py index 399d2906a8..1d9c999f21 100644 --- a/tests/modeltests/test_client/models.py +++ b/tests/modeltests/test_client/models.py @@ -91,7 +91,7 @@ class ClientTest(TestCase): content_type="text/xml") self.assertEqual(response.status_code, 200) self.assertEqual(response.templates[0].name, "Book template") - self.assertEqual(response.content, "Blink - Malcolm Gladwell") + self.assertEqual(response.content, b"Blink - Malcolm Gladwell") def test_redirect(self): "GET a URL that redirects elsewhere" diff --git a/tests/modeltests/timezones/tests.py b/tests/modeltests/timezones/tests.py index a38e4b3f75..4621f0a60c 100644 --- a/tests/modeltests/timezones/tests.py +++ b/tests/modeltests/timezones/tests.py @@ -467,6 +467,7 @@ class NewDatabaseTests(TestCase): [event], transform=lambda d: d) + @requires_tz_support def test_filter_date_field_with_aware_datetime(self): # Regression test for #17742 day = datetime.date(2011, 9, 1) diff --git a/tests/modeltests/transactions/models.py b/tests/modeltests/transactions/models.py index 7a51b9ac1c..0f8d6b16ec 100644 --- a/tests/modeltests/transactions/models.py +++ b/tests/modeltests/transactions/models.py @@ -9,8 +9,10 @@ manually. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) @@ -19,5 +21,5 @@ class Reporter(models.Model): class Meta: ordering = ('first_name', 'last_name') - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) diff --git a/tests/modeltests/unmanaged_models/models.py b/tests/modeltests/unmanaged_models/models.py index 00303cf17a..0eef69977c 100644 --- a/tests/modeltests/unmanaged_models/models.py +++ b/tests/modeltests/unmanaged_models/models.py @@ -4,9 +4,11 @@ is generated for the table on various manage.py operations. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible # All of these models are created in the database by Django. +@python_2_unicode_compatible class A01(models.Model): f_a = models.CharField(max_length=10, db_index=True) f_b = models.IntegerField() @@ -14,9 +16,10 @@ class A01(models.Model): class Meta: db_table = 'a01' - def __unicode__(self): + def __str__(self): return self.f_a +@python_2_unicode_compatible class B01(models.Model): fk_a = models.ForeignKey(A01) f_a = models.CharField(max_length=10, db_index=True) @@ -27,9 +30,10 @@ class B01(models.Model): # 'managed' is True by default. This tests we can set it explicitly. managed = True - def __unicode__(self): + def __str__(self): return self.f_a +@python_2_unicode_compatible class C01(models.Model): mm_a = models.ManyToManyField(A01, db_table='d01') f_a = models.CharField(max_length=10, db_index=True) @@ -38,13 +42,14 @@ class C01(models.Model): class Meta: db_table = 'c01' - def __unicode__(self): + def __str__(self): return self.f_a # All of these models use the same tables as the previous set (they are shadows # of possibly a subset of the columns). There should be no creation errors, # since we have told Django they aren't managed by Django. +@python_2_unicode_compatible class A02(models.Model): f_a = models.CharField(max_length=10, db_index=True) @@ -52,9 +57,10 @@ class A02(models.Model): db_table = 'a01' managed = False - def __unicode__(self): + def __str__(self): return self.f_a +@python_2_unicode_compatible class B02(models.Model): class Meta: db_table = 'b01' @@ -64,11 +70,12 @@ class B02(models.Model): f_a = models.CharField(max_length=10, db_index=True) f_b = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.f_a # To re-use the many-to-many intermediate table, we need to manually set up # things up. +@python_2_unicode_compatible class C02(models.Model): mm_a = models.ManyToManyField(A02, through="Intermediate") f_a = models.CharField(max_length=10, db_index=True) @@ -78,7 +85,7 @@ class C02(models.Model): db_table = 'c01' managed = False - def __unicode__(self): + def __str__(self): return self.f_a class Intermediate(models.Model): diff --git a/tests/modeltests/update/models.py b/tests/modeltests/update/models.py index b93e4a7aae..08472d98b1 100644 --- a/tests/modeltests/update/models.py +++ b/tests/modeltests/update/models.py @@ -5,21 +5,24 @@ updates. from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class DataPoint(models.Model): name = models.CharField(max_length=20) value = models.CharField(max_length=20) another_value = models.CharField(max_length=20, blank=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.name) +@python_2_unicode_compatible class RelatedPoint(models.Model): name = models.CharField(max_length=20) data = models.ForeignKey(DataPoint) - def __unicode__(self): + def __str__(self): return six.text_type(self.name) diff --git a/tests/modeltests/update_only_fields/models.py b/tests/modeltests/update_only_fields/models.py index 968dba9916..bf5dd99166 100644 --- a/tests/modeltests/update_only_fields/models.py +++ b/tests/modeltests/update_only_fields/models.py @@ -1,5 +1,6 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible GENDER_CHOICES = ( ('M', 'Male'), @@ -10,11 +11,13 @@ class Account(models.Model): num = models.IntegerField() +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES) + pid = models.IntegerField(null=True, default=None) - def __unicode__(self): + def __str__(self): return self.name @@ -24,11 +27,12 @@ class Employee(Person): accounts = models.ManyToManyField('Account', related_name='employees', blank=True, null=True) +@python_2_unicode_compatible class Profile(models.Model): name = models.CharField(max_length=200) salary = models.FloatField(default=1000.0) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/modeltests/update_only_fields/tests.py b/tests/modeltests/update_only_fields/tests.py index bce53ca621..c904c97236 100644 --- a/tests/modeltests/update_only_fields/tests.py +++ b/tests/modeltests/update_only_fields/tests.py @@ -18,6 +18,107 @@ class UpdateOnlyFieldsTests(TestCase): self.assertEqual(s.gender, 'F') self.assertEqual(s.name, 'Ian') + def test_update_fields_deferred(self): + s = Person.objects.create(name='Sara', gender='F', pid=22) + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.defer("gender", "pid").get(pk=s.pk) + s1.name = "Emily" + s1.gender = "M" + + with self.assertNumQueries(1): + s1.save() + + s2 = Person.objects.get(pk=s1.pk) + self.assertEqual(s2.name, "Emily") + self.assertEqual(s2.gender, "M") + + def test_update_fields_only_1(self): + s = Person.objects.create(name='Sara', gender='F') + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.only('name').get(pk=s.pk) + s1.name = "Emily" + s1.gender = "M" + + with self.assertNumQueries(1): + s1.save() + + s2 = Person.objects.get(pk=s1.pk) + self.assertEqual(s2.name, "Emily") + self.assertEqual(s2.gender, "M") + + def test_update_fields_only_2(self): + s = Person.objects.create(name='Sara', gender='F', pid=22) + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.only('name').get(pk=s.pk) + s1.name = "Emily" + s1.gender = "M" + + with self.assertNumQueries(2): + s1.save(update_fields=['pid']) + + s2 = Person.objects.get(pk=s1.pk) + self.assertEqual(s2.name, "Sara") + self.assertEqual(s2.gender, "F") + + def test_update_fields_only_repeated(self): + s = Person.objects.create(name='Sara', gender='F') + self.assertEqual(s.gender, 'F') + + s1 = Person.objects.only('name').get(pk=s.pk) + s1.gender = 'M' + with self.assertNumQueries(1): + s1.save() + # Test that the deferred class does not remember that gender was + # set, instead the instace should remember this. + s1 = Person.objects.only('name').get(pk=s.pk) + with self.assertNumQueries(1): + s1.save() + + def test_update_fields_inheritance_defer(self): + profile_boss = Profile.objects.create(name='Boss', salary=3000) + e1 = Employee.objects.create(name='Sara', gender='F', + employee_num=1, profile=profile_boss) + e1 = Employee.objects.only('name').get(pk=e1.pk) + e1.name = 'Linda' + with self.assertNumQueries(1): + e1.save() + self.assertEqual(Employee.objects.get(pk=e1.pk).name, + 'Linda') + + def test_update_fields_fk_defer(self): + profile_boss = Profile.objects.create(name='Boss', salary=3000) + profile_receptionist = Profile.objects.create(name='Receptionist', salary=1000) + e1 = Employee.objects.create(name='Sara', gender='F', + employee_num=1, profile=profile_boss) + e1 = Employee.objects.only('profile').get(pk=e1.pk) + e1.profile = profile_receptionist + with self.assertNumQueries(1): + e1.save() + self.assertEqual(Employee.objects.get(pk=e1.pk).profile, profile_receptionist) + e1.profile_id = profile_boss.pk + with self.assertNumQueries(1): + e1.save() + self.assertEqual(Employee.objects.get(pk=e1.pk).profile, profile_boss) + + def test_select_related_only_interaction(self): + profile_boss = Profile.objects.create(name='Boss', salary=3000) + e1 = Employee.objects.create(name='Sara', gender='F', + employee_num=1, profile=profile_boss) + e1 = Employee.objects.only('profile__salary').select_related('profile').get(pk=e1.pk) + profile_boss.name = 'Clerk' + profile_boss.salary = 1000 + profile_boss.save() + # The loaded salary of 3000 gets saved, the name of 'Clerk' isn't + # overwritten. + with self.assertNumQueries(1): + e1.profile.save() + reloaded_profile = Profile.objects.get(pk=profile_boss.pk) + self.assertEqual(reloaded_profile.name, profile_boss.name) + self.assertEqual(reloaded_profile.salary, 3000) + def test_update_fields_m2m(self): profile_boss = Profile.objects.create(name='Boss', salary=3000) e1 = Employee.objects.create(name='Sara', gender='F', diff --git a/tests/modeltests/validation/models.py b/tests/modeltests/validation/models.py index 26fff4b863..db083290fb 100644 --- a/tests/modeltests/validation/models.py +++ b/tests/modeltests/validation/models.py @@ -4,6 +4,7 @@ from datetime import datetime from django.core.exceptions import ValidationError from django.db import models +from django.utils.encoding import python_2_unicode_compatible def validate_answer_to_universe(value): @@ -66,13 +67,14 @@ class Article(models.Model): if self.pub_date is None: self.pub_date = datetime.now() +@python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=50, unique_for_date='posted', blank=True) slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) posted = models.DateField() - def __unicode__(self): + def __str__(self): return self.name class FlexibleDatePost(models.Model): @@ -89,6 +91,8 @@ class GenericIPAddressTestModel(models.Model): generic_ip = models.GenericIPAddressField(blank=True, null=True, unique=True) v4_ip = models.GenericIPAddressField(blank=True, null=True, protocol="ipv4") v6_ip = models.GenericIPAddressField(blank=True, null=True, protocol="ipv6") + ip_verbose_name = models.GenericIPAddressField("IP Address Verbose", + blank=True, null=True) class GenericIPAddrUnpackUniqueTest(models.Model): generic_v4unpack_ip = models.GenericIPAddressField(blank=True, unique=True, unpack_ipv4=True) diff --git a/tests/modeltests/validation/tests.py b/tests/modeltests/validation/tests.py index 4f658203df..58b7b94e7f 100644 --- a/tests/modeltests/validation/tests.py +++ b/tests/modeltests/validation/tests.py @@ -79,7 +79,7 @@ class ModelFormsTests(TestCase): 'pub_date': '2010-1-10 14:49:00' } form = ArticleForm(data) - self.assertEqual(form.errors.keys(), []) + self.assertEqual(list(form.errors), []) article = form.save(commit=False) article.author = self.author article.save() @@ -95,7 +95,7 @@ class ModelFormsTests(TestCase): } article = Article(author_id=self.author.id) form = ArticleForm(data, instance=article) - self.assertEqual(form.errors.keys(), []) + self.assertEqual(list(form.errors), []) self.assertNotEqual(form.instance.pub_date, None) article = form.save() @@ -108,7 +108,7 @@ class ModelFormsTests(TestCase): } article = Article(author_id=self.author.id) form = ArticleForm(data, instance=article) - self.assertEqual(form.errors.keys(), ['pub_date']) + self.assertEqual(list(form.errors), ['pub_date']) class GenericIPAddressFieldTests(ValidationTestCase): diff --git a/tests/regressiontests/admin_changelist/models.py b/tests/regressiontests/admin_changelist/models.py index 487db50689..4ba2f9c503 100644 --- a/tests/regressiontests/admin_changelist/models.py +++ b/tests/regressiontests/admin_changelist/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Event(models.Model): # Oracle can have problems with a column named "date" @@ -20,17 +21,19 @@ class Band(models.Model): nr_of_members = models.PositiveIntegerField() genres = models.ManyToManyField(Genre) +@python_2_unicode_compatible class Musician(models.Model): name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=30) members = models.ManyToManyField(Musician, through='Membership') - def __unicode__(self): + def __str__(self): return self.name class Membership(models.Model): diff --git a/tests/regressiontests/admin_changelist/tests.py b/tests/regressiontests/admin_changelist/tests.py index 1ed963aaf2..be88c9a161 100644 --- a/tests/regressiontests/admin_changelist/tests.py +++ b/tests/regressiontests/admin_changelist/tests.py @@ -317,7 +317,7 @@ class ChangeListTests(TestCase): m.list_editable, m) self.assertEqual(cl.query_set.count(), 60) self.assertEqual(cl.paginator.count, 60) - self.assertEqual(cl.paginator.page_range, [1, 2, 3, 4, 5, 6]) + self.assertEqual(list(cl.paginator.page_range), [1, 2, 3, 4, 5, 6]) # Test custom queryset m = FilteredChildAdmin(Child, admin.site) @@ -327,7 +327,7 @@ class ChangeListTests(TestCase): m.list_editable, m) self.assertEqual(cl.query_set.count(), 30) self.assertEqual(cl.paginator.count, 30) - self.assertEqual(cl.paginator.page_range, [1, 2, 3]) + self.assertEqual(list(cl.paginator.page_range), [1, 2, 3]) def test_computed_list_display_localization(self): """ diff --git a/tests/regressiontests/admin_custom_urls/fixtures/actions.json b/tests/regressiontests/admin_custom_urls/fixtures/actions.json index d803393a12..a63cf8135c 100644 --- a/tests/regressiontests/admin_custom_urls/fixtures/actions.json +++ b/tests/regressiontests/admin_custom_urls/fixtures/actions.json @@ -40,5 +40,12 @@ "fields": { "description": "An action with a name suspected of being a XSS attempt" } + }, + { + "pk": "The name of an action", + "model": "admin_custom_urls.action", + "fields": { + "description": "A generic action" + } } -]
\ No newline at end of file +] diff --git a/tests/regressiontests/admin_custom_urls/models.py b/tests/regressiontests/admin_custom_urls/models.py index facb4b52e7..a5b4983b09 100644 --- a/tests/regressiontests/admin_custom_urls/models.py +++ b/tests/regressiontests/admin_custom_urls/models.py @@ -2,13 +2,15 @@ from functools import update_wrapper from django.contrib import admin from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Action(models.Model): name = models.CharField(max_length=50, primary_key=True) description = models.CharField(max_length=70) - def __unicode__(self): + def __str__(self): return self.name @@ -26,7 +28,7 @@ class ActionAdmin(admin.ModelAdmin): Remove all entries named 'name' from the ModelAdmin instance URL patterns list """ - return filter(lambda e: e.name != name, super(ActionAdmin, self).get_urls()) + return [url for url in super(ActionAdmin, self).get_urls() if url.name != name] def get_urls(self): # Add the URL of our custom 'add_view' view to the front of the URLs diff --git a/tests/regressiontests/admin_custom_urls/tests.py b/tests/regressiontests/admin_custom_urls/tests.py index 4a6235e7d5..64ff9f6692 100644 --- a/tests/regressiontests/admin_custom_urls/tests.py +++ b/tests/regressiontests/admin_custom_urls/tests.py @@ -10,6 +10,12 @@ from .models import Action @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class AdminCustomUrlsTest(TestCase): + """ + Remember that: + * The Action model has a CharField PK. + * The ModelAdmin for Action customizes the add_view URL, it's + '<app name>/<model name>/!add/' + """ fixtures = ['users.json', 'actions.json'] def setUp(self): @@ -20,23 +26,24 @@ class AdminCustomUrlsTest(TestCase): def testBasicAddGet(self): """ - A smoke test to ensure GET on the add_view works. + Ensure GET on the add_view works. """ response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/') self.assertIsInstance(response, TemplateResponse) self.assertEqual(response.status_code, 200) def testAddWithGETArgs(self): + """ + Ensure GET on the add_view plus specifying a field value in the query + string works. + """ response = self.client.get('/custom_urls/admin/admin_custom_urls/action/!add/', {'name': 'My Action'}) self.assertEqual(response.status_code, 200) - self.assertTrue( - 'value="My Action"' in response.content, - "Couldn't find an input with the right value in the response." - ) + self.assertContains(response, 'value="My Action"') def testBasicAddPost(self): """ - A smoke test to ensure POST on add_view works. + Ensure POST on add_view works. """ post_data = { '_popup': '1', @@ -50,8 +57,7 @@ class AdminCustomUrlsTest(TestCase): def testAdminUrlsNoClash(self): """ - Test that some admin URLs work correctly. The model has a CharField - PK and the add_view URL has been customized. + Test that some admin URLs work correctly. """ # Should get the change_view for model instance with PK 'add', not show # the add_view @@ -60,17 +66,28 @@ class AdminCustomUrlsTest(TestCase): self.assertContains(response, 'Change action') # Ditto, but use reverse() to build the URL - path = reverse('admin:%s_action_change' % Action._meta.app_label, + url = reverse('admin:%s_action_change' % Action._meta.app_label, args=('add',)) - response = self.client.get(path) + response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Change action') # Should correctly get the change_view for the model instance with the - # funny-looking PK - path = reverse('admin:%s_action_change' % Action._meta.app_label, + # funny-looking PK (the one wth a 'path/to/html/document.html' value) + url = reverse('admin:%s_action_change' % Action._meta.app_label, args=("path/to/html/document.html",)) - response = self.client.get(path) + response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Change action') self.assertContains(response, 'value="path/to/html/document.html"') + + def testChangeViewHistoryButton(self): + url = reverse('admin:%s_action_change' % Action._meta.app_label, + args=('The name of an action',)) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + expected_link = reverse('admin:%s_action_history' % + Action._meta.app_label, + args=('The name of an action',)) + self.assertContains(response, '<a href="%s" class="historylink"' % + expected_link) diff --git a/tests/regressiontests/admin_filters/models.py b/tests/regressiontests/admin_filters/models.py index 371c67061f..e0b8bde2de 100644 --- a/tests/regressiontests/admin_filters/models.py +++ b/tests/regressiontests/admin_filters/models.py @@ -2,8 +2,10 @@ from __future__ import unicode_literals from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Book(models.Model): title = models.CharField(max_length=50) year = models.PositiveIntegerField(null=True, blank=True) @@ -13,20 +15,22 @@ class Book(models.Model): date_registered = models.DateField(null=True) no = models.IntegerField(verbose_name='number', blank=True, null=True) # This field is intentionally 2 characters long. See #16080. - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Department(models.Model): code = models.CharField(max_length=4, unique=True) description = models.CharField(max_length=50, blank=True, null=True) - def __unicode__(self): + def __str__(self): return self.description +@python_2_unicode_compatible class Employee(models.Model): department = models.ForeignKey(Department, to_field="code") name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/admin_inlines/models.py b/tests/regressiontests/admin_inlines/models.py index 5a0f4d84b9..5b703a7481 100644 --- a/tests/regressiontests/admin_inlines/models.py +++ b/tests/regressiontests/admin_inlines/models.py @@ -7,22 +7,26 @@ from __future__ import unicode_literals from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Teacher(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Child(models.Model): name = models.CharField(max_length=50) teacher = models.ForeignKey(Teacher) @@ -31,7 +35,7 @@ class Child(models.Model): object_id = models.PositiveIntegerField() parent = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return 'I am %s, a child of %s' % (self.name, self.parent) diff --git a/tests/regressiontests/admin_scripts/custom_templates/project_template/ticket-18091-non-ascii-template.txt b/tests/regressiontests/admin_scripts/custom_templates/project_template/ticket-18091-non-ascii-template.txt new file mode 100644 index 0000000000..873eaded88 --- /dev/null +++ b/tests/regressiontests/admin_scripts/custom_templates/project_template/ticket-18091-non-ascii-template.txt @@ -0,0 +1,2 @@ +Some non-ASCII text for testing ticket #18091: +üäö € diff --git a/tests/regressiontests/admin_scripts/models.py b/tests/regressiontests/admin_scripts/models.py index da803d0174..cd96052098 100644 --- a/tests/regressiontests/admin_scripts/models.py +++ b/tests/regressiontests/admin_scripts/models.py @@ -1,11 +1,13 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/regressiontests/admin_scripts/tests.py b/tests/regressiontests/admin_scripts/tests.py index 546fa7d79c..2710f80929 100644 --- a/tests/regressiontests/admin_scripts/tests.py +++ b/tests/regressiontests/admin_scripts/tests.py @@ -1,8 +1,10 @@ +# -*- coding: utf-8 -*- """ A series of tests to establish that the command-line managment tools work as advertised - especially with regards to the handling of the DJANGO_SETTINGS_MODULE and default settings.py files. """ +from __future__ import unicode_literals import os import re @@ -10,6 +12,7 @@ import shutil import socket import subprocess import sys +import codecs from django import conf, bin, get_version from django.conf import settings @@ -71,6 +74,10 @@ class AdminScriptTestCase(unittest.TestCase): os.remove(full_name + 'c') except OSError: pass + # Also remove a __pycache__ directory, if it exists + cache_name = os.path.join(test_dir, '__pycache__') + if os.path.isdir(cache_name): + shutil.rmtree(cache_name) def _ext_backend_paths(self): """ @@ -110,14 +117,11 @@ class AdminScriptTestCase(unittest.TestCase): python_path.extend(ext_backend_base_dirs) os.environ[python_path_var_name] = os.pathsep.join(python_path) - # Silence the DeprecationWarning caused by having a locale directory - # in the project directory. - cmd = [sys.executable, '-Wignore:::django.utils.translation', script] - # Move to the test directory and run os.chdir(test_dir) - out, err = subprocess.Popen(cmd + args, - stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() + out, err = subprocess.Popen([sys.executable, script] + args, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, + universal_newlines=True).communicate() # Restore the old environment if old_django_settings_module: @@ -1572,3 +1576,17 @@ class StartProject(LiveServerTestCase, AdminScriptTestCase): self.assertOutput(err, "Destination directory '%s' does not exist, please create it first." % testproject_dir) self.assertFalse(os.path.exists(testproject_dir)) + + def test_custom_project_template_with_non_ascii_templates(self): + "Ticket 18091: Make sure the startproject management command is able to render templates with non-ASCII content" + template_path = os.path.join(test_dir, 'admin_scripts', 'custom_templates', 'project_template') + args = ['startproject', '--template', template_path, '--extension=txt', 'customtestproject'] + testproject_dir = os.path.join(test_dir, 'customtestproject') + + out, err = self.run_django_admin(args) + self.addCleanup(shutil.rmtree, testproject_dir) + self.assertNoOutput(err) + self.assertTrue(os.path.isdir(testproject_dir)) + path = os.path.join(testproject_dir, 'ticket-18091-non-ascii-template.txt') + self.assertEqual(codecs.open(path, 'r', 'utf-8').read(), + 'Some non-ASCII text for testing ticket #18091:\nüäö €\n') diff --git a/tests/regressiontests/admin_util/models.py b/tests/regressiontests/admin_util/models.py index 5541097022..b3504a1fa4 100644 --- a/tests/regressiontests/admin_util/models.py +++ b/tests/regressiontests/admin_util/models.py @@ -1,5 +1,6 @@ from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class Article(models.Model): @@ -18,11 +19,12 @@ class Article(models.Model): return "nothing" test_from_model_with_override.short_description = "not What you Expect" +@python_2_unicode_compatible class Count(models.Model): num = models.PositiveSmallIntegerField() parent = models.ForeignKey('self', null=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.num) class Event(models.Model): diff --git a/tests/regressiontests/admin_util/tests.py b/tests/regressiontests/admin_util/tests.py index 6b6dad4336..d04740ce95 100644 --- a/tests/regressiontests/admin_util/tests.py +++ b/tests/regressiontests/admin_util/tests.py @@ -171,7 +171,7 @@ class UtilTests(unittest.TestCase): ) self.assertEqual( label_for_field("__str__", Article), - b"article" + str("article") ) self.assertRaises( diff --git a/tests/regressiontests/admin_validation/models.py b/tests/regressiontests/admin_validation/models.py index 5e080a9232..d23849a2a8 100644 --- a/tests/regressiontests/admin_validation/models.py +++ b/tests/regressiontests/admin_validation/models.py @@ -3,12 +3,14 @@ Tests of ModelAdmin validation logic. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Album(models.Model): title = models.CharField(max_length=150) +@python_2_unicode_compatible class Song(models.Model): title = models.CharField(max_length=150) album = models.ForeignKey(Album) @@ -17,7 +19,7 @@ class Song(models.Model): class Meta: ordering = ('title',) - def __unicode__(self): + def __str__(self): return self.title def readonly_method_on_model(self): diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index aee193b572..0d5e327ecf 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -10,6 +10,7 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.core.files.storage import FileSystemStorage from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Section(models.Model): @@ -20,6 +21,7 @@ class Section(models.Model): name = models.CharField(max_length=100) +@python_2_unicode_compatible class Article(models.Model): """ A simple article to test admin views. Test backwards compatibility. @@ -29,7 +31,7 @@ class Article(models.Model): date = models.DateTimeField() section = models.ForeignKey(Section, null=True, blank=True) - def __unicode__(self): + def __str__(self): return self.title def model_year(self): @@ -38,30 +40,33 @@ class Article(models.Model): model_year.short_description = '' +@python_2_unicode_compatible class Book(models.Model): """ A simple book that has chapters. """ name = models.CharField(max_length=100, verbose_name='¿Name?') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Promo(models.Model): name = models.CharField(max_length=100, verbose_name='¿Name?') book = models.ForeignKey(Book) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Chapter(models.Model): title = models.CharField(max_length=100, verbose_name='¿Title?') content = models.TextField() book = models.ForeignKey(Book) - def __unicode__(self): + def __str__(self): return self.title class Meta: @@ -69,19 +74,21 @@ class Chapter(models.Model): verbose_name = '¿Chapter?' +@python_2_unicode_compatible class ChapterXtra1(models.Model): chap = models.OneToOneField(Chapter, verbose_name='¿Chap?') xtra = models.CharField(max_length=100, verbose_name='¿Xtra?') - def __unicode__(self): + def __str__(self): return '¿Xtra1: %s' % self.xtra +@python_2_unicode_compatible class ChapterXtra2(models.Model): chap = models.OneToOneField(Chapter, verbose_name='¿Chap?') xtra = models.CharField(max_length=100, verbose_name='¿Xtra?') - def __unicode__(self): + def __str__(self): return '¿Xtra2: %s' % self.xtra @@ -94,20 +101,22 @@ class CustomArticle(models.Model): date = models.DateTimeField() +@python_2_unicode_compatible class ModelWithStringPrimaryKey(models.Model): string_pk = models.CharField(max_length=255, primary_key=True) - def __unicode__(self): + def __str__(self): return self.string_pk def get_absolute_url(self): return '/dummy/%s/' % self.string_pk +@python_2_unicode_compatible class Color(models.Model): value = models.CharField(max_length=10) warm = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.value # we replicate Color to register with another ModelAdmin @@ -115,29 +124,33 @@ class Color2(Color): class Meta: proxy = True +@python_2_unicode_compatible class Thing(models.Model): title = models.CharField(max_length=20) color = models.ForeignKey(Color, limit_choices_to={'warm': True}) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Actor(models.Model): name = models.CharField(max_length=50) age = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Inquisition(models.Model): expected = models.BooleanField() leader = models.ForeignKey(Actor) country = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "by %s from %s" % (self.leader, self.country) +@python_2_unicode_compatible class Sketch(models.Model): title = models.CharField(max_length=100) inquisition = models.ForeignKey(Inquisition, limit_choices_to={'leader__name': 'Palin', @@ -145,7 +158,7 @@ class Sketch(models.Model): 'expected': False, }) - def __unicode__(self): + def __str__(self): return self.title @@ -161,6 +174,7 @@ class Fabric(models.Model): surface = models.CharField(max_length=20, choices=NG_CHOICES) +@python_2_unicode_compatible class Person(models.Model): GENDER_CHOICES = ( (1, "Male"), @@ -171,20 +185,22 @@ class Person(models.Model): age = models.IntegerField(default=21) alive = models.BooleanField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Persona(models.Model): """ A simple persona associated with accounts, to test inlining of related accounts which inherit from a common accounts class. """ name = models.CharField(blank=False, max_length=80) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Account(models.Model): """ A simple, generic account encapsulating the information shared by all @@ -194,7 +210,7 @@ class Account(models.Model): persona = models.ForeignKey(Persona, related_name="accounts") servicename = 'generic service' - def __unicode__(self): + def __str__(self): return "%s: %s" % (self.servicename, self.username) @@ -208,11 +224,12 @@ class BarAccount(Account): servicename = 'bar' +@python_2_unicode_compatible class Subscriber(models.Model): name = models.CharField(blank=False, max_length=80) email = models.EmailField(blank=False, max_length=175) - def __unicode__(self): + def __str__(self): return "%s (%s)" % (self.name, self.email) @@ -249,8 +266,9 @@ class Child(models.Model): name = models.CharField(max_length=30, blank=True) +@python_2_unicode_compatible class EmptyModel(models.Model): - def __unicode__(self): + def __str__(self): return "Primary key = %s" % self.id @@ -332,6 +350,7 @@ class FancyDoodad(Doodad): expensive = models.BooleanField(default=True) +@python_2_unicode_compatible class Category(models.Model): collector = models.ForeignKey(Collector) order = models.PositiveIntegerField() @@ -339,7 +358,7 @@ class Category(models.Model): class Meta: ordering = ('order',) - def __unicode__(self): + def __str__(self): return '%s:o%s' % (self.id, self.order) @@ -376,17 +395,19 @@ class Post(models.Model): return "Very awesome." +@python_2_unicode_compatible class Gadget(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Villain(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name @@ -394,6 +415,7 @@ class SuperVillain(Villain): pass +@python_2_unicode_compatible class FunkyTag(models.Model): "Because we all know there's only one real use case for GFKs." name = models.CharField(max_length=25) @@ -401,59 +423,65 @@ class FunkyTag(models.Model): object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Plot(models.Model): name = models.CharField(max_length=100) team_leader = models.ForeignKey(Villain, related_name='lead_plots') contact = models.ForeignKey(Villain, related_name='contact_plots') tags = generic.GenericRelation(FunkyTag) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class PlotDetails(models.Model): details = models.CharField(max_length=100) plot = models.OneToOneField(Plot) - def __unicode__(self): + def __str__(self): return self.details +@python_2_unicode_compatible class SecretHideout(models.Model): """ Secret! Not registered with the admin! """ location = models.CharField(max_length=100) villain = models.ForeignKey(Villain) - def __unicode__(self): + def __str__(self): return self.location +@python_2_unicode_compatible class SuperSecretHideout(models.Model): """ Secret! Not registered with the admin! """ location = models.CharField(max_length=100) supervillain = models.ForeignKey(SuperVillain) - def __unicode__(self): + def __str__(self): return self.location +@python_2_unicode_compatible class CyclicOne(models.Model): name = models.CharField(max_length=25) two = models.ForeignKey('CyclicTwo') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class CyclicTwo(models.Model): name = models.CharField(max_length=25) one = models.ForeignKey(CyclicOne) - def __unicode__(self): + def __str__(self): return self.name @@ -484,11 +512,12 @@ class Question(models.Model): question = models.CharField(max_length=20) +@python_2_unicode_compatible class Answer(models.Model): question = models.ForeignKey(Question, on_delete=models.PROTECT) answer = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.answer @@ -523,11 +552,12 @@ class Paper(models.Model): author = models.CharField(max_length=30, blank=True, null=True) +@python_2_unicode_compatible class CoverLetter(models.Model): author = models.CharField(max_length=30) date_written = models.DateField(null=True, blank=True) - def __unicode__(self): + def __str__(self): return self.author @@ -575,10 +605,11 @@ class AdminOrderedCallable(models.Model): order = models.IntegerField() stuff = models.CharField(max_length=200) +@python_2_unicode_compatible class Report(models.Model): title = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 09be55baba..cf7d4855fb 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -30,7 +30,7 @@ from django.template.response import TemplateResponse from django.test import TestCase from django.utils import formats, translation, unittest from django.utils.cache import get_max_age -from django.utils.encoding import iri_to_uri +from django.utils.encoding import iri_to_uri, force_bytes from django.utils.html import escape from django.utils.http import urlencode from django.utils import six @@ -78,12 +78,22 @@ class AdminViewBasicTest(TestCase): self.client.logout() formats.reset_format_cache() + def assertContentBefore(self, response, text1, text2, failing_msg=None): + """ + Testing utility asserting that text1 appears before text2 in response + content. + """ + self.assertEqual(response.status_code, 200) + self.assertTrue(response.content.index(force_bytes(text1)) < response.content.index(force_bytes(text2)), + failing_msg + ) + def testTrailingSlashRequired(self): """ If you leave off the trailing slash, app should redirect and add it. """ - request = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit) - self.assertRedirects(request, + response = self.client.get('/test_admin/%s/admin_views/article/add' % self.urlbit) + self.assertRedirects(response, '/test_admin/%s/admin_views/article/add/' % self.urlbit, status_code=301 ) @@ -219,12 +229,10 @@ class AdminViewBasicTest(TestCase): (column 2 is callable_year in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': 2}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index('Oldest content') < response.content.index('Middle content') and - response.content.index('Middle content') < response.content.index('Newest content'), - "Results of sorting on callable are out of order." - ) + self.assertContentBefore(response, 'Oldest content', 'Middle content', + "Results of sorting on callable are out of order.") + self.assertContentBefore(response, 'Middle content', 'Newest content', + "Results of sorting on callable are out of order.") def testChangeListSortingModel(self): """ @@ -232,12 +240,10 @@ class AdminViewBasicTest(TestCase): (colunn 3 is 'model_year' in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '-3'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index('Newest content') < response.content.index('Middle content') and - response.content.index('Middle content') < response.content.index('Oldest content'), - "Results of sorting on Model method are out of order." - ) + self.assertContentBefore(response, 'Newest content', 'Middle content', + "Results of sorting on Model method are out of order.") + self.assertContentBefore(response, 'Middle content', 'Oldest content', + "Results of sorting on Model method are out of order.") def testChangeListSortingModelAdmin(self): """ @@ -245,12 +251,10 @@ class AdminViewBasicTest(TestCase): (colunn 4 is 'modeladmin_year' in ArticleAdmin) """ response = self.client.get('/test_admin/%s/admin_views/article/' % self.urlbit, {'o': '4'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index('Oldest content') < response.content.index('Middle content') and - response.content.index('Middle content') < response.content.index('Newest content'), - "Results of sorting on ModelAdmin method are out of order." - ) + self.assertContentBefore(response, 'Oldest content', 'Middle content', + "Results of sorting on ModelAdmin method are out of order.") + self.assertContentBefore(response, 'Middle content', 'Newest content', + "Results of sorting on ModelAdmin method are out of order.") def testChangeListSortingMultiple(self): p1 = Person.objects.create(name="Chris", gender=1, alive=True) @@ -262,19 +266,13 @@ class AdminViewBasicTest(TestCase): # This hard-codes the URL because it'll fail if it runs against the # 'admin2' custom admin (which doesn't have the Person model). response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '1.2'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p3.id) < response.content.index(link % p1.id) and - response.content.index(link % p1.id) < response.content.index(link % p2.id) - ) + self.assertContentBefore(response, link % p3.id, link % p1.id) + self.assertContentBefore(response, link % p1.id, link % p2.id) # Sort by gender descending, name response = self.client.get('/test_admin/admin/admin_views/person/', {'o': '-2.1'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p2.id) < response.content.index(link % p3.id) and - response.content.index(link % p3.id) < response.content.index(link % p1.id) - ) + self.assertContentBefore(response, link % p2.id, link % p3.id) + self.assertContentBefore(response, link % p3.id, link % p1.id) def testChangeListSortingPreserveQuerySetOrdering(self): """ @@ -291,11 +289,8 @@ class AdminViewBasicTest(TestCase): # This hard-codes the URL because it'll fail if it runs against the # 'admin2' custom admin (which doesn't have the Person model). response = self.client.get('/test_admin/admin/admin_views/person/', {}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p3.id) < response.content.index(link % p2.id) and - response.content.index(link % p2.id) < response.content.index(link % p1.id) - ) + self.assertContentBefore(response, link % p3.id, link % p2.id) + self.assertContentBefore(response, link % p2.id, link % p1.id) def testChangeListSortingModelMeta(self): # Test ordering on Model Meta is respected @@ -305,17 +300,11 @@ class AdminViewBasicTest(TestCase): link = '<a href="%s/' response = self.client.get('/test_admin/admin/admin_views/language/', {}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % l2.pk) < response.content.index(link % l1.pk) - ) + self.assertContentBefore(response, link % l2.pk, link % l1.pk) # Test we can override with query string response = self.client.get('/test_admin/admin/admin_views/language/', {'o':'-1'}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % l1.pk) < response.content.index(link % l2.pk) - ) + self.assertContentBefore(response, link % l1.pk, link % l2.pk) def testChangeListSortingOverrideModelAdmin(self): # Test ordering on Model Admin is respected, and overrides Model Meta @@ -325,10 +314,7 @@ class AdminViewBasicTest(TestCase): link = '<a href="%s/' response = self.client.get('/test_admin/admin/admin_views/podcast/', {}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p1.pk) < response.content.index(link % p2.pk) - ) + self.assertContentBefore(response, link % p1.pk, link % p2.pk) def testMultipleSortSameField(self): # Check that we get the columns we expect if we have two columns @@ -339,17 +325,13 @@ class AdminViewBasicTest(TestCase): link = '<a href="%s/' response = self.client.get('/test_admin/admin/admin_views/podcast/', {}) - self.assertEqual(response.status_code, 200) - self.assertTrue( - response.content.index(link % p1.pk) < response.content.index(link % p2.pk) - ) + self.assertContentBefore(response, link % p1.pk, link % p2.pk) p1 = ComplexSortedPerson.objects.create(name="Bob", age=10) p2 = ComplexSortedPerson.objects.create(name="Amy", age=20) link = '<a href="%s/' response = self.client.get('/test_admin/admin/admin_views/complexsortedperson/', {}) - self.assertEqual(response.status_code, 200) # Should have 5 columns (including action checkbox col) self.assertContains(response, '<th scope="col"', count=5) @@ -357,14 +339,10 @@ class AdminViewBasicTest(TestCase): self.assertContains(response, 'Colored name') # Check order - self.assertTrue( - response.content.index('Name') < response.content.index('Colored name') - ) + self.assertContentBefore(response, 'Name', 'Colored name') # Check sorting - should be by name - self.assertTrue( - response.content.index(link % p2.id) < response.content.index(link % p1.id) - ) + self.assertContentBefore(response, link % p2.id, link % p1.id) def testSortIndicatorsAdminOrder(self): """ @@ -389,8 +367,8 @@ class AdminViewBasicTest(TestCase): # the implicit 'action_checkbox' and 1 being the column 'stuff'. self.assertEqual(response.context['cl'].get_ordering_field_columns(), {2: 'asc'}) # Check order of records. - self.assertTrue(response.content.index('The First Item') < - response.content.index('The Middle Item') < response.content.index('The Last Item')) + self.assertContentBefore(response, 'The First Item', 'The Middle Item') + self.assertContentBefore(response, 'The Middle Item', 'The Last Item') def testLimitedFilter(self): """Ensure admin changelist filters do not contain objects excluded via limit_choices_to. @@ -480,12 +458,9 @@ class AdminViewBasicTest(TestCase): has been used in the choices option of a field. """ response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit) - self.assertEqual(response.status_code, 200) - self.assertTrue( - '<a href="1/">Horizontal</a>' in response.content and - '<a href="2/">Vertical</a>' in response.content, - "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group." - ) + fail_msg = "Changelist table isn't showing the right human-readable values set by a model field 'choices' option named group." + self.assertContains(response, '<a href="1/">Horizontal</a>', msg_prefix=fail_msg, html=True) + self.assertContains(response, '<a href="2/">Vertical</a>', msg_prefix=fail_msg, html=True) def testNamedGroupFieldChoicesFilter(self): """ @@ -493,13 +468,12 @@ class AdminViewBasicTest(TestCase): been used in the choices option of a model field. """ response = self.client.get('/test_admin/%s/admin_views/fabric/' % self.urlbit) - self.assertEqual(response.status_code, 200) + fail_msg = "Changelist filter isn't showing options contained inside a model field 'choices' option named group." self.assertContains(response, '<div id="changelist-filter">') - self.assertTrue( - '<a href="?surface__exact=x">Horizontal</a>' in response.content and - '<a href="?surface__exact=y">Vertical</a>' in response.content, - "Changelist filter isn't showing options contained inside a model field 'choices' option named group." - ) + self.assertContains(response, + '<a href="?surface__exact=x">Horizontal</a>', msg_prefix=fail_msg, html=True) + self.assertContains(response, + '<a href="?surface__exact=y">Vertical</a>', msg_prefix=fail_msg, html=True) def testChangeListNullBooleanDisplay(self): Post.objects.create(public=None) @@ -590,8 +564,8 @@ class AdminViewBasicTest(TestCase): user.save() response = self.client.get('/test_admin/admin/') - self.assertFalse(reverse('admin:password_change') in response.content, - msg='The "change password" link should not be displayed if a user does not have a usable password.') + self.assertNotContains(response, reverse('admin:password_change'), + msg_prefix='The "change password" link should not be displayed if a user does not have a usable password.') def test_change_view_with_show_delete_extra_context(self): """ @@ -618,7 +592,7 @@ class AdminViewFormUrlTest(TestCase): def testChangeFormUrlHasCorrectValue(self): """ - Tests whether change_view has form_url in request.context + Tests whether change_view has form_url in response.context """ response = self.client.get('/test_admin/%s/admin_views/section/1/' % self.urlbit) self.assertTrue('form_url' in response.context, msg='form_url not present in response.context') @@ -755,39 +729,39 @@ class CustomModelAdminTest(AdminViewBasicTest): def testCustomAdminSiteLoginTemplate(self): self.client.logout() - request = self.client.get('/test_admin/admin2/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/login.html') - self.assertTrue('Hello from a custom login template' in request.content) + response = self.client.get('/test_admin/admin2/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/login.html') + self.assertContains(response, 'Hello from a custom login template') def testCustomAdminSiteLogoutTemplate(self): - request = self.client.get('/test_admin/admin2/logout/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/logout.html') - self.assertTrue('Hello from a custom logout template' in request.content) + response = self.client.get('/test_admin/admin2/logout/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/logout.html') + self.assertContains(response, 'Hello from a custom logout template') def testCustomAdminSiteIndexViewAndTemplate(self): - request = self.client.get('/test_admin/admin2/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/index.html') - self.assertTrue('Hello from a custom index template *bar*' in request.content) + response = self.client.get('/test_admin/admin2/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/index.html') + self.assertContains(response, 'Hello from a custom index template *bar*') def testCustomAdminSitePasswordChangeTemplate(self): - request = self.client.get('/test_admin/admin2/password_change/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/password_change_form.html') - self.assertTrue('Hello from a custom password change form template' in request.content) + response = self.client.get('/test_admin/admin2/password_change/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/password_change_form.html') + self.assertContains(response, 'Hello from a custom password change form template') def testCustomAdminSitePasswordChangeDoneTemplate(self): - request = self.client.get('/test_admin/admin2/password_change/done/') - self.assertIsInstance(request, TemplateResponse) - self.assertTemplateUsed(request, 'custom_admin/password_change_done.html') - self.assertTrue('Hello from a custom password change done template' in request.content) + response = self.client.get('/test_admin/admin2/password_change/done/') + self.assertIsInstance(response, TemplateResponse) + self.assertTemplateUsed(response, 'custom_admin/password_change_done.html') + self.assertContains(response, 'Hello from a custom password change done template') def testCustomAdminSiteView(self): self.client.login(username='super', password='secret') response = self.client.get('/test_admin/%s/my_view/' % self.urlbit) - self.assertEqual(response.content, "Django is a magical pony!") + self.assertEqual(response.content, b"Django is a magical pony!") def get_perm(Model, perm): """Return the permission object, for the Model""" @@ -885,16 +859,16 @@ class AdminViewPermissionsTest(TestCase): a 200 status code. """ # Super User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.super_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Test if user enters e-mail address - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.super_email_login) self.assertContains(login, "Your e-mail address is not your username") # only correct passwords get a username hint @@ -907,47 +881,47 @@ class AdminViewPermissionsTest(TestCase): self.assertContains(login, ERROR_MESSAGE) # Add User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.adduser_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Change User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.changeuser_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Delete User - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.deleteuser_login) self.assertRedirects(login, '/test_admin/admin/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Regular User should not be able to login. - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.joepublic_login) self.assertEqual(login.status_code, 200) self.assertContains(login, ERROR_MESSAGE) # Requests without username should not return 500 errors. - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/', self.no_username_login) self.assertEqual(login.status_code, 200) form = login.context[0].get('form') self.assertEqual(form.errors['username'][0], 'This field is required.') def testLoginSuccessfullyRedirectsToOriginalUrl(self): - request = self.client.get('/test_admin/admin/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/') + self.assertEqual(response.status_code, 200) query_string = 'the-answer=42' redirect_url = '/test_admin/admin/?%s' % query_string new_next = {REDIRECT_FIELD_NAME: redirect_url} @@ -967,8 +941,8 @@ class AdminViewPermissionsTest(TestCase): self.client.post('/test_admin/admin/', self.changeuser_login) # make sure the view removes test cookie self.assertEqual(self.client.session.test_cookie_worked(), False) - request = self.client.get('/test_admin/admin/admin_views/article/add/') - self.assertEqual(request.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/add/') + self.assertEqual(response.status_code, 403) # Try POST just to make sure post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertEqual(post.status_code, 403) @@ -979,10 +953,9 @@ class AdminViewPermissionsTest(TestCase): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) addpage = self.client.get('/test_admin/admin/admin_views/article/add/') - self.assertEqual(addpage.status_code, 200) change_list_link = '› <a href="/test_admin/admin/admin_views/article/">Articles</a>' - self.assertFalse(change_list_link in addpage.content, - 'User restricted to add permission is given link to change list view in breadcrumbs.') + self.assertNotContains(addpage, change_list_link, + msg_prefix='User restricted to add permission is given link to change list view in breadcrumbs.') post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/') self.assertEqual(Article.objects.all().count(), 4) @@ -994,9 +967,8 @@ class AdminViewPermissionsTest(TestCase): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login) addpage = self.client.get('/test_admin/admin/admin_views/article/add/') - self.assertEqual(addpage.status_code, 200) - self.assertFalse(change_list_link not in addpage.content, - 'Unrestricted user is not given link to change list view in breadcrumbs.') + self.assertContains(addpage, change_list_link, + msg_prefix='Unrestricted user is not given link to change list view in breadcrumbs.') post = self.client.post('/test_admin/admin/admin_views/article/add/', add_dict) self.assertRedirects(post, '/test_admin/admin/admin_views/article/') self.assertEqual(Article.objects.all().count(), 5) @@ -1022,10 +994,10 @@ class AdminViewPermissionsTest(TestCase): # add user shoud not be able to view the list of article or change any of them self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) - request = self.client.get('/test_admin/admin/admin_views/article/') - self.assertEqual(request.status_code, 403) - request = self.client.get('/test_admin/admin/admin_views/article/1/') - self.assertEqual(request.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/') + self.assertEqual(response.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/1/') + self.assertEqual(response.status_code, 403) post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.assertEqual(post.status_code, 403) self.client.get('/test_admin/admin/logout/') @@ -1033,10 +1005,10 @@ class AdminViewPermissionsTest(TestCase): # change user can view all items and edit them self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.changeuser_login) - request = self.client.get('/test_admin/admin/admin_views/article/') - self.assertEqual(request.status_code, 200) - request = self.client.get('/test_admin/admin/admin_views/article/1/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/admin_views/article/') + self.assertEqual(response.status_code, 200) + response = self.client.get('/test_admin/admin/admin_views/article/1/') + self.assertEqual(response.status_code, 200) post = self.client.post('/test_admin/admin/admin_views/article/1/', change_dict) self.assertRedirects(post, '/test_admin/admin/admin_views/article/') self.assertEqual(Article.objects.get(pk=1).content, '<p>edited article</p>') @@ -1058,33 +1030,33 @@ class AdminViewPermissionsTest(TestCase): RowLevelChangePermissionModel.objects.create(id=2, name="even id") for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]: self.client.post('/test_admin/admin/', login_dict) - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') - self.assertEqual(request.status_code, 403) - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(response.status_code, 403) + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') - self.assertEqual(request.status_code, 403) - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') - self.assertEqual(request.status_code, 200) - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'}) + self.assertEqual(response.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(response.status_code, 200) + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') - self.assertRedirects(request, '/test_admin/admin/') + self.assertRedirects(response, '/test_admin/admin/') self.client.get('/test_admin/admin/logout/') for login_dict in [self.joepublic_login, self.no_username_login]: self.client.post('/test_admin/admin/', login_dict) - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') - request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') - request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}) + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') + response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') + response = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}) self.assertEqual(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') - self.assertEqual(request.status_code, 200) - self.assertContains(request, 'login-form') + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'login-form') self.client.get('/test_admin/admin/logout/') def testConditionallyShowAddSectionLink(self): @@ -1114,14 +1086,13 @@ class AdminViewPermissionsTest(TestCase): self.client.post('/test_admin/admin/', self.super_login) # Test custom change list template with custom extra context - request = self.client.get('/test_admin/admin/admin_views/customarticle/') - self.assertEqual(request.status_code, 200) - self.assertTrue("var hello = 'Hello!';" in request.content) - self.assertTemplateUsed(request, 'custom_admin/change_list.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/') + self.assertContains(response, "var hello = 'Hello!';") + self.assertTemplateUsed(response, 'custom_admin/change_list.html') # Test custom add form template - request = self.client.get('/test_admin/admin/admin_views/customarticle/add/') - self.assertTemplateUsed(request, 'custom_admin/add_form.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/add/') + self.assertTemplateUsed(response, 'custom_admin/add_form.html') # Add an article so we can test delete, change, and history views post = self.client.post('/test_admin/admin/admin_views/customarticle/add/', { @@ -1135,18 +1106,18 @@ class AdminViewPermissionsTest(TestCase): # Test custom delete, change, and object history templates # Test custom change form template - request = self.client.get('/test_admin/admin/admin_views/customarticle/%d/' % article_pk) - self.assertTemplateUsed(request, 'custom_admin/change_form.html') - request = self.client.get('/test_admin/admin/admin_views/customarticle/%d/delete/' % article_pk) - self.assertTemplateUsed(request, 'custom_admin/delete_confirmation.html') - request = self.client.post('/test_admin/admin/admin_views/customarticle/', data={ + response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/' % article_pk) + self.assertTemplateUsed(response, 'custom_admin/change_form.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/delete/' % article_pk) + self.assertTemplateUsed(response, 'custom_admin/delete_confirmation.html') + response = self.client.post('/test_admin/admin/admin_views/customarticle/', data={ 'index': 0, 'action': ['delete_selected'], '_selected_action': ['1'], }) - self.assertTemplateUsed(request, 'custom_admin/delete_selected_confirmation.html') - request = self.client.get('/test_admin/admin/admin_views/customarticle/%d/history/' % article_pk) - self.assertTemplateUsed(request, 'custom_admin/object_history.html') + self.assertTemplateUsed(response, 'custom_admin/delete_selected_confirmation.html') + response = self.client.get('/test_admin/admin/admin_views/customarticle/%d/history/' % article_pk) + self.assertTemplateUsed(response, 'custom_admin/object_history.html') self.client.get('/test_admin/admin/logout/') @@ -1158,8 +1129,8 @@ class AdminViewPermissionsTest(TestCase): # add user shoud not be able to delete articles self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.adduser_login) - request = self.client.get('/test_admin/admin/admin_views/article/1/delete/') - self.assertEqual(request.status_code, 403) + response = self.client.get('/test_admin/admin/admin_views/article/1/delete/') + self.assertEqual(response.status_code, 403) post = self.client.post('/test_admin/admin/admin_views/article/1/delete/', delete_dict) self.assertEqual(post.status_code, 403) self.assertEqual(Article.objects.all().count(), 3) @@ -1246,9 +1217,9 @@ class AdminViewDeletedObjectsTest(TestCase): Objects should be nested to display the relationships that cause them to be scheduled for deletion. """ - pattern = re.compile(r"""<li>Plot: <a href=".+/admin_views/plot/1/">World Domination</a>\s*<ul>\s*<li>Plot details: <a href=".+/admin_views/plotdetails/1/">almost finished</a>""") + pattern = re.compile(br"""<li>Plot: <a href=".+/admin_views/plot/1/">World Domination</a>\s*<ul>\s*<li>Plot details: <a href=".+/admin_views/plotdetails/1/">almost finished</a>""") response = self.client.get('/test_admin/admin/admin_views/villain/%s/delete/' % quote(1)) - self.assertTrue(pattern.search(response.content)) + self.assertRegexpMatches(response.content, pattern) def test_cyclic(self): """ @@ -1407,9 +1378,9 @@ class AdminViewStringPrimaryKeyTest(TestCase): logentry.content_type = None logentry.save() - counted_presence_before = response.content.count(should_contain) + counted_presence_before = response.content.count(force_bytes(should_contain)) response = self.client.get('/test_admin/admin/') - counted_presence_after = response.content.count(should_contain) + counted_presence_after = response.content.count(force_bytes(should_contain)) self.assertEqual(counted_presence_before - 1, counted_presence_after) @@ -1512,8 +1483,8 @@ class SecureViewTests(TestCase): self.assertTemplateUsed(response, 'admin/login.html') def test_secure_view_login_successfully_redirects_to_original_url(self): - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) query_string = 'the-answer=42' redirect_url = '/test_admin/admin/secure-view/?%s' % query_string new_next = {REDIRECT_FIELD_NAME: redirect_url} @@ -1529,8 +1500,8 @@ class SecureViewTests(TestCase): a 200 status code. """ # Super User - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/secure-view/', self.super_login) self.assertRedirects(login, '/test_admin/admin/secure-view/') self.assertFalse(login.context) @@ -1539,8 +1510,8 @@ class SecureViewTests(TestCase): self.assertEqual(self.client.session.test_cookie_worked(), False) # Test if user enters e-mail address - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/secure-view/', self.super_email_login) self.assertContains(login, "Your e-mail address is not your username") # only correct passwords get a username hint @@ -1553,32 +1524,32 @@ class SecureViewTests(TestCase): self.assertContains(login, ERROR_MESSAGE) # Add User - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/secure-view/', self.adduser_login) self.assertRedirects(login, '/test_admin/admin/secure-view/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Change User - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/secure-view/', self.changeuser_login) self.assertRedirects(login, '/test_admin/admin/secure-view/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Delete User - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/secure-view/', self.deleteuser_login) self.assertRedirects(login, '/test_admin/admin/secure-view/') self.assertFalse(login.context) self.client.get('/test_admin/admin/logout/') # Regular User should not be able to login. - request = self.client.get('/test_admin/admin/secure-view/') - self.assertEqual(request.status_code, 200) + response = self.client.get('/test_admin/admin/secure-view/') + self.assertEqual(response.status_code, 200) login = self.client.post('/test_admin/admin/secure-view/', self.joepublic_login) self.assertEqual(login.status_code, 200) # Login.context is a list of context dicts we just need to check the first one. @@ -2107,7 +2078,7 @@ class AdminInheritedInlinesTest(TestCase): foo_user = "foo username" bar_user = "bar username" - name_re = re.compile('name="(.*?)"') + name_re = re.compile(b'name="(.*?)"') # test the add case response = self.client.get('/test_admin/admin/admin_views/persona/add/') @@ -2214,7 +2185,7 @@ class AdminActionsTest(TestCase): confirmation = self.client.post('/test_admin/admin/admin_views/subscriber/', action_data) self.assertIsInstance(confirmation, TemplateResponse) self.assertContains(confirmation, "Are you sure you want to delete the selected subscribers?") - self.assertTrue(confirmation.content.count(ACTION_CHECKBOX_NAME) == 2) + self.assertContains(confirmation, ACTION_CHECKBOX_NAME, count=2) response = self.client.post('/test_admin/admin/admin_views/subscriber/', delete_confirmation_data) self.assertEqual(Subscriber.objects.count(), 0) @@ -2390,7 +2361,7 @@ class AdminActionsTest(TestCase): def test_popup_actions(self): """ Actions should not be shown in popups. """ response = self.client.get('/test_admin/admin/admin_views/subscriber/') - self.assertNotEquals(response.context["action_form"], None) + self.assertNotEqual(response.context["action_form"], None) response = self.client.get( '/test_admin/admin/admin_views/subscriber/?%s' % IS_POPUP_VAR) self.assertEqual(response.context["action_form"], None) @@ -2514,7 +2485,7 @@ class AdminInlineFileUploadTest(TestCase): # to use a NamedTemporaryFile. tdir = tempfile.gettempdir() file1 = tempfile.NamedTemporaryFile(suffix=".file1", dir=tdir) - file1.write('a' * (2 ** 21)) + file1.write(b'a' * (2 ** 21)) filename = file1.name file1.close() self.gallery = Gallery(name="Test Gallery") @@ -3197,9 +3168,9 @@ class RawIdFieldsTest(TestCase): country="Spain") response = self.client.get('/test_admin/admin/admin_views/sketch/add/') # Find the link - m = re.search(r'<a href="([^"]*)"[^>]* id="lookup_id_inquisition"', response.content) + m = re.search(br'<a href="([^"]*)"[^>]* id="lookup_id_inquisition"', response.content) self.assertTrue(m) # Got a match - popup_url = m.groups()[0].replace("&", "&") + popup_url = m.groups()[0].decode().replace("&", "&") # Handle relative links popup_url = urljoin(response.request['PATH_INFO'], popup_url) diff --git a/tests/regressiontests/admin_widgets/models.py b/tests/regressiontests/admin_widgets/models.py index 81a4ec7aba..2e452798b7 100644 --- a/tests/regressiontests/admin_widgets/models.py +++ b/tests/regressiontests/admin_widgets/models.py @@ -2,39 +2,44 @@ from __future__ import unicode_literals from django.db import models from django.contrib.auth.models import User +from django.utils.encoding import python_2_unicode_compatible class MyFileField(models.FileField): pass +@python_2_unicode_compatible class Member(models.Model): name = models.CharField(max_length=100) birthdate = models.DateTimeField(blank=True, null=True) gender = models.CharField(max_length=1, blank=True, choices=[('M','Male'), ('F', 'Female')]) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Band(models.Model): name = models.CharField(max_length=100) members = models.ManyToManyField(Member) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Album(models.Model): band = models.ForeignKey(Band) name = models.CharField(max_length=100) cover_art = models.FileField(upload_to='albums') backside_art = MyFileField(upload_to='albums_back', null=True) - def __unicode__(self): + def __str__(self): return self.name class HiddenInventoryManager(models.Manager): def get_query_set(self): return super(HiddenInventoryManager, self).get_query_set().filter(hidden=False) +@python_2_unicode_compatible class Inventory(models.Model): barcode = models.PositiveIntegerField(unique=True) parent = models.ForeignKey('self', to_field='barcode', blank=True, null=True) @@ -45,7 +50,7 @@ class Inventory(models.Model): default_manager = models.Manager() objects = HiddenInventoryManager() - def __unicode__(self): + def __str__(self): return self.name class Event(models.Model): @@ -56,12 +61,13 @@ class Event(models.Model): link = models.URLField(blank=True) min_age = models.IntegerField(blank=True, null=True) +@python_2_unicode_compatible class Car(models.Model): owner = models.ForeignKey(User) make = models.CharField(max_length=30) model = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.make, self.model) class CarTire(models.Model): @@ -103,19 +109,21 @@ class Advisor(models.Model): companies = models.ManyToManyField(Company) +@python_2_unicode_compatible class Student(models.Model): name = models.CharField(max_length=255) - def __unicode__(self): + def __str__(self): return self.name class Meta: ordering = ('name',) +@python_2_unicode_compatible class School(models.Model): name = models.CharField(max_length=255) students = models.ManyToManyField(Student, related_name='current_schools') alumni = models.ManyToManyField(Student, related_name='previous_schools') - def __unicode__(self): - return self.name
\ No newline at end of file + def __str__(self): + return self.name diff --git a/tests/regressiontests/aggregation_regress/models.py b/tests/regressiontests/aggregation_regress/models.py index ccef9a5fc8..dd4ff50aec 100644 --- a/tests/regressiontests/aggregation_regress/models.py +++ b/tests/regressiontests/aggregation_regress/models.py @@ -1,24 +1,28 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() friends = models.ManyToManyField('self', blank=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Publisher(models.Model): name = models.CharField(max_length=255) num_awards = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): isbn = models.CharField(max_length=9) name = models.CharField(max_length=255) @@ -33,17 +37,18 @@ class Book(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Store(models.Model): name = models.CharField(max_length=255) books = models.ManyToManyField(Book) original_opening = models.DateTimeField() friday_night_closing = models.TimeField() - def __unicode__(self): + def __str__(self): return self.name class Entries(models.Model): @@ -58,8 +63,9 @@ class Clues(models.Model): Clue = models.CharField(max_length=150) +@python_2_unicode_compatible class HardbackBook(Book): weight = models.FloatField() - def __unicode__(self): + def __str__(self): return "%s (hardback): %s" % (self.name, self.weight) diff --git a/tests/regressiontests/aggregation_regress/tests.py b/tests/regressiontests/aggregation_regress/tests.py index e24eb43b87..b9f3ab27eb 100644 --- a/tests/regressiontests/aggregation_regress/tests.py +++ b/tests/regressiontests/aggregation_regress/tests.py @@ -866,3 +866,15 @@ class AggregationTests(TestCase): ['Peter Norvig'], lambda b: b.name ) + + def test_type_conversion(self): + # The database backend convert_values function should not try to covert + # CharFields to float. Refs #13844. + from django.db.models import CharField + from django.db import connection + testData = 'not_a_float_value' + testField = CharField() + self.assertEqual( + connection.ops.convert_values(testData, testField), + testData + ) diff --git a/tests/regressiontests/backends/models.py b/tests/regressiontests/backends/models.py index af4952dce8..344cf4c798 100644 --- a/tests/regressiontests/backends/models.py +++ b/tests/regressiontests/backends/models.py @@ -3,21 +3,24 @@ from __future__ import unicode_literals from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models, connection +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Square(models.Model): root = models.IntegerField() square = models.PositiveIntegerField() - def __unicode__(self): + def __str__(self): return "%s ** 2 == %s" % (self.root, self.square) +@python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) @@ -55,18 +58,20 @@ class Post(models.Model): db_table = 'CaseSensitive_Post' +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) - def __unicode__(self): + def __str__(self): return self.headline diff --git a/tests/regressiontests/backends/tests.py b/tests/regressiontests/backends/tests.py index a6425c5591..dc92189344 100644 --- a/tests/regressiontests/backends/tests.py +++ b/tests/regressiontests/backends/tests.py @@ -584,7 +584,7 @@ class ThreadTests(TestCase): connections['default'] = main_thread_connection try: models.Person.objects.get(first_name="John", last_name="Doe") - except DatabaseError as e: + except Exception as e: exceptions.append(e) t = threading.Thread(target=runner, args=[connections['default']]) t.start() @@ -594,21 +594,21 @@ class ThreadTests(TestCase): exceptions = [] do_thread() # Forbidden! - self.assertTrue(isinstance(exceptions[0], DatabaseError)) + self.assertIsInstance(exceptions[0], DatabaseError) # If explicitly setting allow_thread_sharing to False connections['default'].allow_thread_sharing = False exceptions = [] do_thread() # Forbidden! - self.assertTrue(isinstance(exceptions[0], DatabaseError)) + self.assertIsInstance(exceptions[0], DatabaseError) # If explicitly setting allow_thread_sharing to True connections['default'].allow_thread_sharing = True exceptions = [] do_thread() # All good - self.assertEqual(len(exceptions), 0) + self.assertEqual(exceptions, []) def test_closing_non_shared_connections(self): """ diff --git a/tests/regressiontests/builtin_server/tests.py b/tests/regressiontests/builtin_server/tests.py index 4a3b44176b..c8dc77e42e 100644 --- a/tests/regressiontests/builtin_server/tests.py +++ b/tests/regressiontests/builtin_server/tests.py @@ -1,5 +1,8 @@ +from __future__ import unicode_literals + +from io import BytesIO + from django.core.servers.basehttp import ServerHandler -from django.utils.six import StringIO from django.utils.unittest import TestCase # @@ -23,12 +26,12 @@ class FileWrapperHandler(ServerHandler): return True def wsgi_app(environ, start_response): - start_response('200 OK', [('Content-Type', 'text/plain')]) - return ['Hello World!'] + start_response(str('200 OK'), [(str('Content-Type'), str('text/plain'))]) + return [b'Hello World!'] def wsgi_app_file_wrapper(environ, start_response): - start_response('200 OK', [('Content-Type', 'text/plain')]) - return environ['wsgi.file_wrapper'](StringIO('foo')) + start_response(str('200 OK'), [(str('Content-Type'), str('text/plain'))]) + return environ['wsgi.file_wrapper'](BytesIO(b'foo')) class WSGIFileWrapperTests(TestCase): """ @@ -37,15 +40,16 @@ class WSGIFileWrapperTests(TestCase): def test_file_wrapper_uses_sendfile(self): env = {'SERVER_PROTOCOL': 'HTTP/1.0'} - err = StringIO() - handler = FileWrapperHandler(None, StringIO(), err, env) + handler = FileWrapperHandler(None, BytesIO(), BytesIO(), env) handler.run(wsgi_app_file_wrapper) self.assertTrue(handler._used_sendfile) + self.assertEqual(handler.stdout.getvalue(), b'') + self.assertEqual(handler.stderr.getvalue(), b'') def test_file_wrapper_no_sendfile(self): env = {'SERVER_PROTOCOL': 'HTTP/1.0'} - err = StringIO() - handler = FileWrapperHandler(None, StringIO(), err, env) + handler = FileWrapperHandler(None, BytesIO(), BytesIO(), env) handler.run(wsgi_app) self.assertFalse(handler._used_sendfile) - self.assertEqual(handler.stdout.getvalue().splitlines()[-1],'Hello World!') + self.assertEqual(handler.stdout.getvalue().splitlines()[-1], b'Hello World!') + self.assertEqual(handler.stderr.getvalue(), b'') diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py index 8fc749aaa2..a6534c5f54 100644 --- a/tests/regressiontests/cache/tests.py +++ b/tests/regressiontests/cache/tests.py @@ -6,7 +6,9 @@ from __future__ import absolute_import, unicode_literals import hashlib import os +import random import re +import string import tempfile import time import warnings @@ -364,25 +366,25 @@ class BaseCacheTests(object): # Binary strings should be cacheable from zlib import compress, decompress value = 'value_to_be_compressed' - compressed_value = compress(value) + compressed_value = compress(value.encode()) # Test set self.cache.set('binary1', compressed_value) compressed_result = self.cache.get('binary1') self.assertEqual(compressed_value, compressed_result) - self.assertEqual(value, decompress(compressed_result)) + self.assertEqual(value, decompress(compressed_result).decode()) # Test add self.cache.add('binary1-add', compressed_value) compressed_result = self.cache.get('binary1-add') self.assertEqual(compressed_value, compressed_result) - self.assertEqual(value, decompress(compressed_result)) + self.assertEqual(value, decompress(compressed_result).decode()) # Test set_many self.cache.set_many({'binary1-set_many': compressed_value}) compressed_result = self.cache.get('binary1-set_many') self.assertEqual(compressed_value, compressed_result) - self.assertEqual(value, decompress(compressed_result)) + self.assertEqual(value, decompress(compressed_result).decode()) def test_set_many(self): # Multiple keys can be set using set_many @@ -774,13 +776,13 @@ class BaseCacheTests(object): get_cache_data = fetch_middleware.process_request(request) self.assertNotEqual(get_cache_data, None) - self.assertEqual(get_cache_data.content, content) + self.assertEqual(get_cache_data.content, content.encode('utf-8')) self.assertEqual(get_cache_data.cookies, response.cookies) update_middleware.process_response(request, get_cache_data) get_cache_data = fetch_middleware.process_request(request) self.assertNotEqual(get_cache_data, None) - self.assertEqual(get_cache_data.content, content) + self.assertEqual(get_cache_data.content, content.encode('utf-8')) self.assertEqual(get_cache_data.cookies, response.cookies) def custom_key_func(key, key_prefix, version): @@ -932,21 +934,24 @@ class LocMemCacheTests(unittest.TestCase, BaseCacheTests): # memcached backend isn't guaranteed to be available. # To check the memcached backend, the test settings file will -# need to contain a cache backend setting that points at +# need to contain at least one cache backend setting that points at # your memcache server. @unittest.skipUnless( - settings.CACHES[DEFAULT_CACHE_ALIAS]['BACKEND'].startswith('django.core.cache.backends.memcached.'), + any(cache['BACKEND'].startswith('django.core.cache.backends.memcached.') + for cache in settings.CACHES.values()), "memcached not available") class MemcachedCacheTests(unittest.TestCase, BaseCacheTests): - backend_name = 'django.core.cache.backends.memcached.MemcachedCache' def setUp(self): - name = settings.CACHES[DEFAULT_CACHE_ALIAS]['LOCATION'] - self.cache = get_cache(self.backend_name, LOCATION=name) - self.prefix_cache = get_cache(self.backend_name, LOCATION=name, KEY_PREFIX='cacheprefix') - self.v2_cache = get_cache(self.backend_name, LOCATION=name, VERSION=2) - self.custom_key_cache = get_cache(self.backend_name, LOCATION=name, KEY_FUNCTION=custom_key_func) - self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=name, KEY_FUNCTION='regressiontests.cache.tests.custom_key_func') + for cache_key, cache in settings.CACHES.items(): + if cache['BACKEND'].startswith('django.core.cache.backends.memcached.'): + break + random_prefix = ''.join(random.choice(string.ascii_letters) for x in range(10)) + self.cache = get_cache(cache_key) + self.prefix_cache = get_cache(cache_key, KEY_PREFIX=random_prefix) + self.v2_cache = get_cache(cache_key, VERSION=2) + self.custom_key_cache = get_cache(cache_key, KEY_FUNCTION=custom_key_func) + self.custom_key_cache2 = get_cache(cache_key, KEY_FUNCTION='regressiontests.cache.tests.custom_key_func') def tearDown(self): self.cache.clear() @@ -988,7 +993,7 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): """Test that keys are hashed into subdirectories correctly""" self.cache.set("foo", "bar") key = self.cache.make_key("foo") - keyhash = hashlib.md5(key).hexdigest() + keyhash = hashlib.md5(key.encode()).hexdigest() keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) self.assertTrue(os.path.exists(keypath)) @@ -998,7 +1003,7 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests): """ self.cache.set("foo", "bar") key = self.cache.make_key("foo") - keyhash = hashlib.md5(key).hexdigest() + keyhash = hashlib.md5(key.encode()).hexdigest() keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) self.assertTrue(os.path.exists(keypath)) @@ -1222,7 +1227,7 @@ class CacheHEADTest(TestCase): request = self._get_request('HEAD') get_cache_data = FetchFromCacheMiddleware().process_request(request) self.assertNotEqual(get_cache_data, None) - self.assertEqual(test_content, get_cache_data.content) + self.assertEqual(test_content.encode(), get_cache_data.content) def test_head_with_cached_get(self): test_content = 'test content' @@ -1233,7 +1238,7 @@ class CacheHEADTest(TestCase): request = self._get_request('HEAD') get_cache_data = FetchFromCacheMiddleware().process_request(request) self.assertNotEqual(get_cache_data, None) - self.assertEqual(test_content, get_cache_data.content) + self.assertEqual(test_content.encode(), get_cache_data.content) @override_settings( @@ -1308,7 +1313,7 @@ class CacheI18nTest(TestCase): # This is tightly coupled to the implementation, # but it's the most straightforward way to test the key. tz = force_text(timezone.get_current_timezone_name(), errors='ignore') - tz = tz.encode('ascii', 'ignore').replace(' ', '_') + tz = tz.encode('ascii', 'ignore').decode('ascii').replace(' ', '_') response = HttpResponse() key = learn_cache_key(request, response) self.assertIn(tz, key, "Cache keys should include the time zone name when time zones are active") @@ -1320,7 +1325,7 @@ class CacheI18nTest(TestCase): request = self._get_request() lang = translation.get_language() tz = force_text(timezone.get_current_timezone_name(), errors='ignore') - tz = tz.encode('ascii', 'ignore').replace(' ', '_') + tz = tz.encode('ascii', 'ignore').decode('ascii').replace(' ', '_') response = HttpResponse() key = learn_cache_key(request, response) self.assertNotIn(lang, key, "Cache keys shouldn't include the language name when i18n isn't active") @@ -1373,7 +1378,7 @@ class CacheI18nTest(TestCase): get_cache_data = FetchFromCacheMiddleware().process_request(request) # cache must return content self.assertNotEqual(get_cache_data, None) - self.assertEqual(get_cache_data.content, content) + self.assertEqual(get_cache_data.content, content.encode()) # different QUERY_STRING, cache must be empty request = self._get_request_cache(query_string='foo=bar&somethingelse=true') get_cache_data = FetchFromCacheMiddleware().process_request(request) @@ -1388,7 +1393,7 @@ class CacheI18nTest(TestCase): get_cache_data = FetchFromCacheMiddleware().process_request(request) # Check that we can recover the cache self.assertNotEqual(get_cache_data, None) - self.assertEqual(get_cache_data.content, en_message) + self.assertEqual(get_cache_data.content, en_message.encode()) # Check that we use etags self.assertTrue(get_cache_data.has_header('ETag')) # Check that we can disable etags @@ -1404,11 +1409,11 @@ class CacheI18nTest(TestCase): translation.activate('en') # retrieve the content from cache get_cache_data = FetchFromCacheMiddleware().process_request(request) - self.assertEqual(get_cache_data.content, en_message) + self.assertEqual(get_cache_data.content, en_message.encode()) # change again the language translation.activate('es') get_cache_data = FetchFromCacheMiddleware().process_request(request) - self.assertEqual(get_cache_data.content, es_message) + self.assertEqual(get_cache_data.content, es_message.encode()) # reset the language translation.deactivate() @@ -1513,8 +1518,8 @@ class CacheMiddlewareTest(TestCase): # Repeating the request should result in a cache hit result = middleware.process_request(request) - self.assertNotEquals(result, None) - self.assertEqual(result.content, 'Hello World 1') + self.assertNotEqual(result, None) + self.assertEqual(result.content, b'Hello World 1') # The same request through a different middleware won't hit result = prefix_middleware.process_request(request) @@ -1522,8 +1527,8 @@ class CacheMiddlewareTest(TestCase): # The same request with a timeout _will_ hit result = timeout_middleware.process_request(request) - self.assertNotEquals(result, None) - self.assertEqual(result.content, 'Hello World 1') + self.assertNotEqual(result, None) + self.assertEqual(result.content, b'Hello World 1') @override_settings(CACHE_MIDDLEWARE_ANONYMOUS_ONLY=True) def test_cache_middleware_anonymous_only_wont_cause_session_access(self): @@ -1592,43 +1597,43 @@ class CacheMiddlewareTest(TestCase): # Request the view once response = default_view(request, '1') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # Request again -- hit the cache response = default_view(request, '2') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # Requesting the same view with the explicit cache should yield the same result response = explicit_default_view(request, '3') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # Requesting with a prefix will hit a different cache key response = explicit_default_with_prefix_view(request, '4') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # Hitting the same view again gives a cache hit response = explicit_default_with_prefix_view(request, '5') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # And going back to the implicit cache will hit the same cache response = default_with_prefix_view(request, '6') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # Requesting from an alternate cache won't hit cache response = other_view(request, '7') - self.assertEqual(response.content, 'Hello World 7') + self.assertEqual(response.content, b'Hello World 7') # But a repeated hit will hit cache response = other_view(request, '8') - self.assertEqual(response.content, 'Hello World 7') + self.assertEqual(response.content, b'Hello World 7') # And prefixing the alternate cache yields yet another cache entry response = other_with_prefix_view(request, '9') - self.assertEqual(response.content, 'Hello World 9') + self.assertEqual(response.content, b'Hello World 9') # Request from the alternate cache with a new prefix and a custom timeout response = other_with_timeout_view(request, '10') - self.assertEqual(response.content, 'Hello World 10') + self.assertEqual(response.content, b'Hello World 10') # But if we wait a couple of seconds... time.sleep(2) @@ -1636,38 +1641,38 @@ class CacheMiddlewareTest(TestCase): # ... the default cache will still hit cache = get_cache('default') response = default_view(request, '11') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # ... the default cache with a prefix will still hit response = default_with_prefix_view(request, '12') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # ... the explicit default cache will still hit response = explicit_default_view(request, '13') - self.assertEqual(response.content, 'Hello World 1') + self.assertEqual(response.content, b'Hello World 1') # ... the explicit default cache with a prefix will still hit response = explicit_default_with_prefix_view(request, '14') - self.assertEqual(response.content, 'Hello World 4') + self.assertEqual(response.content, b'Hello World 4') # .. but a rapidly expiring cache won't hit response = other_view(request, '15') - self.assertEqual(response.content, 'Hello World 15') + self.assertEqual(response.content, b'Hello World 15') # .. even if it has a prefix response = other_with_prefix_view(request, '16') - self.assertEqual(response.content, 'Hello World 16') + self.assertEqual(response.content, b'Hello World 16') # ... but a view with a custom timeout will still hit response = other_with_timeout_view(request, '17') - self.assertEqual(response.content, 'Hello World 10') + self.assertEqual(response.content, b'Hello World 10') # And if we wait a few more seconds time.sleep(2) - # the custom timeouot cache will miss + # the custom timeout cache will miss response = other_with_timeout_view(request, '18') - self.assertEqual(response.content, 'Hello World 18') + self.assertEqual(response.content, b'Hello World 18') @override_settings( diff --git a/tests/regressiontests/comment_tests/models.py b/tests/regressiontests/comment_tests/models.py index 4093af14b5..472b66decd 100644 --- a/tests/regressiontests/comment_tests/models.py +++ b/tests/regressiontests/comment_tests/models.py @@ -3,9 +3,13 @@ Comments may be attached to any object. See the comment documentation for more information. """ +from __future__ import unicode_literals + from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) @@ -13,6 +17,7 @@ class Author(models.Model): def __str__(self): return '%s %s' % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): author = models.ForeignKey(Author) headline = models.CharField(max_length=100) @@ -20,6 +25,7 @@ class Article(models.Model): def __str__(self): return self.headline +@python_2_unicode_compatible class Entry(models.Model): title = models.CharField(max_length=250) body = models.TextField() diff --git a/tests/regressiontests/conditional_processing/models.py b/tests/regressiontests/conditional_processing/models.py index d1a8ac605b..d0838e153d 100644 --- a/tests/regressiontests/conditional_processing/models.py +++ b/tests/regressiontests/conditional_processing/models.py @@ -1,4 +1,6 @@ # -*- coding:utf-8 -*- +from __future__ import unicode_literals + from datetime import datetime from django.test import TestCase @@ -20,7 +22,7 @@ class ConditionalGet(TestCase): def assertFullResponse(self, response, check_last_modified=True, check_etag=True): self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, FULL_RESPONSE) + self.assertEqual(response.content, FULL_RESPONSE.encode()) if check_last_modified: self.assertEqual(response['Last-Modified'], LAST_MODIFIED_STR) if check_etag: @@ -28,7 +30,7 @@ class ConditionalGet(TestCase): def assertNotModified(self, response): self.assertEqual(response.status_code, 304) - self.assertEqual(response.content, '') + self.assertEqual(response.content, b'') def testWithoutConditions(self): response = self.client.get('/condition/') diff --git a/tests/regressiontests/csrf_tests/tests.py b/tests/regressiontests/csrf_tests/tests.py index 52fd3ea1c7..c5b66a31d1 100644 --- a/tests/regressiontests/csrf_tests/tests.py +++ b/tests/regressiontests/csrf_tests/tests.py @@ -216,7 +216,7 @@ class CsrfViewMiddlewareTest(TestCase): """ req = self._get_GET_no_csrf_cookie_request() resp = token_view(req) - self.assertEqual("", resp.content) + self.assertEqual(resp.content, b'') def test_token_node_empty_csrf_cookie(self): """ diff --git a/tests/regressiontests/custom_columns_regress/models.py b/tests/regressiontests/custom_columns_regress/models.py index c768c12772..169b2246c3 100644 --- a/tests/regressiontests/custom_columns_regress/models.py +++ b/tests/regressiontests/custom_columns_regress/models.py @@ -8,26 +8,29 @@ table creation or queries. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): Article_ID = models.AutoField(primary_key=True, db_column='Article ID') headline = models.CharField(max_length=100) authors = models.ManyToManyField('Author', db_table='my m2m table') primary_author = models.ForeignKey('Author', db_column='Author ID', related_name='primary_set') - def __unicode__(self): + def __str__(self): return self.headline class Meta: ordering = ('headline',) +@python_2_unicode_compatible class Author(models.Model): Author_ID = models.AutoField(primary_key=True, db_column='Author ID') first_name = models.CharField(max_length=30, db_column='first name') last_name = models.CharField(max_length=30, db_column='last name') - def __unicode__(self): + def __str__(self): return '%s %s' % (self.first_name, self.last_name) class Meta: diff --git a/tests/regressiontests/custom_managers_regress/models.py b/tests/regressiontests/custom_managers_regress/models.py index 3c4e621769..71073f0fe7 100644 --- a/tests/regressiontests/custom_managers_regress/models.py +++ b/tests/regressiontests/custom_managers_regress/models.py @@ -3,6 +3,7 @@ Regression tests for custom manager classes. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class RestrictedManager(models.Manager): @@ -12,12 +13,14 @@ class RestrictedManager(models.Manager): def get_query_set(self): return super(RestrictedManager, self).get_query_set().filter(is_public=True) +@python_2_unicode_compatible class RelatedModel(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class RestrictedModel(models.Model): name = models.CharField(max_length=50) is_public = models.BooleanField(default=False) @@ -26,9 +29,10 @@ class RestrictedModel(models.Model): objects = RestrictedManager() plain_manager = models.Manager() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class OneToOneRestrictedModel(models.Model): name = models.CharField(max_length=50) is_public = models.BooleanField(default=False) @@ -37,5 +41,5 @@ class OneToOneRestrictedModel(models.Model): objects = RestrictedManager() plain_manager = models.Manager() - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/datatypes/models.py b/tests/regressiontests/datatypes/models.py index 332ce84307..8e6027dd0f 100644 --- a/tests/regressiontests/datatypes/models.py +++ b/tests/regressiontests/datatypes/models.py @@ -4,8 +4,10 @@ types, which in the past were problematic for some database backends. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Donut(models.Model): name = models.CharField(max_length=100) is_frosted = models.BooleanField(default=False) diff --git a/tests/regressiontests/dates/models.py b/tests/regressiontests/dates/models.py index e1fc1e74fe..e4bffb7199 100644 --- a/tests/regressiontests/dates/models.py +++ b/tests/regressiontests/dates/models.py @@ -1,22 +1,25 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=100) pub_date = models.DateField() categories = models.ManyToManyField("Category", related_name="articles") - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Comment(models.Model): article = models.ForeignKey(Article, related_name="comments") text = models.TextField() pub_date = models.DateField() approval_date = models.DateField(null=True) - def __unicode__(self): + def __str__(self): return 'Comment to %s (%s)' % (self.article.title, self.pub_date) class Category(models.Model): diff --git a/tests/regressiontests/decorators/tests.py b/tests/regressiontests/decorators/tests.py index dceaa62abc..7dc36aa479 100644 --- a/tests/regressiontests/decorators/tests.py +++ b/tests/regressiontests/decorators/tests.py @@ -220,7 +220,7 @@ class MethodDecoratorTests(TestCase): self.assertEqual(getattr(Test.method, 'myattr2', False), True) self.assertEqual(Test.method.__doc__, 'A method') - self.assertEqual(Test.method.__func__.__name__, 'method') + self.assertEqual(Test.method.__name__, 'method') class XFrameOptionsDecoratorsTests(TestCase): diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index 2852572d32..bdf3c867c6 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -9,6 +9,7 @@ from django.test import TestCase from django.utils import six from django.utils import unittest, translation from django.utils.safestring import SafeData +from django.utils.encoding import python_2_unicode_compatible class DefaultFiltersTests(TestCase): @@ -456,10 +457,11 @@ class DefaultFiltersTests(TestCase): 'Lawrence</li>\n\t\t\t<li>Topeka</li>\n\t\t</ul>\n\t\t</li>'\ '\n\t\t<li>Illinois</li>\n\t</ul>\n\t</li>') + @python_2_unicode_compatible class ULItem(object): def __init__(self, title): self.title = title - def __unicode__(self): + def __str__(self): return 'ulitem-%s' % str(self.title) a = ULItem('a') diff --git a/tests/regressiontests/defer_regress/models.py b/tests/regressiontests/defer_regress/models.py index bd4f845f27..d02adccde9 100644 --- a/tests/regressiontests/defer_regress/models.py +++ b/tests/regressiontests/defer_regress/models.py @@ -3,15 +3,17 @@ Regression tests for defer() / only() behavior. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=15) text = models.TextField(default="xyzzy") value = models.IntegerField() other_value = models.IntegerField(default=0) - def __unicode__(self): + def __str__(self): return self.name class RelatedItem(models.Model): @@ -21,13 +23,14 @@ class Child(models.Model): name = models.CharField(max_length=10) value = models.IntegerField() +@python_2_unicode_compatible class Leaf(models.Model): name = models.CharField(max_length=10) child = models.ForeignKey(Child) second_child = models.ForeignKey(Child, related_name="other", null=True) value = models.IntegerField(default=42) - def __unicode__(self): + def __str__(self): return self.name class ResolveThis(models.Model): @@ -38,11 +41,12 @@ class Proxy(Item): class Meta: proxy = True +@python_2_unicode_compatible class SimpleItem(models.Model): name = models.CharField(max_length=15) value = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name class Feature(models.Model): diff --git a/tests/regressiontests/dispatch/tests/test_saferef.py b/tests/regressiontests/dispatch/tests/test_saferef.py index cfe6c5df85..30eaddfe18 100644 --- a/tests/regressiontests/dispatch/tests/test_saferef.py +++ b/tests/regressiontests/dispatch/tests/test_saferef.py @@ -54,10 +54,8 @@ class SaferefTests(unittest.TestCase): sd[s] = 1 for t in self.ts: if hasattr(t, 'x'): - self.assertTrue(sd.has_key(safeRef(t.x))) self.assertTrue(safeRef(t.x) in sd) else: - self.assertTrue(sd.has_key(safeRef(t))) self.assertTrue(safeRef(t) in sd) def testRepresentation(self): diff --git a/tests/regressiontests/expressions_regress/models.py b/tests/regressiontests/expressions_regress/models.py index f3b6999377..711329031d 100644 --- a/tests/regressiontests/expressions_regress/models.py +++ b/tests/regressiontests/expressions_regress/models.py @@ -1,15 +1,17 @@ from __future__ import unicode_literals +from django.utils.encoding import python_2_unicode_compatible """ Model for testing arithmetic expressions. """ from django.db import models +@python_2_unicode_compatible class Number(models.Model): integer = models.IntegerField(db_column='the_integer') float = models.FloatField(null=True, db_column='the_float') - def __unicode__(self): + def __str__(self): return '%i, %.3f' % (self.integer, self.float) class Experiment(models.Model): diff --git a/tests/regressiontests/extra_regress/models.py b/tests/regressiontests/extra_regress/models.py index 7d2a6fab34..11996438b4 100644 --- a/tests/regressiontests/extra_regress/models.py +++ b/tests/regressiontests/extra_regress/models.py @@ -5,14 +5,16 @@ import datetime from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class RevisionableModel(models.Model): base = models.ForeignKey('self', null=True) title = models.CharField(blank=True, max_length=255) when = models.DateTimeField(default=datetime.datetime.now) - def __unicode__(self): + def __str__(self): return "%s (%s, %s)" % (self.title, self.id, self.base.id) def save(self, *args, **kwargs): @@ -32,11 +34,12 @@ class Order(models.Model): created_by = models.ForeignKey(User) text = models.TextField() +@python_2_unicode_compatible class TestObject(models.Model): first = models.CharField(max_length=20) second = models.CharField(max_length=20) third = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return 'TestObject: %s,%s,%s' % (self.first,self.second,self.third) diff --git a/tests/regressiontests/file_storage/tests.py b/tests/regressiontests/file_storage/tests.py index 31e33d915c..87b509e320 100644 --- a/tests/regressiontests/file_storage/tests.py +++ b/tests/regressiontests/file_storage/tests.py @@ -21,6 +21,7 @@ from django.core.files.images import get_image_dimensions from django.core.files.storage import FileSystemStorage, get_storage_class from django.core.files.uploadedfile import UploadedFile from django.test import SimpleTestCase +from django.utils import six from django.utils import unittest from ..servers.tests import LiveServerBase @@ -127,7 +128,7 @@ class FileStorageTests(unittest.TestCase): """ self.assertFalse(self.storage.exists('test.file')) - f = ContentFile(b'custom contents') + f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) atime = self.storage.accessed_time(f_name) @@ -143,7 +144,7 @@ class FileStorageTests(unittest.TestCase): """ self.assertFalse(self.storage.exists('test.file')) - f = ContentFile(b'custom contents') + f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) ctime = self.storage.created_time(f_name) @@ -160,7 +161,7 @@ class FileStorageTests(unittest.TestCase): """ self.assertFalse(self.storage.exists('test.file')) - f = ContentFile(b'custom contents') + f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) mtime = self.storage.modified_time(f_name) @@ -177,7 +178,7 @@ class FileStorageTests(unittest.TestCase): """ self.assertFalse(self.storage.exists('test.file')) - f = ContentFile(b'custom contents') + f = ContentFile('custom contents') f.name = 'test.file' storage_f_name = self.storage.save(None, f) @@ -194,11 +195,11 @@ class FileStorageTests(unittest.TestCase): """ self.assertFalse(self.storage.exists('path/to')) self.storage.save('path/to/test.file', - ContentFile(b'file saved with path')) + ContentFile('file saved with path')) self.assertTrue(self.storage.exists('path/to')) - self.assertEqual(self.storage.open('path/to/test.file').read(), - b'file saved with path') + with self.storage.open('path/to/test.file') as f: + self.assertEqual(f.read(), b'file saved with path') self.assertTrue(os.path.exists( os.path.join(self.temp_dir, 'path', 'to', 'test.file'))) @@ -211,7 +212,7 @@ class FileStorageTests(unittest.TestCase): """ self.assertFalse(self.storage.exists('test.file')) - f = ContentFile(b'custom contents') + f = ContentFile('custom contents') f_name = self.storage.save('test.file', f) self.assertEqual(self.storage.path(f_name), @@ -246,8 +247,8 @@ class FileStorageTests(unittest.TestCase): self.assertFalse(self.storage.exists('storage_test_2')) self.assertFalse(self.storage.exists('storage_dir_1')) - f = self.storage.save('storage_test_1', ContentFile(b'custom content')) - f = self.storage.save('storage_test_2', ContentFile(b'custom content')) + f = self.storage.save('storage_test_1', ContentFile('custom content')) + f = self.storage.save('storage_test_2', ContentFile('custom content')) os.mkdir(os.path.join(self.temp_dir, 'storage_dir_1')) dirs, files = self.storage.listdir('') @@ -304,18 +305,18 @@ class FileStorageTests(unittest.TestCase): os.makedirs = fake_makedirs self.storage.save('normal/test.file', - ContentFile(b'saved normally')) - self.assertEqual(self.storage.open('normal/test.file').read(), - b'saved normally') + ContentFile('saved normally')) + with self.storage.open('normal/test.file') as f: + self.assertEqual(f.read(), b'saved normally') self.storage.save('raced/test.file', - ContentFile(b'saved with race')) - self.assertEqual(self.storage.open('raced/test.file').read(), - b'saved with race') + ContentFile('saved with race')) + with self.storage.open('raced/test.file') as f: + self.assertEqual(f.read(), b'saved with race') # Check that OSErrors aside from EEXIST are still raised. self.assertRaises(OSError, - self.storage.save, 'error/test.file', ContentFile(b'not saved')) + self.storage.save, 'error/test.file', ContentFile('not saved')) finally: os.makedirs = real_makedirs @@ -341,20 +342,31 @@ class FileStorageTests(unittest.TestCase): try: os.remove = fake_remove - self.storage.save('normal.file', ContentFile(b'delete normally')) + self.storage.save('normal.file', ContentFile('delete normally')) self.storage.delete('normal.file') self.assertFalse(self.storage.exists('normal.file')) - self.storage.save('raced.file', ContentFile(b'delete with race')) + self.storage.save('raced.file', ContentFile('delete with race')) self.storage.delete('raced.file') self.assertFalse(self.storage.exists('normal.file')) # Check that OSErrors aside from ENOENT are still raised. - self.storage.save('error.file', ContentFile(b'delete with error')) + self.storage.save('error.file', ContentFile('delete with error')) self.assertRaises(OSError, self.storage.delete, 'error.file') finally: os.remove = real_remove + def test_file_chunks_error(self): + """ + Test behaviour when file.chunks() is raising an error + """ + f1 = ContentFile('chunks fails') + def failing_chunks(): + raise IOError + f1.chunks = failing_chunks + with self.assertRaises(IOError): + self.storage.save('error.file', f1) + class CustomStorage(FileSystemStorage): def get_available_name(self, name): @@ -374,9 +386,9 @@ class CustomStorageTests(FileStorageTests): storage_class = CustomStorage def test_custom_get_available_name(self): - first = self.storage.save('custom_storage', ContentFile(b'custom contents')) + first = self.storage.save('custom_storage', ContentFile('custom contents')) self.assertEqual(first, 'custom_storage') - second = self.storage.save('custom_storage', ContentFile(b'more contents')) + second = self.storage.save('custom_storage', ContentFile('more contents')) self.assertEqual(second, 'custom_storage.2') self.storage.delete(first) self.storage.delete(second) @@ -433,7 +445,7 @@ class FileStoragePermissions(unittest.TestCase): shutil.rmtree(self.storage_dir) def test_file_upload_permissions(self): - name = self.storage.save("the_file", ContentFile(b"data")) + name = self.storage.save("the_file", ContentFile("data")) actual_mode = os.stat(self.storage.path(name))[0] & 0o777 self.assertEqual(actual_mode, 0o666) @@ -453,8 +465,8 @@ class FileStoragePathParsing(unittest.TestCase): sure we still mangle the file name instead of the directory name. """ - self.storage.save('dotted.path/test', ContentFile(b"1")) - self.storage.save('dotted.path/test', ContentFile(b"2")) + self.storage.save('dotted.path/test', ContentFile("1")) + self.storage.save('dotted.path/test', ContentFile("2")) self.assertFalse(os.path.exists(os.path.join(self.storage_dir, 'dotted_.path'))) self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/test'))) @@ -465,8 +477,8 @@ class FileStoragePathParsing(unittest.TestCase): File names with a dot as their first character don't have an extension, and the underscore should get added to the end. """ - self.storage.save('dotted.path/.test', ContentFile(b"1")) - self.storage.save('dotted.path/.test', ContentFile(b"2")) + self.storage.save('dotted.path/.test', ContentFile("1")) + self.storage.save('dotted.path/.test', ContentFile("2")) self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test'))) self.assertTrue(os.path.exists(os.path.join(self.storage_dir, 'dotted.path/.test_1'))) @@ -538,16 +550,25 @@ class InconsistentGetImageDimensionsBug(unittest.TestCase): self.assertEqual(size_1, size_2) class ContentFileTestCase(unittest.TestCase): - """ - Test that the constructor of ContentFile accepts 'name' (#16590). - """ + def test_content_file_default_name(self): self.assertEqual(ContentFile(b"content").name, None) def test_content_file_custom_name(self): + """ + Test that the constructor of ContentFile accepts 'name' (#16590). + """ name = "I can have a name too!" self.assertEqual(ContentFile(b"content", name=name).name, name) + def test_content_file_input_type(self): + """ + Test that ContentFile can accept both bytes and unicode and that the + retrieved content is of the same type. + """ + self.assertTrue(isinstance(ContentFile(b"content").read(), bytes)) + self.assertTrue(isinstance(ContentFile("español").read(), six.text_type)) + class NoNameFileTestCase(unittest.TestCase): """ Other examples of unnamed files may be tempfile.SpooledTemporaryFile or diff --git a/tests/regressiontests/file_uploads/tests.py b/tests/regressiontests/file_uploads/tests.py index d2362d7197..a545ed649e 100644 --- a/tests/regressiontests/file_uploads/tests.py +++ b/tests/regressiontests/file_uploads/tests.py @@ -12,6 +12,7 @@ from django.core.files import temp as tempfile from django.core.files.uploadedfile import SimpleUploadedFile from django.http.multipartparser import MultiPartParser from django.test import TestCase, client +from django.utils.encoding import force_bytes from django.utils.six import StringIO from django.utils import unittest @@ -48,12 +49,12 @@ class FileUploadTests(TestCase): 'file_field2': file2, } - for key in post_data.keys(): + for key in list(post_data): try: post_data[key + '_hash'] = hashlib.sha1(post_data[key].read()).hexdigest() post_data[key].seek(0) except AttributeError: - post_data[key + '_hash'] = hashlib.sha1(post_data[key]).hexdigest() + post_data[key + '_hash'] = hashlib.sha1(force_bytes(post_data[key])).hexdigest() response = self.client.post('/file_uploads/verify/', post_data) @@ -67,7 +68,7 @@ class FileUploadTests(TestCase): 'Content-Type: application/octet-stream', 'Content-Transfer-Encoding: base64', '', - base64.b64encode(test_string), + base64.b64encode(force_bytes(test_string)).decode('ascii'), '--' + client.BOUNDARY + '--', '', ]).encode('utf-8') @@ -79,7 +80,7 @@ class FileUploadTests(TestCase): 'wsgi.input': client.FakePayload(payload), } response = self.client.request(**r) - received = json.loads(response.content) + received = json.loads(response.content.decode('utf-8')) self.assertEqual(received['file'], test_string) @@ -87,7 +88,7 @@ class FileUploadTests(TestCase): tdir = tempfile.gettempdir() # This file contains chinese symbols and an accented char in the name. - with open(os.path.join(tdir, UNICODE_FILENAME.encode('utf-8')), 'w+b') as file1: + with open(os.path.join(tdir, UNICODE_FILENAME), 'w+b') as file1: file1.write(b'b' * (2 ** 10)) file1.seek(0) @@ -150,7 +151,7 @@ class FileUploadTests(TestCase): response = self.client.request(**r) # The filenames should have been sanitized by the time it got to the view. - recieved = json.loads(response.content) + recieved = json.loads(response.content.decode('utf-8')) for i, name in enumerate(scary_file_names): got = recieved["file%s" % i] self.assertEqual(got, "hax0rd.txt") @@ -174,7 +175,7 @@ class FileUploadTests(TestCase): 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(payload), } - got = json.loads(self.client.request(**r).content) + got = json.loads(self.client.request(**r).content.decode('utf-8')) self.assertTrue(len(got['file']) < 256, "Got a long file name (%s characters)." % len(got['file'])) def test_truncated_multipart_handled_gracefully(self): @@ -200,7 +201,7 @@ class FileUploadTests(TestCase): 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(payload), } - got = json.loads(self.client.request(**r).content) + got = json.loads(self.client.request(**r).content.decode('utf-8')) self.assertEqual(got, {}) def test_empty_multipart_handled_gracefully(self): @@ -215,7 +216,7 @@ class FileUploadTests(TestCase): 'REQUEST_METHOD': 'POST', 'wsgi.input': client.FakePayload(b''), } - got = json.loads(self.client.request(**r).content) + got = json.loads(self.client.request(**r).content.decode('utf-8')) self.assertEqual(got, {}) def test_custom_upload_handler(self): @@ -231,12 +232,12 @@ class FileUploadTests(TestCase): # Small file posting should work. response = self.client.post('/file_uploads/quota/', {'f': smallfile}) - got = json.loads(response.content) + got = json.loads(response.content.decode('utf-8')) self.assertTrue('f' in got) # Large files don't go through. response = self.client.post("/file_uploads/quota/", {'f': bigfile}) - got = json.loads(response.content) + got = json.loads(response.content.decode('utf-8')) self.assertTrue('f' not in got) def test_broken_custom_upload_handler(self): @@ -274,7 +275,7 @@ class FileUploadTests(TestCase): 'field5': 'test7', 'file2': (file2, file2a) }) - got = json.loads(response.content) + got = json.loads(response.content.decode('utf-8')) self.assertEqual(got.get('file1'), 1) self.assertEqual(got.get('file2'), 2) @@ -299,8 +300,8 @@ class FileUploadTests(TestCase): # it raises when there is an attempt to read more than the available bytes: try: client.FakePayload(b'a').read(2) - except Exception as reference_error: - pass + except Exception as err: + reference_error = err # install the custom handler that tries to access request.POST self.client.handler = POSTAccessingHandler() diff --git a/tests/regressiontests/file_uploads/views.py b/tests/regressiontests/file_uploads/views.py index c5d2720e1a..fcf32cecea 100644 --- a/tests/regressiontests/file_uploads/views.py +++ b/tests/regressiontests/file_uploads/views.py @@ -7,6 +7,7 @@ import os from django.core.files.uploadedfile import UploadedFile from django.http import HttpResponse, HttpResponseServerError from django.utils import six +from django.utils.encoding import force_bytes from .models import FileModel, UPLOAD_TO from .tests import UNICODE_FILENAME @@ -45,7 +46,7 @@ def file_upload_view_verify(request): if isinstance(value, UploadedFile): new_hash = hashlib.sha1(value.read()).hexdigest() else: - new_hash = hashlib.sha1(value).hexdigest() + new_hash = hashlib.sha1(force_bytes(value)).hexdigest() if new_hash != submitted_hash: return HttpResponseServerError() @@ -95,7 +96,7 @@ def file_upload_echo_content(request): """ Simple view to echo back the content of uploaded files for tests. """ - r = dict([(k, f.read()) for k, f in request.FILES.items()]) + r = dict([(k, f.read().decode('utf-8')) for k, f in request.FILES.items()]) return HttpResponse(json.dumps(r)) def file_upload_quota(request): diff --git a/tests/regressiontests/fixtures_regress/models.py b/tests/regressiontests/fixtures_regress/models.py index 7151cb0ed9..1cc30d2e51 100644 --- a/tests/regressiontests/fixtures_regress/models.py +++ b/tests/regressiontests/fixtures_regress/models.py @@ -3,8 +3,10 @@ from __future__ import absolute_import, unicode_literals from django.contrib.auth.models import User from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Animal(models.Model): name = models.CharField(max_length=150) latin_name = models.CharField(max_length=150) @@ -14,7 +16,7 @@ class Animal(models.Model): # use a non-default name for the default manager specimens = models.Manager() - def __unicode__(self): + def __str__(self): return self.name @@ -25,11 +27,12 @@ class Plant(models.Model): # For testing when upper case letter in app name; regression for #4057 db_table = "Fixtures_regress_plant" +@python_2_unicode_compatible class Stuff(models.Model): name = models.CharField(max_length=20, null=True) owner = models.ForeignKey(User, null=True) - def __unicode__(self): + def __str__(self): return six.text_type(self.name) + ' is owned by ' + six.text_type(self.owner) @@ -68,13 +71,14 @@ class Article(models.Model): # Models to regression test #11428 +@python_2_unicode_compatible class Widget(models.Model): name = models.CharField(max_length=255) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name @@ -89,6 +93,7 @@ class TestManager(models.Manager): return self.get(name=key) +@python_2_unicode_compatible class Store(models.Model): objects = TestManager() name = models.CharField(max_length=255) @@ -97,13 +102,14 @@ class Store(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name def natural_key(self): return (self.name,) +@python_2_unicode_compatible class Person(models.Model): objects = TestManager() name = models.CharField(max_length=255) @@ -111,7 +117,7 @@ class Person(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name # Person doesn't actually have a dependency on store, but we need to define @@ -121,6 +127,7 @@ class Person(models.Model): natural_key.dependencies = ['fixtures_regress.store'] +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=255) author = models.ForeignKey(Person) @@ -129,7 +136,7 @@ class Book(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return '%s by %s (available at %s)' % ( self.name, self.author.name, @@ -142,6 +149,7 @@ class NKManager(models.Manager): return self.get(data=data) +@python_2_unicode_compatible class NKChild(Parent): data = models.CharField(max_length=10, unique=True) objects = NKManager() @@ -149,16 +157,17 @@ class NKChild(Parent): def natural_key(self): return self.data - def __unicode__(self): + def __str__(self): return 'NKChild %s:%s' % (self.name, self.data) +@python_2_unicode_compatible class RefToNKChild(models.Model): text = models.CharField(max_length=10) nk_fk = models.ForeignKey(NKChild, related_name='ref_fks') nk_m2m = models.ManyToManyField(NKChild, related_name='ref_m2ms') - def __unicode__(self): + def __str__(self): return '%s: Reference to %s [%s]' % ( self.text, self.nk_fk, diff --git a/tests/regressiontests/fixtures_regress/tests.py b/tests/regressiontests/fixtures_regress/tests.py index ab93341699..b3d0f42465 100644 --- a/tests/regressiontests/fixtures_regress/tests.py +++ b/tests/regressiontests/fixtures_regress/tests.py @@ -4,7 +4,6 @@ from __future__ import absolute_import, unicode_literals import os import re -from io import BytesIO from django.core import management from django.core.management.base import CommandError @@ -14,6 +13,7 @@ from django.db.models import signals from django.test import (TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature) from django.test.utils import override_settings +from django.utils.six import PY3, StringIO from .models import (Animal, Stuff, Absolute, Parent, Child, Article, Widget, Store, Person, Book, NKChild, RefToNKChild, Circle1, Circle2, Circle3, @@ -126,6 +126,20 @@ class TestFixtures(TestCase): commit=False, ) + @override_settings(SERIALIZATION_MODULES={'unkn': 'unexistent.path'}) + def test_unimportable_serializer(self): + """ + Test that failing serializer import raises the proper error + """ + with self.assertRaisesRegexp(ImportError, + "No module named unexistent.path"): + management.call_command( + 'loaddata', + 'bad_fixture1.unkn', + verbosity=0, + commit=False, + ) + def test_invalid_data(self): """ Test for ticket #4371 -- Loading a fixture file with invalid data @@ -244,7 +258,8 @@ class TestFixtures(TestCase): self.assertEqual( pre_save_checks, [ - ("Count = 42 (<type 'int'>)", "Weight = 1.2 (<type 'float'>)") + ("Count = 42 (<%s 'int'>)" % ('class' if PY3 else 'type'), + "Weight = 1.2 (<%s 'float'>)" % ('class' if PY3 else 'type')) ] ) finally: @@ -276,7 +291,7 @@ class TestFixtures(TestCase): ) animal.save() - stdout = BytesIO() + stdout = StringIO() management.call_command( 'dumpdata', 'fixtures_regress.animal', @@ -305,7 +320,7 @@ class TestFixtures(TestCase): """ Regression for #11428 - Proxy models aren't included when you dumpdata """ - stdout = BytesIO() + stdout = StringIO() # Create an instance of the concrete class widget = Widget.objects.create(name='grommet') management.call_command( @@ -380,7 +395,7 @@ class TestFixtures(TestCase): ) def test_loaddata_not_existant_fixture_file(self): - stdout_output = BytesIO() + stdout_output = StringIO() management.call_command( 'loaddata', 'this_fixture_doesnt_exist', @@ -465,7 +480,7 @@ class NaturalKeyFixtureTests(TestCase): commit=False ) - stdout = BytesIO() + stdout = StringIO() management.call_command( 'dumpdata', 'fixtures_regress.book', diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py index 18e6ddce6d..2f3ee9fa31 100644 --- a/tests/regressiontests/forms/models.py +++ b/tests/regressiontests/forms/models.py @@ -7,6 +7,7 @@ import tempfile from django.core.files.storage import FileSystemStorage from django.db import models +from django.utils.encoding import python_2_unicode_compatible temp_storage_location = tempfile.mkdtemp(dir=os.environ['DJANGO_TEST_TEMP_DIR']) @@ -36,6 +37,7 @@ class ChoiceModel(models.Model): name = models.CharField(max_length=10) +@python_2_unicode_compatible class ChoiceOptionModel(models.Model): """Destination for ChoiceFieldModel's ForeignKey. Can't reuse ChoiceModel because error_message tests require that it have no instances.""" @@ -44,7 +46,7 @@ class ChoiceOptionModel(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return 'ChoiceOption %d' % self.pk @@ -66,10 +68,11 @@ class FileModel(models.Model): file = models.FileField(storage=temp_storage, upload_to='tests') +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return '%s' % self.name diff --git a/tests/regressiontests/forms/tests/error_messages.py b/tests/regressiontests/forms/tests/error_messages.py index f69b419483..b76e122bc0 100644 --- a/tests/regressiontests/forms/tests/error_messages.py +++ b/tests/regressiontests/forms/tests/error_messages.py @@ -5,6 +5,7 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.forms import * from django.test import TestCase from django.utils.safestring import mark_safe +from django.utils.encoding import python_2_unicode_compatible class AssertFormErrorsMixin(object): @@ -213,8 +214,9 @@ class FormsErrorMessagesTestCase(TestCase, AssertFormErrorsMixin): def clean(self): raise ValidationError("I like to be awkward.") + @python_2_unicode_compatible class CustomErrorList(util.ErrorList): - def __unicode__(self): + def __str__(self): return self.as_divs() def as_divs(self): diff --git a/tests/regressiontests/forms/tests/extra.py b/tests/regressiontests/forms/tests/extra.py index e0e62858d0..2ab5d40942 100644 --- a/tests/regressiontests/forms/tests/extra.py +++ b/tests/regressiontests/forms/tests/extra.py @@ -8,8 +8,9 @@ from django.forms import * from django.forms.extras import SelectDateWidget from django.forms.util import ErrorList from django.test import TestCase +from django.utils import six from django.utils import translation -from django.utils.encoding import force_text, smart_text +from django.utils.encoding import force_text, smart_text, python_2_unicode_compatible from .error_messages import AssertFormErrorsMixin @@ -553,14 +554,24 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin): def test_smart_text(self): class Test: - def __str__(self): - return 'ŠĐĆŽćžšđ'.encode('utf-8') + if six.PY3: + def __str__(self): + return 'ŠĐĆŽćžšđ' + else: + def __str__(self): + return 'ŠĐĆŽćžšđ'.encode('utf-8') class TestU: - def __str__(self): - return 'Foo' - def __unicode__(self): - return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' + if six.PY3: + def __str__(self): + return 'ŠĐĆŽćžšđ' + def __bytes__(self): + return b'Foo' + else: + def __str__(self): + return b'Foo' + def __unicode__(self): + return '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111' self.assertEqual(smart_text(Test()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') self.assertEqual(smart_text(TestU()), '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') @@ -585,8 +596,9 @@ class FormsExtraTestCase(TestCase, AssertFormErrorsMixin): self.assertEqual(f.cleaned_data['username'], 'sirrobin') def test_overriding_errorlist(self): + @python_2_unicode_compatible class DivErrorList(ErrorList): - def __unicode__(self): + def __str__(self): return self.as_divs() def as_divs(self): diff --git a/tests/regressiontests/forms/tests/fields.py b/tests/regressiontests/forms/tests/fields.py index feb2ade458..6efdb9682f 100644 --- a/tests/regressiontests/forms/tests/fields.py +++ b/tests/regressiontests/forms/tests/fields.py @@ -668,6 +668,18 @@ class FieldsTests(SimpleTestCase): # Valid IDN self.assertEqual(url, f.clean(url)) + def test_urlfield_10(self): + """Test URLField correctly validates IPv6 (#18779).""" + f = URLField() + urls = ( + 'http://::/', + 'http://6:21b4:92/', + 'http://[12:34:3a53]/', + 'http://[a34:9238::]:8080/', + ) + for url in urls: + self.assertEqual(url, f.clean(url)) + def test_urlfield_not_string(self): f = URLField(required=False) self.assertRaisesMessage(ValidationError, "'Enter a valid URL.'", f.clean, 23) diff --git a/tests/regressiontests/forms/tests/models.py b/tests/regressiontests/forms/tests/models.py index 7687335b48..c351509cee 100644 --- a/tests/regressiontests/forms/tests/models.py +++ b/tests/regressiontests/forms/tests/models.py @@ -178,7 +178,7 @@ class RelatedModelFormTests(TestCase): class Meta: model=A - self.assertRaises(ValueError, ModelFormMetaclass, b'Form', (ModelForm,), {'Meta': Meta}) + self.assertRaises(ValueError, ModelFormMetaclass, str('Form'), (ModelForm,), {'Meta': Meta}) class B(models.Model): pass @@ -196,4 +196,4 @@ class RelatedModelFormTests(TestCase): class Meta: model=A - self.assertTrue(issubclass(ModelFormMetaclass(b'Form', (ModelForm,), {'Meta': Meta}), ModelForm)) + self.assertTrue(issubclass(ModelFormMetaclass(str('Form'), (ModelForm,), {'Meta': Meta}), ModelForm)) diff --git a/tests/regressiontests/forms/tests/util.py b/tests/regressiontests/forms/tests/util.py index b7cc4ec809..c2d213ae23 100644 --- a/tests/regressiontests/forms/tests/util.py +++ b/tests/regressiontests/forms/tests/util.py @@ -7,6 +7,7 @@ from django.test import TestCase from django.utils.safestring import mark_safe from django.utils import six from django.utils.translation import ugettext_lazy +from django.utils.encoding import python_2_unicode_compatible class FormsUtilTestCase(TestCase): @@ -46,8 +47,9 @@ class FormsUtilTestCase(TestCase): self.assertHTMLEqual(str(ErrorList(ValidationError(["First error.", "Not \u03C0.", ugettext_lazy("Error.")]).messages)), '<ul class="errorlist"><li>First error.</li><li>Not π.</li><li>Error.</li></ul>') + @python_2_unicode_compatible class VeryBadError: - def __unicode__(self): return "A very bad error." + def __str__(self): return "A very bad error." # Can take a non-string. self.assertHTMLEqual(str(ErrorList(ValidationError(VeryBadError()).messages)), diff --git a/tests/regressiontests/forms/tests/widgets.py b/tests/regressiontests/forms/tests/widgets.py index 3ea42cf549..c950aef719 100644 --- a/tests/regressiontests/forms/tests/widgets.py +++ b/tests/regressiontests/forms/tests/widgets.py @@ -13,6 +13,7 @@ from django.utils.safestring import mark_safe from django.utils import six from django.utils.translation import activate, deactivate from django.test import TestCase +from django.utils.encoding import python_2_unicode_compatible class FormsWidgetTestCase(TestCase): @@ -1096,6 +1097,7 @@ class WidgetTests(TestCase): self.assertFalse(form.is_valid()) +@python_2_unicode_compatible class FakeFieldFile(object): """ Quacks like a FieldFile (has a .url and unicode representation), but @@ -1104,7 +1106,7 @@ class FakeFieldFile(object): """ url = 'something' - def __unicode__(self): + def __str__(self): return self.url class ClearableFileInputTests(TestCase): @@ -1125,10 +1127,11 @@ class ClearableFileInputTests(TestCase): rendering HTML. Refs #15182. """ + @python_2_unicode_compatible class StrangeFieldFile(object): url = "something?chapter=1§=2©=3&lang=en" - def __unicode__(self): + def __str__(self): return '''something<div onclick="alert('oops')">.jpg''' widget = ClearableFileInput() diff --git a/tests/regressiontests/generic_inline_admin/models.py b/tests/regressiontests/generic_inline_admin/models.py index a1555666b3..dedc351075 100644 --- a/tests/regressiontests/generic_inline_admin/models.py +++ b/tests/regressiontests/generic_inline_admin/models.py @@ -1,6 +1,7 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Episode(models.Model): @@ -9,6 +10,7 @@ class Episode(models.Model): author = models.CharField(max_length=100, blank=True) +@python_2_unicode_compatible class Media(models.Model): """ Media that can associated to any object. @@ -20,7 +22,7 @@ class Media(models.Model): description = models.CharField(max_length=100, blank=True) keywords = models.CharField(max_length=100, blank=True) - def __unicode__(self): + def __str__(self): return self.url # diff --git a/tests/regressiontests/generic_inline_admin/tests.py b/tests/regressiontests/generic_inline_admin/tests.py index c97f4c4f03..fea30b4946 100644 --- a/tests/regressiontests/generic_inline_admin/tests.py +++ b/tests/regressiontests/generic_inline_admin/tests.py @@ -276,7 +276,7 @@ class GenericInlineModelAdminTest(TestCase): ma = EpisodeAdmin(Episode, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), + list(list(ma.get_formsets(request))[0]().forms[0].fields), ['keywords', 'id', 'DELETE']) def test_custom_form_meta_exclude(self): @@ -306,7 +306,7 @@ class GenericInlineModelAdminTest(TestCase): ma = EpisodeAdmin(Episode, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), + list(list(ma.get_formsets(request))[0]().forms[0].fields), ['url', 'keywords', 'id', 'DELETE']) # Then, only with `ModelForm` ----------------- @@ -322,5 +322,5 @@ class GenericInlineModelAdminTest(TestCase): ma = EpisodeAdmin(Episode, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), + list(list(ma.get_formsets(request))[0]().forms[0].fields), ['description', 'keywords', 'id', 'DELETE']) diff --git a/tests/regressiontests/generic_relations_regress/models.py b/tests/regressiontests/generic_relations_regress/models.py index 7ec752b02b..dacc9b380b 100644 --- a/tests/regressiontests/generic_relations_regress/models.py +++ b/tests/regressiontests/generic_relations_regress/models.py @@ -1,31 +1,36 @@ from django.contrib.contenttypes import generic from django.contrib.contenttypes.models import ContentType from django.db import models +from django.utils.encoding import python_2_unicode_compatible __all__ = ('Link', 'Place', 'Restaurant', 'Person', 'Address', 'CharLink', 'TextLink', 'OddRelation1', 'OddRelation2', 'Contact', 'Organization', 'Note') +@python_2_unicode_compatible class Link(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return "Link to %s id=%s" % (self.content_type, self.object_id) +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=100) links = generic.GenericRelation(Link) - def __unicode__(self): + def __str__(self): return "Place: %s" % self.name +@python_2_unicode_compatible class Restaurant(Place): - def __unicode__(self): + def __str__(self): return "Restaurant: %s" % self.name +@python_2_unicode_compatible class Address(models.Model): street = models.CharField(max_length=80) city = models.CharField(max_length=50) @@ -35,15 +40,16 @@ class Address(models.Model): object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return '%s %s, %s %s' % (self.street, self.city, self.state, self.zipcode) +@python_2_unicode_compatible class Person(models.Model): account = models.IntegerField(primary_key=True) name = models.CharField(max_length=128) addresses = generic.GenericRelation(Address) - def __unicode__(self): + def __str__(self): return self.name class CharLink(models.Model): diff --git a/tests/regressiontests/generic_views/base.py b/tests/regressiontests/generic_views/base.py index fc3dc05fd7..439b0d7327 100644 --- a/tests/regressiontests/generic_views/base.py +++ b/tests/regressiontests/generic_views/base.py @@ -69,7 +69,7 @@ class ViewTest(unittest.TestCase): def _assert_simple(self, response): self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'This is a simple view') + self.assertEqual(response.content, b'This is a simple view') def test_no_init_kwargs(self): """ @@ -173,15 +173,6 @@ class ViewTest(unittest.TestCase): """ self.assertTrue(DecoratedDispatchView.as_view().is_decorated) - def test_head_no_get(self): - """ - Test that a view class with no get responds to a HEAD request with HTTP - 405. - """ - request = self.rf.head('/') - view = PostOnlyView.as_view() - self.assertEqual(405, view(request).status_code) - def test_options(self): """ Test that views respond to HTTP OPTIONS requests with an Allow header @@ -275,7 +266,8 @@ class TemplateViewTest(TestCase): """ response = self.client.get('/template/simple/bar/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertEqual(response.context['foo'], 'bar') + self.assertTrue(isinstance(response.context['view'], View)) def test_extra_template_params(self): """ @@ -283,8 +275,9 @@ class TemplateViewTest(TestCase): """ response = self.client.get('/template/custom/bar/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.context['params'], {'foo': 'bar'}) + self.assertEqual(response.context['foo'], 'bar') self.assertEqual(response.context['key'], 'value') + self.assertTrue(isinstance(response.context['view'], View)) def test_cached_views(self): """ diff --git a/tests/regressiontests/generic_views/dates.py b/tests/regressiontests/generic_views/dates.py index ad65fd9f10..c2fa71b376 100644 --- a/tests/regressiontests/generic_views/dates.py +++ b/tests/regressiontests/generic_views/dates.py @@ -1,14 +1,27 @@ from __future__ import absolute_import +import time import datetime from django.core.exceptions import ImproperlyConfigured from django.test import TestCase from django.test.utils import override_settings from django.utils import timezone +from django.utils.unittest import skipUnless from .models import Book, BookSigning +TZ_SUPPORT = hasattr(time, 'tzset') + +# On OSes that don't provide tzset (Windows), we can't set the timezone +# in which the program runs. As a consequence, we must skip tests that +# don't enforce a specific timezone (with timezone.override or equivalent), +# or attempt to interpret naive datetimes in the default timezone. + +requires_tz_support = skipUnless(TZ_SUPPORT, + "This test relies on the ability to run a program in an arbitrary " + "time zone, but your operating system isn't able to do that.") + class ArchiveIndexViewTests(TestCase): fixtures = ['generic-views-test-data.json'] @@ -47,7 +60,6 @@ class ArchiveIndexViewTests(TestCase): res = self.client.get('/dates/books/allow_empty/') self.assertEqual(res.status_code, 200) self.assertEqual(list(res.context['date_list']), []) - self.assertEqual(list(res.context['date_list']), []) self.assertTemplateUsed(res, 'generic_views/book_archive.html') def test_archive_view_template(self): @@ -67,6 +79,11 @@ class ArchiveIndexViewTests(TestCase): def test_archive_view_invalid(self): self.assertRaises(ImproperlyConfigured, self.client.get, '/dates/books/invalid/') + def test_archive_view_by_month(self): + res = self.client.get('/dates/books/by_month/') + self.assertEqual(res.status_code, 200) + self.assertEqual(res.context['date_list'], Book.objects.dates('pubdate', 'month')[::-1]) + def test_paginated_archive_view(self): self._make_books(20, base_date=datetime.date.today()) res = self.client.get('/dates/books/paginated/') @@ -100,6 +117,7 @@ class ArchiveIndexViewTests(TestCase): res = self.client.get('/dates/booksignings/') self.assertEqual(res.status_code, 200) + @requires_tz_support @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_archive_view(self): BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) @@ -480,7 +498,7 @@ class DayArchiveViewTests(TestCase): def test_next_prev_context(self): res = self.client.get('/dates/books/2008/oct/01/') - self.assertEqual(res.content, "Archive for Oct. 1, 2008. Previous day is May 1, 2006") + self.assertEqual(res.content, b"Archive for Oct. 1, 2008. Previous day is May 1, 2006") def test_custom_month_format(self): res = self.client.get('/dates/books/2008/10/01/') @@ -502,6 +520,7 @@ class DayArchiveViewTests(TestCase): res = self.client.get('/dates/booksignings/2008/apr/2/') self.assertEqual(res.status_code, 200) + @requires_tz_support @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_day_view(self): bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) @@ -578,6 +597,7 @@ class DateDetailViewTests(TestCase): res = self.client.get('/dates/booksignings/2008/apr/2/%d/' % bs.pk) self.assertEqual(res.status_code, 200) + @requires_tz_support @override_settings(USE_TZ=True, TIME_ZONE='Africa/Nairobi') def test_aware_datetime_date_detail(self): bs = BookSigning.objects.create(event_date=datetime.datetime(2008, 4, 2, 12, 0, tzinfo=timezone.utc)) diff --git a/tests/regressiontests/generic_views/detail.py b/tests/regressiontests/generic_views/detail.py index 60c5f7afe9..c8f1999a2a 100644 --- a/tests/regressiontests/generic_views/detail.py +++ b/tests/regressiontests/generic_views/detail.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.views.generic.base import View from .models import Artist, Author, Page @@ -14,6 +15,7 @@ class DetailViewTest(TestCase): res = self.client.get('/detail/obj/') self.assertEqual(res.status_code, 200) self.assertEqual(res.context['object'], {'foo': 'bar'}) + self.assertTrue(isinstance(res.context['view'], View)) self.assertTemplateUsed(res, 'generic_views/detail.html') def test_detail_by_pk(self): diff --git a/tests/regressiontests/generic_views/edit.py b/tests/regressiontests/generic_views/edit.py index 651e14fff3..16f4da8efe 100644 --- a/tests/regressiontests/generic_views/edit.py +++ b/tests/regressiontests/generic_views/edit.py @@ -5,6 +5,7 @@ from django.core.urlresolvers import reverse from django import forms from django.test import TestCase from django.utils.unittest import expectedFailure +from django.views.generic.base import View from django.views.generic.edit import FormMixin from . import views @@ -31,6 +32,7 @@ class CreateViewTests(TestCase): res = self.client.get('/edit/authors/create/') self.assertEqual(res.status_code, 200) self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) self.assertFalse('object' in res.context) self.assertFalse('author' in res.context) self.assertTemplateUsed(res, 'generic_views/author_form.html') @@ -101,6 +103,7 @@ class CreateViewTests(TestCase): self.assertEqual(res.status_code, 302) self.assertRedirects(res, 'http://testserver/accounts/login/?next=/edit/authors/create/restricted/') + class UpdateViewTests(TestCase): urls = 'regressiontests.generic_views.urls' @@ -226,6 +229,7 @@ class UpdateViewTests(TestCase): res = self.client.get('/edit/author/update/') self.assertEqual(res.status_code, 200) self.assertTrue(isinstance(res.context['form'], forms.ModelForm)) + self.assertTrue(isinstance(res.context['view'], View)) self.assertEqual(res.context['object'], Author.objects.get(pk=a.pk)) self.assertEqual(res.context['author'], Author.objects.get(pk=a.pk)) self.assertTemplateUsed(res, 'generic_views/author_form.html') @@ -237,6 +241,7 @@ class UpdateViewTests(TestCase): self.assertRedirects(res, 'http://testserver/list/authors/') self.assertQuerysetEqual(Author.objects.all(), ['<Author: Randall Munroe (xkcd)>']) + class DeleteViewTests(TestCase): urls = 'regressiontests.generic_views.urls' diff --git a/tests/regressiontests/generic_views/list.py b/tests/regressiontests/generic_views/list.py index b925758524..14dc1d725d 100644 --- a/tests/regressiontests/generic_views/list.py +++ b/tests/regressiontests/generic_views/list.py @@ -2,6 +2,7 @@ from __future__ import absolute_import from django.core.exceptions import ImproperlyConfigured from django.test import TestCase +from django.views.generic.base import View from .models import Author, Artist @@ -21,6 +22,7 @@ class ListViewTests(TestCase): self.assertEqual(res.status_code, 200) self.assertTemplateUsed(res, 'generic_views/author_list.html') self.assertEqual(list(res.context['object_list']), list(Author.objects.all())) + self.assertTrue(isinstance(res.context['view'], View)) self.assertIs(res.context['author_list'], res.context['object_list']) self.assertIsNone(res.context['paginator']) self.assertIsNone(res.context['page_obj']) diff --git a/tests/regressiontests/generic_views/models.py b/tests/regressiontests/generic_views/models.py index 2355769d58..f59389ef78 100644 --- a/tests/regressiontests/generic_views/models.py +++ b/tests/regressiontests/generic_views/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Artist(models.Model): name = models.CharField(max_length=100) @@ -9,13 +11,14 @@ class Artist(models.Model): verbose_name = 'professional artist' verbose_name_plural = 'professional artists' - def __unicode__(self): + def __str__(self): return self.name @models.permalink def get_absolute_url(self): return ('artist_detail', (), {'pk': self.id}) +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) slug = models.SlugField() @@ -23,9 +26,10 @@ class Author(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=300) slug = models.SlugField() @@ -36,7 +40,7 @@ class Book(models.Model): class Meta: ordering = ['-pubdate'] - def __unicode__(self): + def __str__(self): return self.name class Page(models.Model): diff --git a/tests/regressiontests/generic_views/urls.py b/tests/regressiontests/generic_views/urls.py index 5e15c6c9c1..c72bfecb65 100644 --- a/tests/regressiontests/generic_views/urls.py +++ b/tests/regressiontests/generic_views/urls.py @@ -113,6 +113,8 @@ urlpatterns = patterns('', views.BookArchive.as_view(paginate_by=10)), (r'^dates/books/reverse/$', views.BookArchive.as_view(queryset=models.Book.objects.order_by('pubdate'))), + (r'^dates/books/by_month/$', + views.BookArchive.as_view(date_list_period='month')), (r'^dates/booksignings/$', views.BookSigningArchive.as_view()), diff --git a/tests/regressiontests/httpwrappers/tests.py b/tests/regressiontests/httpwrappers/tests.py index 0f61c2d840..21ba198bc3 100644 --- a/tests/regressiontests/httpwrappers/tests.py +++ b/tests/regressiontests/httpwrappers/tests.py @@ -6,19 +6,22 @@ import pickle from django.core.exceptions import SuspiciousOperation from django.http import (QueryDict, HttpResponse, HttpResponseRedirect, - HttpResponsePermanentRedirect, + HttpResponsePermanentRedirect, HttpResponseNotAllowed, + HttpResponseNotModified, SimpleCookie, BadHeaderError, parse_cookie) +from django.test import TestCase +from django.utils import six from django.utils import unittest class QueryDictTests(unittest.TestCase): def test_missing_key(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertRaises(KeyError, q.__getitem__, 'foo') def test_immutability(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) @@ -28,26 +31,26 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.clear) def test_immutable_get_with_default(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertEqual(q.get('foo', 'default'), 'default') def test_immutable_basic_operations(self): - q = QueryDict('') + q = QueryDict(str('')) self.assertEqual(q.getlist('foo'), []) - self.assertEqual(q.has_key('foo'), False) + if not six.PY3: + self.assertEqual(q.has_key('foo'), False) self.assertEqual('foo' in q, False) - self.assertEqual(q.items(), []) - self.assertEqual(q.lists(), []) - self.assertEqual(q.items(), []) - self.assertEqual(q.keys(), []) - self.assertEqual(q.values(), []) + self.assertEqual(list(six.iteritems(q)), []) + self.assertEqual(list(six.iterlists(q)), []) + self.assertEqual(list(six.iterkeys(q)), []) + self.assertEqual(list(six.itervalues(q)), []) self.assertEqual(len(q), 0) self.assertEqual(q.urlencode(), '') def test_single_key_value(self): """Test QueryDict with one key/value pair""" - q = QueryDict('foo=bar') + q = QueryDict(str('foo=bar')) self.assertEqual(q['foo'], 'bar') self.assertRaises(KeyError, q.__getitem__, 'bar') self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') @@ -60,15 +63,17 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.setlist, 'foo', ['bar']) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) - self.assertTrue(q.has_key('foo')) + if not six.PY3: + self.assertTrue(q.has_key('foo')) self.assertTrue('foo' in q) - self.assertFalse(q.has_key('bar')) + if not six.PY3: + self.assertFalse(q.has_key('bar')) self.assertFalse('bar' in q) - self.assertEqual(q.items(), [('foo', 'bar')]) - self.assertEqual(q.lists(), [('foo', ['bar'])]) - self.assertEqual(q.keys(), ['foo']) - self.assertEqual(q.values(), ['bar']) + self.assertEqual(list(six.iteritems(q)), [('foo', 'bar')]) + self.assertEqual(list(six.iterlists(q)), [('foo', ['bar'])]) + self.assertEqual(list(six.iterkeys(q)), ['foo']) + self.assertEqual(list(six.itervalues(q)), ['bar']) self.assertEqual(len(q), 1) self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) @@ -80,30 +85,30 @@ class QueryDictTests(unittest.TestCase): self.assertEqual(q.urlencode(), 'foo=bar') def test_urlencode(self): - q = QueryDict('', mutable=True) + q = QueryDict(str(''), mutable=True) q['next'] = '/a&b/' self.assertEqual(q.urlencode(), 'next=%2Fa%26b%2F') self.assertEqual(q.urlencode(safe='/'), 'next=/a%26b/') - q = QueryDict('', mutable=True) + q = QueryDict(str(''), mutable=True) q['next'] = '/t\xebst&key/' self.assertEqual(q.urlencode(), 'next=%2Ft%C3%ABst%26key%2F') self.assertEqual(q.urlencode(safe='/'), 'next=/t%C3%ABst%26key/') def test_mutable_copy(self): """A copy of a QueryDict is mutable.""" - q = QueryDict('').copy() + q = QueryDict(str('')).copy() self.assertRaises(KeyError, q.__getitem__, "foo") q['name'] = 'john' self.assertEqual(q['name'], 'john') def test_mutable_delete(self): - q = QueryDict('').copy() + q = QueryDict(str('')).copy() q['name'] = 'john' del q['name'] self.assertFalse('name' in q) def test_basic_mutable_operations(self): - q = QueryDict('').copy() + q = QueryDict(str('')).copy() q['name'] = 'john' self.assertEqual(q.get('foo', 'default'), 'default') self.assertEqual(q.get('name', 'default'), 'john') @@ -117,13 +122,14 @@ class QueryDictTests(unittest.TestCase): q.appendlist('foo', 'another') self.assertEqual(q.getlist('foo'), ['bar', 'baz', 'another']) self.assertEqual(q['foo'], 'another') - self.assertTrue(q.has_key('foo')) + if not six.PY3: + self.assertTrue(q.has_key('foo')) self.assertTrue('foo' in q) - self.assertEqual(q.items(), [('foo', 'another'), ('name', 'john')]) - self.assertEqual(q.lists(), [('foo', ['bar', 'baz', 'another']), ('name', ['john'])]) - self.assertEqual(q.keys(), ['foo', 'name']) - self.assertEqual(q.values(), ['another', 'john']) + self.assertEqual(list(six.iteritems(q)), [('foo', 'another'), ('name', 'john')]) + self.assertEqual(list(six.iterlists(q)), [('foo', ['bar', 'baz', 'another']), ('name', ['john'])]) + self.assertEqual(list(six.iterkeys(q)), ['foo', 'name']) + self.assertEqual(list(six.itervalues(q)), ['another', 'john']) self.assertEqual(len(q), 2) q.update({'foo': 'hello'}) @@ -144,7 +150,7 @@ class QueryDictTests(unittest.TestCase): def test_multiple_keys(self): """Test QueryDict with two key/value pairs with same keys.""" - q = QueryDict('vote=yes&vote=no') + q = QueryDict(str('vote=yes&vote=no')) self.assertEqual(q['vote'], 'no') self.assertRaises(AttributeError, q.__setitem__, 'something', 'bar') @@ -158,14 +164,16 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.setlist, 'foo', ['bar', 'baz']) self.assertRaises(AttributeError, q.appendlist, 'foo', ['bar']) - self.assertEqual(q.has_key('vote'), True) + if not six.PY3: + self.assertEqual(q.has_key('vote'), True) self.assertEqual('vote' in q, True) - self.assertEqual(q.has_key('foo'), False) + if not six.PY3: + self.assertEqual(q.has_key('foo'), False) self.assertEqual('foo' in q, False) - self.assertEqual(q.items(), [('vote', 'no')]) - self.assertEqual(q.lists(), [('vote', ['yes', 'no'])]) - self.assertEqual(q.keys(), ['vote']) - self.assertEqual(q.values(), ['no']) + self.assertEqual(list(six.iteritems(q)), [('vote', 'no')]) + self.assertEqual(list(six.iterlists(q)), [('vote', ['yes', 'no'])]) + self.assertEqual(list(six.iterkeys(q)), ['vote']) + self.assertEqual(list(six.itervalues(q)), ['no']) self.assertEqual(len(q), 1) self.assertRaises(AttributeError, q.update, {'foo': 'bar'}) @@ -175,45 +183,49 @@ class QueryDictTests(unittest.TestCase): self.assertRaises(AttributeError, q.setdefault, 'foo', 'bar') self.assertRaises(AttributeError, q.__delitem__, 'vote') - def test_invalid_input_encoding(self): - """ - QueryDicts must be able to handle invalid input encoding (in this - case, bad UTF-8 encoding). - """ - q = QueryDict(b'foo=bar&foo=\xff') - self.assertEqual(q['foo'], '\ufffd') - self.assertEqual(q.getlist('foo'), ['bar', '\ufffd']) + if not six.PY3: + def test_invalid_input_encoding(self): + """ + QueryDicts must be able to handle invalid input encoding (in this + case, bad UTF-8 encoding). + + This test doesn't apply under Python 3 because the URL is a string + and not a bytestring. + """ + q = QueryDict(str(b'foo=bar&foo=\xff')) + self.assertEqual(q['foo'], '\ufffd') + self.assertEqual(q.getlist('foo'), ['bar', '\ufffd']) def test_pickle(self): - q = QueryDict('') + q = QueryDict(str('')) q1 = pickle.loads(pickle.dumps(q, 2)) self.assertEqual(q == q1, True) - q = QueryDict('a=b&c=d') + q = QueryDict(str('a=b&c=d')) q1 = pickle.loads(pickle.dumps(q, 2)) self.assertEqual(q == q1, True) - q = QueryDict('a=b&c=d&a=1') + q = QueryDict(str('a=b&c=d&a=1')) q1 = pickle.loads(pickle.dumps(q, 2)) self.assertEqual(q == q1, True) def test_update_from_querydict(self): """Regression test for #8278: QueryDict.update(QueryDict)""" - x = QueryDict("a=1&a=2", mutable=True) - y = QueryDict("a=3&a=4") + x = QueryDict(str("a=1&a=2"), mutable=True) + y = QueryDict(str("a=3&a=4")) x.update(y) self.assertEqual(x.getlist('a'), ['1', '2', '3', '4']) def test_non_default_encoding(self): """#13572 - QueryDict with a non-default encoding""" - q = QueryDict(b'sbb=one', encoding='rot_13') - self.assertEqual(q.encoding , 'rot_13' ) - self.assertEqual(q.items() , [('foo', 'bar')] ) - self.assertEqual(q.urlencode() , 'sbb=one' ) + q = QueryDict(str('cur=%A4'), encoding='iso-8859-15') + self.assertEqual(q.encoding, 'iso-8859-15') + self.assertEqual(list(six.iteritems(q)), [('cur', '€')]) + self.assertEqual(q.urlencode(), 'cur=%A4') q = q.copy() - self.assertEqual(q.encoding , 'rot_13' ) - self.assertEqual(q.items() , [('foo', 'bar')] ) - self.assertEqual(q.urlencode() , 'sbb=one' ) - self.assertEqual(copy.copy(q).encoding , 'rot_13' ) - self.assertEqual(copy.deepcopy(q).encoding , 'rot_13') + self.assertEqual(q.encoding, 'iso-8859-15') + self.assertEqual(list(six.iteritems(q)), [('cur', '€')]) + self.assertEqual(q.urlencode(), 'cur=%A4') + self.assertEqual(copy.copy(q).encoding, 'iso-8859-15') + self.assertEqual(copy.deepcopy(q).encoding, 'iso-8859-15') class HttpResponseTests(unittest.TestCase): def test_unicode_headers(self): @@ -260,45 +272,40 @@ class HttpResponseTests(unittest.TestCase): def test_non_string_content(self): #Bug 16494: HttpResponse should behave consistently with non-strings r = HttpResponse(12345) - self.assertEqual(r.content, '12345') + self.assertEqual(r.content, b'12345') #test content via property r = HttpResponse() r.content = 12345 - self.assertEqual(r.content, '12345') + self.assertEqual(r.content, b'12345') def test_iter_content(self): r = HttpResponse(['abc', 'def', 'ghi']) - self.assertEqual(r.content, 'abcdefghi') + self.assertEqual(r.content, b'abcdefghi') #test iter content via property r = HttpResponse() r.content = ['idan', 'alex', 'jacob'] - self.assertEqual(r.content, 'idanalexjacob') + self.assertEqual(r.content, b'idanalexjacob') r = HttpResponse() r.content = [1, 2, 3] - self.assertEqual(r.content, '123') + self.assertEqual(r.content, b'123') #test retrieval explicitly using iter and odd inputs r = HttpResponse() - r.content = ['1', '2', 3, unichr(1950)] - result = [] + r.content = ['1', '2', 3, '\u079e'] my_iter = r.__iter__() - while True: - try: - result.append(next(my_iter)) - except StopIteration: - break + result = list(my_iter) #'\xde\x9e' == unichr(1950).encode('utf-8') - self.assertEqual(result, ['1', '2', '3', b'\xde\x9e']) + self.assertEqual(result, [b'1', b'2', b'3', b'\xde\x9e']) self.assertEqual(r.content, b'123\xde\x9e') #with Content-Encoding header r = HttpResponse([1,1,2,4,8]) r['Content-Encoding'] = 'winning' - self.assertEqual(r.content, '11248') - r.content = [unichr(1950),] + self.assertEqual(r.content, b'11248') + r.content = ['\u079e',] self.assertRaises(UnicodeEncodeError, getattr, r, 'content') @@ -325,6 +332,33 @@ class HttpResponseTests(unittest.TestCase): HttpResponsePermanentRedirect, url) +class HttpResponseSubclassesTests(TestCase): + def test_redirect(self): + response = HttpResponseRedirect('/redirected/') + self.assertEqual(response.status_code, 302) + # Test that standard HttpResponse init args can be used + response = HttpResponseRedirect('/redirected/', + content='The resource has temporarily moved', + content_type='text/html') + self.assertContains(response, 'The resource has temporarily moved', status_code=302) + + def test_not_modified(self): + response = HttpResponseNotModified() + self.assertEqual(response.status_code, 304) + # 304 responses should not have content/content-type + with self.assertRaises(AttributeError): + response.content = "Hello dear" + self.assertNotIn('content-type', response) + + def test_not_allowed(self): + response = HttpResponseNotAllowed(['GET']) + self.assertEqual(response.status_code, 405) + # Test that standard HttpResponse init args can be used + response = HttpResponseNotAllowed(['GET'], + content='Only the GET method is allowed', + content_type='text/html') + self.assertContains(response, 'Only the GET method is allowed', status_code=405) + class CookieTests(unittest.TestCase): def test_encode(self): """ diff --git a/tests/regressiontests/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 9ca66bdb9b..bce3d617d2 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -18,7 +18,7 @@ from django.utils.formats import (get_format, date_format, time_format, number_format) from django.utils.importlib import import_module from django.utils.numberformat import format as nformat -from django.utils.safestring import mark_safe, SafeString, SafeUnicode +from django.utils.safestring import mark_safe, SafeBytes, SafeString, SafeText from django.utils import six from django.utils.six import PY3 from django.utils.translation import (ugettext, ugettext_lazy, activate, @@ -232,12 +232,12 @@ class TranslationTests(TestCase): """ Translating a string requiring no auto-escaping shouldn't change the "safe" status. """ - s = mark_safe(b'Password') + s = mark_safe(str('Password')) self.assertEqual(SafeString, type(s)) with translation.override('de', deactivate=True): - self.assertEqual(SafeUnicode, type(ugettext(s))) - self.assertEqual('aPassword', SafeString('a') + s) - self.assertEqual('Passworda', s + SafeString('a')) + self.assertEqual(SafeText, type(ugettext(s))) + self.assertEqual('aPassword', SafeText('a') + s) + self.assertEqual('Passworda', s + SafeText('a')) self.assertEqual('Passworda', s + mark_safe('a')) self.assertEqual('aPassword', mark_safe('a') + s) self.assertEqual('as', mark_safe('a') + mark_safe('s')) @@ -808,13 +808,13 @@ class MiscTests(TestCase): r.META = {'HTTP_ACCEPT_LANGUAGE': 'de'} self.assertEqual(g(r), 'zh-cn') - def test_get_language_from_path(self): + def test_get_language_from_path_real(self): from django.utils.translation.trans_real import get_language_from_path as g self.assertEqual(g('/pl/'), 'pl') self.assertEqual(g('/pl'), 'pl') self.assertEqual(g('/xyz/'), None) - def test_get_language_from_path(self): + def test_get_language_from_path_null(self): from django.utils.translation.trans_null import get_language_from_path as g self.assertEqual(g('/pl/'), None) self.assertEqual(g('/pl'), None) @@ -897,9 +897,9 @@ class TestModels(TestCase): def test_safestr(self): c = Company(cents_paid=12, products_delivered=1) - c.name = SafeUnicode('Iñtërnâtiônàlizætiøn1') + c.name = SafeText('Iñtërnâtiônàlizætiøn1') c.save() - c.name = SafeString('Iñtërnâtiônàlizætiøn1'.encode('utf-8')) + c.name = SafeBytes('Iñtërnâtiônàlizætiøn1'.encode('utf-8')) c.save() diff --git a/tests/regressiontests/inline_formsets/models.py b/tests/regressiontests/inline_formsets/models.py index d76eea758b..40c85fe739 100644 --- a/tests/regressiontests/inline_formsets/models.py +++ b/tests/regressiontests/inline_formsets/models.py @@ -1,5 +1,6 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible class School(models.Model): @@ -14,15 +15,17 @@ class Child(models.Model): school = models.ForeignKey(School) name = models.CharField(max_length=100) +@python_2_unicode_compatible class Poet(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Poem(models.Model): poet = models.ForeignKey(Poet) name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/inspectdb/models.py b/tests/regressiontests/inspectdb/models.py index 9f815855b6..4a6621402f 100644 --- a/tests/regressiontests/inspectdb/models.py +++ b/tests/regressiontests/inspectdb/models.py @@ -19,3 +19,12 @@ class DigitsInColumnName(models.Model): all_digits = models.CharField(max_length=11, db_column='123') leading_digit = models.CharField(max_length=11, db_column='4extra') leading_digits = models.CharField(max_length=11, db_column='45extra') + +class SpecialColumnName(models.Model): + field = models.IntegerField(db_column='field') + # Underscores + field_field_0 = models.IntegerField(db_column='Field_') + field_field_1 = models.IntegerField(db_column='Field__') + field_field_2 = models.IntegerField(db_column='__field') + # Other chars + prc_x = models.IntegerField(db_column='prc(%) x') diff --git a/tests/regressiontests/inspectdb/tests.py b/tests/regressiontests/inspectdb/tests.py index aae7bc5cc7..484e7f4060 100644 --- a/tests/regressiontests/inspectdb/tests.py +++ b/tests/regressiontests/inspectdb/tests.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from django.core.management import call_command from django.test import TestCase, skipUnlessDBFeature from django.utils.six import StringIO @@ -17,7 +19,6 @@ class InspectDBTestCase(TestCase): # the Django test suite, check that one of its tables hasn't been # inspected self.assertNotIn("class DjangoContentType(models.Model):", out.getvalue(), msg=error_message) - out.close() @skipUnlessDBFeature('can_introspect_foreign_keys') def test_attribute_name_not_python_keyword(self): @@ -27,15 +28,16 @@ class InspectDBTestCase(TestCase): call_command('inspectdb', table_name_filter=lambda tn:tn.startswith('inspectdb_'), stdout=out) + output = out.getvalue() error_message = "inspectdb generated an attribute name which is a python keyword" - self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", out.getvalue(), msg=error_message) + self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", output, msg=error_message) # As InspectdbPeople model is defined after InspectdbMessage, it should be quoted - self.assertIn("from_field = models.ForeignKey('InspectdbPeople')", out.getvalue()) + self.assertIn("from_field = models.ForeignKey('InspectdbPeople', db_column='from_id')", + output) self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)", - out.getvalue()) + output) self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)", - out.getvalue()) - out.close() + output) def test_digits_column_name_introspection(self): """Introspection of column names consist/start with digits (#16536/#17676)""" @@ -45,13 +47,27 @@ class InspectDBTestCase(TestCase): call_command('inspectdb', table_name_filter=lambda tn:tn.startswith('inspectdb_'), stdout=out) + output = out.getvalue() error_message = "inspectdb generated a model field name which is a number" - self.assertNotIn(" 123 = models.CharField", out.getvalue(), msg=error_message) - self.assertIn("number_123 = models.CharField", out.getvalue()) + self.assertNotIn(" 123 = models.CharField", output, msg=error_message) + self.assertIn("number_123 = models.CharField", output) error_message = "inspectdb generated a model field name which starts with a digit" - self.assertNotIn(" 4extra = models.CharField", out.getvalue(), msg=error_message) - self.assertIn("number_4extra = models.CharField", out.getvalue()) + self.assertNotIn(" 4extra = models.CharField", output, msg=error_message) + self.assertIn("number_4extra = models.CharField", output) + + self.assertNotIn(" 45extra = models.CharField", output, msg=error_message) + self.assertIn("number_45extra = models.CharField", output) - self.assertNotIn(" 45extra = models.CharField", out.getvalue(), msg=error_message) - self.assertIn("number_45extra = models.CharField", out.getvalue()) + def test_special_column_name_introspection(self): + """Introspection of column names containing special characters, + unsuitable for Python identifiers + """ + out = StringIO() + call_command('inspectdb', stdout=out) + output = out.getvalue() + self.assertIn("field = models.IntegerField()", output) + self.assertIn("field_field = models.IntegerField(db_column='Field_')", output) + self.assertIn("field_field_0 = models.IntegerField(db_column='Field__')", output) + self.assertIn("field_field_1 = models.IntegerField(db_column='__field')", output) + self.assertIn("prc_x = models.IntegerField(db_column='prc(%) x')", output) diff --git a/tests/regressiontests/introspection/models.py b/tests/regressiontests/introspection/models.py index 3ca80c5aab..6e5beba61d 100644 --- a/tests/regressiontests/introspection/models.py +++ b/tests/regressiontests/introspection/models.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Reporter(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) @@ -12,15 +14,16 @@ class Reporter(models.Model): class Meta: unique_together = ('first_name', 'last_name') - def __unicode__(self): + def __str__(self): return "%s %s" % (self.first_name, self.last_name) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateField() reporter = models.ForeignKey(Reporter) - def __unicode__(self): + def __str__(self): return self.headline class Meta: diff --git a/tests/regressiontests/introspection/tests.py b/tests/regressiontests/introspection/tests.py index 06736a68cb..a54e0c670b 100644 --- a/tests/regressiontests/introspection/tests.py +++ b/tests/regressiontests/introspection/tests.py @@ -4,6 +4,7 @@ from functools import update_wrapper from django.db import connection from django.test import TestCase, skipUnlessDBFeature, skipIfDBFeature +from django.utils import six from .models import Reporter, Article @@ -35,8 +36,7 @@ class IgnoreNotimplementedError(type): attrs[k] = ignore_not_implemented(v) return type.__new__(cls, name, bases, attrs) -class IntrospectionTests(TestCase): - __metaclass__ = IgnoreNotimplementedError +class IntrospectionTests(six.with_metaclass(IgnoreNotimplementedError, TestCase)): def test_table_names(self): tl = connection.introspection.table_names() @@ -89,6 +89,11 @@ class IntrospectionTests(TestCase): [datatype(r[1], r) for r in desc], ['IntegerField', 'CharField', 'CharField', 'CharField', 'BigIntegerField'] ) + # Check also length of CharFields + self.assertEqual( + [r[3] for r in desc if datatype(r[1], r) == 'CharField'], + [30, 30, 75] + ) # Oracle forces null=True under the hood in some cases (see # https://docs.djangoproject.com/en/dev/ref/databases/#null-and-empty-strings) diff --git a/tests/regressiontests/m2m_regress/models.py b/tests/regressiontests/m2m_regress/models.py index be8dcae9c1..7c1108456e 100644 --- a/tests/regressiontests/m2m_regress/models.py +++ b/tests/regressiontests/m2m_regress/models.py @@ -1,37 +1,42 @@ from django.contrib.auth import models as auth from django.db import models +from django.utils.encoding import python_2_unicode_compatible # No related name is needed here, since symmetrical relations are not # explicitly reversible. +@python_2_unicode_compatible class SelfRefer(models.Model): name = models.CharField(max_length=10) references = models.ManyToManyField('self') related = models.ManyToManyField('self') - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return self.name # Regression for #11956 -- a many to many to the base class +@python_2_unicode_compatible class TagCollection(Tag): tags = models.ManyToManyField(Tag, related_name='tag_collections') - def __unicode__(self): + def __str__(self): return self.name # A related_name is required on one of the ManyToManyField entries here because # they are both addressable as reverse relations from Tag. +@python_2_unicode_compatible class Entry(models.Model): name = models.CharField(max_length=10) topics = models.ManyToManyField(Tag) related = models.ManyToManyField(Tag, related_name="similar") - def __unicode__(self): + def __str__(self): return self.name # Two models both inheriting from a base model with a self-referential m2m field diff --git a/tests/regressiontests/m2m_through_regress/models.py b/tests/regressiontests/m2m_through_regress/models.py index 1db3de1a4c..47c24ed5b2 100644 --- a/tests/regressiontests/m2m_through_regress/models.py +++ b/tests/regressiontests/m2m_through_regress/models.py @@ -2,40 +2,45 @@ from __future__ import unicode_literals from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible # Forward declared intermediate model +@python_2_unicode_compatible class Membership(models.Model): person = models.ForeignKey('Person') group = models.ForeignKey('Group') price = models.IntegerField(default=100) - def __unicode__(self): + def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) # using custom id column to test ticket #11107 +@python_2_unicode_compatible class UserMembership(models.Model): id = models.AutoField(db_column='usermembership_id', primary_key=True) user = models.ForeignKey(User) group = models.ForeignKey('Group') price = models.IntegerField(default=100) - def __unicode__(self): + def __str__(self): return "%s is a user and member of %s" % (self.user.username, self.group.name) +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=128) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=128) # Membership object defined as a class members = models.ManyToManyField(Person, through=Membership) user_members = models.ManyToManyField(User, through='UserMembership') - def __unicode__(self): + def __str__(self): return self.name # A set of models that use an non-abstract inherited model as the 'through' model. @@ -55,22 +60,25 @@ class B(models.Model): # Using to_field on the through model +@python_2_unicode_compatible class Car(models.Model): make = models.CharField(max_length=20, unique=True) drivers = models.ManyToManyField('Driver', through='CarDriver') - def __unicode__(self, ): + def __str__(self): return self.make +@python_2_unicode_compatible class Driver(models.Model): name = models.CharField(max_length=20, unique=True) - def __unicode__(self, ): + def __str__(self): return self.name +@python_2_unicode_compatible class CarDriver(models.Model): car = models.ForeignKey('Car', to_field='make') driver = models.ForeignKey('Driver', to_field='name') - def __unicode__(self, ): + def __str__(self): return "pk=%s car=%s driver=%s" % (str(self.pk), self.car, self.driver) diff --git a/tests/regressiontests/m2m_through_regress/tests.py b/tests/regressiontests/m2m_through_regress/tests.py index 73a13654e7..458c194f89 100644 --- a/tests/regressiontests/m2m_through_regress/tests.py +++ b/tests/regressiontests/m2m_through_regress/tests.py @@ -1,10 +1,9 @@ from __future__ import absolute_import -from io import BytesIO - from django.core import management from django.contrib.auth.models import User from django.test import TestCase +from django.utils.six import StringIO from .models import (Person, Group, Membership, UserMembership, Car, Driver, CarDriver) @@ -70,11 +69,11 @@ class M2MThroughTestCase(TestCase): pks = {"p_pk": p.pk, "g_pk": g.pk, "m_pk": m.pk} - out = BytesIO() + out = StringIO() management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out) self.assertEqual(out.getvalue().strip(), """[{"pk": %(m_pk)s, "model": "m2m_through_regress.membership", "fields": {"person": %(p_pk)s, "price": 100, "group": %(g_pk)s}}, {"pk": %(p_pk)s, "model": "m2m_through_regress.person", "fields": {"name": "Bob"}}, {"pk": %(g_pk)s, "model": "m2m_through_regress.group", "fields": {"name": "Roll"}}]""" % pks) - out = BytesIO() + out = StringIO() management.call_command("dumpdata", "m2m_through_regress", format="xml", indent=2, stdout=out) self.assertEqual(out.getvalue().strip(), """ @@ -142,6 +141,6 @@ class ThroughLoadDataTestCase(TestCase): def test_sequence_creation(self): "Check that sequences on an m2m_through are created for the through model, not a phantom auto-generated m2m table. Refs #11107" - out = BytesIO() + out = StringIO() management.call_command("dumpdata", "m2m_through_regress", format="json", stdout=out) self.assertEqual(out.getvalue().strip(), """[{"pk": 1, "model": "m2m_through_regress.usermembership", "fields": {"price": 100, "group": 1, "user": 1}}, {"pk": 1, "model": "m2m_through_regress.person", "fields": {"name": "Guido"}}, {"pk": 1, "model": "m2m_through_regress.group", "fields": {"name": "Python Core Group"}}]""") diff --git a/tests/regressiontests/mail/tests.py b/tests/regressiontests/mail/tests.py index c948662bc3..3e9ae84650 100644 --- a/tests/regressiontests/mail/tests.py +++ b/tests/regressiontests/mail/tests.py @@ -553,6 +553,8 @@ class FileBackendTests(BaseEmailBackendTests, TestCase): msg.send() self.assertEqual(len(os.listdir(self.tmp_dir)), 3) + connection.close() + class ConsoleBackendTests(BaseEmailBackendTests, TestCase): email_backend = 'django.core.mail.backends.console.EmailBackend' diff --git a/tests/regressiontests/managers_regress/models.py b/tests/regressiontests/managers_regress/models.py index fb6c530722..892505f24a 100644 --- a/tests/regressiontests/managers_regress/models.py +++ b/tests/regressiontests/managers_regress/models.py @@ -3,6 +3,7 @@ Various edge-cases for model managers. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class OnlyFred(models.Manager): @@ -44,35 +45,40 @@ class AbstractBase3(models.Model): class Meta: abstract = True +@python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=50) manager = OnlyFred() - def __unicode__(self): + def __str__(self): return self.name # Managers from base classes are inherited and, if no manager is specified # *and* the parent has a manager specified, the first one (in the MRO) will # become the default. +@python_2_unicode_compatible class Child1(AbstractBase1): data = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child2(AbstractBase1, AbstractBase2): data = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child3(AbstractBase1, AbstractBase3): data = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child4(AbstractBase1): data = models.CharField(max_length=25) @@ -80,16 +86,17 @@ class Child4(AbstractBase1): # inherited. default = models.Manager() - def __unicode__(self): + def __str__(self): return self.data +@python_2_unicode_compatible class Child5(AbstractBase3): name = models.CharField(max_length=25) default = OnlyFred() objects = models.Manager() - def __unicode__(self): + def __str__(self): return self.name # Will inherit managers from AbstractBase1, but not Child4. diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py index 1e59c4c8c8..f3727820f8 100644 --- a/tests/regressiontests/many_to_one_regress/models.py +++ b/tests/regressiontests/many_to_one_regress/models.py @@ -4,6 +4,7 @@ Regression tests for a few ForeignKey bugs. from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # If ticket #1578 ever slips back in, these models will not be able to be # created (the field names being lower-cased versions of their opposite @@ -30,18 +31,20 @@ class Child(models.Model): # Multiple paths to the same model (#7110, #7125) +@python_2_unicode_compatible class Category(models.Model): name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.name class Record(models.Model): category = models.ForeignKey(Category) +@python_2_unicode_compatible class Relation(models.Model): left = models.ForeignKey(Record, related_name='left_set') right = models.ForeignKey(Record, related_name='right_set') - def __unicode__(self): + def __str__(self): return "%s - %s" % (self.left.category.name, self.right.category.name) diff --git a/tests/regressiontests/middleware/tests.py b/tests/regressiontests/middleware/tests.py index 08a385e6cf..eb66f2bbf3 100644 --- a/tests/regressiontests/middleware/tests.py +++ b/tests/regressiontests/middleware/tests.py @@ -3,6 +3,7 @@ import gzip import re import random +from io import BytesIO from django.conf import settings from django.core import mail @@ -14,8 +15,9 @@ from django.middleware.http import ConditionalGetMiddleware from django.middleware.gzip import GZipMiddleware from django.test import TestCase, RequestFactory from django.test.utils import override_settings +from django.utils import six from django.utils.six.moves import xrange -from django.utils.six import StringIO + class CommonMiddlewareTest(TestCase): def setUp(self): @@ -506,9 +508,9 @@ class GZipMiddlewareTest(TestCase): """ Tests the GZip middleware. """ - short_string = "This string is too short to be worth compressing." - compressible_string = 'a' * 500 - uncompressible_string = ''.join(chr(random.randint(0, 255)) for _ in xrange(500)) + short_string = b"This string is too short to be worth compressing." + compressible_string = b'a' * 500 + uncompressible_string = b''.join(six.int2byte(random.randint(0, 255)) for _ in xrange(500)) def setUp(self): self.req = HttpRequest() @@ -526,7 +528,7 @@ class GZipMiddlewareTest(TestCase): @staticmethod def decompress(gzipped_string): - return gzip.GzipFile(mode='rb', fileobj=StringIO(gzipped_string)).read() + return gzip.GzipFile(mode='rb', fileobj=BytesIO(gzipped_string)).read() def test_compress_response(self): """ @@ -590,7 +592,7 @@ class ETagGZipMiddlewareTest(TestCase): """ Tests if the ETag middleware behaves correctly with GZip middleware. """ - compressible_string = 'a' * 500 + compressible_string = b'a' * 500 def setUp(self): self.rf = RequestFactory() diff --git a/tests/regressiontests/model_fields/models.py b/tests/regressiontests/model_fields/models.py index 4dcfb17bb3..d9c123fb39 100644 --- a/tests/regressiontests/model_fields/models.py +++ b/tests/regressiontests/model_fields/models.py @@ -69,6 +69,33 @@ class BooleanModel(models.Model): class RenamedField(models.Model): modelname = models.IntegerField(name="fieldname", choices=((1,'One'),)) +class VerboseNameField(models.Model): + id = models.AutoField("verbose pk", primary_key=True) + field1 = models.BigIntegerField("verbose field1") + field2 = models.BooleanField("verbose field2") + field3 = models.CharField("verbose field3", max_length=10) + field4 = models.CommaSeparatedIntegerField("verbose field4", max_length=99) + field5 = models.DateField("verbose field5") + field6 = models.DateTimeField("verbose field6") + field7 = models.DecimalField("verbose field7", max_digits=6, decimal_places=1) + field8 = models.EmailField("verbose field8") + field9 = models.FileField("verbose field9", upload_to="unused") + field10 = models.FilePathField("verbose field10") + field11 = models.FloatField("verbose field11") + # Don't want to depend on PIL in this test + #field_image = models.ImageField("verbose field") + field12 = models.IntegerField("verbose field12") + field13 = models.IPAddressField("verbose field13") + field14 = models.GenericIPAddressField("verbose field14", protocol="ipv4") + field15 = models.NullBooleanField("verbose field15") + field16 = models.PositiveIntegerField("verbose field16") + field17 = models.PositiveSmallIntegerField("verbose field17") + field18 = models.SlugField("verbose field18") + field19 = models.SmallIntegerField("verbose field19") + field20 = models.TextField("verbose field20") + field21 = models.TimeField("verbose field21") + field22 = models.URLField("verbose field22") + # This model isn't used in any test, just here to ensure it validates successfully. # See ticket #16570. class DecimalLessThanOne(models.Model): diff --git a/tests/regressiontests/model_fields/tests.py b/tests/regressiontests/model_fields/tests.py index 7d6071accc..526aca4f98 100644 --- a/tests/regressiontests/model_fields/tests.py +++ b/tests/regressiontests/model_fields/tests.py @@ -12,7 +12,7 @@ from django.utils import six from django.utils import unittest from .models import (Foo, Bar, Whiz, BigD, BigS, Image, BigInt, Post, - NullBooleanModel, BooleanModel, Document, RenamedField) + NullBooleanModel, BooleanModel, Document, RenamedField, VerboseNameField) from .imagefield import (ImageFieldTests, ImageFieldTwoDimensionsTests, TwoImageFieldTests, ImageFieldNoDimensionsTests, @@ -64,6 +64,14 @@ class BasicFieldTests(test.TestCase): self.assertTrue(hasattr(instance, 'get_fieldname_display')) self.assertFalse(hasattr(instance, 'get_modelname_display')) + def test_field_verbose_name(self): + m = VerboseNameField + for i in range(1, 23): + self.assertEqual(m._meta.get_field('field%d' % i).verbose_name, + 'verbose field%d' % i) + + self.assertEqual(m._meta.get_field('id').verbose_name, 'verbose pk') + class DecimalFieldTests(test.TestCase): def test_to_python(self): f = models.DecimalField(max_digits=4, decimal_places=2) diff --git a/tests/regressiontests/model_forms_regress/models.py b/tests/regressiontests/model_forms_regress/models.py index 9259e260f0..f6e08d24dc 100644 --- a/tests/regressiontests/model_forms_regress/models.py +++ b/tests/regressiontests/model_forms_regress/models.py @@ -4,6 +4,7 @@ import os from django.core.exceptions import ValidationError from django.db import models +from django.utils.encoding import python_2_unicode_compatible class Person(models.Model): @@ -20,18 +21,20 @@ class Triple(models.Model): class FilePathModel(models.Model): path = models.FilePathField(path=os.path.dirname(__file__), match=".*\.py$", blank=True) +@python_2_unicode_compatible class Publication(models.Model): title = models.CharField(max_length=30) date_published = models.DateField() - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) publications = models.ManyToManyField(Publication) - def __unicode__(self): + def __str__(self): return self.headline class CustomFileField(models.FileField): diff --git a/tests/regressiontests/model_forms_regress/tests.py b/tests/regressiontests/model_forms_regress/tests.py index 3cb129f84e..90c907f2a6 100644 --- a/tests/regressiontests/model_forms_regress/tests.py +++ b/tests/regressiontests/model_forms_regress/tests.py @@ -485,9 +485,8 @@ class CustomMetaclass(ModelFormMetaclass): new.base_fields = {} return new -class CustomMetaclassForm(forms.ModelForm): - __metaclass__ = CustomMetaclass - +class CustomMetaclassForm(six.with_metaclass(CustomMetaclass, forms.ModelForm)): + pass class CustomMetaclassTestCase(TestCase): def test_modelform_factory_metaclass(self): diff --git a/tests/regressiontests/model_formsets_regress/models.py b/tests/regressiontests/model_formsets_regress/models.py index 189ed8072e..f94ad51929 100644 --- a/tests/regressiontests/model_formsets_regress/models.py +++ b/tests/regressiontests/model_formsets_regress/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class User(models.Model): @@ -22,9 +23,10 @@ class Manager(models.Model): class Network(models.Model): name = models.CharField(max_length=15) +@python_2_unicode_compatible class Host(models.Model): network = models.ForeignKey(Network) hostname = models.CharField(max_length=25) - def __unicode__(self): + def __str__(self): return self.hostname diff --git a/tests/regressiontests/model_inheritance_regress/models.py b/tests/regressiontests/model_inheritance_regress/models.py index 5481298963..811c8175bb 100644 --- a/tests/regressiontests/model_inheritance_regress/models.py +++ b/tests/regressiontests/model_inheritance_regress/models.py @@ -3,7 +3,9 @@ from __future__ import unicode_literals import datetime from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) @@ -11,28 +13,31 @@ class Place(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(Place): serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.name +@python_2_unicode_compatible class ItalianRestaurant(Restaurant): serves_gnocchi = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the italian restaurant" % self.name +@python_2_unicode_compatible class ParkingLot(Place): # An explicit link to the parent (we can control the attribute name). parent = models.OneToOneField(Place, primary_key=True, parent_link=True) capacity = models.IntegerField() - def __unicode__(self): + def __str__(self): return "%s the parking lot" % self.name class ParkingLot2(Place): @@ -64,13 +69,14 @@ class SelfRefParent(models.Model): class SelfRefChild(SelfRefParent): child_data = models.IntegerField() +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pub_date', 'headline') - def __unicode__(self): + def __str__(self): return self.headline class ArticleWithAuthor(Article): @@ -91,17 +97,19 @@ class Evaluation(Article): class QualityControl(Evaluation): assignee = models.CharField(max_length=50) +@python_2_unicode_compatible class BaseM(models.Model): base_name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.base_name +@python_2_unicode_compatible class DerivedM(BaseM): customPK = models.IntegerField(primary_key=True) derived_name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return "PK = %d, base_name = %s, derived_name = %s" \ % (self.customPK, self.base_name, self.derived_name) @@ -120,15 +128,17 @@ class InternalCertificationAudit(CertificationAudit): auditing_dept = models.CharField(max_length=20) # Check that abstract classes don't get m2m tables autocreated. +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=100) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class AbstractEvent(models.Model): name = models.CharField(max_length=100) attendees = models.ManyToManyField(Person, related_name="%(class)s_set") @@ -137,7 +147,7 @@ class AbstractEvent(models.Model): abstract = True ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class BirthdayParty(AbstractEvent): diff --git a/tests/regressiontests/model_inheritance_select_related/models.py b/tests/regressiontests/model_inheritance_select_related/models.py index f810531bff..6b28772620 100644 --- a/tests/regressiontests/model_inheritance_select_related/models.py +++ b/tests/regressiontests/model_inheritance_select_related/models.py @@ -5,27 +5,31 @@ select_related(). from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(Place): serves_sushi = models.BooleanField() serves_steak = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.name +@python_2_unicode_compatible class Person(models.Model): name = models.CharField(max_length=50) favorite_restaurant = models.ForeignKey(Restaurant) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/model_regress/models.py b/tests/regressiontests/model_regress/models.py index aca6d44837..82efb9c783 100644 --- a/tests/regressiontests/model_regress/models.py +++ b/tests/regressiontests/model_regress/models.py @@ -1,5 +1,6 @@ # coding: utf-8 from django.db import models +from django.utils.encoding import python_2_unicode_compatible CHOICES = ( @@ -8,6 +9,7 @@ CHOICES = ( ) +@python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() @@ -20,7 +22,7 @@ class Article(models.Model): # 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): + def __str__(self): return self.headline @@ -38,29 +40,31 @@ class Event(models.Model): when = models.DateTimeField() +@python_2_unicode_compatible class Department(models.Model): id = models.PositiveIntegerField(primary_key=True) name = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Worker(models.Model): department = models.ForeignKey(Department) name = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class BrokenUnicodeMethod(models.Model): name = models.CharField(max_length=7) - def __unicode__(self): - # Intentionally broken (trying to insert a unicode value into a str - # object). - return 'Názov: %s' % self.name + def __str__(self): + # Intentionally broken (invalid start byte in byte string). + return b'Name\xff: %s'.decode() % self.name class NonAutoPK(models.Model): diff --git a/tests/regressiontests/modeladmin/models.py b/tests/regressiontests/modeladmin/models.py index 33202fad8f..fdbcabd187 100644 --- a/tests/regressiontests/modeladmin/models.py +++ b/tests/regressiontests/modeladmin/models.py @@ -1,8 +1,10 @@ # coding: utf-8 from django.contrib.auth.models import User from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Band(models.Model): name = models.CharField(max_length=100) bio = models.TextField() @@ -11,7 +13,7 @@ class Band(models.Model): class Meta: ordering = ('name',) - def __unicode__(self): + def __str__(self): return self.name class Concert(models.Model): diff --git a/tests/regressiontests/modeladmin/tests.py b/tests/regressiontests/modeladmin/tests.py index c2a1ca05ba..d55d50d0a5 100644 --- a/tests/regressiontests/modeladmin/tests.py +++ b/tests/regressiontests/modeladmin/tests.py @@ -5,7 +5,7 @@ from datetime import date from django import forms from django.conf import settings from django.contrib.admin.options import (ModelAdmin, TabularInline, - InlineModelAdmin, HORIZONTAL, VERTICAL) + HORIZONTAL, VERTICAL) from django.contrib.admin.sites import AdminSite from django.contrib.admin.validation import validate from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect @@ -15,7 +15,7 @@ from django.core.exceptions import ImproperlyConfigured from django.forms.models import BaseModelFormSet from django.forms.widgets import Select from django.test import TestCase -from django.test.utils import override_settings, str_prefix +from django.test.utils import str_prefix from django.utils import unittest from .models import Band, Concert, ValidationTestModel, ValidationTestInlineModel @@ -24,6 +24,7 @@ from .models import Band, Concert, ValidationTestModel, ValidationTestInlineMode class MockRequest(object): pass + class MockSuperUser(object): def has_perm(self, perm): return True @@ -47,7 +48,7 @@ class ModelAdminTests(TestCase): def test_default_fields(self): ma = ModelAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date']) def test_default_fieldsets(self): @@ -72,7 +73,7 @@ class ModelAdminTests(TestCase): ma = BandAdmin(Band, self.site) - self.assertEqual( ma.get_fieldsets(request), + self.assertEqual(ma.get_fieldsets(request), [(None, {'fields': ['name']})]) self.assertEqual(ma.get_fieldsets(request, self.band), @@ -90,8 +91,8 @@ class ModelAdminTests(TestCase): fields = ['name'] ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), ['name']) - self.assertEqual(ma.get_form(request, self.band).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name']) + self.assertEqual(list(ma.get_form(request, self.band).base_fields), ['name']) # Using `fieldsets`. @@ -99,8 +100,8 @@ class ModelAdminTests(TestCase): fieldsets = [(None, {'fields': ['name']})] ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), ['name']) - self.assertEqual(ma.get_form(request, self.band).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name']) + self.assertEqual(list(ma.get_form(request, self.band).base_fields), ['name']) # Using `exclude`. @@ -108,7 +109,7 @@ class ModelAdminTests(TestCase): exclude = ['bio'] ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date']) # You can also pass a tuple to `exclude`. @@ -116,7 +117,7 @@ class ModelAdminTests(TestCase): exclude = ('bio',) ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date']) # Using `fields` and `exclude`. @@ -125,7 +126,7 @@ class ModelAdminTests(TestCase): exclude = ['bio'] ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name']) def test_custom_form_meta_exclude_with_readonly(self): @@ -148,8 +149,8 @@ class ModelAdminTests(TestCase): form = AdminBandForm ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), - ['sign_date',]) + self.assertEqual(list(ma.get_form(request).base_fields), + ['sign_date']) # Then, with `InlineModelAdmin` ----------------- @@ -172,8 +173,8 @@ class ModelAdminTests(TestCase): ma = BandAdmin(Band, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), - ['main_band', 'opening_band', 'id', 'DELETE',]) + list(list(ma.get_formsets(request))[0]().forms[0].fields), + ['main_band', 'opening_band', 'id', 'DELETE']) def test_custom_form_meta_exclude(self): """ @@ -194,8 +195,8 @@ class ModelAdminTests(TestCase): form = AdminBandForm ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), - ['bio', 'sign_date',]) + self.assertEqual(list(ma.get_form(request).base_fields), + ['bio', 'sign_date']) # Then, with `InlineModelAdmin` ----------------- @@ -218,8 +219,8 @@ class ModelAdminTests(TestCase): ma = BandAdmin(Band, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), - ['main_band', 'opening_band', 'day', 'id', 'DELETE',]) + list(list(ma.get_formsets(request))[0]().forms[0].fields), + ['main_band', 'opening_band', 'day', 'id', 'DELETE']) def test_custom_form_validation(self): # If we specify a form, it should use it allowing custom validation to work @@ -235,7 +236,7 @@ class ModelAdminTests(TestCase): form = AdminBandForm ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date', 'delete']) self.assertEqual( @@ -255,7 +256,7 @@ class ModelAdminTests(TestCase): exclude = ['name'] class BandAdmin(ModelAdmin): - exclude = ['sign_date',] + exclude = ['sign_date'] form = AdminBandForm def get_form(self, request, obj=None, **kwargs): @@ -263,9 +264,8 @@ class ModelAdminTests(TestCase): return super(BandAdmin, self).get_form(request, obj, **kwargs) ma = BandAdmin(Band, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), - ['name', 'sign_date',]) - + self.assertEqual(list(ma.get_form(request).base_fields), + ['name', 'sign_date']) def test_formset_exclude_kwarg_override(self): """ @@ -296,8 +296,8 @@ class ModelAdminTests(TestCase): ma = BandAdmin(Band, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), - ['main_band', 'day', 'transport', 'id', 'DELETE',]) + list(list(ma.get_formsets(request))[0]().forms[0].fields), + ['main_band', 'day', 'transport', 'id', 'DELETE']) def test_queryset_override(self): # If we need to override the queryset of a ModelChoiceField in our custom form @@ -461,7 +461,7 @@ class ModelAdminTests(TestCase): form = AdminConcertForm ma = ConcertAdmin(Concert, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['main_band', 'opening_band', 'day']) class AdminConcertForm(forms.ModelForm): @@ -475,7 +475,7 @@ class ModelAdminTests(TestCase): form = AdminConcertForm ma = ConcertAdmin(Concert, self.site) - self.assertEqual(ma.get_form(request).base_fields.keys(), + self.assertEqual(list(ma.get_form(request).base_fields), ['extra', 'transport']) class ConcertInline(TabularInline): @@ -491,7 +491,7 @@ class ModelAdminTests(TestCase): ma = BandAdmin(Band, self.site) self.assertEqual( - list(ma.get_formsets(request))[0]().forms[0].fields.keys(), + list(list(ma.get_formsets(request))[0]().forms[0].fields), ['extra', 'transport', 'id', 'DELETE', 'main_band']) diff --git a/tests/regressiontests/multiple_database/models.py b/tests/regressiontests/multiple_database/models.py index 7d655fe3d6..e46438649b 100644 --- a/tests/regressiontests/multiple_database/models.py +++ b/tests/regressiontests/multiple_database/models.py @@ -4,15 +4,17 @@ from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Review(models.Model): source = models.CharField(max_length=100) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey() - def __unicode__(self): + def __str__(self): return self.source class Meta: @@ -22,11 +24,12 @@ class PersonManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) +@python_2_unicode_compatible class Person(models.Model): objects = PersonManager() name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name class Meta: @@ -45,6 +48,7 @@ class BookManager(models.Manager): kwargs.pop('extra_arg', None) return super(BookManager, self).get_or_create(*args, **kwargs) +@python_2_unicode_compatible class Book(models.Model): objects = BookManager() title = models.CharField(max_length=100) @@ -54,17 +58,18 @@ class Book(models.Model): reviews = generic.GenericRelation(Review) pages = models.IntegerField(default=100) - def __unicode__(self): + def __str__(self): return self.title class Meta: ordering = ('title',) +@python_2_unicode_compatible class Pet(models.Model): name = models.CharField(max_length=100) owner = models.ForeignKey(Person) - def __unicode__(self): + def __str__(self): return self.name class Meta: diff --git a/tests/regressiontests/multiple_database/tests.py b/tests/regressiontests/multiple_database/tests.py index 74a5f2f550..782fe2bfc6 100644 --- a/tests/regressiontests/multiple_database/tests.py +++ b/tests/regressiontests/multiple_database/tests.py @@ -1546,6 +1546,21 @@ class RouterTestCase(TestCase): # If you evaluate the query, it should work, running on 'other' self.assertEqual(list(qs.values_list('title', flat=True)), ['Dive into Python']) + def test_deferred_models(self): + mark_def = Person.objects.using('default').create(name="Mark Pilgrim") + mark_other = Person.objects.using('other').create(name="Mark Pilgrim") + orig_b = Book.objects.using('other').create(title="Dive into Python", + published=datetime.date(2009, 5, 4), + editor=mark_other) + b = Book.objects.using('other').only('title').get(pk=orig_b.pk) + self.assertEqual(b.published, datetime.date(2009, 5, 4)) + b = Book.objects.using('other').only('title').get(pk=orig_b.pk) + b.editor = mark_def + b.save(using='default') + self.assertEqual(Book.objects.using('default').get(pk=b.pk).published, + datetime.date(2009, 5, 4)) + + class AuthTestCase(TestCase): multi_db = True diff --git a/tests/regressiontests/nested_foreign_keys/__init__.py b/tests/regressiontests/nested_foreign_keys/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/regressiontests/nested_foreign_keys/__init__.py diff --git a/tests/regressiontests/nested_foreign_keys/models.py b/tests/regressiontests/nested_foreign_keys/models.py new file mode 100644 index 0000000000..50d447951b --- /dev/null +++ b/tests/regressiontests/nested_foreign_keys/models.py @@ -0,0 +1,28 @@ +from django.db import models + + +class Person(models.Model): + name = models.CharField(max_length=200) + + +class Movie(models.Model): + title = models.CharField(max_length=200) + director = models.ForeignKey(Person) + + +class Event(models.Model): + pass + + +class Screening(Event): + movie = models.ForeignKey(Movie) + +class ScreeningNullFK(Event): + movie = models.ForeignKey(Movie, null=True) + + +class Package(models.Model): + screening = models.ForeignKey(Screening, null=True) + +class PackageNullFK(models.Model): + screening = models.ForeignKey(ScreeningNullFK, null=True) diff --git a/tests/regressiontests/nested_foreign_keys/tests.py b/tests/regressiontests/nested_foreign_keys/tests.py new file mode 100644 index 0000000000..a976d12453 --- /dev/null +++ b/tests/regressiontests/nested_foreign_keys/tests.py @@ -0,0 +1,166 @@ +from __future__ import absolute_import +from django.test import TestCase + +from .models import Person, Movie, Event, Screening, ScreeningNullFK, Package, PackageNullFK + + +# These are tests for #16715. The basic scheme is always the same: 3 models with +# 2 relations. The first relation may be null, while the second is non-nullable. +# In some cases, Django would pick the wrong join type for the second relation, +# resulting in missing objects in the queryset. +# +# Model A +# | (Relation A/B : nullable) +# Model B +# | (Relation B/C : non-nullable) +# Model C +# +# Because of the possibility of NULL rows resulting from the LEFT OUTER JOIN +# between Model A and Model B (i.e. instances of A without reference to B), +# the second join must also be LEFT OUTER JOIN, so that we do not ignore +# instances of A that do not reference B. +# +# Relation A/B can either be an explicit foreign key or an implicit reverse +# relation such as introduced by one-to-one relations (through multi-table +# inheritance). +class NestedForeignKeysTests(TestCase): + def setUp(self): + self.director = Person.objects.create(name='Terry Gilliam / Terry Jones') + self.movie = Movie.objects.create(title='Monty Python and the Holy Grail', director=self.director) + + + # This test failed in #16715 because in some cases INNER JOIN was selected + # for the second foreign key relation instead of LEFT OUTER JOIN. + def testInheritance(self): + some_event = Event.objects.create() + screening = Screening.objects.create(movie=self.movie) + + self.assertEqual(len(Event.objects.all()), 2) + self.assertEqual(len(Event.objects.select_related('screening')), 2) + # This failed. + self.assertEqual(len(Event.objects.select_related('screening__movie')), 2) + + self.assertEqual(len(Event.objects.values()), 2) + self.assertEqual(len(Event.objects.values('screening__pk')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__pk')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__title')), 2) + # This failed. + self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__title')), 2) + + # Simple filter/exclude queries for good measure. + self.assertEqual(Event.objects.filter(screening__movie=self.movie).count(), 1) + self.assertEqual(Event.objects.exclude(screening__movie=self.movie).count(), 1) + + + # These all work because the second foreign key in the chain has null=True. + def testInheritanceNullFK(self): + some_event = Event.objects.create() + screening = ScreeningNullFK.objects.create(movie=None) + screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie) + + self.assertEqual(len(Event.objects.all()), 3) + self.assertEqual(len(Event.objects.select_related('screeningnullfk')), 3) + self.assertEqual(len(Event.objects.select_related('screeningnullfk__movie')), 3) + + self.assertEqual(len(Event.objects.values()), 3) + self.assertEqual(len(Event.objects.values('screeningnullfk__pk')), 3) + self.assertEqual(len(Event.objects.values('screeningnullfk__movie__pk')), 3) + self.assertEqual(len(Event.objects.values('screeningnullfk__movie__title')), 3) + self.assertEqual(len(Event.objects.values('screeningnullfk__movie__pk', 'screeningnullfk__movie__title')), 3) + + self.assertEqual(Event.objects.filter(screeningnullfk__movie=self.movie).count(), 1) + self.assertEqual(Event.objects.exclude(screeningnullfk__movie=self.movie).count(), 2) + + + # This test failed in #16715 because in some cases INNER JOIN was selected + # for the second foreign key relation instead of LEFT OUTER JOIN. + def testExplicitForeignKey(self): + package = Package.objects.create() + screening = Screening.objects.create(movie=self.movie) + package_with_screening = Package.objects.create(screening=screening) + + self.assertEqual(len(Package.objects.all()), 2) + self.assertEqual(len(Package.objects.select_related('screening')), 2) + self.assertEqual(len(Package.objects.select_related('screening__movie')), 2) + + self.assertEqual(len(Package.objects.values()), 2) + self.assertEqual(len(Package.objects.values('screening__pk')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__pk')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__title')), 2) + # This failed. + self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__title')), 2) + + self.assertEqual(Package.objects.filter(screening__movie=self.movie).count(), 1) + self.assertEqual(Package.objects.exclude(screening__movie=self.movie).count(), 1) + + + # These all work because the second foreign key in the chain has null=True. + def testExplicitForeignKeyNullFK(self): + package = PackageNullFK.objects.create() + screening = ScreeningNullFK.objects.create(movie=None) + screening_with_movie = ScreeningNullFK.objects.create(movie=self.movie) + package_with_screening = PackageNullFK.objects.create(screening=screening) + package_with_screening_with_movie = PackageNullFK.objects.create(screening=screening_with_movie) + + self.assertEqual(len(PackageNullFK.objects.all()), 3) + self.assertEqual(len(PackageNullFK.objects.select_related('screening')), 3) + self.assertEqual(len(PackageNullFK.objects.select_related('screening__movie')), 3) + + self.assertEqual(len(PackageNullFK.objects.values()), 3) + self.assertEqual(len(PackageNullFK.objects.values('screening__pk')), 3) + self.assertEqual(len(PackageNullFK.objects.values('screening__movie__pk')), 3) + self.assertEqual(len(PackageNullFK.objects.values('screening__movie__title')), 3) + self.assertEqual(len(PackageNullFK.objects.values('screening__movie__pk', 'screening__movie__title')), 3) + + self.assertEqual(PackageNullFK.objects.filter(screening__movie=self.movie).count(), 1) + self.assertEqual(PackageNullFK.objects.exclude(screening__movie=self.movie).count(), 2) + + +# Some additional tests for #16715. The only difference is the depth of the +# nesting as we now use 4 models instead of 3 (and thus 3 relations). This +# checks if promotion of join types works for deeper nesting too. +class DeeplyNestedForeignKeysTests(TestCase): + def setUp(self): + self.director = Person.objects.create(name='Terry Gilliam / Terry Jones') + self.movie = Movie.objects.create(title='Monty Python and the Holy Grail', director=self.director) + + + def testInheritance(self): + some_event = Event.objects.create() + screening = Screening.objects.create(movie=self.movie) + + self.assertEqual(len(Event.objects.all()), 2) + self.assertEqual(len(Event.objects.select_related('screening__movie__director')), 2) + + self.assertEqual(len(Event.objects.values()), 2) + self.assertEqual(len(Event.objects.values('screening__movie__director__pk')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__director__name')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__director__pk', 'screening__movie__director__name')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__director__pk')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__pk', 'screening__movie__director__name')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__title', 'screening__movie__director__pk')), 2) + self.assertEqual(len(Event.objects.values('screening__movie__title', 'screening__movie__director__name')), 2) + + self.assertEqual(Event.objects.filter(screening__movie__director=self.director).count(), 1) + self.assertEqual(Event.objects.exclude(screening__movie__director=self.director).count(), 1) + + + def testExplicitForeignKey(self): + package = Package.objects.create() + screening = Screening.objects.create(movie=self.movie) + package_with_screening = Package.objects.create(screening=screening) + + self.assertEqual(len(Package.objects.all()), 2) + self.assertEqual(len(Package.objects.select_related('screening__movie__director')), 2) + + self.assertEqual(len(Package.objects.values()), 2) + self.assertEqual(len(Package.objects.values('screening__movie__director__pk')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__director__name')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__director__pk', 'screening__movie__director__name')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__director__pk')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__pk', 'screening__movie__director__name')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__title', 'screening__movie__director__pk')), 2) + self.assertEqual(len(Package.objects.values('screening__movie__title', 'screening__movie__director__name')), 2) + + self.assertEqual(Package.objects.filter(screening__movie__director=self.director).count(), 1) + self.assertEqual(Package.objects.exclude(screening__movie__director=self.director).count(), 1) diff --git a/tests/regressiontests/null_fk/models.py b/tests/regressiontests/null_fk/models.py index e32ff542a0..c86ee8a5a9 100644 --- a/tests/regressiontests/null_fk/models.py +++ b/tests/regressiontests/null_fk/models.py @@ -3,6 +3,7 @@ Regression tests for proper working of ForeignKey(null=True). """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible class SystemDetails(models.Model): @@ -16,13 +17,15 @@ class Forum(models.Model): system_info = models.ForeignKey(SystemInfo) forum_name = models.CharField(max_length=32) +@python_2_unicode_compatible class Post(models.Model): forum = models.ForeignKey(Forum, null=True) title = models.CharField(max_length=32) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) comment_text = models.CharField(max_length=250) @@ -30,7 +33,7 @@ class Comment(models.Model): class Meta: ordering = ('comment_text',) - def __unicode__(self): + def __str__(self): return self.comment_text # Ticket 15823 diff --git a/tests/regressiontests/null_fk_ordering/models.py b/tests/regressiontests/null_fk_ordering/models.py index e4a19f1512..3caff0d594 100644 --- a/tests/regressiontests/null_fk_ordering/models.py +++ b/tests/regressiontests/null_fk_ordering/models.py @@ -8,17 +8,19 @@ xpected results from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible # The first two models represent a very simple null FK ordering case. class Author(models.Model): name = models.CharField(max_length=150) +@python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=150) author = models.ForeignKey(Author, null=True) - def __unicode__(self): + def __str__(self): return 'Article titled: %s' % (self.title, ) class Meta: @@ -33,13 +35,15 @@ class Forum(models.Model): system_info = models.ForeignKey(SystemInfo) forum_name = models.CharField(max_length=32) +@python_2_unicode_compatible class Post(models.Model): forum = models.ForeignKey(Forum, null=True) title = models.CharField(max_length=32) - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) comment_text = models.CharField(max_length=250) @@ -47,5 +51,5 @@ class Comment(models.Model): class Meta: ordering = ['post__forum__system_info__system_name', 'comment_text'] - def __unicode__(self): + def __str__(self): return self.comment_text diff --git a/tests/regressiontests/null_queries/models.py b/tests/regressiontests/null_queries/models.py index 886bd75276..25560fbab7 100644 --- a/tests/regressiontests/null_queries/models.py +++ b/tests/regressiontests/null_queries/models.py @@ -1,19 +1,22 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Poll(models.Model): question = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return "Q: %s " % self.question +@python_2_unicode_compatible class Choice(models.Model): poll = models.ForeignKey(Poll) choice = models.CharField(max_length=200) - def __unicode__(self): + def __str__(self): return "Choice: %s in poll %s" % (self.choice, self.poll) # A set of models with an inner one pointing to two outer ones. diff --git a/tests/regressiontests/one_to_one_regress/models.py b/tests/regressiontests/one_to_one_regress/models.py index 5d32bf03f8..38b801f40e 100644 --- a/tests/regressiontests/one_to_one_regress/models.py +++ b/tests/regressiontests/one_to_one_regress/models.py @@ -1,39 +1,44 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) - def __unicode__(self): + def __str__(self): return "%s the place" % self.name +@python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place) serves_hot_dogs = models.BooleanField() serves_pizza = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the restaurant" % self.place.name +@python_2_unicode_compatible class Bar(models.Model): place = models.OneToOneField(Place) serves_cocktails = models.BooleanField() - def __unicode__(self): + def __str__(self): return "%s the bar" % self.place.name class UndergroundBar(models.Model): place = models.OneToOneField(Place, null=True) serves_cocktails = models.BooleanField() +@python_2_unicode_compatible class Favorites(models.Model): name = models.CharField(max_length = 50) restaurants = models.ManyToManyField(Restaurant) - def __unicode__(self): + def __str__(self): return "Favorites for %s" % self.name class Target(models.Model): diff --git a/tests/regressiontests/pagination_regress/tests.py b/tests/regressiontests/pagination_regress/tests.py index 59ac41e062..e98352e006 100644 --- a/tests/regressiontests/pagination_regress/tests.py +++ b/tests/regressiontests/pagination_regress/tests.py @@ -17,14 +17,16 @@ class PaginatorTests(TestCase): paginator = Paginator(*params) self.check_attribute('count', paginator, count, params) self.check_attribute('num_pages', paginator, num_pages, params) - self.check_attribute('page_range', paginator, page_range, params) + self.check_attribute('page_range', paginator, page_range, params, coerce=list) - def check_attribute(self, name, paginator, expected, params): + def check_attribute(self, name, paginator, expected, params, coerce=None): """ Helper method that checks a single attribute and gives a nice error message upon test failure. """ got = getattr(paginator, name) + if coerce is not None: + got = coerce(got) self.assertEqual(expected, got, "For '%s', expected %s but got %s. Paginator parameters were: %s" % (name, expected, got, params)) diff --git a/tests/regressiontests/queries/models.py b/tests/regressiontests/queries/models.py index 6328776e91..f0178a0256 100644 --- a/tests/regressiontests/queries/models.py +++ b/tests/regressiontests/queries/models.py @@ -7,6 +7,7 @@ import threading from django.db import models from django.utils import six +from django.utils.encoding import python_2_unicode_compatible class DumbCategory(models.Model): @@ -19,6 +20,7 @@ class ProxyCategory(DumbCategory): class NamedCategory(DumbCategory): name = models.CharField(max_length=10) +@python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, @@ -28,9 +30,10 @@ class Tag(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Note(models.Model): note = models.CharField(max_length=100) misc = models.CharField(max_length=10) @@ -38,7 +41,7 @@ class Note(models.Model): class Meta: ordering = ['note'] - def __unicode__(self): + def __str__(self): return self.note def __init__(self, *args, **kwargs): @@ -48,14 +51,16 @@ class Note(models.Model): # that use objects of that type as an argument. self.lock = threading.Lock() +@python_2_unicode_compatible class Annotation(models.Model): name = models.CharField(max_length=10) tag = models.ForeignKey(Tag) notes = models.ManyToManyField(Note) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ExtraInfo(models.Model): info = models.CharField(max_length=100) note = models.ForeignKey(Note) @@ -63,9 +68,10 @@ class ExtraInfo(models.Model): class Meta: ordering = ['info'] - def __unicode__(self): + def __str__(self): return self.info +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=10) num = models.IntegerField(unique=True) @@ -74,9 +80,10 @@ class Author(models.Model): class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=10) created = models.DateTimeField() @@ -88,16 +95,18 @@ class Item(models.Model): class Meta: ordering = ['-note', 'name'] - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Report(models.Model): name = models.CharField(max_length=10) creator = models.ForeignKey(Author, to_field='num', null=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Ranking(models.Model): rank = models.IntegerField() author = models.ForeignKey(Author) @@ -106,9 +115,10 @@ class Ranking(models.Model): # A complex ordering specification. Should stress the system a bit. ordering = ('author__extra__note', 'author__name', 'rank') - def __unicode__(self): + def __str__(self): return '%d: %s' % (self.rank, self.author.name) +@python_2_unicode_compatible class Cover(models.Model): title = models.CharField(max_length=50) item = models.ForeignKey(Item) @@ -116,13 +126,14 @@ class Cover(models.Model): class Meta: ordering = ['item'] - def __unicode__(self): + def __str__(self): return self.title +@python_2_unicode_compatible class Number(models.Model): num = models.IntegerField() - def __unicode__(self): + def __str__(self): return six.text_type(self.num) # Symmetrical m2m field with a normal field using the reverse accesor name @@ -168,6 +179,7 @@ class CustomManager(models.Manager): qs = super(CustomManager, self).get_query_set() return qs.filter(public=True, tag__name='t1') +@python_2_unicode_compatible class ManagedModel(models.Model): data = models.CharField(max_length=10) tag = models.ForeignKey(Tag) @@ -176,7 +188,7 @@ class ManagedModel(models.Model): objects = CustomManager() normal_manager = models.Manager() - def __unicode__(self): + def __str__(self): return self.data # An inter-related setup with multiple paths from Child to Detail. @@ -211,11 +223,12 @@ class Related(models.Model): # An inter-related setup with a model subclass that has a nullable # path to another model, and a return path from that model. +@python_2_unicode_compatible class Celebrity(models.Model): name = models.CharField("Name", max_length=20) greatest_fan = models.ForeignKey("Fan", null=True, unique=True) - def __unicode__(self): + def __str__(self): return self.name class TvChef(Celebrity): @@ -225,10 +238,11 @@ class Fan(models.Model): fan_of = models.ForeignKey(Celebrity) # Multiple foreign keys +@python_2_unicode_compatible class LeafA(models.Model): data = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return self.data class LeafB(models.Model): @@ -238,11 +252,12 @@ class Join(models.Model): a = models.ForeignKey(LeafA) b = models.ForeignKey(LeafB) +@python_2_unicode_compatible class ReservedName(models.Model): name = models.CharField(max_length=20) order = models.IntegerField() - def __unicode__(self): + def __str__(self): return self.name # A simpler shared-foreign-key setup that can expose some problems. @@ -256,13 +271,14 @@ class PointerB(models.Model): connection = models.ForeignKey(SharedConnection) # Multi-layer ordering +@python_2_unicode_compatible class SingleObject(models.Model): name = models.CharField(max_length=10) class Meta: ordering = ['name'] - def __unicode__(self): + def __str__(self): return self.name class RelatedObject(models.Model): @@ -271,6 +287,7 @@ class RelatedObject(models.Model): class Meta: ordering = ['single'] +@python_2_unicode_compatible class Plaything(models.Model): name = models.CharField(max_length=10) others = models.ForeignKey(RelatedObject, null=True) @@ -278,79 +295,89 @@ class Plaything(models.Model): class Meta: ordering = ['others'] - def __unicode__(self): + def __str__(self): return self.name class Article(models.Model): name = models.CharField(max_length=20) created = models.DateTimeField() +@python_2_unicode_compatible class Food(models.Model): name = models.CharField(max_length=20, unique=True) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Eaten(models.Model): food = models.ForeignKey(Food, to_field="name") meal = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return "%s at %s" % (self.food, self.meal) +@python_2_unicode_compatible class Node(models.Model): num = models.IntegerField(unique=True) parent = models.ForeignKey("self", to_field="num", null=True) - def __unicode__(self): + def __str__(self): return "%s" % self.num # Bug #12252 +@python_2_unicode_compatible class ObjectA(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ObjectB(models.Model): name = models.CharField(max_length=50) objecta = models.ForeignKey(ObjectA) num = models.PositiveSmallIntegerField() - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class ObjectC(models.Model): name = models.CharField(max_length=50) objecta = models.ForeignKey(ObjectA) objectb = models.ForeignKey(ObjectB) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class SimpleCategory(models.Model): name = models.CharField(max_length=15) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class SpecialCategory(SimpleCategory): special_name = models.CharField(max_length=15) - def __unicode__(self): + def __str__(self): return self.name + " " + self.special_name +@python_2_unicode_compatible class CategoryItem(models.Model): category = models.ForeignKey(SimpleCategory) - def __unicode__(self): + def __str__(self): return "category item: " + str(self.category) +@python_2_unicode_compatible class OneToOneCategory(models.Model): new_name = models.CharField(max_length=15) category = models.OneToOneField(SimpleCategory) - def __unicode__(self): + def __str__(self): return "one2one " + self.new_name class NullableName(models.Model): @@ -358,3 +385,18 @@ class NullableName(models.Model): class Meta: ordering = ['id'] + +class ModelD(models.Model): + name = models.TextField() + +class ModelC(models.Model): + name = models.TextField() + +class ModelB(models.Model): + name = models.TextField() + c = models.ForeignKey(ModelC) + +class ModelA(models.Model): + name = models.TextField() + b = models.ForeignKey(ModelB, null=True) + d = models.ForeignKey(ModelD) diff --git a/tests/regressiontests/queries/tests.py b/tests/regressiontests/queries/tests.py index 1582993dfc..005aa9650b 100644 --- a/tests/regressiontests/queries/tests.py +++ b/tests/regressiontests/queries/tests.py @@ -23,7 +23,7 @@ from .models import (Annotation, Article, Author, Celebrity, Child, Cover, Ranking, Related, Report, ReservedName, Tag, TvChef, Valid, X, Food, Eaten, Node, ObjectA, ObjectB, ObjectC, CategoryItem, SimpleCategory, SpecialCategory, OneToOneCategory, NullableName, ProxyCategory, - SingleObject, RelatedObject) + SingleObject, RelatedObject, ModelA, ModelD) class BaseQuerysetTest(TestCase): @@ -806,7 +806,7 @@ class Queries1Tests(BaseQuerysetTest): qs = Tag.objects.values_list('id', flat=True).order_by('id') qs.query.bump_prefix() first = qs[0] - self.assertEqual(list(qs), range(first, first+5)) + self.assertEqual(list(qs), list(range(first, first+5))) def test_ticket8439(self): # Complex combinations of conjunctions, disjunctions and nullable @@ -1272,8 +1272,8 @@ class Queries5Tests(TestCase): # them in a values() query. dicts = qs.values('id', 'rank').order_by('id') self.assertEqual( - [d.items()[1] for d in dicts], - [('rank', 2), ('rank', 1), ('rank', 3)] + [d['rank'] for d in dicts], + [2, 1, 3] ) def test_ticket7256(self): @@ -2043,65 +2043,88 @@ class WhereNodeTest(TestCase): def test_empty_full_handling_conjunction(self): qn = connection.ops.quote_name w = WhereNode(children=[EverythingNode()]) - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w = WhereNode(children=[NothingNode()]) self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w.negate() - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w = WhereNode(children=[EverythingNode(), EverythingNode()]) - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w = WhereNode(children=[EverythingNode(), self.DummyNode()]) - self.assertEquals(w.as_sql(qn, connection), ('dummy', [])) + self.assertEqual(w.as_sql(qn, connection), ('dummy', [])) w = WhereNode(children=[self.DummyNode(), self.DummyNode()]) - self.assertEquals(w.as_sql(qn, connection), ('(dummy AND dummy)', [])) + self.assertEqual(w.as_sql(qn, connection), ('(dummy AND dummy)', [])) w.negate() - self.assertEquals(w.as_sql(qn, connection), ('NOT (dummy AND dummy)', [])) + self.assertEqual(w.as_sql(qn, connection), ('NOT (dummy AND dummy)', [])) w = WhereNode(children=[NothingNode(), self.DummyNode()]) self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w.negate() - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) def test_empty_full_handling_disjunction(self): qn = connection.ops.quote_name w = WhereNode(children=[EverythingNode()], connector='OR') - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w = WhereNode(children=[NothingNode()], connector='OR') self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w.negate() - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w = WhereNode(children=[EverythingNode(), EverythingNode()], connector='OR') - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w = WhereNode(children=[EverythingNode(), self.DummyNode()], connector='OR') - self.assertEquals(w.as_sql(qn, connection), ('', [])) + self.assertEqual(w.as_sql(qn, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) w = WhereNode(children=[self.DummyNode(), self.DummyNode()], connector='OR') - self.assertEquals(w.as_sql(qn, connection), ('(dummy OR dummy)', [])) + self.assertEqual(w.as_sql(qn, connection), ('(dummy OR dummy)', [])) w.negate() - self.assertEquals(w.as_sql(qn, connection), ('NOT (dummy OR dummy)', [])) + self.assertEqual(w.as_sql(qn, connection), ('NOT (dummy OR dummy)', [])) w = WhereNode(children=[NothingNode(), self.DummyNode()], connector='OR') - self.assertEquals(w.as_sql(qn, connection), ('dummy', [])) + self.assertEqual(w.as_sql(qn, connection), ('dummy', [])) w.negate() - self.assertEquals(w.as_sql(qn, connection), ('NOT (dummy)', [])) + self.assertEqual(w.as_sql(qn, connection), ('NOT (dummy)', [])) def test_empty_nodes(self): qn = connection.ops.quote_name empty_w = WhereNode() w = WhereNode(children=[empty_w, empty_w]) - self.assertEquals(w.as_sql(qn, connection), (None, [])) + self.assertEqual(w.as_sql(qn, connection), (None, [])) w.negate() - self.assertEquals(w.as_sql(qn, connection), (None, [])) + self.assertEqual(w.as_sql(qn, connection), (None, [])) w.connector = 'OR' - self.assertEquals(w.as_sql(qn, connection), (None, [])) + self.assertEqual(w.as_sql(qn, connection), (None, [])) w.negate() - self.assertEquals(w.as_sql(qn, connection), (None, [])) + self.assertEqual(w.as_sql(qn, connection), (None, [])) w = WhereNode(children=[empty_w, NothingNode()], connector='OR') self.assertRaises(EmptyResultSet, w.as_sql, qn, connection) + +class NullJoinPromotionOrTest(TestCase): + def setUp(self): + d = ModelD.objects.create(name='foo') + ModelA.objects.create(name='bar', d=d) + + def test_ticket_17886(self): + # The first Q-object is generating the match, the rest of the filters + # should not remove the match even if they do not match anything. The + # problem here was that b__name generates a LOUTER JOIN, then + # b__c__name generates join to c, which the ORM tried to promote but + # failed as that join isn't nullable. + q_obj = ( + Q(d__name='foo')| + Q(b__name='foo')| + Q(b__c__name='foo') + ) + qset = ModelA.objects.filter(q_obj) + self.assertEqual(len(qset), 1) + # We generate one INNER JOIN to D. The join is direct and not nullable + # so we can use INNER JOIN for it. However, we can NOT use INNER JOIN + # for the b->c join, as a->b is nullable. + self.assertEqual(str(qset.query).count('INNER JOIN'), 1) diff --git a/tests/regressiontests/requests/tests.py b/tests/regressiontests/requests/tests.py index f192459246..f9e1112b2e 100644 --- a/tests/regressiontests/requests/tests.py +++ b/tests/regressiontests/requests/tests.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import time import warnings from datetime import datetime, timedelta +from io import BytesIO from django.conf import settings from django.core.handlers.wsgi import WSGIRequest, LimitedStream @@ -10,17 +11,16 @@ from django.http import HttpRequest, HttpResponse, parse_cookie, build_request_r from django.test.utils import str_prefix from django.utils import unittest from django.utils.http import cookie_date -from django.utils.six import StringIO from django.utils.timezone import utc class RequestsTests(unittest.TestCase): def test_httprequest(self): request = HttpRequest() - self.assertEqual(request.GET.keys(), []) - self.assertEqual(request.POST.keys(), []) - self.assertEqual(request.COOKIES.keys(), []) - self.assertEqual(request.META.keys(), []) + self.assertEqual(list(request.GET.keys()), []) + self.assertEqual(list(request.POST.keys()), []) + self.assertEqual(list(request.COOKIES.keys()), []) + self.assertEqual(list(request.META.keys()), []) def test_httprequest_repr(self): request = HttpRequest() @@ -35,17 +35,17 @@ class RequestsTests(unittest.TestCase): str_prefix("<HttpRequest\npath:/otherpath/,\nGET:{%(_)s'a': %(_)s'b'},\nPOST:{%(_)s'c': %(_)s'd'},\nCOOKIES:{%(_)s'e': %(_)s'f'},\nMETA:{%(_)s'g': %(_)s'h'}>")) def test_wsgirequest(self): - request = WSGIRequest({'PATH_INFO': 'bogus', 'REQUEST_METHOD': 'bogus', 'wsgi.input': StringIO('')}) - self.assertEqual(request.GET.keys(), []) - self.assertEqual(request.POST.keys(), []) - self.assertEqual(request.COOKIES.keys(), []) + request = WSGIRequest({'PATH_INFO': 'bogus', 'REQUEST_METHOD': 'bogus', 'wsgi.input': BytesIO(b'')}) + self.assertEqual(list(request.GET.keys()), []) + self.assertEqual(list(request.POST.keys()), []) + self.assertEqual(list(request.COOKIES.keys()), []) self.assertEqual(set(request.META.keys()), set(['PATH_INFO', 'REQUEST_METHOD', 'SCRIPT_NAME', 'wsgi.input'])) self.assertEqual(request.META['PATH_INFO'], 'bogus') self.assertEqual(request.META['REQUEST_METHOD'], 'bogus') self.assertEqual(request.META['SCRIPT_NAME'], '') def test_wsgirequest_repr(self): - request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': StringIO('')}) + request = WSGIRequest({'PATH_INFO': '/somepath/', 'REQUEST_METHOD': 'get', 'wsgi.input': BytesIO(b'')}) request.GET = {'get-key': 'get-value'} request.POST = {'post-key': 'post-value'} request.COOKIES = {'post-key': 'post-value'} @@ -207,26 +207,26 @@ class RequestsTests(unittest.TestCase): def test_limited_stream(self): # Read all of a limited stream - stream = LimitedStream(StringIO(b'test'), 2) + stream = LimitedStream(BytesIO(b'test'), 2) self.assertEqual(stream.read(), b'te') # Reading again returns nothing. self.assertEqual(stream.read(), b'') # Read a number of characters greater than the stream has to offer - stream = LimitedStream(StringIO(b'test'), 2) + stream = LimitedStream(BytesIO(b'test'), 2) self.assertEqual(stream.read(5), b'te') # Reading again returns nothing. self.assertEqual(stream.readline(5), b'') # Read sequentially from a stream - stream = LimitedStream(StringIO(b'12345678'), 8) + stream = LimitedStream(BytesIO(b'12345678'), 8) self.assertEqual(stream.read(5), b'12345') self.assertEqual(stream.read(5), b'678') # Reading again returns nothing. self.assertEqual(stream.readline(5), b'') # Read lines from a stream - stream = LimitedStream(StringIO(b'1234\n5678\nabcd\nefgh\nijkl'), 24) + stream = LimitedStream(BytesIO(b'1234\n5678\nabcd\nefgh\nijkl'), 24) # Read a full line, unconditionally self.assertEqual(stream.readline(), b'1234\n') # Read a number of characters less than a line @@ -246,7 +246,7 @@ class RequestsTests(unittest.TestCase): # If a stream contains a newline, but the provided length # is less than the number of provided characters, the newline # doesn't reset the available character count - stream = LimitedStream(StringIO(b'1234\nabcdef'), 9) + stream = LimitedStream(BytesIO(b'1234\nabcdef'), 9) self.assertEqual(stream.readline(10), b'1234\n') self.assertEqual(stream.readline(3), b'abc') # Now expire the available characters @@ -255,7 +255,7 @@ class RequestsTests(unittest.TestCase): self.assertEqual(stream.readline(2), b'') # Same test, but with read, not readline. - stream = LimitedStream(StringIO(b'1234\nabcdef'), 9) + stream = LimitedStream(BytesIO(b'1234\nabcdef'), 9) self.assertEqual(stream.read(6), b'1234\na') self.assertEqual(stream.read(2), b'bc') self.assertEqual(stream.read(2), b'd') @@ -266,7 +266,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.read(), b'name=value') def test_read_after_value(self): @@ -277,7 +277,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.POST, {'name': ['value']}) self.assertEqual(request.body, b'name=value') self.assertEqual(request.read(), b'name=value') @@ -290,7 +290,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.read(2), b'na') self.assertRaises(Exception, lambda: request.body) self.assertEqual(request.POST, {}) @@ -312,7 +312,7 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.POST, {'name': ['value']}) self.assertRaises(Exception, lambda: request.body) @@ -334,14 +334,14 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': 0, - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(request.POST, {}) def test_read_by_lines(self): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) self.assertEqual(list(request), [b'name=value']) def test_POST_after_body_read(self): @@ -351,7 +351,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) raw_data = request.body self.assertEqual(request.POST, {'name': ['value']}) @@ -363,7 +363,7 @@ class RequestsTests(unittest.TestCase): payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) raw_data = request.body self.assertEqual(request.read(1), b'n') self.assertEqual(request.POST, {'name': ['value']}) @@ -383,7 +383,7 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_TYPE': 'multipart/form-data; boundary=boundary', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload)}) + 'wsgi.input': BytesIO(payload)}) raw_data = request.body # Consume enough data to mess up the parsing: self.assertEqual(request.read(13), b'--boundary\r\nC') @@ -397,7 +397,7 @@ class RequestsTests(unittest.TestCase): request = WSGIRequest({ 'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': StringIO(payload) + 'wsgi.input': BytesIO(payload) }) with warnings.catch_warnings(record=True): @@ -408,14 +408,14 @@ class RequestsTests(unittest.TestCase): If wsgi.input.read() raises an exception while trying to read() the POST, the exception should be identifiable (not a generic IOError). """ - class ExplodingStringIO(StringIO): + class ExplodingBytesIO(BytesIO): def read(self, len=0): raise IOError("kaboom!") payload = b'name=value' request = WSGIRequest({'REQUEST_METHOD': 'POST', 'CONTENT_LENGTH': len(payload), - 'wsgi.input': ExplodingStringIO(payload)}) + 'wsgi.input': ExplodingBytesIO(payload)}) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") diff --git a/tests/regressiontests/select_related_onetoone/models.py b/tests/regressiontests/select_related_onetoone/models.py index 3d6da9b4c5..3284defb11 100644 --- a/tests/regressiontests/select_related_onetoone/models.py +++ b/tests/regressiontests/select_related_onetoone/models.py @@ -1,44 +1,50 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class User(models.Model): username = models.CharField(max_length=100) email = models.EmailField() - def __unicode__(self): + def __str__(self): return self.username +@python_2_unicode_compatible class UserProfile(models.Model): user = models.OneToOneField(User) city = models.CharField(max_length=100) state = models.CharField(max_length=2) - def __unicode__(self): + def __str__(self): return "%s, %s" % (self.city, self.state) +@python_2_unicode_compatible class UserStatResult(models.Model): results = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return 'UserStatResults, results = %s' % (self.results,) +@python_2_unicode_compatible class UserStat(models.Model): user = models.OneToOneField(User, primary_key=True) posts = models.IntegerField() results = models.ForeignKey(UserStatResult) - def __unicode__(self): + def __str__(self): return 'UserStat, posts = %s' % (self.posts,) +@python_2_unicode_compatible class StatDetails(models.Model): base_stats = models.OneToOneField(UserStat) comments = models.IntegerField() - def __unicode__(self): + def __str__(self): return 'StatDetails, comments = %s' % (self.comments,) diff --git a/tests/regressiontests/select_related_regress/models.py b/tests/regressiontests/select_related_regress/models.py index 1af9ff4bca..a291a547f7 100644 --- a/tests/regressiontests/select_related_regress/models.py +++ b/tests/regressiontests/select_related_regress/models.py @@ -1,34 +1,39 @@ from __future__ import unicode_literals from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Building(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return "Building: %s" % self.name +@python_2_unicode_compatible class Device(models.Model): building = models.ForeignKey('Building') name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return "device '%s' in building %s" % (self.name, self.building) +@python_2_unicode_compatible class Port(models.Model): device = models.ForeignKey('Device') port_number = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return "%s/%s" % (self.device.name, self.port_number) +@python_2_unicode_compatible class Connection(models.Model): start = models.ForeignKey(Port, related_name='connection_start', unique=True) end = models.ForeignKey(Port, related_name='connection_end', unique=True) - def __unicode__(self): + def __str__(self): return "%s to %s" % (self.start, self.end) # Another non-tree hierarchy that exercises code paths similar to the above @@ -72,18 +77,20 @@ class SpecialClient(Client): value = models.IntegerField() # Some model inheritance exercises +@python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=10) - def __unicode__(self): + def __str__(self): return self.name class Child(Parent): value = models.IntegerField() +@python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=10) child = models.ForeignKey(Child, null=True) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/serializers_regress/tests.py b/tests/regressiontests/serializers_regress/tests.py index 4e73be015c..f4b3adaa5e 100644 --- a/tests/regressiontests/serializers_regress/tests.py +++ b/tests/regressiontests/serializers_regress/tests.py @@ -10,7 +10,6 @@ from __future__ import absolute_import, unicode_literals import datetime import decimal -from io import BytesIO try: import yaml @@ -23,6 +22,7 @@ from django.core.serializers.base import DeserializationError from django.db import connection, models from django.http import HttpResponse from django.test import TestCase +from django.utils import six from django.utils.functional import curry from django.utils.unittest import skipUnless @@ -502,17 +502,17 @@ def streamTest(format, self): obj.save_base(raw=True) # Serialize the test database to a stream - for stream in (BytesIO(), HttpResponse()): + for stream in (six.StringIO(), HttpResponse()): serializers.serialize(format, [obj], indent=2, stream=stream) # Serialize normally for a comparison string_data = serializers.serialize(format, [obj], indent=2) # Check that the two are the same - if isinstance(stream, BytesIO): + if isinstance(stream, six.StringIO): self.assertEqual(string_data, stream.getvalue()) else: - self.assertEqual(string_data, stream.content) + self.assertEqual(string_data, stream.content.decode('utf-8')) stream.close() for format in serializers.get_serializer_formats(): diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py index b98b4b73c2..c90c785a6e 100644 --- a/tests/regressiontests/servers/tests.py +++ b/tests/regressiontests/servers/tests.py @@ -144,7 +144,7 @@ class LiveServerDatabase(LiveServerBase): Refs #2879. """ f = self.urlopen('/model_view/') - self.assertEqual(f.read().splitlines(), ['jane', 'robert']) + self.assertEqual(f.read().splitlines(), [b'jane', b'robert']) def test_database_writes(self): """ diff --git a/tests/regressiontests/settings_tests/tests.py b/tests/regressiontests/settings_tests/tests.py index ffcb79eb09..7225fb03ef 100644 --- a/tests/regressiontests/settings_tests/tests.py +++ b/tests/regressiontests/settings_tests/tests.py @@ -148,6 +148,16 @@ class SettingsTests(TestCase): def test_settings_delete_wrapped(self): self.assertRaises(TypeError, delattr, settings, '_wrapped') + def test_override_settings_delete(self): + """ + Allow deletion of a setting in an overriden settings set (#18824) + """ + previous_i18n = settings.USE_I18N + with self.settings(USE_I18N=False): + del settings.USE_I18N + self.assertRaises(AttributeError, getattr, settings, 'USE_I18N') + self.assertEqual(settings.USE_I18N, previous_i18n) + def test_allowed_include_roots_string(self): """ ALLOWED_INCLUDE_ROOTS is not allowed to be incorrectly set to a string diff --git a/tests/regressiontests/signals_regress/models.py b/tests/regressiontests/signals_regress/models.py index bf64f69e8c..829314c06c 100644 --- a/tests/regressiontests/signals_regress/models.py +++ b/tests/regressiontests/signals_regress/models.py @@ -1,15 +1,18 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=20) - def __unicode__(self): + def __str__(self): return self.name +@python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=20) authors = models.ManyToManyField(Author) - def __unicode__(self): + def __str__(self): return self.name diff --git a/tests/regressiontests/signing/tests.py b/tests/regressiontests/signing/tests.py index 2368405060..05c8b3dc74 100644 --- a/tests/regressiontests/signing/tests.py +++ b/tests/regressiontests/signing/tests.py @@ -4,7 +4,8 @@ import time from django.core import signing from django.test import TestCase -from django.utils.encoding import force_text +from django.utils.encoding import force_str +from django.utils import six class TestSigner(TestCase): @@ -21,7 +22,7 @@ class TestSigner(TestCase): self.assertEqual( signer.signature(s), signing.base64_hmac(signer.salt + 'signer', s, - 'predictable-secret') + 'predictable-secret').decode() ) self.assertNotEqual(signer.signature(s), signer2.signature(s)) @@ -31,7 +32,8 @@ class TestSigner(TestCase): self.assertEqual( signer.signature('hello'), signing.base64_hmac('extra-salt' + 'signer', - 'hello', 'predictable-secret')) + 'hello', 'predictable-secret').decode() + ) self.assertNotEqual( signing.Signer('predictable-secret', salt='one').signature('hello'), signing.Signer('predictable-secret', salt='two').signature('hello')) @@ -39,17 +41,20 @@ class TestSigner(TestCase): def test_sign_unsign(self): "sign/unsign should be reversible" signer = signing.Signer('predictable-secret') - examples = ( + examples = [ 'q;wjmbk;wkmb', '3098247529087', '3098247:529:087:', 'jkw osanteuh ,rcuh nthu aou oauh ,ud du', '\u2019', - ) + ] + if not six.PY3: + examples.append(b'a byte string') for example in examples: - self.assertNotEqual( - force_text(example), force_text(signer.sign(example))) - self.assertEqual(example, signer.unsign(signer.sign(example))) + signed = signer.sign(example) + self.assertIsInstance(signed, str) + self.assertNotEqual(force_str(example), signed) + self.assertEqual(example, signer.unsign(signed)) def unsign_detects_tampering(self): "unsign should raise an exception if the value has been tampered with" @@ -69,15 +74,18 @@ class TestSigner(TestCase): def test_dumps_loads(self): "dumps and loads be reversible for any JSON serializable object" - objects = ( + objects = [ ['a', 'list'], - b'a string', 'a unicode string \u2019', {'a': 'dictionary'}, - ) + ] + if not six.PY3: + objects.append(b'a byte string') for o in objects: self.assertNotEqual(o, signing.dumps(o)) self.assertEqual(o, signing.loads(signing.dumps(o))) + self.assertNotEqual(o, signing.dumps(o, compress=True)) + self.assertEqual(o, signing.loads(signing.dumps(o, compress=True))) def test_decode_detects_tampering(self): "loads should raise exception for tampered objects" diff --git a/tests/regressiontests/sites_framework/models.py b/tests/regressiontests/sites_framework/models.py index 9ecc3e6660..55c4f4992e 100644 --- a/tests/regressiontests/sites_framework/models.py +++ b/tests/regressiontests/sites_framework/models.py @@ -1,7 +1,9 @@ from django.contrib.sites.managers import CurrentSiteManager from django.contrib.sites.models import Site from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class AbstractArticle(models.Model): title = models.CharField(max_length=50) @@ -11,7 +13,7 @@ class AbstractArticle(models.Model): class Meta: abstract = True - def __unicode__(self): + def __str__(self): return self.title class SyndicatedArticle(AbstractArticle): diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py index 19951f100b..078788a6a9 100644 --- a/tests/regressiontests/staticfiles_tests/tests.py +++ b/tests/regressiontests/staticfiles_tests/tests.py @@ -7,7 +7,6 @@ import posixpath import shutil import sys import tempfile -from io import BytesIO from django.template import loader, Context from django.conf import settings @@ -194,19 +193,18 @@ class TestFindStatic(CollectionTestCase, TestDefaults): Test ``findstatic`` management command. """ def _get_file(self, filepath): - out = BytesIO() + out = six.StringIO() call_command('findstatic', filepath, all=False, verbosity=0, stdout=out) out.seek(0) lines = [l.strip() for l in out.readlines()] - contents = codecs.open( - smart_text(lines[1].strip()), "r", "utf-8").read() - return contents + with codecs.open(smart_text(lines[1].strip()), "r", "utf-8") as f: + return f.read() def test_all_files(self): """ Test that findstatic returns all candidate files if run without --first. """ - out = BytesIO() + out = six.StringIO() call_command('findstatic', 'test/file.txt', verbosity=0, stdout=out) out.seek(0) lines = [l.strip() for l in out.readlines()] @@ -528,11 +526,11 @@ class TestCollectionCachedStorage(BaseCollectionTestCase, """ Handle cache key creation correctly, see #17861. """ - name = b"/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/" + chr(22) + chr(180) + name = "/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/long filename/ with spaces Here and ?#%#$/other/stuff/some crazy/" + "\x16" + "\xb4" cache_key = storage.staticfiles_storage.cache_key(name) cache_validator = BaseCache({}) cache_validator.validate_key(cache_key) - self.assertEqual(cache_key, 'staticfiles:e95bbc36387084582df2a70750d7b351') + self.assertEqual(cache_key, 'staticfiles:821ea71ef36f95b3922a77f7364670e7') # we set DEBUG to False here since the template tag wouldn't work otherwise @@ -570,8 +568,8 @@ class TestCollectionSimpleCachedStorage(BaseCollectionTestCase, self.assertEqual(relpath, "cached/styles.deploy12345.css") with storage.staticfiles_storage.open(relpath) as relfile: content = relfile.read() - self.assertNotIn("cached/other.css", content) - self.assertIn("other.deploy12345.css", content) + self.assertNotIn(b"cached/other.css", content) + self.assertIn(b"other.deploy12345.css", content) if sys.platform != 'win32': diff --git a/tests/regressiontests/string_lookup/models.py b/tests/regressiontests/string_lookup/models.py index 53687a22cb..a2d64cd0b2 100644 --- a/tests/regressiontests/string_lookup/models.py +++ b/tests/regressiontests/string_lookup/models.py @@ -1,42 +1,51 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Foo(models.Model): name = models.CharField(max_length=50) friend = models.CharField(max_length=50, blank=True) - def __unicode__(self): + def __str__(self): return "Foo %s" % self.name +@python_2_unicode_compatible class Bar(models.Model): name = models.CharField(max_length=50) normal = models.ForeignKey(Foo, related_name='normal_foo') fwd = models.ForeignKey("Whiz") back = models.ForeignKey("Foo") - def __unicode__(self): + def __str__(self): return "Bar %s" % self.place.name +@python_2_unicode_compatible class Whiz(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Whiz %s" % self.name +@python_2_unicode_compatible class Child(models.Model): parent = models.OneToOneField('Base') name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Child %s" % self.name +@python_2_unicode_compatible class Base(models.Model): name = models.CharField(max_length=50) - def __unicode__(self): + def __str__(self): return "Base %s" % self.name +@python_2_unicode_compatible class Article(models.Model): name = models.CharField(max_length=50) text = models.TextField() diff --git a/tests/regressiontests/syndication/models.py b/tests/regressiontests/syndication/models.py index a2c504e57f..10b3fe3a0c 100644 --- a/tests/regressiontests/syndication/models.py +++ b/tests/regressiontests/syndication/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Entry(models.Model): title = models.CharField(max_length=200) date = models.DateTimeField() @@ -8,17 +10,18 @@ class Entry(models.Model): class Meta: ordering = ('date',) - def __unicode__(self): + def __str__(self): return self.title def get_absolute_url(self): return "/blog/%s/" % self.pk +@python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=200) entry = models.ForeignKey(Entry) - def __unicode__(self): + def __str__(self): return self.title diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py index fd570700af..8b4aebbf22 100644 --- a/tests/regressiontests/templates/filters.py +++ b/tests/regressiontests/templates/filters.py @@ -13,14 +13,17 @@ from datetime import date, datetime, timedelta from django.test.utils import str_prefix from django.utils.tzinfo import LocalTimezone, FixedOffset from django.utils.safestring import mark_safe +from django.utils.encoding import python_2_unicode_compatible # These two classes are used to test auto-escaping of __unicode__ output. +@python_2_unicode_compatible class UnsafeClass: - def __unicode__(self): + def __str__(self): return 'you & me' +@python_2_unicode_compatible class SafeClass: - def __unicode__(self): + def __str__(self): return mark_safe('you > me') # RESULT SYNTAX -- @@ -111,7 +114,7 @@ def get_filter_tests(): # The make_list filter can destroy existing escaping, so the results are # escaped. 'filter-make_list01': ("{% autoescape off %}{{ a|make_list }}{% endautoescape %}", {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")), - 'filter-make_list02': ("{{ a|make_list }}", {"a": mark_safe("&")}, "[u'&']"), + 'filter-make_list02': ("{{ a|make_list }}", {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")), 'filter-make_list03': ('{% autoescape off %}{{ a|make_list|stringformat:"s"|safe }}{% endautoescape %}', {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")), 'filter-make_list04': ('{{ a|make_list|stringformat:"s"|safe }}', {"a": mark_safe("&")}, str_prefix("[%(_)s'&']")), @@ -338,11 +341,11 @@ def get_filter_tests(): 'join04': (r'{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}', {'a': ['alpha', 'beta & me']}, 'alpha & beta & me'), # Test that joining with unsafe joiners don't result in unsafe strings (#11377) - 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha & beta & me'), - 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), - 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha & beta & me'), - 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), - + 'join05': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': ' & '}, 'alpha & beta & me'), + 'join06': (r'{{ a|join:var }}', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), + 'join07': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': ' & ' }, 'alpha & beta & me'), + 'join08': (r'{{ a|join:var|lower }}', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}, 'alpha & beta & me'), + 'date01': (r'{{ d|date:"m" }}', {'d': datetime(2008, 1, 1)}, '01'), 'date02': (r'{{ d|date }}', {'d': datetime(2008, 1, 1)}, 'Jan. 1, 2008'), #Ticket 9520: Make sure |date doesn't blow up on non-dates diff --git a/tests/regressiontests/templates/response.py b/tests/regressiontests/templates/response.py index 3c45b7a9d4..93919b95cd 100644 --- a/tests/regressiontests/templates/response.py +++ b/tests/regressiontests/templates/response.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + import os import pickle import time @@ -29,16 +31,16 @@ class SimpleTemplateResponseTest(TestCase): def test_template_resolving(self): response = SimpleTemplateResponse('first/test.html') response.render() - self.assertEqual('First template\n', response.content) + self.assertEqual(response.content, b'First template\n') templates = ['foo.html', 'second/test.html', 'first/test.html'] response = SimpleTemplateResponse(templates) response.render() - self.assertEqual('Second template\n', response.content) + self.assertEqual(response.content, b'Second template\n') response = self._response() response.render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') def test_explicit_baking(self): # explicit baking @@ -50,17 +52,17 @@ class SimpleTemplateResponseTest(TestCase): def test_render(self): # response is not re-rendered without the render call response = self._response().render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') # rebaking doesn't change the rendered content response.template_name = Template('bar{{ baz }}') response.render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') # but rendered content can be overridden by manually # setting content response.content = 'bar' - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_iteration_unrendered(self): # unrendered response raises an exception on iteration @@ -77,7 +79,7 @@ class SimpleTemplateResponseTest(TestCase): # iteration works for rendered responses response = self._response().render() res = [x for x in response] - self.assertEqual(res, ['foo']) + self.assertEqual(res, [b'foo']) def test_content_access_unrendered(self): # unrendered response raises an exception when content is accessed @@ -89,7 +91,7 @@ class SimpleTemplateResponseTest(TestCase): def test_content_access_rendered(self): # rendered response content can be accessed response = self._response().render() - self.assertEqual(response.content, 'foo') + self.assertEqual(response.content, b'foo') def test_set_content(self): # content can be overriden @@ -97,23 +99,23 @@ class SimpleTemplateResponseTest(TestCase): self.assertFalse(response.is_rendered) response.content = 'spam' self.assertTrue(response.is_rendered) - self.assertEqual(response.content, 'spam') + self.assertEqual(response.content, b'spam') response.content = 'baz' - self.assertEqual(response.content, 'baz') + self.assertEqual(response.content, b'baz') def test_dict_context(self): response = self._response('{{ foo }}{{ processors }}', {'foo': 'bar'}) self.assertEqual(response.context_data, {'foo': 'bar'}) response.render() - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_context_instance(self): response = self._response('{{ foo }}{{ processors }}', Context({'foo': 'bar'})) self.assertEqual(response.context_data.__class__, Context) response.render() - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_kwargs(self): response = self._response(content_type = 'application/json', status=504) @@ -140,7 +142,7 @@ class SimpleTemplateResponseTest(TestCase): # When the content is rendered, all the callbacks are invoked, too. response.render() - self.assertEqual('First template\n', response.content) + self.assertEqual(response.content, b'First template\n') self.assertEqual(post, ['post1','post2']) @@ -202,17 +204,17 @@ class TemplateResponseTest(TestCase): def test_render(self): response = self._response('{{ foo }}{{ processors }}').render() - self.assertEqual(response.content, 'yes') + self.assertEqual(response.content, b'yes') def test_render_with_requestcontext(self): response = self._response('{{ foo }}{{ processors }}', {'foo': 'bar'}).render() - self.assertEqual(response.content, 'baryes') + self.assertEqual(response.content, b'baryes') def test_render_with_context(self): response = self._response('{{ foo }}{{ processors }}', Context({'foo': 'bar'})).render() - self.assertEqual(response.content, 'bar') + self.assertEqual(response.content, b'bar') def test_kwargs(self): response = self._response(content_type = 'application/json', diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index edbb21b6bd..41f40e7467 100644 --- a/tests/regressiontests/templates/tests.py +++ b/tests/regressiontests/templates/tests.py @@ -27,6 +27,7 @@ from django.test import RequestFactory from django.test.utils import (setup_test_template_loader, restore_template_loaders, override_settings) from django.utils import unittest +from django.utils.encoding import python_2_unicode_compatible from django.utils.formats import date_format from django.utils.translation import activate, deactivate, ugettext as _ from django.utils.safestring import mark_safe @@ -46,7 +47,7 @@ from .response import (TemplateResponseTest, CacheMiddlewareTest, try: from .loaders import RenderToStringTest, EggLoaderTest except ImportError as e: - if "pkg_resources" in e.message: + if "pkg_resources" in e.args[0]: pass # If setuptools isn't installed, that's fine. Just move on. else: raise @@ -148,10 +149,11 @@ class SilentAttrClass(object): raise SomeException b = property(b) +@python_2_unicode_compatible class UTF8Class: - "Class whose __str__ returns non-ASCII data" + "Class whose __str__ returns non-ASCII data on Python 2" def __str__(self): - return 'ŠĐĆŽćžšđ'.encode('utf-8') + return 'ŠĐĆŽćžšđ' class Templates(unittest.TestCase): def setUp(self): @@ -408,8 +410,7 @@ class Templates(unittest.TestCase): ) failures = [] - tests = template_tests.items() - tests.sort() + tests = sorted(template_tests.items()) # Turn TEMPLATE_DEBUG off, because tests assume that. old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, False @@ -418,7 +419,7 @@ class Templates(unittest.TestCase): old_invalid = settings.TEMPLATE_STRING_IF_INVALID expected_invalid_str = 'INVALID' - #Set ALLOWED_INCLUDE_ROOTS so that ssi works. + # Set ALLOWED_INCLUDE_ROOTS so that ssi works. old_allowed_include_roots = settings.ALLOWED_INCLUDE_ROOTS settings.ALLOWED_INCLUDE_ROOTS = ( os.path.dirname(os.path.abspath(__file__)), @@ -1452,8 +1453,9 @@ class Templates(unittest.TestCase): 'widthratio04': ('{% widthratio a b 100 %}', {'a':50,'b':100}, '50'), 'widthratio05': ('{% widthratio a b 100 %}', {'a':100,'b':100}, '100'), - # 62.5 should round to 63 - 'widthratio06': ('{% widthratio a b 100 %}', {'a':50,'b':80}, '63'), + # 62.5 should round to 63 on Python 2 and 62 on Python 3 + # See http://docs.python.org/py3k/whatsnew/3.0.html + 'widthratio06': ('{% widthratio a b 100 %}', {'a':50,'b':80}, '62' if six.PY3 else '63'), # 71.4 should round to 71 'widthratio07': ('{% widthratio a b 100 %}', {'a':50,'b':70}, '71'), @@ -1466,6 +1468,14 @@ class Templates(unittest.TestCase): # #10043: widthratio should allow max_width to be a variable 'widthratio11': ('{% widthratio a b c %}', {'a':50,'b':100, 'c': 100}, '50'), + # #18739: widthratio should handle None args consistently with non-numerics + 'widthratio12a': ('{% widthratio a b c %}', {'a':'a','b':100,'c':100}, ''), + 'widthratio12b': ('{% widthratio a b c %}', {'a':None,'b':100,'c':100}, ''), + 'widthratio13a': ('{% widthratio a b c %}', {'a':0,'b':'b','c':100}, ''), + 'widthratio13b': ('{% widthratio a b c %}', {'a':0,'b':None,'c':100}, ''), + 'widthratio14a': ('{% widthratio a b c %}', {'a':0,'b':100,'c':'c'}, template.TemplateSyntaxError), + 'widthratio14b': ('{% widthratio a b c %}', {'a':0,'b':100,'c':None}, template.TemplateSyntaxError), + ### WITH TAG ######################################################## 'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), 'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), diff --git a/tests/regressiontests/test_client_regress/tests.py b/tests/regressiontests/test_client_regress/tests.py index 59f8f423dd..d80293b358 100644 --- a/tests/regressiontests/test_client_regress/tests.py +++ b/tests/regressiontests/test_client_regress/tests.py @@ -858,7 +858,7 @@ class UnicodePayloadTests(TestCase): json = '{"english": "mountain pass"}' response = self.client.post("/test_client_regress/parse_unicode_json/", json, content_type="application/json") - self.assertEqual(response.content, json) + self.assertEqual(response.content, json.encode()) def test_unicode_payload_utf8(self): "A non-ASCII unicode data encoded as UTF-8 can be POSTed" @@ -888,7 +888,7 @@ class DummyFile(object): def __init__(self, filename): self.name = filename def read(self): - return 'TEST_FILE_CONTENT' + return b'TEST_FILE_CONTENT' class UploadedFileEncodingTest(TestCase): def test_file_encoding(self): diff --git a/tests/regressiontests/test_client_regress/views.py b/tests/regressiontests/test_client_regress/views.py index 8792a97dc0..9b0654806b 100644 --- a/tests/regressiontests/test_client_regress/views.py +++ b/tests/regressiontests/test_client_regress/views.py @@ -80,9 +80,7 @@ def return_json_file(request): # This just checks that the uploaded data is JSON obj_dict = json.loads(request.body.decode(charset)) - obj_json = json.dumps(obj_dict, encoding=charset, - cls=DjangoJSONEncoder, - ensure_ascii=False) + obj_json = json.dumps(obj_dict, cls=DjangoJSONEncoder, ensure_ascii=False) response = HttpResponse(obj_json.encode(charset), status=200, content_type='application/json; charset=%s' % charset) response['Content-Disposition'] = 'attachment; filename=testfile.json' diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py index f43a855a59..3749014372 100644 --- a/tests/regressiontests/test_utils/tests.py +++ b/tests/regressiontests/test_utils/tests.py @@ -5,6 +5,7 @@ from django.forms import EmailField, IntegerField from django.http import HttpResponse from django.template.loader import render_to_string from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature +from django.utils import six from django.utils.unittest import skip from .models import Person @@ -495,12 +496,6 @@ __test__ = {"API_TEST": r""" >>> from django.utils.xmlutils import SimplerXMLGenerator >>> from django.utils.six import StringIO ->>> def produce_long(): -... return 42L - ->>> def produce_int(): -... return 42 - >>> def produce_json(): ... return json.dumps(['foo', {'bar': ('baz', None, 1.0, 2), 'whiz': 42}]) @@ -529,14 +524,6 @@ __test__ = {"API_TEST": r""" ... xml.endElement("bar") ... return stream.getvalue() -# Long values are normalized and are comparable to normal integers ... ->>> produce_long() -42 - -# ... and vice versa ->>> produce_int() -42L - # JSON output is normalized for field order, so it doesn't matter # which order json dictionary attributes are listed in output >>> produce_json() @@ -560,3 +547,21 @@ __test__ = {"API_TEST": r""" '<foo bbb="2.0" aaa="1.0">Hello</foo><bar ddd="4.0" ccc="3.0"></bar>' """} + +if not six.PY3: + __test__["API_TEST"] += """ +>>> def produce_long(): +... return 42L + +>>> def produce_int(): +... return 42 + +# Long values are normalized and are comparable to normal integers ... +>>> produce_long() +42 + +# ... and vice versa +>>> produce_int() +42L + +""" diff --git a/tests/regressiontests/urlpatterns_reverse/tests.py b/tests/regressiontests/urlpatterns_reverse/tests.py index 500a0e0327..168892719a 100644 --- a/tests/regressiontests/urlpatterns_reverse/tests.py +++ b/tests/regressiontests/urlpatterns_reverse/tests.py @@ -1,12 +1,13 @@ """ Unit tests for reverse URL lookups. """ -from __future__ import absolute_import +from __future__ import absolute_import, unicode_literals from django.conf import settings from django.core.exceptions import ImproperlyConfigured, ViewDoesNotExist -from django.core.urlresolvers import (reverse, resolve, NoReverseMatch, - Resolver404, ResolverMatch, RegexURLResolver, RegexURLPattern) +from django.core.urlresolvers import (reverse, resolve, get_callable, + get_resolver, NoReverseMatch, Resolver404, ResolverMatch, RegexURLResolver, + RegexURLPattern) from django.http import HttpResponseRedirect, HttpResponsePermanentRedirect from django.shortcuts import redirect from django.test import TestCase @@ -171,6 +172,16 @@ class URLPatternReverse(TestCase): self.assertRaises(NoReverseMatch, reverse, None) class ResolverTests(unittest.TestCase): + def test_resolver_repr(self): + """ + Test repr of RegexURLResolver, especially when urlconf_name is a list + (#17892). + """ + # Pick a resolver from a namespaced urlconf + resolver = get_resolver('regressiontests.urlpatterns_reverse.namespace_urls') + sub_resolver = resolver.namespace_dict['test-ns1'][1] + self.assertIn('<RegexURLPattern list>', repr(sub_resolver)) + def test_non_regex(self): """ Verifies that we raise a Resolver404 if what we are resolving doesn't @@ -405,8 +416,8 @@ class RequestURLconfTests(TestCase): def test_urlconf(self): response = self.client.get('/test/me/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'outer:/test/me/,' - 'inner:/inner_urlconf/second_test/') + self.assertEqual(response.content, b'outer:/test/me/,' + b'inner:/inner_urlconf/second_test/') response = self.client.get('/inner_urlconf/second_test/') self.assertEqual(response.status_code, 200) response = self.client.get('/second_test/') @@ -422,7 +433,7 @@ class RequestURLconfTests(TestCase): self.assertEqual(response.status_code, 404) response = self.client.get('/second_test/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'outer:,inner:/second_test/') + self.assertEqual(response.content, b'outer:,inner:/second_test/') def test_urlconf_overridden_with_null(self): settings.MIDDLEWARE_CLASSES += ( @@ -519,3 +530,16 @@ class ErroneousViewTests(TestCase): """ # The regex error will be hit before NoReverseMatch can be raised self.assertRaises(ImproperlyConfigured, reverse, 'whatever blah blah') + +class ViewLoadingTests(TestCase): + def test_view_loading(self): + # A missing view (identified by an AttributeError) should raise + # ViewDoesNotExist, ... + self.assertRaisesRegexp(ViewDoesNotExist, ".*View does not exist in.*", + get_callable, + 'regressiontests.urlpatterns_reverse.views.i_should_not_exist') + # ... but if the AttributeError is caused by something else don't + # swallow it. + self.assertRaises(AttributeError, get_callable, + 'regressiontests.urlpatterns_reverse.views_broken.i_am_broken') + diff --git a/tests/regressiontests/urlpatterns_reverse/views_broken.py b/tests/regressiontests/urlpatterns_reverse/views_broken.py new file mode 100644 index 0000000000..4953aab239 --- /dev/null +++ b/tests/regressiontests/urlpatterns_reverse/views_broken.py @@ -0,0 +1,2 @@ +# I just raise an AttributeError to confuse the view loading mechanism +raise AttributeError('I am here to confuse django.core.urlresolvers.get_callable') diff --git a/tests/regressiontests/utils/archive.py b/tests/regressiontests/utils/archive.py index 0927c624f3..5575f340f6 100644 --- a/tests/regressiontests/utils/archive.py +++ b/tests/regressiontests/utils/archive.py @@ -27,12 +27,14 @@ class ArchiveTester(object): os.chdir(self.old_cwd) def test_extract_method(self): - Archive(self.archive).extract(self.tmpdir) + with Archive(self.archive) as archive: + archive.extract(self.tmpdir) self.check_files(self.tmpdir) def test_extract_method_no_to_path(self): os.chdir(self.tmpdir) - Archive(self.archive_path).extract() + with Archive(self.archive_path) as archive: + archive.extract() self.check_files(self.tmpdir) def test_extract_function(self): diff --git a/tests/regressiontests/utils/crypto.py b/tests/regressiontests/utils/crypto.py index 52a286cb27..4c6b722ca9 100644 --- a/tests/regressiontests/utils/crypto.py +++ b/tests/regressiontests/utils/crypto.py @@ -6,7 +6,17 @@ import timeit import hashlib from django.utils import unittest -from django.utils.crypto import pbkdf2 +from django.utils.crypto import constant_time_compare, pbkdf2 + + +class TestUtilsCryptoMisc(unittest.TestCase): + + def test_constant_time_compare(self): + # It's hard to test for constant time, just test the result. + self.assertTrue(constant_time_compare(b'spam', b'spam')) + self.assertFalse(constant_time_compare(b'spam', b'eggs')) + self.assertTrue(constant_time_compare('spam', 'spam')) + self.assertFalse(constant_time_compare('spam', 'eggs')) class TestUtilsCryptoPBKDF2(unittest.TestCase): diff --git a/tests/regressiontests/utils/dateparse.py b/tests/regressiontests/utils/dateparse.py new file mode 100644 index 0000000000..1a6ca646b8 --- /dev/null +++ b/tests/regressiontests/utils/dateparse.py @@ -0,0 +1,44 @@ +from __future__ import unicode_literals + +from datetime import date, time, datetime + +from django.utils.dateparse import parse_date, parse_time, parse_datetime +from django.utils import unittest +from django.utils.tzinfo import FixedOffset + + +class DateParseTests(unittest.TestCase): + + def test_parse_date(self): + # Valid inputs + self.assertEqual(parse_date('2012-04-23'), date(2012, 4, 23)) + self.assertEqual(parse_date('2012-4-9'), date(2012, 4, 9)) + # Invalid inputs + self.assertEqual(parse_date('20120423'), None) + self.assertRaises(ValueError, parse_date, '2012-04-56') + + def test_parse_time(self): + # Valid inputs + self.assertEqual(parse_time('09:15:00'), time(9, 15)) + self.assertEqual(parse_time('10:10'), time(10, 10)) + self.assertEqual(parse_time('10:20:30.400'), time(10, 20, 30, 400000)) + self.assertEqual(parse_time('4:8:16'), time(4, 8, 16)) + # Invalid inputs + self.assertEqual(parse_time('091500'), None) + self.assertRaises(ValueError, parse_time, '09:15:90') + + def test_parse_datetime(self): + # Valid inputs + self.assertEqual(parse_datetime('2012-04-23T09:15:00'), + datetime(2012, 4, 23, 9, 15)) + self.assertEqual(parse_datetime('2012-4-9 4:8:16'), + datetime(2012, 4, 9, 4, 8, 16)) + self.assertEqual(parse_datetime('2012-04-23T09:15:00Z'), + datetime(2012, 4, 23, 9, 15, 0, 0, FixedOffset(0))) + self.assertEqual(parse_datetime('2012-4-9 4:8:16-0320'), + datetime(2012, 4, 9, 4, 8, 16, 0, FixedOffset(-200))) + self.assertEqual(parse_datetime('2012-04-23T10:20:30.400+02:30'), + datetime(2012, 4, 23, 10, 20, 30, 400000, FixedOffset(150))) + # Invalid inputs + self.assertEqual(parse_datetime('20120423091500'), None) + self.assertRaises(ValueError, parse_datetime, '2012-04-56T09:15:90') diff --git a/tests/regressiontests/utils/encoding.py b/tests/regressiontests/utils/encoding.py new file mode 100644 index 0000000000..d191845518 --- /dev/null +++ b/tests/regressiontests/utils/encoding.py @@ -0,0 +1,17 @@ +# -*- encoding: utf-8 -*- +from __future__ import unicode_literals + +from django.utils import unittest +from django.utils.encoding import force_bytes + + +class TestEncodingUtils(unittest.TestCase): + def test_force_bytes_exception(self): + """ + Test that force_bytes knows how to convert to bytes an exception + containing non-ASCII characters in its args. + """ + error_msg = "This is an exception, voilà" + exc = ValueError(error_msg) + result = force_bytes(exc) + self.assertEqual(result, error_msg.encode('utf-8')) diff --git a/tests/regressiontests/utils/html.py b/tests/regressiontests/utils/html.py index fe40d4eaae..98df80a5e2 100644 --- a/tests/regressiontests/utils/html.py +++ b/tests/regressiontests/utils/html.py @@ -146,3 +146,12 @@ class TestUtilsHtml(unittest.TestCase): ) for value, output in items: self.check_output(f, value, output) + + def test_remove_tags(self): + f = html.remove_tags + items = ( + ("<b><i>Yes</i></b>", "b i", "Yes"), + ("<a>x</a> <p><b>y</b></p>", "a b", "x <p>y</p>"), + ) + for value, tags, output in items: + self.assertEquals(f(value, tags), output) diff --git a/tests/regressiontests/utils/module_loading.py b/tests/regressiontests/utils/module_loading.py index cbd81c7294..dffb51966c 100644 --- a/tests/regressiontests/utils/module_loading.py +++ b/tests/regressiontests/utils/module_loading.py @@ -109,7 +109,12 @@ class ProxyFinder(object): def find_module(self, fullname, path=None): tail = fullname.rsplit('.', 1)[-1] try: - self._cache[fullname] = imp.find_module(tail, path) + fd, fn, info = imp.find_module(tail, path) + if fullname in self._cache: + old_fd = self._cache[fullname][0] + if old_fd: + old_fd.close() + self._cache[fullname] = (fd, fn, info) except ImportError: return None else: @@ -119,7 +124,11 @@ class ProxyFinder(object): if fullname in sys.modules: return sys.modules[fullname] fd, fn, info = self._cache[fullname] - return imp.load_module(fullname, fd, fn, info) + try: + return imp.load_module(fullname, fd, fn, info) + finally: + if fd: + fd.close() class TestFinder(object): def __init__(self, *args, **kwargs): diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py index a7deeeefae..f4fa75b177 100644 --- a/tests/regressiontests/utils/tests.py +++ b/tests/regressiontests/utils/tests.py @@ -3,26 +3,28 @@ Tests for django.utils. """ from __future__ import absolute_import +from .archive import TestBzip2Tar, TestGzipTar, TestTar, TestZip +from .baseconv import TestBaseConv +from .checksums import TestUtilsChecksums +from .crypto import TestUtilsCryptoMisc, TestUtilsCryptoPBKDF2 +from .datastructures import (DictWrapperTests, ImmutableListTests, + MergeDictTests, MultiValueDictTests, SortedDictTests) from .dateformat import DateFormatTests +from .dateparse import DateParseTests +from .datetime_safe import DatetimeTests +from .decorators import DecoratorFromMiddlewareTests +from .encoding import TestEncodingUtils from .feedgenerator import FeedgeneratorTest -from .module_loading import DefaultLoader, EggLoader, CustomLoader -from .termcolors import TermColorTests +from .functional import FunctionalTestCase from .html import TestUtilsHtml from .http import TestUtilsHttp -from .checksums import TestUtilsChecksums -from .text import TestUtilsText +from .ipv6 import TestUtilsIPv6 +from .jslex import JsToCForGettextTest, JsTokensTest +from .module_loading import CustomLoader, DefaultLoader, EggLoader +from .regex_helper import NormalizeTests from .simplelazyobject import TestUtilsSimpleLazyObject -from .decorators import DecoratorFromMiddlewareTests -from .functional import FunctionalTestCase +from .termcolors import TermColorTests +from .text import TestUtilsText from .timesince import TimesinceTests -from .datastructures import (MultiValueDictTests, SortedDictTests, - DictWrapperTests, ImmutableListTests, MergeDictTests) -from .tzinfo import TzinfoTests -from .datetime_safe import DatetimeTests -from .baseconv import TestBaseConv -from .jslex import JsTokensTest, JsToCForGettextTest -from .ipv6 import TestUtilsIPv6 from .timezone import TimezoneTests -from .crypto import TestUtilsCryptoPBKDF2 -from .archive import TestZip, TestTar, TestGzipTar, TestBzip2Tar -from .regex_helper import NormalizeTests +from .tzinfo import TzinfoTests diff --git a/tests/regressiontests/utils/text.py b/tests/regressiontests/utils/text.py index dd6de63841..ebf67952f9 100644 --- a/tests/regressiontests/utils/text.py +++ b/tests/regressiontests/utils/text.py @@ -113,3 +113,11 @@ class TestUtilsText(SimpleTestCase): self.assertEqual(text.wrap(long_word, 20), long_word) self.assertEqual(text.wrap('a %s word' % long_word, 10), 'a\n%s\nword' % long_word) + + def test_slugify(self): + items = ( + ('Hello, World!', 'hello-world'), + ('spam & eggs', 'spam-eggs'), + ) + for value, output in items: + self.assertEqual(text.slugify(value), output) diff --git a/tests/regressiontests/views/models.py b/tests/regressiontests/views/models.py index 54f5c1c1f0..461f98c028 100644 --- a/tests/regressiontests/views/models.py +++ b/tests/regressiontests/views/models.py @@ -3,16 +3,19 @@ Regression tests for Django built-in views. """ from django.db import models +from django.utils.encoding import python_2_unicode_compatible +@python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=100) - def __unicode__(self): + def __str__(self): return self.name def get_absolute_url(self): return '/views/authors/%s/' % self.id +@python_2_unicode_compatible class BaseArticle(models.Model): """ An abstract article Model so that we can create article models with and @@ -25,7 +28,7 @@ class BaseArticle(models.Model): class Meta: abstract = True - def __unicode__(self): + def __str__(self): return self.title class Article(BaseArticle): diff --git a/tests/regressiontests/views/tests/debug.py b/tests/regressiontests/views/tests/debug.py index e8a7d49e79..56383ac196 100644 --- a/tests/regressiontests/views/tests/debug.py +++ b/tests/regressiontests/views/tests/debug.py @@ -36,7 +36,7 @@ class DebugViewTests(TestCase): self.assertEqual(response.status_code, 500) data = { - 'file_data.txt': SimpleUploadedFile('file_data.txt', 'haha'), + 'file_data.txt': SimpleUploadedFile('file_data.txt', b'haha'), } response = self.client.post('/raises/', data) self.assertContains(response, 'file_data.txt', status_code=500) diff --git a/tests/regressiontests/views/tests/i18n.py b/tests/regressiontests/views/tests/i18n.py index cb580267d2..601df6d512 100644 --- a/tests/regressiontests/views/tests/i18n.py +++ b/tests/regressiontests/views/tests/i18n.py @@ -7,7 +7,8 @@ from os import path from django.conf import settings from django.core.urlresolvers import reverse from django.test import TestCase -from django.utils.translation import override, activate, get_language +from django.utils import six +from django.utils.translation import override, get_language from django.utils.text import javascript_quote from ..urls import locale_dir @@ -29,20 +30,21 @@ class I18NTests(TestCase): def test_jsi18n(self): """The javascript_catalog can be deployed with language settings""" - saved_lang = get_language() for lang_code in ['es', 'fr', 'ru']: - activate(lang_code) - catalog = gettext.translation('djangojs', locale_dir, [lang_code]) - trans_txt = catalog.ugettext('this is to be translated') - response = self.client.get('/views/jsi18n/') - # in response content must to be a line like that: - # catalog['this is to be translated'] = 'same_that_trans_txt' - # javascript_quote is used to be able to check unicode strings - self.assertContains(response, javascript_quote(trans_txt), 1) - if lang_code == 'fr': - # Message with context (msgctxt) - self.assertContains(response, "['month name\x04May'] = 'mai';", 1) - activate(saved_lang) + with override(lang_code): + catalog = gettext.translation('djangojs', locale_dir, [lang_code]) + if six.PY3: + trans_txt = catalog.gettext('this is to be translated') + else: + trans_txt = catalog.ugettext('this is to be translated') + response = self.client.get('/views/jsi18n/') + # in response content must to be a line like that: + # catalog['this is to be translated'] = 'same_that_trans_txt' + # javascript_quote is used to be able to check unicode strings + self.assertContains(response, javascript_quote(trans_txt), 1) + if lang_code == 'fr': + # Message with context (msgctxt) + self.assertContains(response, "['month name\x04May'] = 'mai';", 1) class JsI18NTests(TestCase): diff --git a/tests/regressiontests/views/tests/shortcuts.py b/tests/regressiontests/views/tests/shortcuts.py index a48bb25d0b..62bd82f4a7 100644 --- a/tests/regressiontests/views/tests/shortcuts.py +++ b/tests/regressiontests/views/tests/shortcuts.py @@ -12,44 +12,44 @@ class ShortcutTests(TestCase): def test_render_to_response(self): response = self.client.get('/shortcuts/render_to_response/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR..\n') + self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_render_to_response_with_request_context(self): response = self.client.get('/shortcuts/render_to_response/request_context/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_render_to_response_with_mimetype(self): response = self.client.get('/shortcuts/render_to_response/mimetype/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR..\n') + self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response['Content-Type'], 'application/x-rendertest') def test_render(self): response = self.client.get('/shortcuts/render/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') self.assertEqual(response.context.current_app, None) def test_render_with_base_context(self): response = self.client.get('/shortcuts/render/base_context/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR..\n') + self.assertEqual(response.content, b'FOO.BAR..\n') self.assertEqual(response['Content-Type'], 'text/html; charset=utf-8') def test_render_with_content_type(self): response = self.client.get('/shortcuts/render/content_type/') self.assertEqual(response.status_code, 200) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') self.assertEqual(response['Content-Type'], 'application/x-rendertest') def test_render_with_status(self): response = self.client.get('/shortcuts/render/status/') self.assertEqual(response.status_code, 403) - self.assertEqual(response.content, 'FOO.BAR../path/to/static/media/\n') + self.assertEqual(response.content, b'FOO.BAR../path/to/static/media/\n') def test_render_with_current_app(self): response = self.client.get('/shortcuts/render/current_app/') diff --git a/tests/regressiontests/wsgi/tests.py b/tests/regressiontests/wsgi/tests.py index a482a5c1eb..5a40bd88e2 100644 --- a/tests/regressiontests/wsgi/tests.py +++ b/tests/regressiontests/wsgi/tests.py @@ -40,8 +40,8 @@ class WSGITest(TestCase): response_data["headers"], [('Content-Type', 'text/html; charset=utf-8')]) self.assertEqual( - six.text_type(response), - "Content-Type: text/html; charset=utf-8\n\nHello World!") + bytes(response), + b"Content-Type: text/html; charset=utf-8\r\n\r\nHello World!") class GetInternalWSGIApplicationTest(unittest.TestCase): |
