diff --git a/django/forms/widgets.py b/django/forms/widgets.py index 88dc28d664..e6707f9557 100644 --- a/django/forms/widgets.py +++ b/django/forms/widgets.py @@ -104,6 +104,7 @@ class Media(object): getattr(combined, 'add_' + name)(getattr(other, '_' + name, None)) return combined + def media_property(cls): def _media(self): # Get the media property of the superclass, if it exists @@ -131,6 +132,7 @@ def media_property(cls): return base return property(_media) + class MediaDefiningClass(type): """ Metaclass for classes that can have media definitions. @@ -162,6 +164,7 @@ class SubWidget(object): args.append(self.choices) return self.parent_widget.render(*args) + class Widget(six.with_metaclass(MediaDefiningClass)): is_hidden = False # Determines whether this corresponds to an . needs_multipart_form = False # Determines does this widget need multipart form @@ -224,6 +227,7 @@ class Widget(six.with_metaclass(MediaDefiningClass)): """ return id_ + class Input(Widget): """ Base class for all widgets (except type='checkbox' and @@ -279,10 +283,12 @@ class PasswordInput(TextInput): value = None return super(PasswordInput, self).render(name, value, attrs) + class HiddenInput(Input): input_type = 'hidden' is_hidden = True + class MultipleHiddenInput(HiddenInput): """ A widget that handles for fields that have a list @@ -313,6 +319,7 @@ class MultipleHiddenInput(HiddenInput): return data.getlist(name) return data.get(name, None) + class FileInput(Input): input_type = 'file' needs_multipart_form = True @@ -327,6 +334,7 @@ class FileInput(Input): FILE_INPUT_CONTRADICTION = object() + class ClearableFileInput(FileInput): initial_text = ugettext_lazy('Currently') input_text = ugettext_lazy('Change') @@ -379,7 +387,8 @@ class ClearableFileInput(FileInput): def value_from_datadict(self, data, files, name): upload = super(ClearableFileInput, self).value_from_datadict(data, files, name) if not self.is_required and CheckboxInput().value_from_datadict( - data, files, self.clear_checkbox_name(name)): + data, files, self.clear_checkbox_name(name)): + if upload: # If the user contradicts themselves (uploads a new file AND # checks the "clear" checkbox), we return a unique marker @@ -389,6 +398,7 @@ class ClearableFileInput(FileInput): return False return upload + class Textarea(Widget): def __init__(self, attrs=None): # The 'rows' and 'cols' attributes are required for HTML correctness. @@ -515,6 +525,7 @@ class Select(Widget): output.append(self.render_option(selected_choices, option_value, option_label)) return '\n'.join(output) + class NullBooleanSelect(Select): """ A Select Widget intended to be used with NullBooleanField. @@ -849,6 +860,7 @@ class SplitDateTimeWidget(MultiWidget): return [value.date(), value.time().replace(microsecond=0)] return [None, None] + class SplitHiddenDateTimeWidget(SplitDateTimeWidget): """ A Widget that splits datetime input into two inputs. diff --git a/setup.cfg b/setup.cfg index dd708a5d9a..9a4776a319 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ install-script = scripts/rpm-install.sh [flake8] exclude=./django/utils/dictconfig.py,./django/contrib/comments/*,./django/utils/unittest.py,./tests/comment_tests/*,./django/test/_doctest.py,./django/utils/six.py,./django/conf/app_template/* -ignore=E124,E125,E127,E128,E226,E251,E302,E501,E261,W601 +ignore=E124,E125,E127,E128,E226,E251,E501,E261,W601 [metadata] license-file = LICENSE diff --git a/tests/multiple_database/tests.py b/tests/multiple_database/tests.py index f7c5862167..046b8fb14b 100644 --- a/tests/multiple_database/tests.py +++ b/tests/multiple_database/tests.py @@ -50,7 +50,8 @@ class QueryTestCase(TestCase): except Book.DoesNotExist: self.fail('"Pro Django" should exist on default database') - self.assertRaises(Book.DoesNotExist, + self.assertRaises( + Book.DoesNotExist, Book.objects.using('other').get, title="Pro Django" ) @@ -61,7 +62,8 @@ class QueryTestCase(TestCase): except Book.DoesNotExist: self.fail('"Dive into Python" should exist on default database') - self.assertRaises(Book.DoesNotExist, + self.assertRaises( + Book.DoesNotExist, Book.objects.using('other').get, title="Dive into Python" ) @@ -84,11 +86,13 @@ class QueryTestCase(TestCase): except Book.DoesNotExist: self.fail('"Pro Django" should exist on other database') - self.assertRaises(Book.DoesNotExist, + self.assertRaises( + Book.DoesNotExist, Book.objects.get, title="Pro Django" ) - self.assertRaises(Book.DoesNotExist, + self.assertRaises( + Book.DoesNotExist, Book.objects.using('default').get, title="Pro Django" ) @@ -98,11 +102,13 @@ class QueryTestCase(TestCase): except Book.DoesNotExist: self.fail('"Dive into Python" should exist on other database') - self.assertRaises(Book.DoesNotExist, + self.assertRaises( + Book.DoesNotExist, Book.objects.get, title="Dive into Python" ) - self.assertRaises(Book.DoesNotExist, + self.assertRaises( + Book.DoesNotExist, Book.objects.using('default').get, title="Dive into Python" ) @@ -164,14 +170,14 @@ class QueryTestCase(TestCase): # Check that queries work across m2m joins self.assertEqual(list(Book.objects.using('default').filter(authors__name='Marty Alchin').values_list('title', flat=True)), - ['Pro Django']) + ['Pro Django']) self.assertEqual(list(Book.objects.using('other').filter(authors__name='Marty Alchin').values_list('title', flat=True)), - []) + []) self.assertEqual(list(Book.objects.using('default').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)), - []) + []) self.assertEqual(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)), - ['Dive into Python']) + ['Dive into Python']) # Reget the objects to clear caches dive = Book.objects.using('other').get(title="Dive into Python") @@ -179,10 +185,10 @@ class QueryTestCase(TestCase): # Retrive related object by descriptor. Related objects should be database-baound self.assertEqual(list(dive.authors.all().values_list('name', flat=True)), - ['Mark Pilgrim']) + ['Mark Pilgrim']) self.assertEqual(list(mark.book_set.all().values_list('title', flat=True)), - ['Dive into Python']) + ['Dive into Python']) def test_m2m_forward_operations(self): "M2M forward manipulations are all constrained to a single DB" @@ -198,13 +204,13 @@ class QueryTestCase(TestCase): # Add a second author john = Person.objects.using('other').create(name="John Smith") self.assertEqual(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)), - []) + []) dive.authors.add(john) self.assertEqual(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)), - ['Dive into Python']) + ['Dive into Python']) self.assertEqual(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)), - ['Dive into Python']) + ['Dive into Python']) # Remove the second author dive.authors.remove(john) diff --git a/tests/mutually_referential/models.py b/tests/mutually_referential/models.py index 0f1bd65cb1..25bc45731f 100644 --- a/tests/mutually_referential/models.py +++ b/tests/mutually_referential/models.py @@ -13,6 +13,7 @@ class Parent(models.Model): # Use a simple string for forward declarations. bestchild = models.ForeignKey("Child", null=True, related_name="favoured_by") + class Child(models.Model): name = models.CharField(max_length=100) diff --git a/tests/nested_foreign_keys/models.py b/tests/nested_foreign_keys/models.py index 50d447951b..3b99cfcb67 100644 --- a/tests/nested_foreign_keys/models.py +++ b/tests/nested_foreign_keys/models.py @@ -17,6 +17,7 @@ class Event(models.Model): class Screening(Event): movie = models.ForeignKey(Movie) + class ScreeningNullFK(Event): movie = models.ForeignKey(Movie, null=True) @@ -24,5 +25,6 @@ class ScreeningNullFK(Event): class Package(models.Model): screening = models.ForeignKey(Screening, null=True) + class PackageNullFK(models.Model): screening = models.ForeignKey(ScreeningNullFK, null=True) diff --git a/tests/null_fk/models.py b/tests/null_fk/models.py index c86ee8a5a9..9309ae940f 100644 --- a/tests/null_fk/models.py +++ b/tests/null_fk/models.py @@ -9,14 +9,17 @@ from django.utils.encoding import python_2_unicode_compatible class SystemDetails(models.Model): details = models.TextField() + class SystemInfo(models.Model): system_details = models.ForeignKey(SystemDetails) system_name = models.CharField(max_length=32) + 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) @@ -25,6 +28,7 @@ class Post(models.Model): def __str__(self): return self.title + @python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) @@ -38,12 +42,15 @@ class Comment(models.Model): # Ticket 15823 + class Item(models.Model): title = models.CharField(max_length=100) + class PropertyValue(models.Model): label = models.CharField(max_length=100) + class Property(models.Model): item = models.ForeignKey(Item, related_name='props') key = models.CharField(max_length=100) diff --git a/tests/null_fk_ordering/models.py b/tests/null_fk_ordering/models.py index 3caff0d594..dac1c4f6ae 100644 --- a/tests/null_fk_ordering/models.py +++ b/tests/null_fk_ordering/models.py @@ -15,6 +15,7 @@ from django.utils.encoding import python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=150) + @python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=150) @@ -31,10 +32,12 @@ class Article(models.Model): class SystemInfo(models.Model): system_name = models.CharField(max_length=32) + 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) @@ -43,6 +46,7 @@ class Post(models.Model): def __str__(self): return self.title + @python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) diff --git a/tests/null_queries/models.py b/tests/null_queries/models.py index 9070dd4873..1c3808f407 100644 --- a/tests/null_queries/models.py +++ b/tests/null_queries/models.py @@ -11,6 +11,7 @@ class Poll(models.Model): def __str__(self): return "Q: %s " % self.question + @python_2_unicode_compatible class Choice(models.Model): poll = models.ForeignKey(Poll) @@ -20,12 +21,16 @@ class Choice(models.Model): return "Choice: %s in poll %s" % (self.choice, self.poll) # A set of models with an inner one pointing to two outer ones. + + class OuterA(models.Model): pass + class OuterB(models.Model): data = models.CharField(max_length=10) + class Inner(models.Model): first = models.ForeignKey(OuterA) # second would clash with the __second lookup. diff --git a/tests/one_to_one/models.py b/tests/one_to_one/models.py index ff809be22d..b54cabbaa3 100644 --- a/tests/one_to_one/models.py +++ b/tests/one_to_one/models.py @@ -19,6 +19,7 @@ class Place(models.Model): def __str__(self): return "%s the place" % self.name + @python_2_unicode_compatible class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True) @@ -28,6 +29,7 @@ class Restaurant(models.Model): def __str__(self): return "%s the restaurant" % self.place.name + @python_2_unicode_compatible class Waiter(models.Model): restaurant = models.ForeignKey(Restaurant) @@ -36,13 +38,16 @@ class Waiter(models.Model): def __str__(self): return "%s the waiter at %s" % (self.name, self.restaurant) + class ManualPrimaryKey(models.Model): primary_key = models.CharField(max_length=10, primary_key=True) - name = models.CharField(max_length = 50) + name = models.CharField(max_length=50) + class RelatedModel(models.Model): link = models.OneToOneField(ManualPrimaryKey) - name = models.CharField(max_length = 50) + name = models.CharField(max_length=50) + @python_2_unicode_compatible class MultiModel(models.Model): diff --git a/tests/one_to_one/tests.py b/tests/one_to_one/tests.py index da36908d28..84ada2cc51 100644 --- a/tests/one_to_one/tests.py +++ b/tests/one_to_one/tests.py @@ -6,6 +6,7 @@ from django.test import TestCase from .models import (Place, Restaurant, Waiter, ManualPrimaryKey, RelatedModel, MultiModel) + class OneToOneTests(TestCase): def setUp(self): diff --git a/tests/order_with_respect_to/models.py b/tests/order_with_respect_to/models.py index 06bb56b141..18dbd08dbc 100644 --- a/tests/order_with_respect_to/models.py +++ b/tests/order_with_respect_to/models.py @@ -10,6 +10,7 @@ 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) @@ -21,6 +22,7 @@ class Answer(models.Model): def __str__(self): return six.text_type(self.text) + @python_2_unicode_compatible class Post(models.Model): title = models.CharField(max_length=200) diff --git a/tests/ordering/models.py b/tests/ordering/models.py index e516c2f5a8..415b28bb41 100644 --- a/tests/ordering/models.py +++ b/tests/ordering/models.py @@ -28,6 +28,7 @@ class Article(models.Model): def __str__(self): return self.headline + @python_2_unicode_compatible class ArticlePKOrdering(models.Model): headline = models.CharField(max_length=100) diff --git a/tests/prefetch_related/models.py b/tests/prefetch_related/models.py index 307cee1900..5e4bf4a3e4 100644 --- a/tests/prefetch_related/models.py +++ b/tests/prefetch_related/models.py @@ -5,6 +5,7 @@ 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) @@ -55,6 +56,7 @@ class Book(models.Model): class Meta: ordering = ['id'] + class BookWithYear(Book): book = models.OneToOneField(Book, parent_link=True) published_year = models.IntegerField() @@ -73,12 +75,14 @@ class Reader(models.Model): class Meta: ordering = ['id'] + class BookReview(models.Model): book = models.ForeignKey(BookWithYear) notes = models.TextField(null=True, blank=True) ## Models for default manager tests + class Qualification(models.Model): name = models.CharField(max_length=10) @@ -161,6 +165,7 @@ class House(models.Model): class Meta: ordering = ['id'] + class Room(models.Model): name = models.CharField(max_length=50) house = models.ForeignKey(House, related_name='rooms') diff --git a/tests/proxy_model_inheritance/models.py b/tests/proxy_model_inheritance/models.py index ef9ac6b0d3..7a10b77c90 100644 --- a/tests/proxy_model_inheritance/models.py +++ b/tests/proxy_model_inheritance/models.py @@ -5,9 +5,11 @@ from django.db import models class ConcreteModel(models.Model): pass + class ConcreteModelSubclass(ConcreteModel): pass + class ConcreteModelSubclassProxy(ConcreteModelSubclass): class Meta: proxy = True diff --git a/tests/proxy_models/models.py b/tests/proxy_models/models.py index 2e5741ab6b..2e28c3b996 100644 --- a/tests/proxy_models/models.py +++ b/tests/proxy_models/models.py @@ -9,14 +9,17 @@ from django.utils.encoding import python_2_unicode_compatible # A couple of managers for testing managing overriding in proxy model cases. + class PersonManager(models.Manager): def get_queryset(self): return super(PersonManager, self).get_queryset().exclude(name="fred") + class SubManager(models.Manager): def get_queryset(self): return super(SubManager, self).get_queryset().exclude(name="wilma") + @python_2_unicode_compatible class Person(models.Model): """ @@ -29,6 +32,7 @@ class Person(models.Model): def __str__(self): return self.name + class Abstract(models.Model): """ A simple abstract base class, to be used for error checking. @@ -38,6 +42,7 @@ class Abstract(models.Model): class Meta: abstract = True + class MyPerson(Person): """ A proxy subclass, this should not get a new table. Overrides the default @@ -56,12 +61,14 @@ class MyPerson(Person): def has_special_name(self): return self.name.lower() == "special" + class ManagerMixin(models.Model): excluder = SubManager() class Meta: abstract = True + class OtherPerson(Person, ManagerMixin): """ A class with the default manager from Person, plus an secondary manager. @@ -70,6 +77,7 @@ class OtherPerson(Person, ManagerMixin): proxy = True ordering = ["name"] + class StatusPerson(MyPerson): """ A non-proxy subclass of a proxy, it should get a new table. @@ -77,13 +85,17 @@ class StatusPerson(MyPerson): status = models.CharField(max_length=80) # We can even have proxies of proxies (and subclass of those). + + class MyPersonProxy(MyPerson): class Meta: proxy = True + class LowerStatusPerson(MyPersonProxy): status = models.CharField(max_length=80) + @python_2_unicode_compatible class User(models.Model): name = models.CharField(max_length=100) @@ -91,18 +103,23 @@ class User(models.Model): def __str__(self): return self.name + class UserProxy(User): class Meta: proxy = True + class UserProxyProxy(UserProxy): class Meta: proxy = True # We can still use `select_related()` to include related models in our querysets. + + class Country(models.Model): name = models.CharField(max_length=50) + @python_2_unicode_compatible class State(models.Model): name = models.CharField(max_length=50) @@ -111,12 +128,15 @@ class State(models.Model): def __str__(self): return self.name + class StateProxy(State): class Meta: proxy = True # Proxy models still works with filters (on related fields) # and select_related, even when mixed with model inheritance + + @python_2_unicode_compatible class BaseUser(models.Model): name = models.CharField(max_length=255) @@ -124,9 +144,11 @@ class BaseUser(models.Model): def __str__(self): return ':'.join((self.__class__.__name__, self.name,)) + class TrackerUser(BaseUser): status = models.CharField(max_length=50) + class ProxyTrackerUser(TrackerUser): class Meta: proxy = True @@ -140,10 +162,12 @@ class Issue(models.Model): def __str__(self): return ':'.join((self.__class__.__name__, self.summary,)) + class Bug(Issue): version = models.CharField(max_length=50) reporter = models.ForeignKey(BaseUser) + class ProxyBug(Bug): """ Proxy of an inherited class @@ -159,6 +183,7 @@ class ProxyProxyBug(ProxyBug): class Meta: proxy = True + class Improvement(Issue): """ A model that has relation to a proxy model @@ -168,6 +193,7 @@ class Improvement(Issue): reporter = models.ForeignKey(ProxyTrackerUser) associated_bug = models.ForeignKey(ProxyProxyBug) + class ProxyImprovement(Improvement): class Meta: proxy = True diff --git a/tests/queries/models.py b/tests/queries/models.py index 53b716abd1..ec924c03c2 100644 --- a/tests/queries/models.py +++ b/tests/queries/models.py @@ -13,10 +13,12 @@ from django.utils.encoding import python_2_unicode_compatible class DumbCategory(models.Model): pass + class ProxyCategory(DumbCategory): class Meta: proxy = True + @python_2_unicode_compatible class NamedCategory(DumbCategory): name = models.CharField(max_length=10) @@ -24,6 +26,7 @@ class NamedCategory(DumbCategory): def __str__(self): return self.name + @python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) @@ -37,6 +40,7 @@ class Tag(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Note(models.Model): note = models.CharField(max_length=100) @@ -55,6 +59,7 @@ 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) @@ -64,6 +69,7 @@ class Annotation(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class ExtraInfo(models.Model): info = models.CharField(max_length=100) @@ -76,6 +82,7 @@ class ExtraInfo(models.Model): def __str__(self): return self.info + @python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=10) @@ -88,6 +95,7 @@ class Author(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=10) @@ -103,6 +111,7 @@ class Item(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Report(models.Model): name = models.CharField(max_length=10) @@ -111,6 +120,7 @@ class Report(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Ranking(models.Model): rank = models.IntegerField() @@ -123,6 +133,7 @@ class Ranking(models.Model): 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) @@ -134,6 +145,7 @@ class Cover(models.Model): def __str__(self): return self.title + @python_2_unicode_compatible class Number(models.Model): num = models.IntegerField() @@ -143,6 +155,8 @@ class Number(models.Model): # Symmetrical m2m field with a normal field using the reverse accesor name # ("valid"). + + class Valid(models.Model): valid = models.CharField(max_length=10) parent = models.ManyToManyField('self') @@ -152,38 +166,49 @@ class Valid(models.Model): # Some funky cross-linked models for testing a couple of infinite recursion # cases. + + class X(models.Model): y = models.ForeignKey('Y') + class Y(models.Model): x1 = models.ForeignKey(X, related_name='y1') # Some models with a cycle in the default ordering. This would be bad if we # didn't catch the infinite loop. + + class LoopX(models.Model): y = models.ForeignKey('LoopY') class Meta: ordering = ['y'] + class LoopY(models.Model): x = models.ForeignKey(LoopX) class Meta: ordering = ['x'] + class LoopZ(models.Model): z = models.ForeignKey('self') class Meta: ordering = ['z'] + # A model and custom default manager combination. + + class CustomManager(models.Manager): def get_queryset(self): qs = super(CustomManager, self).get_queryset() return qs.filter(public=True, tag__name='t1') + @python_2_unicode_compatible class ManagedModel(models.Model): data = models.CharField(max_length=10) @@ -197,24 +222,31 @@ class ManagedModel(models.Model): return self.data # An inter-related setup with multiple paths from Child to Detail. + + class Detail(models.Model): data = models.CharField(max_length=10) + class MemberManager(models.Manager): def get_queryset(self): return super(MemberManager, self).get_queryset().select_related("details") + class Member(models.Model): name = models.CharField(max_length=10) details = models.OneToOneField(Detail, primary_key=True) objects = MemberManager() + class Child(models.Model): person = models.OneToOneField(Member, primary_key=True) parent = models.ForeignKey(Member, related_name="children") # Custom primary keys interfered with ordering in the past. + + class CustomPk(models.Model): name = models.CharField(max_length=10, primary_key=True) extra = models.CharField(max_length=10) @@ -222,12 +254,14 @@ class CustomPk(models.Model): class Meta: ordering = ['name', 'extra'] + class Related(models.Model): custom = models.ForeignKey(CustomPk) # 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) @@ -236,13 +270,17 @@ class Celebrity(models.Model): def __str__(self): return self.name + class TvChef(Celebrity): pass + 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) @@ -250,13 +288,16 @@ class LeafA(models.Model): def __str__(self): return self.data + class LeafB(models.Model): data = models.CharField(max_length=10) + 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) @@ -266,6 +307,8 @@ class ReservedName(models.Model): return self.name # A simpler shared-foreign-key setup that can expose some problems. + + @python_2_unicode_compatible class SharedConnection(models.Model): data = models.CharField(max_length=10) @@ -273,13 +316,17 @@ class SharedConnection(models.Model): def __str__(self): return self.data + class PointerA(models.Model): connection = models.ForeignKey(SharedConnection) + 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) @@ -290,6 +337,7 @@ class SingleObject(models.Model): def __str__(self): return self.name + class RelatedObject(models.Model): single = models.ForeignKey(SingleObject, null=True) f = models.IntegerField(null=True) @@ -297,6 +345,7 @@ class RelatedObject(models.Model): class Meta: ordering = ['single'] + @python_2_unicode_compatible class Plaything(models.Model): name = models.CharField(max_length=10) @@ -308,10 +357,12 @@ class Plaything(models.Model): 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) @@ -319,6 +370,7 @@ class Food(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Eaten(models.Model): food = models.ForeignKey(Food, to_field="name", null=True) @@ -327,6 +379,7 @@ class Eaten(models.Model): def __str__(self): return "%s at %s" % (self.food, self.meal) + @python_2_unicode_compatible class Node(models.Model): num = models.IntegerField(unique=True) @@ -336,6 +389,8 @@ class Node(models.Model): return "%s" % self.num # Bug #12252 + + @python_2_unicode_compatible class ObjectA(models.Model): name = models.CharField(max_length=50) @@ -343,6 +398,7 @@ class ObjectA(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class ObjectB(models.Model): name = models.CharField(max_length=50) @@ -352,6 +408,7 @@ class ObjectB(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class ObjectC(models.Model): name = models.CharField(max_length=50) @@ -361,6 +418,7 @@ class ObjectC(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class SimpleCategory(models.Model): name = models.CharField(max_length=15) @@ -368,6 +426,7 @@ class SimpleCategory(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class SpecialCategory(SimpleCategory): special_name = models.CharField(max_length=15) @@ -375,6 +434,7 @@ class SpecialCategory(SimpleCategory): def __str__(self): return self.name + " " + self.special_name + @python_2_unicode_compatible class CategoryItem(models.Model): category = models.ForeignKey(SimpleCategory) @@ -382,6 +442,7 @@ class CategoryItem(models.Model): def __str__(self): return "category item: " + str(self.category) + @python_2_unicode_compatible class OneToOneCategory(models.Model): new_name = models.CharField(max_length=15) @@ -390,31 +451,38 @@ class OneToOneCategory(models.Model): def __str__(self): return "one2one " + self.new_name + class CategoryRelationship(models.Model): first = models.ForeignKey(SimpleCategory, related_name='first_rel') second = models.ForeignKey(SimpleCategory, related_name='second_rel') + class NullableName(models.Model): name = models.CharField(max_length=20, null=True) 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) + @python_2_unicode_compatible class Job(models.Model): name = models.CharField(max_length=20, unique=True) @@ -422,10 +490,12 @@ class Job(models.Model): def __str__(self): return self.name + class JobResponsibilities(models.Model): job = models.ForeignKey(Job, to_field='name') responsibility = models.ForeignKey('Responsibility', to_field='description') + @python_2_unicode_compatible class Responsibility(models.Model): description = models.CharField(max_length=20, unique=True) @@ -436,23 +506,29 @@ class Responsibility(models.Model): return self.description # Models for disjunction join promotion low level testing. + + class FK1(models.Model): f1 = models.TextField() f2 = models.TextField() + class FK2(models.Model): f1 = models.TextField() f2 = models.TextField() + class FK3(models.Model): f1 = models.TextField() f2 = models.TextField() + class BaseA(models.Model): a = models.ForeignKey(FK1, null=True) b = models.ForeignKey(FK2, null=True) c = models.ForeignKey(FK3, null=True) + @python_2_unicode_compatible class Identifier(models.Model): name = models.CharField(max_length=100) @@ -460,17 +536,21 @@ class Identifier(models.Model): def __str__(self): return self.name + class Program(models.Model): identifier = models.OneToOneField(Identifier) + class Channel(models.Model): programs = models.ManyToManyField(Program) identifier = models.OneToOneField(Identifier) + class Book(models.Model): title = models.TextField() chapter = models.ForeignKey('Chapter') + class Chapter(models.Model): title = models.TextField() paragraph = models.ForeignKey('Paragraph') @@ -480,15 +560,19 @@ class Paragraph(models.Model): text = models.TextField() page = models.ManyToManyField('Page') + class Page(models.Model): text = models.TextField() + class MyObject(models.Model): parent = models.ForeignKey('self', null=True, blank=True, related_name='children') data = models.CharField(max_length=100) created_at = models.DateTimeField(auto_now_add=True) # Models for #17600 regressions + + @python_2_unicode_compatible class Order(models.Model): id = models.IntegerField(primary_key=True) @@ -499,6 +583,7 @@ class Order(models.Model): def __str__(self): return '%s' % self.pk + @python_2_unicode_compatible class OrderItem(models.Model): order = models.ForeignKey(Order, related_name='items') @@ -510,9 +595,11 @@ class OrderItem(models.Model): def __str__(self): return '%s' % self.pk + class BaseUser(models.Model): pass + @python_2_unicode_compatible class Task(models.Model): title = models.CharField(max_length=10) @@ -522,6 +609,7 @@ class Task(models.Model): def __str__(self): return self.title + @python_2_unicode_compatible class Staff(models.Model): name = models.CharField(max_length=10) @@ -529,6 +617,7 @@ class Staff(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class StaffUser(BaseUser): staff = models.OneToOneField(Staff, related_name='user') @@ -536,11 +625,13 @@ class StaffUser(BaseUser): def __str__(self): return self.staff + class Ticket21203Parent(models.Model): parentid = models.AutoField(primary_key=True) parent_bool = models.BooleanField(default=True) created = models.DateTimeField(auto_now=True) + class Ticket21203Child(models.Model): childid = models.AutoField(primary_key=True) parent = models.ForeignKey(Ticket21203Parent) diff --git a/tests/queries/tests.py b/tests/queries/tests.py index c1d461f485..d1e9bd7363 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -28,6 +28,7 @@ from .models import ( CategoryRelationship, Ticket21203Parent, Ticket21203Child, Person, Company, Employment) + class BaseQuerysetTest(TestCase): def assertValueQuerysetEqual(self, qs, values): return self.assertQuerysetEqual(qs, values, transform=lambda x: x) @@ -827,7 +828,7 @@ class Queries1Tests(BaseQuerysetTest): qs = Tag.objects.values_list('id', flat=True).order_by('id') qs.query.bump_prefix(qs.query) first = qs[0] - self.assertEqual(list(qs), list(range(first, first+5))) + self.assertEqual(list(qs), list(range(first, first + 5))) def test_ticket8439(self): # Complex combinations of conjunctions, disjunctions and nullable @@ -1262,6 +1263,7 @@ class Queries3Tests(BaseQuerysetTest): Item.objects.datetimes, 'name', 'month' ) + class Queries4Tests(BaseQuerysetTest): def setUp(self): generic = NamedCategory.objects.create(name="Generic") @@ -1445,8 +1447,8 @@ class Queries4Tests(BaseQuerysetTest): c0 = SimpleCategory.objects.create(name="cat0") c1 = SimpleCategory.objects.create(name="category1") - OneToOneCategory.objects.create(category = c1, new_name="new1") - OneToOneCategory.objects.create(category = c0, new_name="new2") + OneToOneCategory.objects.create(category=c1, new_name="new1") + OneToOneCategory.objects.create(category=c0, new_name="new2") CategoryItem.objects.create(category=c) ci2 = CategoryItem.objects.create(category=c0) @@ -1461,8 +1463,8 @@ class Queries4Tests(BaseQuerysetTest): c0 = SimpleCategory.objects.create(name="cat0") c1 = SimpleCategory.objects.create(name="category1") - OneToOneCategory.objects.create(category = c1, new_name="new1") - OneToOneCategory.objects.create(category = c0, new_name="new2") + OneToOneCategory.objects.create(category=c1, new_name="new1") + OneToOneCategory.objects.create(category=c0, new_name="new2") ci1 = CategoryItem.objects.create(category=c) CategoryItem.objects.create(category=c0) @@ -1477,8 +1479,8 @@ class Queries4Tests(BaseQuerysetTest): c0 = SimpleCategory.objects.create(name="cat0") c1 = SimpleCategory.objects.create(name="category1") - OneToOneCategory.objects.create(category = c1, new_name="new1") - OneToOneCategory.objects.create(category = c0, new_name="new2") + OneToOneCategory.objects.create(category=c1, new_name="new1") + OneToOneCategory.objects.create(category=c0, new_name="new2") ci1 = CategoryItem.objects.create(category=c) CategoryItem.objects.create(category=c0) @@ -1493,8 +1495,8 @@ class Queries4Tests(BaseQuerysetTest): c0 = SimpleCategory.objects.create(name="cat0") c1 = SimpleCategory.objects.create(name="category1") - OneToOneCategory.objects.create(category = c1, new_name="new1") - OneToOneCategory.objects.create(category = c0, new_name="new2") + OneToOneCategory.objects.create(category=c1, new_name="new1") + OneToOneCategory.objects.create(category=c0, new_name="new2") CategoryItem.objects.create(category=c) ci2 = CategoryItem.objects.create(category=c0) @@ -2019,6 +2021,7 @@ class CloneTests(TestCase): else: opts_class.__deepcopy__ = note_deepcopy + class EmptyQuerySetTests(TestCase): def test_emptyqueryset_values(self): # #14366 -- Calling .values() on an empty QuerySet and then cloning @@ -2224,12 +2227,12 @@ class ConditionalTests(BaseQuerysetTest): self.assertRaisesMessage( FieldError, 'Infinite loop caused by ordering.', - lambda: list(LoopX.objects.all()) # Force queryset evaluation with list() + lambda: list(LoopX.objects.all()) # Force queryset evaluation with list() ) self.assertRaisesMessage( FieldError, 'Infinite loop caused by ordering.', - lambda: list(LoopZ.objects.all()) # Force queryset evaluation with list() + lambda: list(LoopZ.objects.all()) # Force queryset evaluation with list() ) # Note that this doesn't cause an infinite loop, since the default @@ -2353,6 +2356,7 @@ class DefaultValuesInsertTest(TestCase): except TypeError: self.fail("Creation of an instance of a model with only the PK field shouldn't error out after bulk insert refactoring (#17056)") + class ExcludeTests(TestCase): def setUp(self): f1 = Food.objects.create(name='apples') @@ -2504,6 +2508,7 @@ class ExcludeTest17600(TestCase): Order.objects.exclude(~Q(items__status=1)).distinct(), ['']) + class Exclude15786(TestCase): """Regression test for #15786""" def test_ticket15786(self): @@ -2562,6 +2567,7 @@ class NullInExcludeTest(TestCase): 'IS NOT NULL', str(NullableName.objects.filter(~~Q(name='i1')).query)) + class EmptyStringsAsNullTest(TestCase): """ Test that filtering on non-null character fields works as expected. @@ -2591,6 +2597,7 @@ class EmptyStringsAsNullTest(TestCase): [foo.pk], attrgetter('pk') ) + class ProxyQueryCleanupTest(TestCase): def test_evaluated_proxy_count(self): """ @@ -2603,6 +2610,7 @@ class ProxyQueryCleanupTest(TestCase): str(qs.query) self.assertEqual(qs.count(), 1) + class WhereNodeTest(TestCase): class DummyNode(object): def as_sql(self, qn, connection): @@ -2768,6 +2776,7 @@ class NullJoinPromotionOrTest(TestCase): self.assertQuerysetEqual( qs.order_by('name'), [r2, r1], lambda x: x) + class ReverseJoinTrimmingTest(TestCase): def test_reverse_trimming(self): # Check that we don't accidentally trim reverse joins - we can't know @@ -2778,6 +2787,7 @@ class ReverseJoinTrimmingTest(TestCase): self.assertIn('INNER JOIN', str(qs.query)) self.assertEqual(list(qs), []) + class JoinReuseTest(TestCase): """ Test that the queries reuse joins sensibly (for example, direct joins @@ -2811,6 +2821,7 @@ class JoinReuseTest(TestCase): qs = Author.objects.filter(report__name='r4').filter(report__name='r1') self.assertEqual(str(qs.query).count('JOIN'), 2) + class DisjunctionPromotionTests(TestCase): def test_disjuction_promotion_select_related(self): fk1 = FK1.objects.create(f1='f1', f2='f2') @@ -2986,6 +2997,7 @@ class ManyToManyExcludeTest(TestCase): self.assertIn(b2, q) self.assertIn(b3, q) + class RelabelCloneTest(TestCase): def test_ticket_19964(self): my1 = MyObject.objects.create(data='foo') @@ -3000,6 +3012,7 @@ class RelabelCloneTest(TestCase): self.assertEqual(list(children), [my2]) self.assertEqual(list(parents), [my1]) + class Ticket20101Tests(TestCase): def test_ticket_20101(self): """ @@ -3016,6 +3029,7 @@ class Ticket20101Tests(TestCase): self.assertFalse(n in qs2) self.assertTrue(n in (qs1 | qs2)) + class EmptyStringPromotionTests(TestCase): def test_empty_string_promotion(self): qs = RelatedObject.objects.filter(single__name='') @@ -3024,6 +3038,7 @@ class EmptyStringPromotionTests(TestCase): else: self.assertNotIn('LEFT OUTER JOIN', str(qs.query)) + class ValuesSubqueryTests(TestCase): def test_values_in_subquery(self): # Check that if a values() queryset is used, then the given values @@ -3041,6 +3056,7 @@ class ValuesSubqueryTests(TestCase): Order.objects.filter(items__in=OrderItem.objects.values_list('status')), [o1.pk], lambda x: x.pk) + class DoubleInSubqueryTests(TestCase): def test_double_subquery_in(self): lfa1 = LeafA.objects.create(data='foo') @@ -3055,6 +3071,7 @@ class DoubleInSubqueryTests(TestCase): self.assertQuerysetEqual( qs, [lfb1], lambda x: x) + class Ticket18785Tests(TestCase): def test_ticket_18785(self): # Test join trimming from ticket18785 @@ -3085,6 +3102,7 @@ class Ticket20788Tests(TestCase): self.assertQuerysetEqual( sentences_not_in_pub, [book2], lambda x: x) + class Ticket12807Tests(TestCase): def test_ticket_12807(self): p1 = Paragraph.objects.create() @@ -3111,6 +3129,7 @@ class RelatedLookupTypeTests(TestCase): ObjectB.objects.filter(objecta__in=[wrong_type]), [ob], lambda x: x) + class Ticket14056Tests(TestCase): def test_ticket_14056(self): s1 = SharedConnection.objects.create(data='s1') @@ -3126,6 +3145,7 @@ class Ticket14056Tests(TestCase): expected_ordering, lambda x: x ) + class Ticket20955Tests(TestCase): def test_ticket_20955(self): jack = Staff.objects.create(name='jackstaff') @@ -3146,6 +3166,7 @@ class Ticket20955Tests(TestCase): self.assertEqual(task_select_related.owner.staffuser.staff, task_get.owner.staffuser.staff) + class Ticket21203Tests(TestCase): def test_ticket_21203(self): p = Ticket21203Parent.objects.create(parent_bool=True) @@ -3154,6 +3175,7 @@ class Ticket21203Tests(TestCase): self.assertQuerysetEqual(qs, [c], lambda x: x) self.assertIs(qs[0].parent.parent_bool, True) + class ValuesJoinPromotionTests(TestCase): def test_values_no_promotion_for_existing(self): qs = Node.objects.filter(parent__parent__isnull=False) diff --git a/tests/queryset_pickle/models.py b/tests/queryset_pickle/models.py index 10d34180e4..1e73774cd2 100644 --- a/tests/queryset_pickle/models.py +++ b/tests/queryset_pickle/models.py @@ -7,6 +7,7 @@ from django.utils.translation import ugettext_lazy as _ def standalone_number(): return 1 + class Numbers(object): @staticmethod def get_static_number(): @@ -21,12 +22,15 @@ class Numbers(object): nn = Numbers() + class Group(models.Model): name = models.CharField(_('name'), max_length=100) + class Event(models.Model): group = models.ForeignKey(Group) + class Happening(models.Model): when = models.DateTimeField(blank=True, default=datetime.datetime.now) name = models.CharField(blank=True, max_length=100, default=lambda: "test") @@ -35,6 +39,7 @@ class Happening(models.Model): number3 = models.IntegerField(blank=True, default=Numbers.get_class_number) number4 = models.IntegerField(blank=True, default=nn.get_member_number) + class Container(object): # To test pickling we need a class that isn't defined on module, but # is still available from app-cache. So, the Container class moves @@ -42,5 +47,6 @@ class Container(object): class SomeModel(models.Model): somefield = models.IntegerField() + class M2MModel(models.Model): groups = models.ManyToManyField(Group) diff --git a/tests/raw_query/models.py b/tests/raw_query/models.py index 33f754958e..1e5e176bcf 100644 --- a/tests/raw_query/models.py +++ b/tests/raw_query/models.py @@ -15,17 +15,21 @@ class Author(models.Model): assert k in [f.attname for f in self._meta.fields], \ "Author.__init__ got an unexpected parameter: %s" % k + class Book(models.Model): title = models.CharField(max_length=255) author = models.ForeignKey(Author) paperback = models.BooleanField(default=False) opening_line = models.TextField() + class Coffee(models.Model): brand = models.CharField(max_length=255, db_column="name") + class Reviewer(models.Model): reviewed = models.ManyToManyField(Book) + class FriendlyAuthor(Author): pass diff --git a/tests/responses/tests.py b/tests/responses/tests.py index e5320f5af9..afdd4220fb 100644 --- a/tests/responses/tests.py +++ b/tests/responses/tests.py @@ -1,6 +1,7 @@ from django.http import HttpResponse import unittest + class HttpResponseTests(unittest.TestCase): def test_status_code(self): diff --git a/tests/reverse_lookup/models.py b/tests/reverse_lookup/models.py index ed58177770..b216c03120 100644 --- a/tests/reverse_lookup/models.py +++ b/tests/reverse_lookup/models.py @@ -15,6 +15,7 @@ class User(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Poll(models.Model): question = models.CharField(max_length=200) @@ -23,6 +24,7 @@ class Poll(models.Model): def __str__(self): return self.question + @python_2_unicode_compatible class Choice(models.Model): name = models.CharField(max_length=100) diff --git a/tests/reverse_single_related/models.py b/tests/reverse_single_related/models.py index 5d53e04772..c7ec2edc6b 100644 --- a/tests/reverse_single_related/models.py +++ b/tests/reverse_single_related/models.py @@ -5,9 +5,11 @@ class SourceManager(models.Manager): def get_queryset(self): return super(SourceManager, self).get_queryset().filter(is_public=True) + class Source(models.Model): is_public = models.BooleanField(default=False) objects = SourceManager() + class Item(models.Model): source = models.ForeignKey(Source) diff --git a/tests/select_related/models.py b/tests/select_related/models.py index 3b796acdaf..93d699f73e 100644 --- a/tests/select_related/models.py +++ b/tests/select_related/models.py @@ -12,6 +12,7 @@ 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) @@ -19,6 +20,7 @@ class Domain(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Kingdom(models.Model): name = models.CharField(max_length=50) @@ -27,6 +29,7 @@ class Kingdom(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Phylum(models.Model): name = models.CharField(max_length=50) @@ -35,6 +38,7 @@ class Phylum(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Klass(models.Model): name = models.CharField(max_length=50) @@ -43,6 +47,7 @@ class Klass(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Order(models.Model): name = models.CharField(max_length=50) @@ -51,6 +56,7 @@ class Order(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Family(models.Model): name = models.CharField(max_length=50) @@ -59,6 +65,7 @@ class Family(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Genus(models.Model): name = models.CharField(max_length=50) @@ -67,6 +74,7 @@ class Genus(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Species(models.Model): name = models.CharField(max_length=50) @@ -76,6 +84,8 @@ class Species(models.Model): return self.name # and we'll invent a new thing so we have a model with two foreign keys + + @python_2_unicode_compatible class HybridSpecies(models.Model): name = models.CharField(max_length=50) diff --git a/tests/select_related_onetoone/models.py b/tests/select_related_onetoone/models.py index d32faafbb9..9af1b3f31b 100644 --- a/tests/select_related_onetoone/models.py +++ b/tests/select_related_onetoone/models.py @@ -95,8 +95,10 @@ class Child2(Parent1): def __str__(self): return self.name1 + class Child3(Child2): value3 = models.IntegerField() + class Child4(Child1): value4 = models.IntegerField() diff --git a/tests/select_related_regress/models.py b/tests/select_related_regress/models.py index 0858c8fc2c..c2611b4843 100644 --- a/tests/select_related_regress/models.py +++ b/tests/select_related_regress/models.py @@ -11,6 +11,7 @@ class Building(models.Model): def __str__(self): return "Building: %s" % self.name + @python_2_unicode_compatible class Device(models.Model): building = models.ForeignKey('Building') @@ -19,6 +20,7 @@ class Device(models.Model): 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') @@ -27,6 +29,7 @@ class Port(models.Model): 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', @@ -38,45 +41,60 @@ class Connection(models.Model): # Another non-tree hierarchy that exercises code paths similar to the above # example, but in a slightly different configuration. + + class TUser(models.Model): name = models.CharField(max_length=200) + class Person(models.Model): user = models.ForeignKey(TUser, unique=True) + class Organizer(models.Model): person = models.ForeignKey(Person) + class Student(models.Model): person = models.ForeignKey(Person) + class Class(models.Model): org = models.ForeignKey(Organizer) + class Enrollment(models.Model): std = models.ForeignKey(Student) cls = models.ForeignKey(Class) # Models for testing bug #8036. + + class Country(models.Model): name = models.CharField(max_length=50) + class State(models.Model): name = models.CharField(max_length=50) country = models.ForeignKey(Country) + class ClientStatus(models.Model): name = models.CharField(max_length=50) + class Client(models.Model): name = models.CharField(max_length=50) state = models.ForeignKey(State, null=True) status = models.ForeignKey(ClientStatus) + class SpecialClient(Client): value = models.IntegerField() # Some model inheritance exercises + + @python_2_unicode_compatible class Parent(models.Model): name = models.CharField(max_length=10) @@ -84,9 +102,11 @@ class Parent(models.Model): 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) @@ -96,6 +116,8 @@ class Item(models.Model): return self.name # Models for testing bug #19870. + + @python_2_unicode_compatible class Fowl(models.Model): name = models.CharField(max_length=10) @@ -103,12 +125,15 @@ class Fowl(models.Model): def __str__(self): return self.name + class Hen(Fowl): pass + class Chick(Fowl): mother = models.ForeignKey(Hen) + class Base(models.Model): name = models.CharField(max_length=10) lots_of_text = models.TextField() @@ -116,12 +141,15 @@ class Base(models.Model): class Meta: abstract = True + class A(Base): a_field = models.CharField(max_length=10) + class B(Base): b_field = models.CharField(max_length=10) + class C(Base): c_a = models.ForeignKey(A) c_b = models.ForeignKey(B) diff --git a/tests/serializers/tests.py b/tests/serializers/tests.py index a381396b40..b9e3bb543f 100644 --- a/tests/serializers/tests.py +++ b/tests/serializers/tests.py @@ -26,7 +26,7 @@ from .models import (Category, Author, Article, AuthorProfile, Actor, Movie, @override_settings( - SERIALIZATION_MODULES = { + SERIALIZATION_MODULES={ "json2": "django.core.serializers.json", } ) @@ -71,6 +71,7 @@ class SerializerRegistrationTests(TestCase): self.assertIn('python', all_formats) self.assertNotIn('python', public_formats) + class SerializersTestBase(object): @staticmethod def _comparison_value(value): @@ -241,9 +242,9 @@ class SerializersTestBase(object): # Regression for #12524 -- dates before 1000AD get prefixed # 0's on the year a = Article.objects.create( - author = self.jane, - headline = "Nobody remembers the early years", - pub_date = datetime(1, 2, 3, 4, 5, 6)) + author=self.jane, + headline="Nobody remembers the early years", + pub_date=datetime(1, 2, 3, 4, 5, 6)) serial_str = serializers.serialize(self.serializer_name, [a]) date_values = self._get_field_values(serial_str, "pub_date") @@ -338,6 +339,7 @@ class XmlSerializerTestCase(SerializersTestBase, TestCase): ret_list.append("".join(temp)) return ret_list + class XmlSerializerTransactionTestCase(SerializersTransactionTestBase, TransactionTestCase): serializer_name = "xml" fwd_ref_str = """ @@ -438,6 +440,8 @@ class JsonSerializerTransactionTestCase(SerializersTransactionTestBase, Transact YAML_IMPORT_ERROR_MESSAGE = r'No module named yaml' + + class YamlImportModuleMock(object): """Provides a wrapped import_module function to simulate yaml ImportError diff --git a/tests/serializers_regress/models.py b/tests/serializers_regress/models.py index 9403a2d534..2c4a43123e 100644 --- a/tests/serializers_regress/models.py +++ b/tests/serializers_regress/models.py @@ -13,74 +13,96 @@ from django.contrib.contenttypes.models import ContentType # The following classes are for testing basic data # marshalling, including NULL values, where allowed. + class BinaryData(models.Model): data = models.BinaryField(null=True) + class BooleanData(models.Model): data = models.BooleanField(default=False) + class CharData(models.Model): data = models.CharField(max_length=30, null=True) + class DateData(models.Model): data = models.DateField(null=True) + class DateTimeData(models.Model): data = models.DateTimeField(null=True) + class DecimalData(models.Model): data = models.DecimalField(null=True, decimal_places=3, max_digits=5) + class EmailData(models.Model): data = models.EmailField(null=True) + class FileData(models.Model): data = models.FileField(null=True, upload_to='/foo/bar') + class FilePathData(models.Model): data = models.FilePathField(null=True) + class FloatData(models.Model): data = models.FloatField(null=True) + class IntegerData(models.Model): data = models.IntegerField(null=True) + class BigIntegerData(models.Model): data = models.BigIntegerField(null=True) # class ImageData(models.Model): # data = models.ImageField(null=True) + class IPAddressData(models.Model): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") data = models.IPAddressField(null=True) + class GenericIPAddressData(models.Model): data = models.GenericIPAddressField(null=True) + class NullBooleanData(models.Model): data = models.NullBooleanField(null=True) + class PositiveIntegerData(models.Model): data = models.PositiveIntegerField(null=True) + class PositiveSmallIntegerData(models.Model): data = models.PositiveSmallIntegerField(null=True) + class SlugData(models.Model): data = models.SlugField(null=True) + class SmallData(models.Model): data = models.SmallIntegerField(null=True) + class TextData(models.Model): data = models.TextField(null=True) + class TimeData(models.Model): data = models.TimeField(null=True) + class Tag(models.Model): """A tag on an item.""" data = models.SlugField() @@ -92,6 +114,7 @@ class Tag(models.Model): class Meta: ordering = ["data"] + class GenericData(models.Model): data = models.CharField(max_length=30) @@ -101,6 +124,7 @@ class GenericData(models.Model): # of related objects; in particular, forward, backward, # and self references. + class Anchor(models.Model): """This is a model that can be used as something for other models to point at""" @@ -110,10 +134,12 @@ class Anchor(models.Model): class Meta: ordering = ('id',) + class NaturalKeyAnchorManager(models.Manager): def get_by_natural_key(self, data): return self.get(data=data) + class NaturalKeyAnchor(models.Model): objects = NaturalKeyAnchorManager() @@ -123,40 +149,51 @@ class NaturalKeyAnchor(models.Model): def natural_key(self): return (self.data,) + class UniqueAnchor(models.Model): """This is a model that can be used as something for other models to point at""" data = models.CharField(unique=True, max_length=30) + class FKData(models.Model): data = models.ForeignKey(Anchor, null=True) + class FKDataNaturalKey(models.Model): data = models.ForeignKey(NaturalKeyAnchor, null=True) + class M2MData(models.Model): data = models.ManyToManyField(Anchor, null=True) + class O2OData(models.Model): # One to one field can't be null here, since it is a PK. data = models.OneToOneField(Anchor, primary_key=True) + class FKSelfData(models.Model): data = models.ForeignKey('self', null=True) + class M2MSelfData(models.Model): data = models.ManyToManyField('self', null=True, symmetrical=False) + class FKDataToField(models.Model): data = models.ForeignKey(UniqueAnchor, null=True, to_field='data') + class FKDataToO2O(models.Model): data = models.ForeignKey(O2OData, null=True) + class M2MIntermediateData(models.Model): data = models.ManyToManyField(Anchor, null=True, through='Intermediate') + class Intermediate(models.Model): left = models.ForeignKey(M2MIntermediateData) right = models.ForeignKey(Anchor) @@ -169,9 +206,11 @@ class Intermediate(models.Model): # because they can't be used as a primary key on one # or all database backends. + class BooleanPKData(models.Model): data = models.BooleanField(primary_key=True, default=False) + class CharPKData(models.Model): data = models.CharField(max_length=30, primary_key=True) @@ -181,32 +220,39 @@ class CharPKData(models.Model): # class DateTimePKData(models.Model): # data = models.DateTimeField(primary_key=True) + class DecimalPKData(models.Model): data = models.DecimalField(primary_key=True, decimal_places=3, max_digits=5) + class EmailPKData(models.Model): data = models.EmailField(primary_key=True) # class FilePKData(models.Model): # data = models.FileField(primary_key=True, upload_to='/foo/bar') + class FilePathPKData(models.Model): data = models.FilePathField(primary_key=True) + class FloatPKData(models.Model): data = models.FloatField(primary_key=True) + class IntegerPKData(models.Model): data = models.IntegerField(primary_key=True) # class ImagePKData(models.Model): # data = models.ImageField(primary_key=True) + class IPAddressPKData(models.Model): with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") data = models.IPAddressField(primary_key=True) + class GenericIPAddressPKData(models.Model): data = models.GenericIPAddressField(primary_key=True) @@ -214,15 +260,19 @@ class GenericIPAddressPKData(models.Model): # class NullBooleanPKData(models.Model): # data = models.NullBooleanField(primary_key=True) + class PositiveIntegerPKData(models.Model): data = models.PositiveIntegerField(primary_key=True) + class PositiveSmallIntegerPKData(models.Model): data = models.PositiveSmallIntegerField(primary_key=True) + class SlugPKData(models.Model): data = models.SlugField(primary_key=True) + class SmallPKData(models.Model): data = models.SmallIntegerField(primary_key=True) @@ -232,6 +282,7 @@ class SmallPKData(models.Model): # class TimePKData(models.Model): # data = models.TimeField(primary_key=True) + class ComplexModel(models.Model): field1 = models.CharField(max_length=10) field2 = models.CharField(max_length=10) @@ -239,9 +290,12 @@ class ComplexModel(models.Model): # Tests for handling fields with pre_save functions, or # models with save functions that modify data + + class AutoNowDateTimeData(models.Model): data = models.DateTimeField(null=True, auto_now=True) + class ModifyingSaveData(models.Model): data = models.IntegerField(null=True) @@ -256,33 +310,42 @@ class ModifyingSaveData(models.Model): # Tests for serialization of models using inheritance. # Regression for #7202, #7350 + + class AbstractBaseModel(models.Model): parent_data = models.IntegerField() class Meta: abstract = True + class InheritAbstractModel(AbstractBaseModel): child_data = models.IntegerField() + class BaseModel(models.Model): parent_data = models.IntegerField() + class InheritBaseModel(BaseModel): child_data = models.IntegerField() + class ExplicitInheritBaseModel(BaseModel): parent = models.OneToOneField(BaseModel) child_data = models.IntegerField() + class ProxyBaseModel(BaseModel): class Meta: proxy = True + class ProxyProxyBaseModel(ProxyBaseModel): class Meta: proxy = True + class LengthModel(models.Model): data = models.IntegerField() diff --git a/tests/serializers_regress/tests.py b/tests/serializers_regress/tests.py index a56abe0b6b..c8f29f471e 100644 --- a/tests/serializers_regress/tests.py +++ b/tests/serializers_regress/tests.py @@ -48,12 +48,15 @@ from .models import (BinaryData, BooleanData, CharData, DateData, DateTimeData, # The save method is a raw base model save, to make # sure that the data in the database matches the # exact test case. + + def data_create(pk, klass, data): instance = klass(id=pk) instance.data = data models.Model.save_base(instance, raw=True) return [instance] + def generic_create(pk, klass, data): instance = klass(id=pk) instance.data = data[0] @@ -62,23 +65,27 @@ def generic_create(pk, klass, data): instance.tags.create(data=tag) return [instance] + def fk_create(pk, klass, data): instance = klass(id=pk) setattr(instance, 'data_id', data) models.Model.save_base(instance, raw=True) return [instance] + def m2m_create(pk, klass, data): instance = klass(id=pk) models.Model.save_base(instance, raw=True) instance.data = data return [instance] + def im2m_create(pk, klass, data): instance = klass(id=pk) models.Model.save_base(instance, raw=True) return [instance] + def im_create(pk, klass, data): instance = klass(id=pk) instance.right_id = data['right'] @@ -88,18 +95,21 @@ def im_create(pk, klass, data): models.Model.save_base(instance, raw=True) return [instance] + def o2o_create(pk, klass, data): instance = klass() instance.data_id = data models.Model.save_base(instance, raw=True) return [instance] + def pk_create(pk, klass, data): instance = klass() instance.data = data models.Model.save_base(instance, raw=True) return [instance] + def inherited_create(pk, klass, data): instance = klass(id=pk, **data) # This isn't a raw save because: @@ -115,6 +125,8 @@ def inherited_create(pk, klass, data): # A set of functions that can be used to compare # test data objects of various kinds + + def data_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) if klass == BinaryData and data is not None: @@ -129,23 +141,28 @@ def data_compare(testcase, pk, klass, data): pk, data, type(data), instance, type(instance.data)) ) + def generic_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data[0], instance.data) testcase.assertEqual(data[1:], [t.data for t in instance.tags.order_by('id')]) + def fk_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data, instance.data_id) + def m2m_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data, [obj.id for obj in instance.data.order_by('id')]) + def im2m_compare(testcase, pk, klass, data): klass.objects.get(id=pk) # actually nothing else to check, the instance just should exist + def im_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) testcase.assertEqual(data['left'], instance.left_id) @@ -155,14 +172,17 @@ def im_compare(testcase, pk, klass, data): else: testcase.assertEqual("doesn't matter", instance.extra) + def o2o_compare(testcase, pk, klass, data): instance = klass.objects.get(data=data) testcase.assertEqual(data, instance.data_id) + def pk_compare(testcase, pk, klass, data): instance = klass.objects.get(data=data) testcase.assertEqual(data, instance.data) + def inherited_compare(testcase, pk, klass, data): instance = klass.objects.get(id=pk) for key, value in data.items(): @@ -256,21 +276,21 @@ The end."""), (data_obj, 301, Anchor, "Anchor 2"), (data_obj, 302, UniqueAnchor, "UAnchor 1"), - (fk_obj, 400, FKData, 300), # Post reference - (fk_obj, 401, FKData, 500), # Pre reference - (fk_obj, 402, FKData, None), # Empty reference + (fk_obj, 400, FKData, 300), # Post reference + (fk_obj, 401, FKData, 500), # Pre reference + (fk_obj, 402, FKData, None), # Empty reference - (m2m_obj, 410, M2MData, []), # Empty set - (m2m_obj, 411, M2MData, [300, 301]), # Post reference - (m2m_obj, 412, M2MData, [500, 501]), # Pre reference - (m2m_obj, 413, M2MData, [300, 301, 500, 501]), # Pre and Post reference + (m2m_obj, 410, M2MData, []), # Empty set + (m2m_obj, 411, M2MData, [300, 301]), # Post reference + (m2m_obj, 412, M2MData, [500, 501]), # Pre reference + (m2m_obj, 413, M2MData, [300, 301, 500, 501]), # Pre and Post reference - (o2o_obj, None, O2OData, 300), # Post reference - (o2o_obj, None, O2OData, 500), # Pre reference + (o2o_obj, None, O2OData, 300), # Post reference + (o2o_obj, None, O2OData, 500), # Pre reference - (fk_obj, 430, FKSelfData, 431), # Pre reference - (fk_obj, 431, FKSelfData, 430), # Post reference - (fk_obj, 432, FKSelfData, None), # Empty reference + (fk_obj, 430, FKSelfData, 431), # Pre reference + (fk_obj, 431, FKSelfData, 430), # Post reference + (fk_obj, 432, FKSelfData, None), # Empty reference (m2m_obj, 440, M2MSelfData, []), (m2m_obj, 441, M2MSelfData, []), @@ -380,6 +400,8 @@ if connection.features.allows_primary_key_0: # Dynamically create serializer tests to ensure that all # registered serializers are automatically tested. + + class SerializerTests(TestCase): def test_get_unknown_serializer(self): """ @@ -496,6 +518,7 @@ def naturalKeySerializerTest(format, self): for klass, count in instance_count.items(): self.assertEqual(count, klass.objects.count()) + def fieldsTest(format, self): obj = ComplexModel(field1='first', field2='second', field3='third') obj.save_base(raw=True) @@ -509,6 +532,7 @@ def fieldsTest(format, self): self.assertEqual(result.object.field2, '') self.assertEqual(result.object.field3, 'third') + def streamTest(format, self): obj = ComplexModel(field1='first', field2='second', field3='third') obj.save_base(raw=True) diff --git a/tests/servers/tests.py b/tests/servers/tests.py index 77c2e39521..5ce86d3529 100644 --- a/tests/servers/tests.py +++ b/tests/servers/tests.py @@ -107,6 +107,7 @@ class LiveServerAddress(LiveServerBase): # test runner and the overridden setUpClass() method is executed. pass + class LiveServerViews(LiveServerBase): def test_404(self): """ diff --git a/tests/settings_tests/tests.py b/tests/settings_tests/tests.py index b1321d50f3..3597982457 100644 --- a/tests/settings_tests/tests.py +++ b/tests/settings_tests/tests.py @@ -76,6 +76,7 @@ class ClassDecoratedTestCase(ClassDecoratedTestCaseSuper): class ParentDecoratedTestCase(TestCase): pass + @override_settings(TEST='override-child') class ChildDecoratedTestCase(ParentDecoratedTestCase): def test_override_settings_inheritance(self): diff --git a/tests/signals/models.py b/tests/signals/models.py index e54a22fce9..765230a44e 100644 --- a/tests/signals/models.py +++ b/tests/signals/models.py @@ -15,6 +15,7 @@ class Person(models.Model): 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) diff --git a/tests/signals/tests.py b/tests/signals/tests.py index b7ee222c94..1eb2ffb4a3 100644 --- a/tests/signals/tests.py +++ b/tests/signals/tests.py @@ -18,6 +18,7 @@ class PostDeleteHandler(object): (instance, instance.id is None) ) + class MyReceiver(object): def __init__(self, param): self.param = param @@ -27,6 +28,7 @@ class MyReceiver(object): self._run = True signal.disconnect(receiver=self, sender=sender) + class SignalTests(TestCase): def test_basic(self): # Save up the number of connected signals so that we can check at the diff --git a/tests/signals_regress/models.py b/tests/signals_regress/models.py index 829314c06c..5c28b76669 100644 --- a/tests/signals_regress/models.py +++ b/tests/signals_regress/models.py @@ -9,6 +9,7 @@ class Author(models.Model): def __str__(self): return self.name + @python_2_unicode_compatible class Book(models.Model): name = models.CharField(max_length=20) diff --git a/tests/signing/tests.py b/tests/signing/tests.py index 3486cf0591..fc8390e9e8 100644 --- a/tests/signing/tests.py +++ b/tests/signing/tests.py @@ -105,6 +105,7 @@ class TestSigner(TestCase): self.assertRaises( signing.BadSignature, signing.loads, transform(encoded)) + class TestTimestampSigner(TestCase): def test_timestamp_signer(self): diff --git a/tests/sites_framework/models.py b/tests/sites_framework/models.py index 55c4f4992e..12b1d08dd8 100644 --- a/tests/sites_framework/models.py +++ b/tests/sites_framework/models.py @@ -3,6 +3,7 @@ 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) @@ -16,23 +17,28 @@ class AbstractArticle(models.Model): def __str__(self): return self.title + class SyndicatedArticle(AbstractArticle): sites = models.ManyToManyField(Site) + class ExclusiveArticle(AbstractArticle): site = models.ForeignKey(Site) + class CustomArticle(AbstractArticle): places_this_article_should_appear = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager("places_this_article_should_appear") + class InvalidArticle(AbstractArticle): site = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager("places_this_article_should_appear") + class ConfusedArticle(AbstractArticle): site = models.IntegerField() diff --git a/tests/staticfiles_tests/storage.py b/tests/staticfiles_tests/storage.py index 4d49e6fbb2..e47af18b22 100644 --- a/tests/staticfiles_tests/storage.py +++ b/tests/staticfiles_tests/storage.py @@ -2,6 +2,7 @@ from datetime import datetime from django.core.files import storage from django.contrib.staticfiles.storage import CachedStaticFilesStorage + class DummyStorage(storage.Storage): """ A storage class that does implement modified_time() but raises diff --git a/tests/str/models.py b/tests/str/models.py index 488012e861..e203de411e 100644 --- a/tests/str/models.py +++ b/tests/str/models.py @@ -27,6 +27,7 @@ class Article(models.Model): # in ASCII. return self.headline + @python_2_unicode_compatible class InternationalArticle(models.Model): headline = models.CharField(max_length=100) diff --git a/tests/string_lookup/models.py b/tests/string_lookup/models.py index 43ed90a462..4037c2950e 100644 --- a/tests/string_lookup/models.py +++ b/tests/string_lookup/models.py @@ -14,6 +14,7 @@ class Foo(models.Model): def __str__(self): return "Foo %s" % self.name + @python_2_unicode_compatible class Bar(models.Model): name = models.CharField(max_length=50) @@ -24,6 +25,7 @@ class Bar(models.Model): def __str__(self): return "Bar %s" % self.place.name + @python_2_unicode_compatible class Whiz(models.Model): name = models.CharField(max_length=50) @@ -31,6 +33,7 @@ class Whiz(models.Model): def __str__(self): return "Whiz %s" % self.name + @python_2_unicode_compatible class Child(models.Model): parent = models.OneToOneField('Base') @@ -39,6 +42,7 @@ class Child(models.Model): def __str__(self): return "Child %s" % self.name + @python_2_unicode_compatible class Base(models.Model): name = models.CharField(max_length=50) @@ -46,6 +50,7 @@ class Base(models.Model): def __str__(self): return "Base %s" % self.name + @python_2_unicode_compatible class Article(models.Model): name = models.CharField(max_length=50) diff --git a/tests/syndication/tests.py b/tests/syndication/tests.py index eeadf85600..a3b75369be 100644 --- a/tests/syndication/tests.py +++ b/tests/syndication/tests.py @@ -49,6 +49,7 @@ class FeedTestCase(TestCase): # Feed view ###################################### + class SyndicationFeedTest(FeedTestCase): """ Tests for the high-level syndication feed framework. diff --git a/tests/tablespaces/models.py b/tests/tablespaces/models.py index d5d179303f..ecd1944294 100644 --- a/tests/tablespaces/models.py +++ b/tests/tablespaces/models.py @@ -7,15 +7,18 @@ from django.db import models # "reference" models to avoid errors when other tests run 'migrate' # (proxy_models_inheritance does). + class ScientistRef(models.Model): name = models.CharField(max_length=50) + class ArticleRef(models.Model): title = models.CharField(max_length=50, unique=True) code = models.CharField(max_length=50, unique=True) authors = models.ManyToManyField(ScientistRef, related_name='articles_written_set') reviewers = models.ManyToManyField(ScientistRef, related_name='articles_reviewed_set') + class Scientist(models.Model): name = models.CharField(max_length=50) @@ -24,6 +27,7 @@ class Scientist(models.Model): db_tablespace = 'tbl_tbsp' managed = False + class Article(models.Model): title = models.CharField(max_length=50, unique=True) code = models.CharField(max_length=50, unique=True, db_tablespace='idx_tbsp') diff --git a/tests/tablespaces/tests.py b/tests/tablespaces/tests.py index f04bf29611..6a81643a0c 100644 --- a/tests/tablespaces/tests.py +++ b/tests/tablespaces/tests.py @@ -14,9 +14,11 @@ from .models import Article, ArticleRef, Authors, Reviewers, Scientist, Scientis # because they're evaluated when the model class is defined. As a consequence, # @override_settings doesn't work, and the tests depend + def sql_for_table(model): return '\n'.join(connection.creation.sql_create_model(model, no_style())[0]) + def sql_for_index(model): return '\n'.join(connection.creation.sql_indexes_for_model(model, no_style())) diff --git a/tests/template_tests/filters.py b/tests/template_tests/filters.py index 36a519fd90..0531fb09cf 100644 --- a/tests/template_tests/filters.py +++ b/tests/template_tests/filters.py @@ -16,11 +16,14 @@ from django.utils.safestring import mark_safe from django.utils import timezone # These two classes are used to test auto-escaping of __unicode__ output. + + @python_2_unicode_compatible class UnsafeClass: def __str__(self): return 'you & me' + @python_2_unicode_compatible class SafeClass: def __str__(self): @@ -29,6 +32,8 @@ class SafeClass: # RESULT SYNTAX -- # 'template_name': ('template contents', 'context dict', # 'expected string output' or Exception class) + + def get_filter_tests(): now = datetime.now() now_tz = timezone.make_aware(now, timezone.get_default_timezone()) @@ -38,9 +43,9 @@ def get_filter_tests(): # NOTE: \xa0 avoids wrapping between value and unit return { # Default compare with datetime.now() - 'filter-timesince01': ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds = -10)}, '1\xa0minute'), - 'filter-timesince02': ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes = 1)}, '1\xa0day'), - 'filter-timesince03': ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds = 10)}, '1\xa0hour, 25\xa0minutes'), + 'filter-timesince01': ('{{ a|timesince }}', {'a': datetime.now() + timedelta(minutes=-1, seconds=-10)}, '1\xa0minute'), + 'filter-timesince02': ('{{ a|timesince }}', {'a': datetime.now() - timedelta(days=1, minutes=1)}, '1\xa0day'), + 'filter-timesince03': ('{{ a|timesince }}', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds=10)}, '1\xa0hour, 25\xa0minutes'), # Compare to a given parameter 'filter-timesince04': ('{{ a|timesince:b }}', {'a': now - timedelta(days=2), 'b': now - timedelta(days=1)}, '1\xa0day'), @@ -71,7 +76,7 @@ def get_filter_tests(): # Default compare with datetime.now() 'filter-timeuntil01': ('{{ a|timeuntil }}', {'a': datetime.now() + timedelta(minutes=2, seconds=10)}, '2\xa0minutes'), 'filter-timeuntil02': ('{{ a|timeuntil }}', {'a': (datetime.now() + timedelta(days=1, seconds=10))}, '1\xa0day'), - 'filter-timeuntil03': ('{{ a|timeuntil }}', {'a': (datetime.now() + timedelta(hours=8, minutes=10, seconds = 10))}, '8\xa0hours, 10\xa0minutes'), + 'filter-timeuntil03': ('{{ a|timeuntil }}', {'a': (datetime.now() + timedelta(hours=8, minutes=10, seconds=10))}, '8\xa0hours, 10\xa0minutes'), # Compare to a given parameter 'filter-timeuntil04': ('{{ a|timeuntil:b }}', {'a': now - timedelta(days=1), 'b': now - timedelta(days=2)}, '1\xa0day'), diff --git a/tests/template_tests/templatetags/bad_tag.py b/tests/template_tests/templatetags/bad_tag.py index 3cceb31eb0..b806423df3 100644 --- a/tests/template_tests/templatetags/bad_tag.py +++ b/tests/template_tests/templatetags/bad_tag.py @@ -3,10 +3,12 @@ from django import template register = template.Library() + @register.tag def badtag(parser, token): raise RuntimeError("I am a bad tag") + @register.simple_tag def badsimpletag(): raise RuntimeError("I am a bad simpletag") diff --git a/tests/template_tests/templatetags/custom.py b/tests/template_tests/templatetags/custom.py index 9d328f8d54..9aa51931e0 100644 --- a/tests/template_tests/templatetags/custom.py +++ b/tests/template_tests/templatetags/custom.py @@ -7,11 +7,13 @@ from django.utils import six register = template.Library() + @register.filter @stringfilter def trim(value, num): return value[:num] + @register.filter def noop(value, param=None): """A noop filter that always return its first argument and does nothing with @@ -19,60 +21,70 @@ def noop(value, param=None): Useful for testing out whitespace in filter arguments (see #19882).""" return value + @register.simple_tag def no_params(): """Expected no_params __doc__""" return "no_params - Expected result" no_params.anything = "Expected no_params __dict__" + @register.simple_tag def one_param(arg): """Expected one_param __doc__""" return "one_param - Expected result: %s" % arg one_param.anything = "Expected one_param __dict__" + @register.simple_tag(takes_context=False) def explicit_no_context(arg): """Expected explicit_no_context __doc__""" return "explicit_no_context - Expected result: %s" % arg explicit_no_context.anything = "Expected explicit_no_context __dict__" + @register.simple_tag(takes_context=True) def no_params_with_context(context): """Expected no_params_with_context __doc__""" return "no_params_with_context - Expected result (context value: %s)" % context['value'] no_params_with_context.anything = "Expected no_params_with_context __dict__" + @register.simple_tag(takes_context=True) def params_and_context(context, arg): """Expected params_and_context __doc__""" return "params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) params_and_context.anything = "Expected params_and_context __dict__" + @register.simple_tag def simple_two_params(one, two): """Expected simple_two_params __doc__""" return "simple_two_params - Expected result: %s, %s" % (one, two) simple_two_params.anything = "Expected simple_two_params __dict__" + @register.simple_tag def simple_one_default(one, two='hi'): """Expected simple_one_default __doc__""" return "simple_one_default - Expected result: %s, %s" % (one, two) simple_one_default.anything = "Expected simple_one_default __dict__" + @register.simple_tag def simple_unlimited_args(one, two='hi', *args): """Expected simple_unlimited_args __doc__""" return "simple_unlimited_args - Expected result: %s" % (', '.join(six.text_type(arg) for arg in [one, two] + list(args))) simple_unlimited_args.anything = "Expected simple_unlimited_args __dict__" + @register.simple_tag def simple_only_unlimited_args(*args): """Expected simple_only_unlimited_args __doc__""" return "simple_only_unlimited_args - Expected result: %s" % ', '.join(six.text_type(arg) for arg in args) simple_only_unlimited_args.anything = "Expected simple_only_unlimited_args __dict__" + @register.simple_tag def simple_unlimited_args_kwargs(one, two='hi', *args, **kwargs): """Expected simple_unlimited_args_kwargs __doc__""" @@ -84,146 +96,171 @@ def simple_unlimited_args_kwargs(one, two='hi', *args, **kwargs): ) simple_unlimited_args_kwargs.anything = "Expected simple_unlimited_args_kwargs __dict__" + @register.simple_tag(takes_context=True) def simple_tag_without_context_parameter(arg): """Expected simple_tag_without_context_parameter __doc__""" return "Expected result" simple_tag_without_context_parameter.anything = "Expected simple_tag_without_context_parameter __dict__" + @register.simple_tag(takes_context=True) def current_app(context): return "%s" % context.current_app + @register.simple_tag(takes_context=True) def use_l10n(context): return "%s" % context.use_l10n + @register.simple_tag(name='minustwo') def minustwo_overridden_name(value): return value - 2 register.simple_tag(lambda x: x - 1, name='minusone') + @register.inclusion_tag('inclusion.html') def inclusion_no_params(): """Expected inclusion_no_params __doc__""" return {"result": "inclusion_no_params - Expected result"} inclusion_no_params.anything = "Expected inclusion_no_params __dict__" + @register.inclusion_tag(get_template('inclusion.html')) def inclusion_no_params_from_template(): """Expected inclusion_no_params_from_template __doc__""" return {"result": "inclusion_no_params_from_template - Expected result"} inclusion_no_params_from_template.anything = "Expected inclusion_no_params_from_template __dict__" + @register.inclusion_tag('inclusion.html') def inclusion_one_param(arg): """Expected inclusion_one_param __doc__""" return {"result": "inclusion_one_param - Expected result: %s" % arg} inclusion_one_param.anything = "Expected inclusion_one_param __dict__" + @register.inclusion_tag(get_template('inclusion.html')) def inclusion_one_param_from_template(arg): """Expected inclusion_one_param_from_template __doc__""" return {"result": "inclusion_one_param_from_template - Expected result: %s" % arg} inclusion_one_param_from_template.anything = "Expected inclusion_one_param_from_template __dict__" + @register.inclusion_tag('inclusion.html', takes_context=False) def inclusion_explicit_no_context(arg): """Expected inclusion_explicit_no_context __doc__""" return {"result": "inclusion_explicit_no_context - Expected result: %s" % arg} inclusion_explicit_no_context.anything = "Expected inclusion_explicit_no_context __dict__" + @register.inclusion_tag(get_template('inclusion.html'), takes_context=False) def inclusion_explicit_no_context_from_template(arg): """Expected inclusion_explicit_no_context_from_template __doc__""" return {"result": "inclusion_explicit_no_context_from_template - Expected result: %s" % arg} inclusion_explicit_no_context_from_template.anything = "Expected inclusion_explicit_no_context_from_template __dict__" + @register.inclusion_tag('inclusion.html', takes_context=True) def inclusion_no_params_with_context(context): """Expected inclusion_no_params_with_context __doc__""" return {"result": "inclusion_no_params_with_context - Expected result (context value: %s)" % context['value']} inclusion_no_params_with_context.anything = "Expected inclusion_no_params_with_context __dict__" + @register.inclusion_tag(get_template('inclusion.html'), takes_context=True) def inclusion_no_params_with_context_from_template(context): """Expected inclusion_no_params_with_context_from_template __doc__""" return {"result": "inclusion_no_params_with_context_from_template - Expected result (context value: %s)" % context['value']} inclusion_no_params_with_context_from_template.anything = "Expected inclusion_no_params_with_context_from_template __dict__" + @register.inclusion_tag('inclusion.html', takes_context=True) def inclusion_params_and_context(context, arg): """Expected inclusion_params_and_context __doc__""" return {"result": "inclusion_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg)} inclusion_params_and_context.anything = "Expected inclusion_params_and_context __dict__" + @register.inclusion_tag(get_template('inclusion.html'), takes_context=True) def inclusion_params_and_context_from_template(context, arg): """Expected inclusion_params_and_context_from_template __doc__""" return {"result": "inclusion_params_and_context_from_template - Expected result (context value: %s): %s" % (context['value'], arg)} inclusion_params_and_context_from_template.anything = "Expected inclusion_params_and_context_from_template __dict__" + @register.inclusion_tag('inclusion.html') def inclusion_two_params(one, two): """Expected inclusion_two_params __doc__""" return {"result": "inclusion_two_params - Expected result: %s, %s" % (one, two)} inclusion_two_params.anything = "Expected inclusion_two_params __dict__" + @register.inclusion_tag(get_template('inclusion.html')) def inclusion_two_params_from_template(one, two): """Expected inclusion_two_params_from_template __doc__""" return {"result": "inclusion_two_params_from_template - Expected result: %s, %s" % (one, two)} inclusion_two_params_from_template.anything = "Expected inclusion_two_params_from_template __dict__" + @register.inclusion_tag('inclusion.html') def inclusion_one_default(one, two='hi'): """Expected inclusion_one_default __doc__""" return {"result": "inclusion_one_default - Expected result: %s, %s" % (one, two)} inclusion_one_default.anything = "Expected inclusion_one_default __dict__" + @register.inclusion_tag(get_template('inclusion.html')) def inclusion_one_default_from_template(one, two='hi'): """Expected inclusion_one_default_from_template __doc__""" return {"result": "inclusion_one_default_from_template - Expected result: %s, %s" % (one, two)} inclusion_one_default_from_template.anything = "Expected inclusion_one_default_from_template __dict__" + @register.inclusion_tag('inclusion.html') def inclusion_unlimited_args(one, two='hi', *args): """Expected inclusion_unlimited_args __doc__""" return {"result": "inclusion_unlimited_args - Expected result: %s" % (', '.join(six.text_type(arg) for arg in [one, two] + list(args)))} inclusion_unlimited_args.anything = "Expected inclusion_unlimited_args __dict__" + @register.inclusion_tag(get_template('inclusion.html')) def inclusion_unlimited_args_from_template(one, two='hi', *args): """Expected inclusion_unlimited_args_from_template __doc__""" return {"result": "inclusion_unlimited_args_from_template - Expected result: %s" % (', '.join(six.text_type(arg) for arg in [one, two] + list(args)))} inclusion_unlimited_args_from_template.anything = "Expected inclusion_unlimited_args_from_template __dict__" + @register.inclusion_tag('inclusion.html') def inclusion_only_unlimited_args(*args): """Expected inclusion_only_unlimited_args __doc__""" return {"result": "inclusion_only_unlimited_args - Expected result: %s" % (', '.join(six.text_type(arg) for arg in args))} inclusion_only_unlimited_args.anything = "Expected inclusion_only_unlimited_args __dict__" + @register.inclusion_tag(get_template('inclusion.html')) def inclusion_only_unlimited_args_from_template(*args): """Expected inclusion_only_unlimited_args_from_template __doc__""" return {"result": "inclusion_only_unlimited_args_from_template - Expected result: %s" % (', '.join(six.text_type(arg) for arg in args))} inclusion_only_unlimited_args_from_template.anything = "Expected inclusion_only_unlimited_args_from_template __dict__" + @register.inclusion_tag('test_incl_tag_current_app.html', takes_context=True) def inclusion_tag_current_app(context): """Expected inclusion_tag_current_app __doc__""" return {} inclusion_tag_current_app.anything = "Expected inclusion_tag_current_app __dict__" + @register.inclusion_tag('test_incl_tag_use_l10n.html', takes_context=True) def inclusion_tag_use_l10n(context): """Expected inclusion_tag_use_l10n __doc__""" return {} inclusion_tag_use_l10n.anything = "Expected inclusion_tag_use_l10n __dict__" + @register.inclusion_tag('inclusion.html') def inclusion_unlimited_args_kwargs(one, two='hi', *args, **kwargs): """Expected inclusion_unlimited_args_kwargs __doc__""" @@ -235,66 +272,77 @@ def inclusion_unlimited_args_kwargs(one, two='hi', *args, **kwargs): )} inclusion_unlimited_args_kwargs.anything = "Expected inclusion_unlimited_args_kwargs __dict__" + @register.inclusion_tag('inclusion.html', takes_context=True) def inclusion_tag_without_context_parameter(arg): """Expected inclusion_tag_without_context_parameter __doc__""" return {} inclusion_tag_without_context_parameter.anything = "Expected inclusion_tag_without_context_parameter __dict__" + @register.assignment_tag def assignment_no_params(): """Expected assignment_no_params __doc__""" return "assignment_no_params - Expected result" assignment_no_params.anything = "Expected assignment_no_params __dict__" + @register.assignment_tag def assignment_one_param(arg): """Expected assignment_one_param __doc__""" return "assignment_one_param - Expected result: %s" % arg assignment_one_param.anything = "Expected assignment_one_param __dict__" + @register.assignment_tag(takes_context=False) def assignment_explicit_no_context(arg): """Expected assignment_explicit_no_context __doc__""" return "assignment_explicit_no_context - Expected result: %s" % arg assignment_explicit_no_context.anything = "Expected assignment_explicit_no_context __dict__" + @register.assignment_tag(takes_context=True) def assignment_no_params_with_context(context): """Expected assignment_no_params_with_context __doc__""" return "assignment_no_params_with_context - Expected result (context value: %s)" % context['value'] assignment_no_params_with_context.anything = "Expected assignment_no_params_with_context __dict__" + @register.assignment_tag(takes_context=True) def assignment_params_and_context(context, arg): """Expected assignment_params_and_context __doc__""" return "assignment_params_and_context - Expected result (context value: %s): %s" % (context['value'], arg) assignment_params_and_context.anything = "Expected assignment_params_and_context __dict__" + @register.assignment_tag def assignment_two_params(one, two): """Expected assignment_two_params __doc__""" return "assignment_two_params - Expected result: %s, %s" % (one, two) assignment_two_params.anything = "Expected assignment_two_params __dict__" + @register.assignment_tag def assignment_one_default(one, two='hi'): """Expected assignment_one_default __doc__""" return "assignment_one_default - Expected result: %s, %s" % (one, two) assignment_one_default.anything = "Expected assignment_one_default __dict__" + @register.assignment_tag def assignment_unlimited_args(one, two='hi', *args): """Expected assignment_unlimited_args __doc__""" return "assignment_unlimited_args - Expected result: %s" % (', '.join(six.text_type(arg) for arg in [one, two] + list(args))) assignment_unlimited_args.anything = "Expected assignment_unlimited_args __dict__" + @register.assignment_tag def assignment_only_unlimited_args(*args): """Expected assignment_only_unlimited_args __doc__""" return "assignment_only_unlimited_args - Expected result: %s" % ', '.join(six.text_type(arg) for arg in args) assignment_only_unlimited_args.anything = "Expected assignment_only_unlimited_args __dict__" + @register.assignment_tag def assignment_unlimited_args_kwargs(one, two='hi', *args, **kwargs): """Expected assignment_unlimited_args_kwargs __doc__""" @@ -306,6 +354,7 @@ def assignment_unlimited_args_kwargs(one, two='hi', *args, **kwargs): ) assignment_unlimited_args_kwargs.anything = "Expected assignment_unlimited_args_kwargs __dict__" + @register.assignment_tag(takes_context=True) def assignment_tag_without_context_parameter(arg): """Expected assignment_tag_without_context_parameter __doc__""" diff --git a/tests/template_tests/templatetags/subpackage/echo.py b/tests/template_tests/templatetags/subpackage/echo.py index 0e4e862887..43cf107b3d 100644 --- a/tests/template_tests/templatetags/subpackage/echo.py +++ b/tests/template_tests/templatetags/subpackage/echo.py @@ -2,6 +2,7 @@ from django import template register = template.Library() + @register.simple_tag def echo2(arg): return arg diff --git a/tests/template_tests/test_callables.py b/tests/template_tests/test_callables.py index d4e551e1df..9a47a5b96f 100644 --- a/tests/template_tests/test_callables.py +++ b/tests/template_tests/test_callables.py @@ -4,6 +4,7 @@ from unittest import TestCase from django import template + class CallableVariablesTests(TestCase): def test_callable(self): diff --git a/tests/template_tests/test_loaders.py b/tests/template_tests/test_loaders.py index 1eef3942a6..57da0af56e 100644 --- a/tests/template_tests/test_loaders.py +++ b/tests/template_tests/test_loaders.py @@ -34,6 +34,7 @@ from django.utils.six import StringIO class MockLoader(object): pass + def create_egg(name, resources): """ Creates a mock egg with a list of resources. @@ -105,7 +106,7 @@ class EggLoaderTest(TestCase): @override_settings( - TEMPLATE_LOADERS = ( + TEMPLATE_LOADERS=( ('django.template.loaders.cached.Loader', ( 'django.template.loaders.filesystem.Loader', )), @@ -139,7 +140,7 @@ class CachedLoader(TestCase): @override_settings( - TEMPLATE_DIRS = ( + TEMPLATE_DIRS=( os.path.join(os.path.dirname(upath(__file__)), 'templates'), ) ) @@ -160,13 +161,13 @@ class RenderToStringTest(TestCase): def test_empty_list(self): six.assertRaisesRegex(self, TemplateDoesNotExist, - 'No template names provided$', - loader.render_to_string, []) + 'No template names provided$', + loader.render_to_string, []) def test_select_templates_from_empty_list(self): six.assertRaisesRegex(self, TemplateDoesNotExist, - 'No template names provided$', - loader.select_template, []) + 'No template names provided$', + loader.select_template, []) class TemplateDirsOverrideTest(unittest.TestCase): diff --git a/tests/template_tests/test_nodelist.py b/tests/template_tests/test_nodelist.py index 755c0c3bea..12abd20331 100644 --- a/tests/template_tests/test_nodelist.py +++ b/tests/template_tests/test_nodelist.py @@ -4,6 +4,7 @@ from django.template import VariableNode, Context from django.template.loader import get_template_from_string from django.test.utils import override_settings + class NodelistTest(TestCase): def test_for(self): @@ -36,7 +37,7 @@ class ErrorIndexTest(TestCase): Checks whether index of error is calculated correctly in template debugger in for loops. Refs ticket #5831 """ - @override_settings(DEBUG=True, TEMPLATE_DEBUG = True) + @override_settings(DEBUG=True, TEMPLATE_DEBUG=True) def test_correct_exception_index(self): tests = [ ('{% load bad_tag %}{% for i in range %}{% badsimpletag %}{% endfor %}', (38, 56)), diff --git a/tests/template_tests/test_response.py b/tests/template_tests/test_response.py index dc8ffd1a0a..f5f6158b2f 100644 --- a/tests/template_tests/test_response.py +++ b/tests/template_tests/test_response.py @@ -13,6 +13,7 @@ from django.template.response import (TemplateResponse, SimpleTemplateResponse, from django.test.utils import override_settings from django.utils._os import upath + def test_processor(request): return {'processors': 'yes'} test_processor_name = 'template_tests.test_response.test_processor' @@ -119,7 +120,7 @@ class SimpleTemplateResponseTest(TestCase): self.assertEqual(response.content, b'bar') def test_kwargs(self): - response = self._response(content_type = 'application/json', status=504) + response = self._response(content_type='application/json', status=504) self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.status_code, 504) @@ -233,7 +234,7 @@ class TemplateResponseTest(TestCase): self.assertEqual(response.content, b'bar') def test_kwargs(self): - response = self._response(content_type = 'application/json', + response = self._response(content_type='application/json', status=504) self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.status_code, 504) diff --git a/tests/template_tests/test_smartif.py b/tests/template_tests/test_smartif.py index d23a023708..9115acef4e 100644 --- a/tests/template_tests/test_smartif.py +++ b/tests/template_tests/test_smartif.py @@ -2,6 +2,7 @@ import unittest from django.template.smartif import IfParser + class SmartIfTests(unittest.TestCase): def assertCalcEqual(self, expected, tokens): diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index a39f952e4c..9ee845ec51 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -45,6 +45,7 @@ from . import filters register = template.Library() + class EchoNode(template.Node): def __init__(self, contents): self.contents = contents @@ -52,9 +53,11 @@ class EchoNode(template.Node): def render(self, context): return " ".join(self.contents) + def do_echo(parser, token): return EchoNode(token.contents.split()[1:]) + def do_upper(value): return value.upper() @@ -68,18 +71,23 @@ template.libraries['testtags'] = register # Helper objects for template tests # ##################################### + class SomeException(Exception): silent_variable_failure = True + class SomeOtherException(Exception): pass + class ContextStackException(Exception): pass + class ShouldNotExecuteException(Exception): pass + class SomeClass: def __init__(self): self.otherclass = OtherClass() @@ -114,10 +122,12 @@ class SomeClass: raise SomeOtherException noisy_fail_attribute = property(noisy_fail_attribute) + class OtherClass: def method(self): return "OtherClass.method" + class TestObj(object): def is_true(self): return True @@ -128,15 +138,18 @@ class TestObj(object): def is_bad(self): raise ShouldNotExecuteException() + class SilentGetItemClass(object): def __getitem__(self, key): raise SomeException + class SilentAttrClass(object): def b(self): raise SomeException b = property(b) + @python_2_unicode_compatible class UTF8Class: "Class whose __str__ returns non-ASCII data on Python 2" @@ -643,7 +656,7 @@ class TemplateTests(TransRealMixin, TestCase): settings.ALLOWED_INCLUDE_ROOTS = old_allowed_include_roots self.assertEqual(failures, [], "Tests failed:\n%s\n%s" % - ('-'*70, ("\n%s\n" % ('-'*70)).join(failures))) + ('-' * 70, ("\n%s\n" % ('-' * 70)).join(failures))) def render(self, test_template, vals): context = template.Context(vals[1]) @@ -1538,10 +1551,10 @@ class TemplateTests(TransRealMixin, TestCase): '{% endfor %},' '{% endfor %}', {'data': [{'foo': 'c', 'bar': 1}, - {'foo': 'd', 'bar': 1}, - {'foo': 'a', 'bar': 2}, - {'foo': 'b', 'bar': 2}, - {'foo': 'x', 'bar': 3}]}, + {'foo': 'd', 'bar': 1}, + {'foo': 'a', 'bar': 2}, + {'foo': 'b', 'bar': 2}, + {'foo': 'x', 'bar': 3}]}, '1:cd,2:ab,3:x,'), # Test for silent failure when target variable isn't found @@ -1582,13 +1595,13 @@ class TemplateTests(TransRealMixin, TestCase): # Test syntax 'regroup05': ('{% regroup data by bar as %}', {}, - template.TemplateSyntaxError), + template.TemplateSyntaxError), 'regroup06': ('{% regroup data by bar thisaintright grouped %}', {}, - template.TemplateSyntaxError), + template.TemplateSyntaxError), 'regroup07': ('{% regroup data thisaintright bar as grouped %}', {}, - template.TemplateSyntaxError), + template.TemplateSyntaxError), 'regroup08': ('{% regroup data by bar as grouped toomanyargs %}', {}, - template.TemplateSyntaxError), + template.TemplateSyntaxError), ### SSI TAG ######################################################## diff --git a/tests/template_tests/views.py b/tests/template_tests/views.py index ed15893239..d031bdc2b6 100644 --- a/tests/template_tests/views.py +++ b/tests/template_tests/views.py @@ -6,17 +6,22 @@ from django.template.response import TemplateResponse def index(request): pass + def client(request, id): pass + def client_action(request, id, action): pass + def client2(request, tag): pass + def template_response_view(request): return TemplateResponse(request, 'response.html', {}) + def snark(request): return HttpResponse('Found him!') diff --git a/tests/test_client/tests.py b/tests/test_client/tests.py index cd59f50213..75801427d3 100644 --- a/tests/test_client/tests.py +++ b/tests/test_client/tests.py @@ -28,6 +28,7 @@ from django.test.utils import override_settings from .views import get_view + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class ClientTest(TestCase): fixtures = ['testdata.json'] @@ -479,7 +480,7 @@ class ClientTest(TestCase): @override_settings( - MIDDLEWARE_CLASSES = ('django.middleware.csrf.CsrfViewMiddleware',) + MIDDLEWARE_CLASSES=('django.middleware.csrf.CsrfViewMiddleware',) ) class CSRFEnabledClientTests(TestCase): def test_csrf_enabled_client(self): @@ -498,6 +499,7 @@ class CSRFEnabledClientTests(TestCase): class CustomTestClient(Client): i_am_customized = "Yes" + class CustomTestClientTest(TestCase): client_class = CustomTestClient diff --git a/tests/test_client/views.py b/tests/test_client/views.py index 85aefd47bb..53d008d5b1 100644 --- a/tests/test_client/views.py +++ b/tests/test_client/views.py @@ -11,6 +11,7 @@ from django.template import Context, Template from django.utils.decorators import method_decorator from django.utils.six.moves.urllib.parse import urlencode + def get_view(request): "A simple view that expects a GET request, and returns a rendered template" t = Template('This is a test. {{ var }} is the value.', name='GET Template') @@ -18,6 +19,7 @@ def get_view(request): return HttpResponse(t.render(c)) + def post_view(request): """A view that expects a POST, and returns a different template depending on whether any POST data is available @@ -35,12 +37,14 @@ def post_view(request): return HttpResponse(t.render(c)) + def view_with_header(request): "A view that has a custom header" response = HttpResponse() response['X-DJANGO-TEST'] = 'Slartibartfast' return response + def raw_post_view(request): """A view which expects raw XML to be posted and returns content extracted from the XML""" @@ -56,6 +60,7 @@ def raw_post_view(request): return HttpResponse(t.render(c)) + def redirect_view(request): "A view that redirects all requests to the GET view" if request.GET: @@ -64,6 +69,7 @@ def redirect_view(request): query = '' return HttpResponseRedirect('/test_client/get_view/' + query) + def view_with_secure(request): "A view that indicates if the request was secure" response = HttpResponse() @@ -71,10 +77,12 @@ def view_with_secure(request): response.test_server_port = request.META.get('SERVER_PORT', 80) return response + def double_redirect_view(request): "A view that redirects all requests to a redirection view" return HttpResponseRedirect('/test_client/permanent_redirect_view/') + def bad_view(request): "A view that returns a 404 with some error content" return HttpResponseNotFound('Not found!. This page contains some MAGIC content') @@ -87,6 +95,7 @@ TestChoices = ( ('e', 'Fifth Choice') ) + class TestForm(Form): text = fields.CharField() email = fields.EmailField() @@ -100,6 +109,7 @@ class TestForm(Form): raise ValidationError("Non-field error.") return cleaned_data + def form_view(request): "A view that tests a simple form" if request.method == 'POST': @@ -117,6 +127,7 @@ def form_view(request): return HttpResponse(t.render(c)) + def form_view_with_template(request): "A view that tests a simple form" if request.method == 'POST': @@ -135,6 +146,7 @@ def form_view_with_template(request): } ) + class BaseTestFormSet(BaseFormSet): def clean(self): """Checks that no two email addresses are the same.""" @@ -154,6 +166,7 @@ class BaseTestFormSet(BaseFormSet): TestFormSet = formset_factory(TestForm, BaseTestFormSet) + def formset_view(request): "A view that tests a simple formset" if request.method == 'POST': @@ -172,6 +185,7 @@ def formset_view(request): c = Context({'my_formset': formset}) return HttpResponse(t.render(c)) + def login_protected_view(request): "A simple view that is login protected." t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template') @@ -180,6 +194,7 @@ def login_protected_view(request): return HttpResponse(t.render(c)) login_protected_view = login_required(login_protected_view) + def login_protected_view_changed_redirect(request): "A simple view that is login protected with a custom redirect field set" t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template') @@ -188,6 +203,7 @@ def login_protected_view_changed_redirect(request): return HttpResponse(t.render(c)) login_protected_view_changed_redirect = login_required(redirect_field_name="redirect_to")(login_protected_view_changed_redirect) + def _permission_protected_view(request): "A simple view that is permission protected." t = Template('This is a permission protected test. ' @@ -199,6 +215,7 @@ def _permission_protected_view(request): permission_protected_view = permission_required('permission_not_granted')(_permission_protected_view) permission_protected_view_exception = permission_required('permission_not_granted', raise_exception=True)(_permission_protected_view) + class _ViewManager(object): @method_decorator(login_required) def login_protected_view(self, request): @@ -221,6 +238,7 @@ _view_manager = _ViewManager() login_protected_method_view = _view_manager.login_protected_view permission_protected_method_view = _view_manager.permission_protected_view + def session_view(request): "A view that modifies the session" request.session['tobacconist'] = 'hovercraft' @@ -230,10 +248,12 @@ def session_view(request): c = Context() return HttpResponse(t.render(c)) + def broken_view(request): """A view which just raises an exception, simulating a broken view.""" raise KeyError("Oops! Looks like you wrote some bad code.") + def mail_sending_view(request): mail.EmailMessage( "Test message", @@ -242,6 +262,7 @@ def mail_sending_view(request): ['first@example.com', 'second@example.com']).send() return HttpResponse("Mail sent") + def mass_mail_sending_view(request): m1 = mail.EmailMessage( 'First Test message', @@ -259,5 +280,6 @@ def mass_mail_sending_view(request): return HttpResponse("Mail sent") + def django_project_redirect(request): return HttpResponseRedirect('https://www.djangoproject.com/') diff --git a/tests/test_client_regress/session.py b/tests/test_client_regress/session.py index 665729cf00..47c192718b 100644 --- a/tests/test_client_regress/session.py +++ b/tests/test_client_regress/session.py @@ -1,5 +1,6 @@ from django.contrib.sessions.backends.base import SessionBase + class SessionStore(SessionBase): """ A simple cookie-based session storage implementation. diff --git a/tests/test_client_regress/tests.py b/tests/test_client_regress/tests.py index 8a371cb52b..83d6a0097e 100644 --- a/tests/test_client_regress/tests.py +++ b/tests/test_client_regress/tests.py @@ -23,6 +23,7 @@ from django.contrib.auth.models import User from .models import CustomUser from .views import CustomTestException + @override_settings( TEMPLATE_DIRS=(os.path.join(os.path.dirname(upath(__file__)), 'templates'),) ) @@ -185,6 +186,7 @@ class AssertContainsTests(TestCase): response = HttpResponse('Hello') self.assertNotContains(response, 'Bye') + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class AssertTemplateUsedTests(TestCase): fixtures = ['testdata.json'] @@ -256,6 +258,7 @@ class AssertTemplateUsedTests(TestCase): except AssertionError as e: self.assertIn("Template 'Valid POST Template' was not a template used to render the response. Actual template(s) used: form_view.html, base.html", str(e)) + class AssertRedirectsTests(TestCase): def test_redirect_page(self): "An assertion is raised if the original page couldn't be retrieved as expected" @@ -545,6 +548,7 @@ class AssertFormErrorTests(TestCase): except AssertionError as e: self.assertIn("abc: The form 'form' in context 0 does not contain the non-field error 'Some error.' (actual errors: )", str(e)) + class AssertFormsetErrorTests(TestCase): msg_prefixes = [("", {}), ("abc: ", {"msg_prefix": "abc"})] @@ -737,6 +741,7 @@ class AssertFormsetErrorTests(TestCase): 'addresses.', **kwargs) + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class LoginTests(TestCase): fixtures = ['testdata'] @@ -801,6 +806,7 @@ class URLEscapingTests(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'Hi, Arthur') + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class ExceptionTests(TestCase): fixtures = ['testdata.json'] @@ -846,6 +852,7 @@ class TemplateExceptionTests(TestCase): except TemplateSyntaxError: pass + # We need two different tests to check URLconf substitution - one to check # it was changed, and another one (without self.urls) to check it was reverted on # teardown. This pair of tests relies upon the alphabetical ordering of test execution. @@ -857,6 +864,7 @@ class UrlconfSubstitutionTests(TestCase): url = reverse('arg_view', args=['somename']) self.assertEqual(url, '/arg_view/somename/') + # This test needs to run *after* UrlconfSubstitutionTests; the zz prefix in the # name is to ensure alphabetical ordering. class zzUrlconfSubstitutionTests(TestCase): @@ -865,6 +873,7 @@ class zzUrlconfSubstitutionTests(TestCase): url = reverse('arg_view', args=['somename']) self.assertEqual(url, '/test_client_regress/arg_view/somename/') + @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',)) class ContextTests(TestCase): fixtures = ['testdata'] @@ -1114,6 +1123,7 @@ class RequestMethodStringDataTests(TestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.content, b'request method: PATCH') + class QueryStringTests(TestCase): def test_get_like_requests(self): # See: https://code.djangoproject.com/ticket/10571. @@ -1166,6 +1176,7 @@ class QueryStringTests(TestCase): self.assertEqual(response.context['request-foo'], 'whiz') self.assertEqual(response.context['request-bar'], 'bang') + class UnicodePayloadTests(TestCase): def test_simple_unicode_payload(self): "A simple ASCII-only unicode JSON document can be POSTed" @@ -1199,6 +1210,7 @@ class UnicodePayloadTests(TestCase): content_type="application/json; charset=koi8-r") self.assertEqual(response.content, json.encode('koi8-r')) + class DummyFile(object): def __init__(self, filename): self.name = filename @@ -1206,6 +1218,7 @@ class DummyFile(object): def read(self): return b'TEST_FILE_CONTENT' + class UploadedFileEncodingTest(TestCase): def test_file_encoding(self): encoded_file = encode_file('TEST_BOUNDARY', 'TEST_KEY', DummyFile('test_name.bin')) @@ -1226,6 +1239,7 @@ class UploadedFileEncodingTest(TestCase): self.assertEqual(b'Content-Type: application/octet-stream', encode_file('IGNORE', 'IGNORE', DummyFile("file.unknown"))[2]) + class RequestHeadersTest(TestCase): def test_client_headers(self): "A test client can receive custom headers" @@ -1268,17 +1282,19 @@ class ReadLimitedStreamTest(TestCase): """HttpRequest.read() on a test client PUT request with some payload should return that payload.""" payload = b'foobar' - self.assertEqual(self.client.put("/test_client_regress/read_all/", - data=payload, - content_type='text/plain').content, payload) + self.assertEqual(self.client.put( + "/test_client_regress/read_all/", + data=payload, + content_type='text/plain').content, payload) def test_read_numbytes_from_nonempty_request(self): """HttpRequest.read(LARGE_BUFFER) on a test client PUT request with some payload should return that payload.""" payload = b'foobar' - self.assertEqual(self.client.put("/test_client_regress/read_buffer/", - data=payload, - content_type='text/plain').content, payload) + self.assertEqual( + self.client.put("/test_client_regress/read_buffer/", + data=payload, + content_type='text/plain').content, payload) class RequestFactoryStateTest(TestCase): diff --git a/tests/test_client_regress/views.py b/tests/test_client_regress/views.py index d4bbd55b6f..2fbbce0c43 100644 --- a/tests/test_client_regress/views.py +++ b/tests/test_client_regress/views.py @@ -15,10 +15,12 @@ from django.test.utils import setup_test_environment class CustomTestException(Exception): pass + def no_template_view(request): "A simple view that expects a GET request, and returns a rendered template" return HttpResponse("No template used. Sample content: twice once twice. Content ends.") + def staff_only_view(request): "A view that can only be visited by staff. Non staff members get an exception" if request.user.is_staff: @@ -26,11 +28,13 @@ def staff_only_view(request): else: raise CustomTestException() + def get_view(request): "A simple login protected view" return HttpResponse("Hello world") get_view = login_required(get_view) + def request_data(request, template='base.html', data='sausage'): "A simple view that returns the request data in the context" @@ -50,6 +54,7 @@ def request_data(request, template='base.html', data='sausage'): 'data': data, }) + def view_with_argument(request, name): """A view that takes a string argument @@ -62,6 +67,7 @@ def view_with_argument(request, name): else: return HttpResponse('Howdy, %s' % name) + def nested_view(request): """ A view that uses test client to call another view. @@ -71,32 +77,39 @@ def nested_view(request): c.get("/test_client_regress/no_template_view") return render_to_response('base.html', {'nested': 'yes'}) + def login_protected_redirect_view(request): "A view that redirects all requests to the GET view" return HttpResponseRedirect('/test_client_regress/get_view/') login_protected_redirect_view = login_required(login_protected_redirect_view) + def set_session_view(request): "A view that sets a session variable" request.session['session_var'] = 'YES' return HttpResponse('set_session') + def check_session_view(request): "A view that reads a session variable" return HttpResponse(request.session.get('session_var', 'NO')) + def request_methods_view(request): "A view that responds with the request method" return HttpResponse('request method: %s' % request.method) + def return_unicode(request): return render_to_response('unicode.html') + def return_undecodable_binary(request): return HttpResponse( b'%PDF-1.4\r\n%\x93\x8c\x8b\x9e ReportLab Generated PDF document http://www.reportlab.com' ) + def return_json_file(request): "A view that parses and returns a JSON string as a file." match = CONTENT_TYPE_RE.match(request.META['CONTENT_TYPE']) @@ -113,22 +126,27 @@ def return_json_file(request): response['Content-Disposition'] = 'attachment; filename=testfile.json' return response + def check_headers(request): "A view that responds with value of the X-ARG-CHECK header" return HttpResponse('HTTP_X_ARG_CHECK: %s' % request.META.get('HTTP_X_ARG_CHECK', 'Undefined')) + def body(request): "A view that is requested with GET and accesses request.body. Refs #14753." return HttpResponse(request.body) + def read_all(request): "A view that is requested with accesses request.read()." return HttpResponse(request.read()) + def read_buffer(request): "A view that is requested with accesses request.read(LARGE_BUFFER)." return HttpResponse(request.read(99999)) + def request_context_view(request): # Special attribute that won't be present on a plain HttpRequest request.special_path = request.path diff --git a/tests/test_runner/models.py b/tests/test_runner/models.py index 9a072e627c..20cb384b03 100644 --- a/tests/test_runner/models.py +++ b/tests/test_runner/models.py @@ -1,5 +1,6 @@ from django.db import models + class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) diff --git a/tests/test_runner_deprecation_app/tests.py b/tests/test_runner_deprecation_app/tests.py index 24d716f2bb..0d947c35c2 100644 --- a/tests/test_runner_deprecation_app/tests.py +++ b/tests/test_runner_deprecation_app/tests.py @@ -4,6 +4,7 @@ from django.test import TestCase warnings.warn("module-level warning from deprecation_app", DeprecationWarning) + class DummyTest(TestCase): def test_warn(self): warnings.warn("warning from test", DeprecationWarning) diff --git a/tests/test_utils/models.py b/tests/test_utils/models.py index 85a1031c02..4c6ee0d19a 100644 --- a/tests/test_utils/models.py +++ b/tests/test_utils/models.py @@ -1,6 +1,7 @@ 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=100) diff --git a/tests/timezones/admin.py b/tests/timezones/admin.py index 81b49a4ab6..bd6c4e10c8 100644 --- a/tests/timezones/admin.py +++ b/tests/timezones/admin.py @@ -2,11 +2,13 @@ from django.contrib import admin from .models import Event, Timestamp + class EventAdmin(admin.ModelAdmin): list_display = ('dt',) admin.site.register(Event, EventAdmin) + class TimestampAdmin(admin.ModelAdmin): readonly_fields = ('created', 'updated') diff --git a/tests/timezones/forms.py b/tests/timezones/forms.py index d99c9b77d5..b92562396e 100644 --- a/tests/timezones/forms.py +++ b/tests/timezones/forms.py @@ -2,20 +2,25 @@ from django import forms from .models import Event + class EventForm(forms.Form): dt = forms.DateTimeField() + class EventSplitForm(forms.Form): dt = forms.SplitDateTimeField() + class EventLocalizedForm(forms.Form): dt = forms.DateTimeField(localize=True) + class EventModelForm(forms.ModelForm): class Meta: model = Event fields = '__all__' + class EventLocalizedModelForm(forms.ModelForm): class Meta: model = Event diff --git a/tests/timezones/models.py b/tests/timezones/models.py index c49e42f887..73b198f32c 100644 --- a/tests/timezones/models.py +++ b/tests/timezones/models.py @@ -1,21 +1,27 @@ from django.db import models + class Event(models.Model): dt = models.DateTimeField() + class MaybeEvent(models.Model): dt = models.DateTimeField(blank=True, null=True) + class Session(models.Model): name = models.CharField(max_length=20) + class SessionEvent(models.Model): dt = models.DateTimeField() session = models.ForeignKey(Session, related_name='events') + class Timestamp(models.Model): created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) + class AllDayEvent(models.Model): day = models.DateField() diff --git a/tests/transactions/tests.py b/tests/transactions/tests.py index 8c0c535d31..5c38bc8ef2 100644 --- a/tests/transactions/tests.py +++ b/tests/transactions/tests.py @@ -395,7 +395,8 @@ class TransactionTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): """ The default behavior is to autocommit after each save() action. """ - self.assertRaises(Exception, + self.assertRaises( + Exception, self.create_a_reporter_then_fail, "Alice", "Smith" ) @@ -411,7 +412,8 @@ class TransactionTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): autocomitted_create_then_fail = transaction.autocommit( self.create_a_reporter_then_fail ) - self.assertRaises(Exception, + self.assertRaises( + Exception, autocomitted_create_then_fail, "Alice", "Smith" ) @@ -426,7 +428,8 @@ class TransactionTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): autocomitted_create_then_fail = transaction.autocommit(using='default')( self.create_a_reporter_then_fail ) - self.assertRaises(Exception, + self.assertRaises( + Exception, autocomitted_create_then_fail, "Alice", "Smith" ) @@ -453,7 +456,8 @@ class TransactionTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): using_committed_on_success = transaction.commit_on_success(using='default')( self.create_a_reporter_then_fail ) - self.assertRaises(Exception, + self.assertRaises( + Exception, using_committed_on_success, "Dirk", "Gently" ) @@ -519,7 +523,8 @@ class TransactionTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): using_manually_managed_mistake = transaction.commit_manually(using='default')( self.manually_managed_mistake ) - self.assertRaises(transaction.TransactionManagementError, + self.assertRaises( + transaction.TransactionManagementError, using_manually_managed_mistake ) @@ -544,6 +549,7 @@ class TransactionRollbackTests(IgnoreDeprecationWarningsMixin, TransactionTestCa self.assertRaises(IntegrityError, execute_bad_sql) transaction.rollback() + class TransactionContextManagerTests(IgnoreDeprecationWarningsMixin, TransactionTestCase): available_apps = ['transactions'] diff --git a/tests/transactions_regress/models.py b/tests/transactions_regress/models.py index e09e81d93d..32234d9ba4 100644 --- a/tests/transactions_regress/models.py +++ b/tests/transactions_regress/models.py @@ -4,9 +4,11 @@ from django.db import models class Mod(models.Model): fld = models.IntegerField() + class SubMod(Mod): cnt = models.IntegerField(unique=True) + class M2mA(models.Model): others = models.ManyToManyField('M2mB') diff --git a/tests/transactions_regress/tests.py b/tests/transactions_regress/tests.py index a67d36e4eb..24bd860731 100644 --- a/tests/transactions_regress/tests.py +++ b/tests/transactions_regress/tests.py @@ -10,6 +10,7 @@ from django.test.utils import override_settings, IgnoreDeprecationWarningsMixin from .models import Mod, M2mA, M2mB, SubMod + class ModelInheritanceTests(TransactionTestCase): available_apps = ['transactions_regress'] @@ -31,6 +32,7 @@ class ModelInheritanceTests(TransactionTestCase): self.assertEqual(SubMod.objects.count(), 1) self.assertEqual(Mod.objects.count(), 1) + class TestTransactionClosing(IgnoreDeprecationWarningsMixin, TransactionTestCase): """ Tests to make sure that transactions are properly closed @@ -191,6 +193,7 @@ class TestTransactionClosing(IgnoreDeprecationWarningsMixin, TransactionTestCase """ self.test_failing_query_transaction_closed() + @skipIf(connection.vendor == 'sqlite' and connection.settings_dict['TEST_NAME'] in (None, '', ':memory:'), "Cannot establish two connections to an in-memory SQLite database.") @@ -372,7 +375,7 @@ class SavepointTest(IgnoreDeprecationWarningsMixin, TransactionTestCase): # _mysql_storage_engine issues a query and as such can't be applied in # a skipIf decorator since that would execute the query on module load. if (connection.vendor == 'mysql' and - connection.features._mysql_storage_engine == 'MyISAM'): + connection.features._mysql_storage_engine == 'MyISAM'): raise SkipTest("MyISAM MySQL storage engine doesn't support savepoints") @commit_manually diff --git a/tests/unmanaged_models/models.py b/tests/unmanaged_models/models.py index 0eef69977c..b0dd5c8719 100644 --- a/tests/unmanaged_models/models.py +++ b/tests/unmanaged_models/models.py @@ -8,6 +8,7 @@ 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) @@ -19,6 +20,7 @@ class A01(models.Model): def __str__(self): return self.f_a + @python_2_unicode_compatible class B01(models.Model): fk_a = models.ForeignKey(A01) @@ -33,6 +35,7 @@ class B01(models.Model): def __str__(self): return self.f_a + @python_2_unicode_compatible class C01(models.Model): mm_a = models.ManyToManyField(A01, db_table='d01') @@ -49,6 +52,7 @@ class C01(models.Model): # 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) @@ -60,6 +64,7 @@ class A02(models.Model): def __str__(self): return self.f_a + @python_2_unicode_compatible class B02(models.Model): class Meta: @@ -73,6 +78,7 @@ class B02(models.Model): 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 @@ -88,6 +94,7 @@ class C02(models.Model): def __str__(self): return self.f_a + class Intermediate(models.Model): a02 = models.ForeignKey(A02, db_column="a01_id") c02 = models.ForeignKey(C02, db_column="c01_id") @@ -96,7 +103,7 @@ class Intermediate(models.Model): db_table = 'd01' managed = False -# + # These next models test the creation (or not) of many to many join tables # between managed and unmanaged models. A join table between two unmanaged # models shouldn't be automatically created (see #10647). @@ -109,15 +116,18 @@ class Proxy1(models.Model): class Meta: db_table = "unmanaged_models_proxy1" + class Proxy2(models.Model): class Meta: db_table = "unmanaged_models_proxy2" + class Unmanaged1(models.Model): class Meta: managed = False db_table = "unmanaged_models_proxy1" + # Unmanged with an m2m to unmanaged: the intermediary table won't be created. class Unmanaged2(models.Model): mm = models.ManyToManyField(Unmanaged1) @@ -126,6 +136,7 @@ class Unmanaged2(models.Model): managed = False db_table = "unmanaged_models_proxy2" + # Here's an unmanaged model with an m2m to a managed one; the intermediary # table *will* be created (unless given a custom `through` as for C02 above). class Managed1(models.Model): diff --git a/tests/update/models.py b/tests/update/models.py index 08472d98b1..ebc9c3353c 100644 --- a/tests/update/models.py +++ b/tests/update/models.py @@ -17,6 +17,7 @@ class DataPoint(models.Model): def __str__(self): return six.text_type(self.name) + @python_2_unicode_compatible class RelatedPoint(models.Model): name = models.CharField(max_length=20) @@ -29,12 +30,15 @@ class RelatedPoint(models.Model): class A(models.Model): x = models.IntegerField(default=10) + class B(models.Model): a = models.ForeignKey(A) y = models.IntegerField(default=10) + class C(models.Model): y = models.IntegerField(default=10) + class D(C): a = models.ForeignKey(A) diff --git a/tests/update_only_fields/models.py b/tests/update_only_fields/models.py index bf5dd99166..ea319834c0 100644 --- a/tests/update_only_fields/models.py +++ b/tests/update_only_fields/models.py @@ -7,6 +7,7 @@ GENDER_CHOICES = ( ('F', 'Female'), ) + class Account(models.Model): num = models.IntegerField() diff --git a/tests/urlpatterns_reverse/erroneous_views_module.py b/tests/urlpatterns_reverse/erroneous_views_module.py index 52b9bc5163..3f7f5b2ac7 100644 --- a/tests/urlpatterns_reverse/erroneous_views_module.py +++ b/tests/urlpatterns_reverse/erroneous_views_module.py @@ -1,4 +1,5 @@ import non_existent # NOQA + def erroneous_view(request): pass diff --git a/tests/urlpatterns_reverse/middleware.py b/tests/urlpatterns_reverse/middleware.py index 0de692835f..cc80f14145 100644 --- a/tests/urlpatterns_reverse/middleware.py +++ b/tests/urlpatterns_reverse/middleware.py @@ -8,24 +8,29 @@ class ChangeURLconfMiddleware(object): def process_request(self, request): request.urlconf = urlconf_inner.__name__ + class NullChangeURLconfMiddleware(object): def process_request(self, request): request.urlconf = None + class ReverseInnerInResponseMiddleware(object): def process_response(self, *args, **kwargs): return HttpResponse(reverse('inner')) + class ReverseOuterInResponseMiddleware(object): def process_response(self, *args, **kwargs): return HttpResponse(reverse('outer')) + class ReverseInnerInStreaming(object): def process_view(self, *args, **kwargs): def stream(): yield reverse('inner') return StreamingHttpResponse(stream()) + class ReverseOuterInStreaming(object): def process_view(self, *args, **kwargs): def stream(): diff --git a/tests/urlpatterns_reverse/tests.py b/tests/urlpatterns_reverse/tests.py index 4134057b23..b2a6d83443 100644 --- a/tests/urlpatterns_reverse/tests.py +++ b/tests/urlpatterns_reverse/tests.py @@ -148,6 +148,7 @@ test_data = ( ('defaults', NoReverseMatch, [], {'arg2': 1}), ) + class NoURLPatternsTests(TestCase): urls = 'urlpatterns_reverse.no_urls' @@ -161,6 +162,7 @@ class NoURLPatternsTests(TestCase): "The included urlconf urlpatterns_reverse.no_urls " "doesn't have any patterns in it", getattr, resolver, 'url_patterns') + class URLPatternReverse(TestCase): urls = 'urlpatterns_reverse.urls' @@ -282,6 +284,7 @@ class ResolverTests(unittest.TestCase): else: self.assertEqual(t.name, e['name'], 'Wrong URL name. Expected "%s", got "%s".' % (e['name'], t.name)) + class ReverseLazyTest(TestCase): urls = 'urlpatterns_reverse.reverse_lazy_urls' @@ -297,6 +300,7 @@ class ReverseLazyTest(TestCase): response = self.client.get('/login_required_view/') self.assertEqual(response.status_code, 200) + class ReverseShortcutTests(TestCase): urls = 'urlpatterns_reverse.urls' @@ -454,7 +458,7 @@ class NamespaceTests(TestCase): self.assertEqual('/inc78/extra/foobar/', reverse('inc-ns5:inner-extra', args=['78', 'foobar'])) -@override_settings(ROOT_URLCONF = urlconf_outer.__name__) +@override_settings(ROOT_URLCONF=urlconf_outer.__name__) class RequestURLconfTests(TestCase): def test_urlconf(self): response = self.client.get('/test/me/') @@ -549,6 +553,7 @@ class RequestURLconfTests(TestCase): self.client.get('/second_test/') b''.join(self.client.get('/second_test/')) + class ErrorHandlerResolutionTests(TestCase): """Tests for handler400, handler404 and handler500""" @@ -573,6 +578,7 @@ class ErrorHandlerResolutionTests(TestCase): self.assertEqual(self.callable_resolver.resolve404(), handler) self.assertEqual(self.callable_resolver.resolve500(), handler) + class DefaultErrorHandlerTests(TestCase): urls = 'urlpatterns_reverse.urls_without_full_import' @@ -589,6 +595,7 @@ class DefaultErrorHandlerTests(TestCase): except AttributeError: self.fail("Shouldn't get an AttributeError due to undefined 500 handler") + class NoRootUrlConfTests(TestCase): """Tests for handler404 and handler500 if urlconf is None""" urls = None @@ -596,6 +603,7 @@ class NoRootUrlConfTests(TestCase): def test_no_handler_exception(self): self.assertRaises(ImproperlyConfigured, self.client.get, '/test/me/') + class ResolverMatchTests(TestCase): urls = 'urlpatterns_reverse.namespace_urls' @@ -631,6 +639,7 @@ class ResolverMatchTests(TestCase): request = HttpRequest() self.assertIsNone(request.resolver_match) + class ErroneousViewTests(TestCase): urls = 'urlpatterns_reverse.erroneous_urls' @@ -650,6 +659,7 @@ 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 diff --git a/tests/urlpatterns_reverse/urlconf_inner.py b/tests/urlpatterns_reverse/urlconf_inner.py index 6d9d2346f7..6d5cd27b35 100644 --- a/tests/urlpatterns_reverse/urlconf_inner.py +++ b/tests/urlpatterns_reverse/urlconf_inner.py @@ -2,6 +2,7 @@ from django.conf.urls import patterns, url from django.template import Template, Context from django.http import HttpResponse + def inner_view(request): content = Template('{% url "outer" as outer_url %}outer:{{ outer_url }},' '{% url "inner" as inner_url %}inner:{{ inner_url }}').render(Context()) diff --git a/tests/urlpatterns_reverse/views.py b/tests/urlpatterns_reverse/views.py index 6bb8849472..f23b4bf257 100644 --- a/tests/urlpatterns_reverse/views.py +++ b/tests/urlpatterns_reverse/views.py @@ -4,21 +4,27 @@ from django.core.urlresolvers import reverse_lazy from django.contrib.auth.decorators import user_passes_test + def empty_view(request, *args, **kwargs): return HttpResponse('') + def kwargs_view(request, arg1=1, arg2=2): return HttpResponse('') + def absolute_kwargs_view(request, arg1=1, arg2=2): return HttpResponse('') + def defaults_view(request, arg1, arg2): pass + def erroneous_view(request): import non_existent # NOQA + def pass_resolver_match_view(request, *args, **kwargs): response = HttpResponse('') response.resolver_match = request.resolver_match @@ -26,18 +32,22 @@ def pass_resolver_match_view(request, *args, **kwargs): uncallable = "Can I be a view? Pleeeease?" + class ViewClass(object): def __call__(self, request, *args, **kwargs): return HttpResponse('') view_class_instance = ViewClass() + class LazyRedirectView(RedirectView): url = reverse_lazy('named-lazy-url-redirected-to') + @user_passes_test(lambda u: u.is_authenticated(), login_url=reverse_lazy('some-login-page')) def login_required_view(request): return HttpResponse('Hello you') + def bad_view(request, *args, **kwargs): raise ValueError("I don't think I'm getting good value for this view") diff --git a/tests/user_commands/management/commands/leave_locale_alone_false.py b/tests/user_commands/management/commands/leave_locale_alone_false.py index 8ebb607d5a..e03ad1cab0 100644 --- a/tests/user_commands/management/commands/leave_locale_alone_false.py +++ b/tests/user_commands/management/commands/leave_locale_alone_false.py @@ -1,6 +1,7 @@ from django.core.management.base import BaseCommand from django.utils import translation + class Command(BaseCommand): can_import_settings = True diff --git a/tests/user_commands/management/commands/leave_locale_alone_true.py b/tests/user_commands/management/commands/leave_locale_alone_true.py index e0f923591e..9861221e39 100644 --- a/tests/user_commands/management/commands/leave_locale_alone_true.py +++ b/tests/user_commands/management/commands/leave_locale_alone_true.py @@ -1,6 +1,7 @@ from django.core.management.base import BaseCommand from django.utils import translation + class Command(BaseCommand): can_import_settings = True diff --git a/tests/utils_tests/test_baseconv.py b/tests/utils_tests/test_baseconv.py index d49dde1092..0af1c4e3f0 100644 --- a/tests/utils_tests/test_baseconv.py +++ b/tests/utils_tests/test_baseconv.py @@ -3,6 +3,7 @@ from unittest import TestCase from django.utils.baseconv import base2, base16, base36, base56, base62, base64, BaseConverter from django.utils.six.moves import xrange + class TestBaseConv(TestCase): def test_baseconv(self): diff --git a/tests/utils_tests/test_checksums.py b/tests/utils_tests/test_checksums.py index cee6dca2a8..9c9f244b6c 100644 --- a/tests/utils_tests/test_checksums.py +++ b/tests/utils_tests/test_checksums.py @@ -2,6 +2,7 @@ import unittest from django.utils import checksums + class TestUtilsChecksums(unittest.TestCase): def check_output(self, function, value, output=None): diff --git a/tests/utils_tests/test_datetime_safe.py b/tests/utils_tests/test_datetime_safe.py index c9c34dbf40..3a8d31d6d8 100644 --- a/tests/utils_tests/test_datetime_safe.py +++ b/tests/utils_tests/test_datetime_safe.py @@ -3,6 +3,7 @@ import unittest from datetime import date as original_date, datetime as original_datetime from django.utils.datetime_safe import date, datetime + class DatetimeTests(unittest.TestCase): def setUp(self): diff --git a/tests/utils_tests/test_decorators.py b/tests/utils_tests/test_decorators.py index 2d8af0a6e9..a05a736c30 100644 --- a/tests/utils_tests/test_decorators.py +++ b/tests/utils_tests/test_decorators.py @@ -11,6 +11,7 @@ class ProcessViewMiddleware(object): process_view_dec = decorator_from_middleware(ProcessViewMiddleware) + @process_view_dec def process_view(request): return HttpResponse() diff --git a/tests/utils_tests/test_ipv6.py b/tests/utils_tests/test_ipv6.py index 662e8b4135..6cd0cebc84 100644 --- a/tests/utils_tests/test_ipv6.py +++ b/tests/utils_tests/test_ipv6.py @@ -4,6 +4,7 @@ import unittest from django.utils.ipv6 import is_valid_ipv6_address, clean_ipv6_address + class TestUtilsIPv6(unittest.TestCase): def test_validates_correct_plain_address(self): diff --git a/tests/utils_tests/test_jslex.py b/tests/utils_tests/test_jslex.py index 084e2feb5b..97e6dbc109 100644 --- a/tests/utils_tests/test_jslex.py +++ b/tests/utils_tests/test_jslex.py @@ -5,6 +5,7 @@ from django.test import TestCase from django.utils.jslex import JsLexer, prepare_js_for_gettext + class JsTokensTest(TestCase): LEX_CASES = [ # ids @@ -105,6 +106,7 @@ class JsTokensTest(TestCase): r'string "\")"', "punct ;"]), ] + def make_function(input, toks): def test_func(self): lexer = JsLexer() @@ -207,6 +209,7 @@ GETTEXT_CASES = ( class JsToCForGettextTest(TestCase): pass + def make_function(js, c): def test_func(self): self.assertMultiLineEqual(prepare_js_for_gettext(js), c) diff --git a/tests/utils_tests/test_module_loading.py b/tests/utils_tests/test_module_loading.py index 1886631006..5a7eadcedf 100644 --- a/tests/utils_tests/test_module_loading.py +++ b/tests/utils_tests/test_module_loading.py @@ -52,6 +52,7 @@ class DefaultLoader(unittest.TestCase): self.assertRaises(ImportError, import_module, 'utils_tests.test_no_submodule.anything') + class EggLoader(unittest.TestCase): def setUp(self): self.old_path = sys.path[:] @@ -133,6 +134,7 @@ class ModuleImportTestCase(unittest.TestCase): self.assertIsNotNone(traceback.tb_next.tb_next, 'Should have more than the calling frame in the traceback.') + @override_settings(INSTALLED_APPS=('utils_tests.test_module',)) class AutodiscoverModulesTestCase(SimpleTestCase): @@ -188,6 +190,7 @@ class ProxyFinder(object): if fd: fd.close() + class TestFinder(object): def __init__(self, *args, **kwargs): self.importer = zipimporter(*args, **kwargs) @@ -198,6 +201,7 @@ class TestFinder(object): return return TestLoader(importer) + class TestLoader(object): def __init__(self, importer): self.importer = importer @@ -207,6 +211,7 @@ class TestLoader(object): mod.__loader__ = self return mod + class CustomLoader(EggLoader): """The Custom Loader test is exactly the same as the EggLoader, but it uses a custom defined Loader and Finder that is intentionally diff --git a/tests/utils_tests/test_simplelazyobject.py b/tests/utils_tests/test_simplelazyobject.py index 072db52e9e..14ad393bfa 100644 --- a/tests/utils_tests/test_simplelazyobject.py +++ b/tests/utils_tests/test_simplelazyobject.py @@ -177,6 +177,7 @@ class TestUtilsSimpleLazyObject(TestCase): self.assertEqual(len(lazy_list), 5) self.assertEqual(len(lazy_set), 4) + class TestUtilsSimpleLazyObjectDjangoTestCase(DjangoTestCase): def test_pickle_py2_regression(self): diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py index 441898c0b4..62e792fab6 100644 --- a/tests/utils_tests/test_text.py +++ b/tests/utils_tests/test_text.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals from django.test import SimpleTestCase from django.utils import text + class TestUtilsText(SimpleTestCase): def test_truncate_chars(self): diff --git a/tests/utils_tests/test_tzinfo.py b/tests/utils_tests/test_tzinfo.py index 43522bb1d0..85d6b673bb 100644 --- a/tests/utils_tests/test_tzinfo.py +++ b/tests/utils_tests/test_tzinfo.py @@ -13,6 +13,7 @@ with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=PendingDeprecationWarning) from django.utils.tzinfo import FixedOffset, LocalTimezone + class TzinfoTests(IgnorePendingDeprecationWarningsMixin, unittest.TestCase): @classmethod @@ -47,10 +48,10 @@ class TzinfoTests(IgnorePendingDeprecationWarningsMixin, unittest.TestCase): self.assertEqual(repr(FixedOffset(-280)), '-0440') self.assertEqual(repr(FixedOffset(-78.4)), '-0118') self.assertEqual(repr(FixedOffset(78.4)), '+0118') - self.assertEqual(repr(FixedOffset(-5.5*60)), '-0530') - self.assertEqual(repr(FixedOffset(5.5*60)), '+0530') - self.assertEqual(repr(FixedOffset(-.5*60)), '-0030') - self.assertEqual(repr(FixedOffset(.5*60)), '+0030') + self.assertEqual(repr(FixedOffset(-5.5 * 60)), '-0530') + self.assertEqual(repr(FixedOffset(5.5 * 60)), '+0530') + self.assertEqual(repr(FixedOffset(-.5 * 60)), '-0030') + self.assertEqual(repr(FixedOffset(.5 * 60)), '+0030') def test_16899(self): if not self.tz_tests: diff --git a/tests/validation/models.py b/tests/validation/models.py index ee5bcfc9aa..157b8cb158 100644 --- a/tests/validation/models.py +++ b/tests/validation/models.py @@ -11,6 +11,7 @@ def validate_answer_to_universe(value): if value != 42: raise ValidationError('This is not the answer to life, universe and everything!', code='not42') + class ModelToValidate(models.Model): name = models.CharField(max_length=100) created = models.DateTimeField(default=datetime.now) @@ -26,14 +27,17 @@ class ModelToValidate(models.Model): if self.number == 11: raise ValidationError('Invalid number supplied!') + class UniqueFieldsModel(models.Model): unique_charfield = models.CharField(max_length=100, unique=True) unique_integerfield = models.IntegerField(unique=True) non_unique_field = models.IntegerField() + class CustomPKModel(models.Model): my_pk_field = models.CharField(max_length=100, primary_key=True) + class UniqueTogetherModel(models.Model): cfield = models.CharField(max_length=100) ifield = models.IntegerField() @@ -42,6 +46,7 @@ class UniqueTogetherModel(models.Model): class Meta: unique_together = (('ifield', 'cfield',), ['ifield', 'efield']) + class UniqueForDateModel(models.Model): start_date = models.DateField() end_date = models.DateTimeField() @@ -49,6 +54,7 @@ class UniqueForDateModel(models.Model): order = models.IntegerField(unique_for_month="end_date") name = models.CharField(max_length=100) + class CustomMessagesModel(models.Model): other = models.IntegerField(blank=True, null=True) number = models.IntegerField(db_column='number_val', @@ -56,9 +62,11 @@ class CustomMessagesModel(models.Model): validators=[validate_answer_to_universe] ) + class Author(models.Model): name = models.CharField(max_length=100) + class Article(models.Model): title = models.CharField(max_length=100) author = models.ForeignKey(Author) @@ -68,6 +76,7 @@ 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) @@ -78,16 +87,19 @@ class Post(models.Model): def __str__(self): return self.name + class FlexibleDatePost(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(blank=True, null=True) + class UniqueErrorsModel(models.Model): name = models.CharField(max_length=100, unique=True, error_messages={'unique': 'Custom unique name message.'}) no = models.IntegerField(unique=True, error_messages={'unique': 'Custom unique number message.'}) + class GenericIPAddressTestModel(models.Model): generic_ip = models.GenericIPAddressField(blank=True, null=True, unique=True) v4_ip = models.GenericIPAddressField(blank=True, null=True, protocol="ipv4") @@ -95,6 +107,7 @@ class GenericIPAddressTestModel(models.Model): ip_verbose_name = models.GenericIPAddressField("IP Address Verbose", blank=True, null=True) + class GenericIPAddrUnpackUniqueTest(models.Model): generic_v4unpack_ip = models.GenericIPAddressField(null=True, blank=True, unique=True, unpack_ipv4=True) diff --git a/tests/validation/test_unique.py b/tests/validation/test_unique.py index 7d4ba26ff0..14ac5a2471 100644 --- a/tests/validation/test_unique.py +++ b/tests/validation/test_unique.py @@ -55,6 +55,7 @@ class GetUniqueCheckTests(unittest.TestCase): ), m._get_unique_checks(exclude='start_date') ) + class PerformUniqueChecksTest(TestCase): def test_primary_key_unique_check_not_performed_when_adding_and_pk_not_specified(self): # Regression test for #12560 diff --git a/tests/validation/tests.py b/tests/validation/tests.py index 02839eaf26..7f81cafccf 100644 --- a/tests/validation/tests.py +++ b/tests/validation/tests.py @@ -52,7 +52,7 @@ class BaseModelValidationTests(ValidationTestCase): self.assertFieldFailsValidationWithMessage(mtv.full_clean, 'url', ['Enter a valid URL.']) def test_text_greater_that_charfields_max_length_raises_erros(self): - mtv = ModelToValidate(number=10, name='Some Name'*100) + mtv = ModelToValidate(number=10, name='Some Name' * 100) self.assertFailsValidation(mtv.full_clean, ['name']) def test_malformed_slug_raises_error(self): @@ -65,6 +65,7 @@ class ArticleForm(forms.ModelForm): model = Article exclude = ['author'] + class ModelFormsTests(TestCase): def setUp(self): self.author = Author.objects.create(name='Joseph Kocherhans') diff --git a/tests/validators/tests.py b/tests/validators/tests.py index e3f418ee73..b0d1376aee 100644 --- a/tests/validators/tests.py +++ b/tests/validators/tests.py @@ -131,12 +131,12 @@ TEST_DATA = ( (MinValueValidator(NOW), NOW - timedelta(days=1), ValidationError), (MaxLengthValidator(10), '', None), - (MaxLengthValidator(10), 10*'x', None), + (MaxLengthValidator(10), 10 * 'x', None), - (MaxLengthValidator(10), 15*'x', ValidationError), + (MaxLengthValidator(10), 15 * 'x', ValidationError), - (MinLengthValidator(10), 15*'x', None), - (MinLengthValidator(10), 10*'x', None), + (MinLengthValidator(10), 15 * 'x', None), + (MinLengthValidator(10), 10 * 'x', None), (MinLengthValidator(10), '', ValidationError), @@ -182,6 +182,7 @@ TEST_DATA = ( (RegexValidator(re.compile('x')), 'y', ValidationError), ) + def create_simple_test_method(validator, expected, value, num): if expected is not None and issubclass(expected, Exception): test_mask = 'test_%s_raises_error_%d' @@ -214,6 +215,7 @@ def create_simple_test_method(validator, expected, value, num): # Dynamically assemble a test class with the contents of TEST_DATA + class TestSimpleValidators(TestCase): def test_single_message(self): v = ValidationError('Not Valid') diff --git a/tests/version/tests.py b/tests/version/tests.py index 8663d52cc5..b4c4283c04 100644 --- a/tests/version/tests.py +++ b/tests/version/tests.py @@ -3,6 +3,7 @@ from unittest import TestCase from django import get_version from django.utils import six + class VersionTests(TestCase): def test_development(self): diff --git a/tests/view_tests/__init__.py b/tests/view_tests/__init__.py index 3f16dadae2..b031e205e0 100644 --- a/tests/view_tests/__init__.py +++ b/tests/view_tests/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals + class BrokenException(Exception): pass diff --git a/tests/view_tests/models.py b/tests/view_tests/models.py index 461f98c028..f2707b1796 100644 --- a/tests/view_tests/models.py +++ b/tests/view_tests/models.py @@ -5,6 +5,7 @@ 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) @@ -15,6 +16,7 @@ class Author(models.Model): def get_absolute_url(self): return '/views/authors/%s/' % self.id + @python_2_unicode_compatible class BaseArticle(models.Model): """ @@ -31,9 +33,11 @@ class BaseArticle(models.Model): def __str__(self): return self.title + class Article(BaseArticle): date_created = models.DateTimeField() + class UrlArticle(BaseArticle): """ An Article class with a get_absolute_url defined. @@ -44,6 +48,7 @@ class UrlArticle(BaseArticle): return '/urlarticles/%s/' % self.slug get_absolute_url.purge = True + class DateArticle(BaseArticle): """ An article Model with a DateField instead of DateTimeField, diff --git a/tests/view_tests/templatetags/debugtags.py b/tests/view_tests/templatetags/debugtags.py index 5aafc3a9e0..1b9f3f9828 100644 --- a/tests/view_tests/templatetags/debugtags.py +++ b/tests/view_tests/templatetags/debugtags.py @@ -5,6 +5,7 @@ from ..views import BrokenException register = template.Library() + @register.simple_tag def go_boom(arg): raise BrokenException(arg) diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index b22c7aad43..fbbcdc9a40 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -185,7 +185,7 @@ class ExceptionReporterTests(TestCase): for newline in ['\n', '\r\n', '\r']: fd, filename = mkstemp(text=False) - os.write(fd, force_bytes(newline.join(LINES)+newline)) + os.write(fd, force_bytes(newline.join(LINES) + newline)) os.close(fd) try: @@ -375,7 +375,7 @@ class ExceptionReportTestMixin(object): Asserts that potentially sensitive info are displayed in the email report. """ with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): - mail.outbox = [] # Empty outbox + mail.outbox = [] # Empty outbox request = self.rf.post('/some_url/', self.breakfast_data) view(request) self.assertEqual(len(mail.outbox), 1) @@ -408,7 +408,7 @@ class ExceptionReportTestMixin(object): Asserts that certain sensitive info are not displayed in the email report. """ with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): - mail.outbox = [] # Empty outbox + mail.outbox = [] # Empty outbox request = self.rf.post('/some_url/', self.breakfast_data) view(request) self.assertEqual(len(mail.outbox), 1) @@ -448,7 +448,7 @@ class ExceptionReportTestMixin(object): Asserts that no variables or POST parameters are displayed in the email report. """ with self.settings(ADMINS=(('Admin', 'admin@fattie-breakie.com'),)): - mail.outbox = [] # Empty outbox + mail.outbox = [] # Empty outbox request = self.rf.post('/some_url/', self.breakfast_data) view(request) self.assertEqual(len(mail.outbox), 1) @@ -641,6 +641,7 @@ class ExceptionReporterFilterTests(TestCase, ExceptionReportTestMixin): response = self.client.get('/views/raises500/') self.assertNotContains(response, 'should not be displayed', status_code=500) + class AjaxResponseExceptionReporterFilter(TestCase, ExceptionReportTestMixin): """ Ensure that sensitive information can be filtered out of error reports. diff --git a/tests/view_tests/tests/test_shortcuts.py b/tests/view_tests/tests/test_shortcuts.py index 707df5ee7e..eb08433a44 100644 --- a/tests/view_tests/tests/test_shortcuts.py +++ b/tests/view_tests/tests/test_shortcuts.py @@ -1,6 +1,7 @@ from django.test import TestCase from django.test.utils import override_settings + @override_settings( TEMPLATE_CONTEXT_PROCESSORS=('django.core.context_processors.static',), STATIC_URL='/path/to/static/media/', diff --git a/tests/view_tests/views.py b/tests/view_tests/views.py index ed4e98a4c7..4373ef12f9 100644 --- a/tests/view_tests/views.py +++ b/tests/view_tests/views.py @@ -23,6 +23,7 @@ def index_page(request): """Dummy index page""" return HttpResponse('Dummy page') + def raises(request): # Make sure that a callable that raises an exception in the stack frame's # local vars won't hijack the technical 500 response. See: @@ -34,6 +35,7 @@ def raises(request): except Exception: return technical_500_response(request, *sys.exc_info()) + def raises500(request): # We need to inspect the HTML generated by the fancy 500 debug view but # the test client ignores it, so we send it explicitly. @@ -42,85 +44,102 @@ def raises500(request): except Exception: return technical_500_response(request, *sys.exc_info()) + def raises400(request): raise SuspiciousOperation + def raises403(request): raise PermissionDenied + def raises404(request): resolver = get_resolver(None) resolver.resolve('') + def redirect(request): """ Forces an HTTP redirect. """ return HttpResponseRedirect("target/") + def view_exception(request, n): raise BrokenException(except_args[int(n)]) + def template_exception(request, n): return render_to_response('debug/template_exception.html', {'arg': except_args[int(n)]}) + def jsi18n(request): return render_to_response('jsi18n.html') # Some views to exercise the shortcuts + def render_to_response_view(request): return render_to_response('debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }) + def render_to_response_view_with_request_context(request): return render_to_response('debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }, context_instance=RequestContext(request)) + def render_to_response_view_with_content_type(request): return render_to_response('debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }, content_type='application/x-rendertest') + def render_to_response_view_with_dirs(request): return render_to_response('render_dirs_test.html', dirs=dirs) + def render_view(request): return render(request, 'debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }) + def render_view_with_base_context(request): return render(request, 'debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }, context_instance=Context()) + def render_view_with_content_type(request): return render(request, 'debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }, content_type='application/x-rendertest') + def render_view_with_status(request): return render(request, 'debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }, status=403) + def render_view_with_current_app(request): return render(request, 'debug/render_test.html', { 'foo': 'FOO', 'bar': 'BAR', }, current_app="foobar_app") + def render_view_with_current_app_conflict(request): # This should fail because we don't passing both a current_app and # context_instance: @@ -129,9 +148,11 @@ def render_view_with_current_app_conflict(request): 'bar': 'BAR', }, current_app="foobar_app", context_instance=RequestContext(request)) + def render_with_dirs(request): return render(request, 'render_dirs_test.html', dirs=dirs) + def raises_template_does_not_exist(request, path='i_dont_exist.html'): # We need to inspect the HTML generated by the fancy 500 debug view but # the test client ignores it, so we send it explicitly. @@ -140,11 +161,13 @@ def raises_template_does_not_exist(request, path='i_dont_exist.html'): except TemplateDoesNotExist: return technical_500_response(request, *sys.exc_info()) + def render_no_template(request): # If we do not specify a template, we need to make sure the debug # view doesn't blow up. return render(request, [], {}) + def send_log(request, exc_info): logger = getLogger('django.request') # The default logging config has a logging filter to ensure admin emails are @@ -167,6 +190,7 @@ def send_log(request, exc_info): ) admin_email_handler.filters = orig_filters + def non_sensitive_view(request): # Do not just use plain strings for the variables' values in the code # so that the tests don't return false positives when the function's source @@ -180,6 +204,7 @@ def non_sensitive_view(request): send_log(request, exc_info) return technical_500_response(request, *exc_info) + @sensitive_variables('sauce') @sensitive_post_parameters('bacon-key', 'sausage-key') def sensitive_view(request): @@ -195,6 +220,7 @@ def sensitive_view(request): send_log(request, exc_info) return technical_500_response(request, *exc_info) + @sensitive_variables() @sensitive_post_parameters() def paranoid_view(request): @@ -210,6 +236,7 @@ def paranoid_view(request): send_log(request, exc_info) return technical_500_response(request, *exc_info) + def sensitive_args_function_caller(request): try: sensitive_args_function(''.join(['w', 'o', 'r', 'c', 'e', 's', 't', 'e', 'r', 's', 'h', 'i', 'r', 'e'])) @@ -218,6 +245,7 @@ def sensitive_args_function_caller(request): send_log(request, exc_info) return technical_500_response(request, *exc_info) + @sensitive_variables('sauce') def sensitive_args_function(sauce): # Do not just use plain strings for the variables' values in the code @@ -226,6 +254,7 @@ def sensitive_args_function(sauce): cooked_eggs = ''.join(['s', 'c', 'r', 'a', 'm', 'b', 'l', 'e', 'd']) # NOQA raise Exception + def sensitive_kwargs_function_caller(request): try: sensitive_kwargs_function(''.join(['w', 'o', 'r', 'c', 'e', 's', 't', 'e', 'r', 's', 'h', 'i', 'r', 'e'])) @@ -234,6 +263,7 @@ def sensitive_kwargs_function_caller(request): send_log(request, exc_info) return technical_500_response(request, *exc_info) + @sensitive_variables('sauce') def sensitive_kwargs_function(sauce=None): # Do not just use plain strings for the variables' values in the code @@ -242,6 +272,7 @@ def sensitive_kwargs_function(sauce=None): cooked_eggs = ''.join(['s', 'c', 'r', 'a', 'm', 'b', 'l', 'e', 'd']) # NOQA raise Exception + class UnsafeExceptionReporterFilter(SafeExceptionReporterFilter): """ Ignores all the filtering done by its parent class. @@ -287,6 +318,7 @@ class Klass(object): send_log(request, exc_info) return technical_500_response(request, *exc_info) + def sensitive_method_view(request): return Klass().method(request) diff --git a/tests/wsgi/urls.py b/tests/wsgi/urls.py index 7c6701adec..563b7238af 100644 --- a/tests/wsgi/urls.py +++ b/tests/wsgi/urls.py @@ -1,6 +1,7 @@ from django.conf.urls import url, patterns from django.http import HttpResponse + def helloworld(request): return HttpResponse("Hello World!")