Django-1.8.7/0000775000175000017500000000000012625116243012341 5ustar timtim00000000000000Django-1.8.7/LICENSE0000664000175000017500000000302012603513306013336 0ustar timtim00000000000000Copyright (c) Django Software Foundation and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Django nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Django-1.8.7/tests/0000775000175000017500000000000012625116243013503 5ustar timtim00000000000000Django-1.8.7/tests/timezones/0000775000175000017500000000000012625116243015520 5ustar timtim00000000000000Django-1.8.7/tests/timezones/urls.py0000664000175000017500000000031112625116214017050 0ustar timtim00000000000000from django.conf.urls import include, url from django.contrib import admin from . import admin as tz_admin # NOQA: register tz_admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), ] Django-1.8.7/tests/timezones/tests.py0000664000175000017500000015162612625116214017245 0ustar timtim00000000000000from __future__ import unicode_literals import datetime import re import sys import warnings from unittest import skipIf from xml.dom.minidom import parseString from django.core import serializers from django.core.urlresolvers import reverse from django.db.models import Max, Min from django.http import HttpRequest from django.template import ( Context, RequestContext, Template, TemplateSyntaxError, context_processors, ) from django.test import ( TestCase, override_settings, skipIfDBFeature, skipUnlessDBFeature, ) from django.test.utils import requires_tz_support from django.utils import six, timezone from .forms import ( EventForm, EventLocalizedForm, EventLocalizedModelForm, EventModelForm, EventSplitForm, ) from .models import ( AllDayEvent, Event, MaybeEvent, Session, SessionEvent, Timestamp, ) try: import pytz except ImportError: pytz = None # These tests use the EAT (Eastern Africa Time) and ICT (Indochina Time) # who don't have Daylight Saving Time, so we can represent them easily # with FixedOffset, and use them directly as tzinfo in the constructors. # settings.TIME_ZONE is forced to EAT. Most tests use a variant of # datetime.datetime(2011, 9, 1, 13, 20, 30), which translates to # 10:20:30 in UTC and 17:20:30 in ICT. UTC = timezone.utc EAT = timezone.get_fixed_timezone(180) # Africa/Nairobi ICT = timezone.get_fixed_timezone(420) # Asia/Bangkok @override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=False) class LegacyDatabaseTests(TestCase): def test_naive_datetime(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30) Event.objects.create(dt=dt) event = Event.objects.get() self.assertEqual(event.dt, dt) @skipUnlessDBFeature('supports_microsecond_precision') def test_naive_datetime_with_microsecond(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060) Event.objects.create(dt=dt) event = Event.objects.get() self.assertEqual(event.dt, dt) @skipIfDBFeature('supports_microsecond_precision') def test_naive_datetime_with_microsecond_unsupported(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060) Event.objects.create(dt=dt) event = Event.objects.get() # microseconds are lost during a round-trip in the database self.assertEqual(event.dt, dt.replace(microsecond=0)) @skipUnlessDBFeature('supports_timezones') def test_aware_datetime_in_local_timezone(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # interpret the naive datetime in local time to get the correct value self.assertEqual(event.dt.replace(tzinfo=EAT), dt) @skipUnlessDBFeature('supports_timezones') @skipUnlessDBFeature('supports_microsecond_precision') def test_aware_datetime_in_local_timezone_with_microsecond(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # interpret the naive datetime in local time to get the correct value self.assertEqual(event.dt.replace(tzinfo=EAT), dt) # This combination actually never happens. @skipUnlessDBFeature('supports_timezones') @skipIfDBFeature('supports_microsecond_precision') def test_aware_datetime_in_local_timezone_with_microsecond_unsupported(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # interpret the naive datetime in local time to get the correct value # microseconds are lost during a round-trip in the database self.assertEqual(event.dt.replace(tzinfo=EAT), dt.replace(microsecond=0)) @skipUnlessDBFeature('supports_timezones') @skipIfDBFeature('needs_datetime_string_cast') def test_aware_datetime_in_utc(self): dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # interpret the naive datetime in local time to get the correct value self.assertEqual(event.dt.replace(tzinfo=EAT), dt) # This combination is no longer possible since timezone support # was removed from the SQLite backend -- it didn't work. @skipUnlessDBFeature('supports_timezones') @skipUnlessDBFeature('needs_datetime_string_cast') def test_aware_datetime_in_utc_unsupported(self): dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # django.db.backends.utils.typecast_dt will just drop the # timezone, so a round-trip in the database alters the data (!) # interpret the naive datetime in local time and you get a wrong value self.assertNotEqual(event.dt.replace(tzinfo=EAT), dt) # interpret the naive datetime in original time to get the correct value self.assertEqual(event.dt.replace(tzinfo=UTC), dt) @skipUnlessDBFeature('supports_timezones') @skipIfDBFeature('needs_datetime_string_cast') def test_aware_datetime_in_other_timezone(self): dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # interpret the naive datetime in local time to get the correct value self.assertEqual(event.dt.replace(tzinfo=EAT), dt) # This combination is no longer possible since timezone support # was removed from the SQLite backend -- it didn't work. @skipUnlessDBFeature('supports_timezones') @skipUnlessDBFeature('needs_datetime_string_cast') def test_aware_datetime_in_other_timezone_unsupported(self): dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertIsNone(event.dt.tzinfo) # django.db.backends.utils.typecast_dt will just drop the # timezone, so a round-trip in the database alters the data (!) # interpret the naive datetime in local time and you get a wrong value self.assertNotEqual(event.dt.replace(tzinfo=EAT), dt) # interpret the naive datetime in original time to get the correct value self.assertEqual(event.dt.replace(tzinfo=ICT), dt) @skipIfDBFeature('supports_timezones') def test_aware_datetime_unspported(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) with self.assertRaises(ValueError): Event.objects.create(dt=dt) def test_auto_now_and_auto_now_add(self): now = datetime.datetime.now() past = now - datetime.timedelta(seconds=2) future = now + datetime.timedelta(seconds=2) Timestamp.objects.create() ts = Timestamp.objects.get() self.assertLess(past, ts.created) self.assertLess(past, ts.updated) self.assertGreater(future, ts.updated) self.assertGreater(future, ts.updated) def test_query_filter(self): dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30) dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30) Event.objects.create(dt=dt1) Event.objects.create(dt=dt2) self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2) self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1) self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1) self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0) def test_query_datetime_lookups(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0)) self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2) self.assertEqual(Event.objects.filter(dt__month=1).count(), 2) self.assertEqual(Event.objects.filter(dt__day=1).count(), 2) self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2) self.assertEqual(Event.objects.filter(dt__hour=1).count(), 1) self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2) self.assertEqual(Event.objects.filter(dt__second=0).count(), 2) def test_query_aggregation(self): # Only min and max make sense for datetimes. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20)) Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30)) Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40)) result = Event.objects.all().aggregate(Min('dt'), Max('dt')) self.assertEqual(result, { 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40), 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20), }) def test_query_annotation(self): # Only min and max make sense for datetimes. morning = Session.objects.create(name='morning') afternoon = Session.objects.create(name='afternoon') SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20), session=afternoon) SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30), session=afternoon) SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40), session=morning) morning_min_dt = datetime.datetime(2011, 9, 1, 3, 20, 40) afternoon_min_dt = datetime.datetime(2011, 9, 1, 13, 20, 30) self.assertQuerysetEqual( Session.objects.annotate(dt=Min('events__dt')).order_by('dt'), [morning_min_dt, afternoon_min_dt], transform=lambda d: d.dt) self.assertQuerysetEqual( Session.objects.annotate(dt=Min('events__dt')).filter(dt__lt=afternoon_min_dt), [morning_min_dt], transform=lambda d: d.dt) self.assertQuerysetEqual( Session.objects.annotate(dt=Min('events__dt')).filter(dt__gte=afternoon_min_dt), [afternoon_min_dt], transform=lambda d: d.dt) def test_query_datetimes(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0)) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'year'), [datetime.datetime(2011, 1, 1, 0, 0, 0)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'month'), [datetime.datetime(2011, 1, 1, 0, 0, 0)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'day'), [datetime.datetime(2011, 1, 1, 0, 0, 0)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'hour'), [datetime.datetime(2011, 1, 1, 1, 0, 0), datetime.datetime(2011, 1, 1, 4, 0, 0)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'minute'), [datetime.datetime(2011, 1, 1, 1, 30, 0), datetime.datetime(2011, 1, 1, 4, 30, 0)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'second'), [datetime.datetime(2011, 1, 1, 1, 30, 0), datetime.datetime(2011, 1, 1, 4, 30, 0)], transform=lambda d: d) def test_raw_sql(self): # Regression test for #17755 dt = datetime.datetime(2011, 9, 1, 13, 20, 30) event = Event.objects.create(dt=dt) self.assertQuerysetEqual( Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt]), [event], transform=lambda d: d) def test_filter_date_field_with_aware_datetime(self): # Regression test for #17742 day = datetime.date(2011, 9, 1) AllDayEvent.objects.create(day=day) # This is 2011-09-02T01:30:00+03:00 in EAT dt = datetime.datetime(2011, 9, 1, 22, 30, 0, tzinfo=UTC) self.assertTrue(AllDayEvent.objects.filter(day__gte=dt).exists()) @override_settings(TIME_ZONE='Africa/Nairobi', USE_TZ=True) class NewDatabaseTests(TestCase): @requires_tz_support def test_naive_datetime(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30) with warnings.catch_warnings(record=True) as recorded: warnings.simplefilter('always') Event.objects.create(dt=dt) self.assertEqual(len(recorded), 1) msg = str(recorded[0].message) self.assertTrue(msg.startswith("DateTimeField Event.dt received " "a naive datetime")) event = Event.objects.get() # naive datetimes are interpreted in local time self.assertEqual(event.dt, dt.replace(tzinfo=EAT)) @requires_tz_support def test_datetime_from_date(self): dt = datetime.date(2011, 9, 1) with warnings.catch_warnings(record=True) as recorded: warnings.simplefilter('always') Event.objects.create(dt=dt) self.assertEqual(len(recorded), 1) msg = str(recorded[0].message) self.assertTrue(msg.startswith("DateTimeField Event.dt received " "a naive datetime")) event = Event.objects.get() self.assertEqual(event.dt, datetime.datetime(2011, 9, 1, tzinfo=EAT)) @requires_tz_support @skipUnlessDBFeature('supports_microsecond_precision') def test_naive_datetime_with_microsecond(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060) with warnings.catch_warnings(record=True) as recorded: warnings.simplefilter('always') Event.objects.create(dt=dt) self.assertEqual(len(recorded), 1) msg = str(recorded[0].message) self.assertTrue(msg.startswith("DateTimeField Event.dt received " "a naive datetime")) event = Event.objects.get() # naive datetimes are interpreted in local time self.assertEqual(event.dt, dt.replace(tzinfo=EAT)) @requires_tz_support @skipIfDBFeature('supports_microsecond_precision') def test_naive_datetime_with_microsecond_unsupported(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060) with warnings.catch_warnings(record=True) as recorded: warnings.simplefilter('always') Event.objects.create(dt=dt) self.assertEqual(len(recorded), 1) msg = str(recorded[0].message) self.assertTrue(msg.startswith("DateTimeField Event.dt received " "a naive datetime")) event = Event.objects.get() # microseconds are lost during a round-trip in the database # naive datetimes are interpreted in local time self.assertEqual(event.dt, dt.replace(microsecond=0, tzinfo=EAT)) def test_aware_datetime_in_local_timezone(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertEqual(event.dt, dt) @skipUnlessDBFeature('supports_microsecond_precision') def test_aware_datetime_in_local_timezone_with_microsecond(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertEqual(event.dt, dt) @skipIfDBFeature('supports_microsecond_precision') def test_aware_datetime_in_local_timezone_with_microsecond_unsupported(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060, tzinfo=EAT) Event.objects.create(dt=dt) event = Event.objects.get() # microseconds are lost during a round-trip in the database self.assertEqual(event.dt, dt.replace(microsecond=0)) def test_aware_datetime_in_utc(self): dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC) Event.objects.create(dt=dt) event = Event.objects.get() self.assertEqual(event.dt, dt) def test_aware_datetime_in_other_timezone(self): dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT) Event.objects.create(dt=dt) event = Event.objects.get() self.assertEqual(event.dt, dt) def test_auto_now_and_auto_now_add(self): now = timezone.now() past = now - datetime.timedelta(seconds=2) future = now + datetime.timedelta(seconds=2) Timestamp.objects.create() ts = Timestamp.objects.get() self.assertLess(past, ts.created) self.assertLess(past, ts.updated) self.assertGreater(future, ts.updated) self.assertGreater(future, ts.updated) def test_query_filter(self): dt1 = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT) dt2 = datetime.datetime(2011, 9, 1, 14, 20, 30, tzinfo=EAT) Event.objects.create(dt=dt1) Event.objects.create(dt=dt2) self.assertEqual(Event.objects.filter(dt__gte=dt1).count(), 2) self.assertEqual(Event.objects.filter(dt__gt=dt1).count(), 1) self.assertEqual(Event.objects.filter(dt__gte=dt2).count(), 1) self.assertEqual(Event.objects.filter(dt__gt=dt2).count(), 0) @skipIf(pytz is None, "this test requires pytz") def test_query_filter_with_pytz_timezones(self): tz = pytz.timezone('Europe/Paris') dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=tz) Event.objects.create(dt=dt) next = dt + datetime.timedelta(seconds=3) prev = dt - datetime.timedelta(seconds=3) self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1) self.assertEqual(Event.objects.filter(dt__exact=next).count(), 0) self.assertEqual(Event.objects.filter(dt__in=(prev, next)).count(), 0) self.assertEqual(Event.objects.filter(dt__in=(prev, dt, next)).count(), 1) self.assertEqual(Event.objects.filter(dt__range=(prev, next)).count(), 1) @requires_tz_support def test_query_filter_with_naive_datetime(self): dt = datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT) Event.objects.create(dt=dt) dt = dt.replace(tzinfo=None) with warnings.catch_warnings(record=True) as recorded: warnings.simplefilter('always') # naive datetimes are interpreted in local time self.assertEqual(Event.objects.filter(dt__exact=dt).count(), 1) self.assertEqual(Event.objects.filter(dt__lte=dt).count(), 1) self.assertEqual(Event.objects.filter(dt__gt=dt).count(), 0) self.assertEqual(len(recorded), 3) for warning in recorded: msg = str(warning.message) self.assertTrue(msg.startswith("DateTimeField Event.dt " "received a naive datetime")) @skipUnlessDBFeature('has_zoneinfo_database') def test_query_datetime_lookups(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) self.assertEqual(Event.objects.filter(dt__year=2011).count(), 2) self.assertEqual(Event.objects.filter(dt__month=1).count(), 2) self.assertEqual(Event.objects.filter(dt__day=1).count(), 2) self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 2) self.assertEqual(Event.objects.filter(dt__hour=1).count(), 1) self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2) self.assertEqual(Event.objects.filter(dt__second=0).count(), 2) @skipUnlessDBFeature('has_zoneinfo_database') def test_query_datetime_lookups_in_other_timezone(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) with timezone.override(UTC): # These two dates fall in the same day in EAT, but in different days, # years and months in UTC. self.assertEqual(Event.objects.filter(dt__year=2011).count(), 1) self.assertEqual(Event.objects.filter(dt__month=1).count(), 1) self.assertEqual(Event.objects.filter(dt__day=1).count(), 1) self.assertEqual(Event.objects.filter(dt__week_day=7).count(), 1) self.assertEqual(Event.objects.filter(dt__hour=22).count(), 1) self.assertEqual(Event.objects.filter(dt__minute=30).count(), 2) self.assertEqual(Event.objects.filter(dt__second=0).count(), 2) def test_query_aggregation(self): # Only min and max make sense for datetimes. Event.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT)) result = Event.objects.all().aggregate(Min('dt'), Max('dt')) self.assertEqual(result, { 'dt__min': datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT), 'dt__max': datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT), }) def test_query_annotation(self): # Only min and max make sense for datetimes. morning = Session.objects.create(name='morning') afternoon = Session.objects.create(name='afternoon') SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 23, 20, 20, tzinfo=EAT), session=afternoon) SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), session=afternoon) SessionEvent.objects.create(dt=datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT), session=morning) morning_min_dt = datetime.datetime(2011, 9, 1, 3, 20, 40, tzinfo=EAT) afternoon_min_dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) self.assertQuerysetEqual( Session.objects.annotate(dt=Min('events__dt')).order_by('dt'), [morning_min_dt, afternoon_min_dt], transform=lambda d: d.dt) self.assertQuerysetEqual( Session.objects.annotate(dt=Min('events__dt')).filter(dt__lt=afternoon_min_dt), [morning_min_dt], transform=lambda d: d.dt) self.assertQuerysetEqual( Session.objects.annotate(dt=Min('events__dt')).filter(dt__gte=afternoon_min_dt), [afternoon_min_dt], transform=lambda d: d.dt) @skipUnlessDBFeature('has_zoneinfo_database') def test_query_datetimes(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'year'), [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'month'), [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'day'), [datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=EAT)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'hour'), [datetime.datetime(2011, 1, 1, 1, 0, 0, tzinfo=EAT), datetime.datetime(2011, 1, 1, 4, 0, 0, tzinfo=EAT)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'minute'), [datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT), datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'second'), [datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT), datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)], transform=lambda d: d) @skipUnlessDBFeature('has_zoneinfo_database') def test_query_datetimes_in_other_timezone(self): Event.objects.create(dt=datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=EAT)) Event.objects.create(dt=datetime.datetime(2011, 1, 1, 4, 30, 0, tzinfo=EAT)) with timezone.override(UTC): self.assertQuerysetEqual( Event.objects.datetimes('dt', 'year'), [datetime.datetime(2010, 1, 1, 0, 0, 0, tzinfo=UTC), datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'month'), [datetime.datetime(2010, 12, 1, 0, 0, 0, tzinfo=UTC), datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'day'), [datetime.datetime(2010, 12, 31, 0, 0, 0, tzinfo=UTC), datetime.datetime(2011, 1, 1, 0, 0, 0, tzinfo=UTC)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'hour'), [datetime.datetime(2010, 12, 31, 22, 0, 0, tzinfo=UTC), datetime.datetime(2011, 1, 1, 1, 0, 0, tzinfo=UTC)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'minute'), [datetime.datetime(2010, 12, 31, 22, 30, 0, tzinfo=UTC), datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=UTC)], transform=lambda d: d) self.assertQuerysetEqual( Event.objects.datetimes('dt', 'second'), [datetime.datetime(2010, 12, 31, 22, 30, 0, tzinfo=UTC), datetime.datetime(2011, 1, 1, 1, 30, 0, tzinfo=UTC)], transform=lambda d: d) def test_raw_sql(self): # Regression test for #17755 dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) event = Event.objects.create(dt=dt) self.assertQuerysetEqual( Event.objects.raw('SELECT * FROM timezones_event WHERE dt = %s', [dt]), [event], transform=lambda d: d) @requires_tz_support def test_filter_date_field_with_aware_datetime(self): # Regression test for #17742 day = datetime.date(2011, 9, 1) AllDayEvent.objects.create(day=day) # This is 2011-09-02T01:30:00+03:00 in EAT dt = datetime.datetime(2011, 9, 1, 22, 30, 0, tzinfo=UTC) self.assertFalse(AllDayEvent.objects.filter(day__gte=dt).exists()) def test_null_datetime(self): # Regression test for #17294 e = MaybeEvent.objects.create() self.assertEqual(e.dt, None) @override_settings(TIME_ZONE='Africa/Nairobi') class SerializationTests(TestCase): # Backend-specific notes: # - JSON supports only milliseconds, microseconds will be truncated. # - PyYAML dumps the UTC offset correctly for timezone-aware datetimes, # but when it loads this representation, it substracts the offset and # returns a naive datetime object in UTC (http://pyyaml.org/ticket/202). # Tests are adapted to take these quirks into account. def assert_python_contains_datetime(self, objects, dt): self.assertEqual(objects[0]['fields']['dt'], dt) def assert_json_contains_datetime(self, json, dt): self.assertIn('"fields": {"dt": "%s"}' % dt, json) def assert_xml_contains_datetime(self, xml, dt): field = parseString(xml).getElementsByTagName('field')[0] self.assertXMLEqual(field.childNodes[0].wholeText, dt) def assert_yaml_contains_datetime(self, yaml, dt): # Depending on the yaml dumper, '!timestamp' might be absent six.assertRegex(self, yaml, r"- fields: {dt: !(!timestamp)? '%s'}" % re.escape(dt)) def test_naive_datetime(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30) data = serializers.serialize('python', [Event(dt=dt)]) self.assert_python_contains_datetime(data, dt) obj = next(serializers.deserialize('python', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('json', [Event(dt=dt)]) self.assert_json_contains_datetime(data, "2011-09-01T13:20:30") obj = next(serializers.deserialize('json', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('xml', [Event(dt=dt)]) self.assert_xml_contains_datetime(data, "2011-09-01T13:20:30") obj = next(serializers.deserialize('xml', data)).object self.assertEqual(obj.dt, dt) if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer): data = serializers.serialize('yaml', [Event(dt=dt)]) self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30") obj = next(serializers.deserialize('yaml', data)).object self.assertEqual(obj.dt, dt) def test_naive_datetime_with_microsecond(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, 405060) data = serializers.serialize('python', [Event(dt=dt)]) self.assert_python_contains_datetime(data, dt) obj = next(serializers.deserialize('python', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('json', [Event(dt=dt)]) self.assert_json_contains_datetime(data, "2011-09-01T13:20:30.405") obj = next(serializers.deserialize('json', data)).object self.assertEqual(obj.dt, dt.replace(microsecond=405000)) data = serializers.serialize('xml', [Event(dt=dt)]) self.assert_xml_contains_datetime(data, "2011-09-01T13:20:30.405060") obj = next(serializers.deserialize('xml', data)).object self.assertEqual(obj.dt, dt) if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer): data = serializers.serialize('yaml', [Event(dt=dt)]) self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30.405060") obj = next(serializers.deserialize('yaml', data)).object self.assertEqual(obj.dt, dt) def test_aware_datetime_with_microsecond(self): dt = datetime.datetime(2011, 9, 1, 17, 20, 30, 405060, tzinfo=ICT) data = serializers.serialize('python', [Event(dt=dt)]) self.assert_python_contains_datetime(data, dt) obj = next(serializers.deserialize('python', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('json', [Event(dt=dt)]) self.assert_json_contains_datetime(data, "2011-09-01T17:20:30.405+07:00") obj = next(serializers.deserialize('json', data)).object self.assertEqual(obj.dt, dt.replace(microsecond=405000)) data = serializers.serialize('xml', [Event(dt=dt)]) self.assert_xml_contains_datetime(data, "2011-09-01T17:20:30.405060+07:00") obj = next(serializers.deserialize('xml', data)).object self.assertEqual(obj.dt, dt) if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer): data = serializers.serialize('yaml', [Event(dt=dt)]) self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30.405060+07:00") obj = next(serializers.deserialize('yaml', data)).object self.assertEqual(obj.dt.replace(tzinfo=UTC), dt) def test_aware_datetime_in_utc(self): dt = datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC) data = serializers.serialize('python', [Event(dt=dt)]) self.assert_python_contains_datetime(data, dt) obj = next(serializers.deserialize('python', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('json', [Event(dt=dt)]) self.assert_json_contains_datetime(data, "2011-09-01T10:20:30Z") obj = next(serializers.deserialize('json', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('xml', [Event(dt=dt)]) self.assert_xml_contains_datetime(data, "2011-09-01T10:20:30+00:00") obj = next(serializers.deserialize('xml', data)).object self.assertEqual(obj.dt, dt) if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer): data = serializers.serialize('yaml', [Event(dt=dt)]) self.assert_yaml_contains_datetime(data, "2011-09-01 10:20:30+00:00") obj = next(serializers.deserialize('yaml', data)).object self.assertEqual(obj.dt.replace(tzinfo=UTC), dt) def test_aware_datetime_in_local_timezone(self): dt = datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT) data = serializers.serialize('python', [Event(dt=dt)]) self.assert_python_contains_datetime(data, dt) obj = next(serializers.deserialize('python', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('json', [Event(dt=dt)]) self.assert_json_contains_datetime(data, "2011-09-01T13:20:30+03:00") obj = next(serializers.deserialize('json', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('xml', [Event(dt=dt)]) self.assert_xml_contains_datetime(data, "2011-09-01T13:20:30+03:00") obj = next(serializers.deserialize('xml', data)).object self.assertEqual(obj.dt, dt) if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer): data = serializers.serialize('yaml', [Event(dt=dt)]) self.assert_yaml_contains_datetime(data, "2011-09-01 13:20:30+03:00") obj = next(serializers.deserialize('yaml', data)).object self.assertEqual(obj.dt.replace(tzinfo=UTC), dt) def test_aware_datetime_in_other_timezone(self): dt = datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT) data = serializers.serialize('python', [Event(dt=dt)]) self.assert_python_contains_datetime(data, dt) obj = next(serializers.deserialize('python', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('json', [Event(dt=dt)]) self.assert_json_contains_datetime(data, "2011-09-01T17:20:30+07:00") obj = next(serializers.deserialize('json', data)).object self.assertEqual(obj.dt, dt) data = serializers.serialize('xml', [Event(dt=dt)]) self.assert_xml_contains_datetime(data, "2011-09-01T17:20:30+07:00") obj = next(serializers.deserialize('xml', data)).object self.assertEqual(obj.dt, dt) if not isinstance(serializers.get_serializer('yaml'), serializers.BadSerializer): data = serializers.serialize('yaml', [Event(dt=dt)]) self.assert_yaml_contains_datetime(data, "2011-09-01 17:20:30+07:00") obj = next(serializers.deserialize('yaml', data)).object self.assertEqual(obj.dt.replace(tzinfo=UTC), dt) @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True) class TemplateTests(TestCase): @requires_tz_support def test_localtime_templatetag_and_filters(self): """ Test the {% localtime %} templatetag and related filters. """ datetimes = { 'utc': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC), 'eat': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), 'ict': datetime.datetime(2011, 9, 1, 17, 20, 30, tzinfo=ICT), 'naive': datetime.datetime(2011, 9, 1, 13, 20, 30), } templates = { 'notag': Template("{% load tz %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:ICT }}"), 'noarg': Template("{% load tz %}{% localtime %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:ICT }}{% endlocaltime %}"), 'on': Template("{% load tz %}{% localtime on %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:ICT }}{% endlocaltime %}"), 'off': Template("{% load tz %}{% localtime off %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:ICT }}{% endlocaltime %}"), } # Transform a list of keys in 'datetimes' to the expected template # output. This makes the definition of 'results' more readable. def t(*result): return '|'.join(datetimes[key].isoformat() for key in result) # Results for USE_TZ = True results = { 'utc': { 'notag': t('eat', 'eat', 'utc', 'ict'), 'noarg': t('eat', 'eat', 'utc', 'ict'), 'on': t('eat', 'eat', 'utc', 'ict'), 'off': t('utc', 'eat', 'utc', 'ict'), }, 'eat': { 'notag': t('eat', 'eat', 'utc', 'ict'), 'noarg': t('eat', 'eat', 'utc', 'ict'), 'on': t('eat', 'eat', 'utc', 'ict'), 'off': t('eat', 'eat', 'utc', 'ict'), }, 'ict': { 'notag': t('eat', 'eat', 'utc', 'ict'), 'noarg': t('eat', 'eat', 'utc', 'ict'), 'on': t('eat', 'eat', 'utc', 'ict'), 'off': t('ict', 'eat', 'utc', 'ict'), }, 'naive': { 'notag': t('naive', 'eat', 'utc', 'ict'), 'noarg': t('naive', 'eat', 'utc', 'ict'), 'on': t('naive', 'eat', 'utc', 'ict'), 'off': t('naive', 'eat', 'utc', 'ict'), } } for k1, dt in six.iteritems(datetimes): for k2, tpl in six.iteritems(templates): ctx = Context({'dt': dt, 'ICT': ICT}) actual = tpl.render(ctx) expected = results[k1][k2] self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected)) # Changes for USE_TZ = False results['utc']['notag'] = t('utc', 'eat', 'utc', 'ict') results['ict']['notag'] = t('ict', 'eat', 'utc', 'ict') with self.settings(USE_TZ=False): for k1, dt in six.iteritems(datetimes): for k2, tpl in six.iteritems(templates): ctx = Context({'dt': dt, 'ICT': ICT}) actual = tpl.render(ctx) expected = results[k1][k2] self.assertEqual(actual, expected, '%s / %s: %r != %r' % (k1, k2, actual, expected)) @skipIf(pytz is None, "this test requires pytz") def test_localtime_filters_with_pytz(self): """ Test the |localtime, |utc, and |timezone filters with pytz. """ # Use a pytz timezone as local time tpl = Template("{% load tz %}{{ dt|localtime }}|{{ dt|utc }}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 12, 20, 30)}) with self.settings(TIME_ZONE='Europe/Paris'): self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00|2011-09-01T10:20:30+00:00") # Use a pytz timezone as argument tpl = Template("{% load tz %}{{ dt|timezone:tz }}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': pytz.timezone('Europe/Paris')}) self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00") # Use a pytz timezone name as argument tpl = Template("{% load tz %}{{ dt|timezone:'Europe/Paris' }}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': pytz.timezone('Europe/Paris')}) self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00") def test_localtime_templatetag_invalid_argument(self): with self.assertRaises(TemplateSyntaxError): Template("{% load tz %}{% localtime foo %}{% endlocaltime %}").render() def test_localtime_filters_do_not_raise_exceptions(self): """ Test the |localtime, |utc, and |timezone filters on bad inputs. """ tpl = Template("{% load tz %}{{ dt }}|{{ dt|localtime }}|{{ dt|utc }}|{{ dt|timezone:tz }}") with self.settings(USE_TZ=True): # bad datetime value ctx = Context({'dt': None, 'tz': ICT}) self.assertEqual(tpl.render(ctx), "None|||") ctx = Context({'dt': 'not a date', 'tz': ICT}) self.assertEqual(tpl.render(ctx), "not a date|||") # bad timezone value tpl = Template("{% load tz %}{{ dt|timezone:tz }}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': None}) self.assertEqual(tpl.render(ctx), "") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30), 'tz': 'not a tz'}) self.assertEqual(tpl.render(ctx), "") @requires_tz_support def test_timezone_templatetag(self): """ Test the {% timezone %} templatetag. """ tpl = Template( "{% load tz %}" "{{ dt }}|" "{% timezone tz1 %}" "{{ dt }}|" "{% timezone tz2 %}" "{{ dt }}" "{% endtimezone %}" "{% endtimezone %}" ) ctx = Context({'dt': datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC), 'tz1': ICT, 'tz2': None}) self.assertEqual(tpl.render(ctx), "2011-09-01T13:20:30+03:00|2011-09-01T17:20:30+07:00|2011-09-01T13:20:30+03:00") @skipIf(pytz is None, "this test requires pytz") def test_timezone_templatetag_with_pytz(self): """ Test the {% timezone %} templatetag with pytz. """ tpl = Template("{% load tz %}{% timezone tz %}{{ dt }}{% endtimezone %}") # Use a pytz timezone as argument ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), 'tz': pytz.timezone('Europe/Paris')}) self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00") # Use a pytz timezone name as argument ctx = Context({'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT), 'tz': 'Europe/Paris'}) self.assertEqual(tpl.render(ctx), "2011-09-01T12:20:30+02:00") def test_timezone_templatetag_invalid_argument(self): with self.assertRaises(TemplateSyntaxError): Template("{% load tz %}{% timezone %}{% endtimezone %}").render() with self.assertRaises(ValueError if pytz is None else pytz.UnknownTimeZoneError): Template("{% load tz %}{% timezone tz %}{% endtimezone %}").render(Context({'tz': 'foobar'})) @skipIf(sys.platform.startswith('win'), "Windows uses non-standard time zone names") def test_get_current_timezone_templatetag(self): """ Test the {% get_current_timezone %} templatetag. """ tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}") self.assertEqual(tpl.render(Context()), "Africa/Nairobi" if pytz else "EAT") with timezone.override(UTC): self.assertEqual(tpl.render(Context()), "UTC") tpl = Template("{% load tz %}{% timezone tz %}{% get_current_timezone as time_zone %}{% endtimezone %}{{ time_zone }}") self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700") with timezone.override(UTC): self.assertEqual(tpl.render(Context({'tz': ICT})), "+0700") @skipIf(pytz is None, "this test requires pytz") def test_get_current_timezone_templatetag_with_pytz(self): """ Test the {% get_current_timezone %} templatetag with pytz. """ tpl = Template("{% load tz %}{% get_current_timezone as time_zone %}{{ time_zone }}") with timezone.override(pytz.timezone('Europe/Paris')): self.assertEqual(tpl.render(Context()), "Europe/Paris") tpl = Template("{% load tz %}{% timezone 'Europe/Paris' %}{% get_current_timezone as time_zone %}{% endtimezone %}{{ time_zone }}") self.assertEqual(tpl.render(Context()), "Europe/Paris") def test_get_current_timezone_templatetag_invalid_argument(self): with self.assertRaises(TemplateSyntaxError): Template("{% load tz %}{% get_current_timezone %}").render() @skipIf(sys.platform.startswith('win'), "Windows uses non-standard time zone names") def test_tz_template_context_processor(self): """ Test the django.template.context_processors.tz template context processor. """ tpl = Template("{{ TIME_ZONE }}") context = Context() self.assertEqual(tpl.render(context), "") request_context = RequestContext(HttpRequest(), processors=[context_processors.tz]) self.assertEqual(tpl.render(request_context), "Africa/Nairobi" if pytz else "EAT") @requires_tz_support def test_date_and_time_template_filters(self): tpl = Template("{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)}) self.assertEqual(tpl.render(ctx), "2011-09-01 at 23:20:20") with timezone.override(ICT): self.assertEqual(tpl.render(ctx), "2011-09-02 at 03:20:20") def test_date_and_time_template_filters_honor_localtime(self): tpl = Template("{% load tz %}{% localtime off %}{{ dt|date:'Y-m-d' }} at {{ dt|time:'H:i:s' }}{% endlocaltime %}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 20, 20, 20, tzinfo=UTC)}) self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20") with timezone.override(ICT): self.assertEqual(tpl.render(ctx), "2011-09-01 at 20:20:20") def test_localtime_with_time_zone_setting_set_to_none(self): # Regression for #17274 tpl = Template("{% load tz %}{{ dt }}") ctx = Context({'dt': datetime.datetime(2011, 9, 1, 12, 20, 30, tzinfo=EAT)}) with self.settings(TIME_ZONE=None): # the actual value depends on the system time zone of the host self.assertTrue(tpl.render(ctx).startswith("2011")) @requires_tz_support def test_now_template_tag_uses_current_time_zone(self): # Regression for #17343 tpl = Template("{% now \"O\" %}") self.assertEqual(tpl.render(Context({})), "+0300") with timezone.override(ICT): self.assertEqual(tpl.render(Context({})), "+0700") @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=False) class LegacyFormsTests(TestCase): def test_form(self): form = EventForm({'dt': '2011-09-01 13:20:30'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30)) @skipIf(pytz is None, "this test requires pytz") def test_form_with_non_existent_time(self): form = EventForm({'dt': '2011-03-27 02:30:00'}) with timezone.override(pytz.timezone('Europe/Paris')): # this is obviously a bug self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 3, 27, 2, 30, 0)) @skipIf(pytz is None, "this test requires pytz") def test_form_with_ambiguous_time(self): form = EventForm({'dt': '2011-10-30 02:30:00'}) with timezone.override(pytz.timezone('Europe/Paris')): # this is obviously a bug self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 10, 30, 2, 30, 0)) def test_split_form(self): form = EventSplitForm({'dt_0': '2011-09-01', 'dt_1': '13:20:30'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 13, 20, 30)) def test_model_form(self): EventModelForm({'dt': '2011-09-01 13:20:30'}).save() e = Event.objects.get() self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 13, 20, 30)) @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True) class NewFormsTests(TestCase): @requires_tz_support def test_form(self): form = EventForm({'dt': '2011-09-01 13:20:30'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) def test_form_with_other_timezone(self): form = EventForm({'dt': '2011-09-01 17:20:30'}) with timezone.override(ICT): self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) def test_form_with_explicit_timezone(self): form = EventForm({'dt': '2011-09-01 17:20:30+07:00'}) # Datetime inputs formats don't allow providing a time zone. self.assertFalse(form.is_valid()) @skipIf(pytz is None, "this test requires pytz") def test_form_with_non_existent_time(self): with timezone.override(pytz.timezone('Europe/Paris')): form = EventForm({'dt': '2011-03-27 02:30:00'}) self.assertFalse(form.is_valid()) self.assertEqual(form.errors['dt'], ["2011-03-27 02:30:00 couldn't be interpreted in time zone " "Europe/Paris; it may be ambiguous or it may not exist."]) @skipIf(pytz is None, "this test requires pytz") def test_form_with_ambiguous_time(self): with timezone.override(pytz.timezone('Europe/Paris')): form = EventForm({'dt': '2011-10-30 02:30:00'}) self.assertFalse(form.is_valid()) self.assertEqual(form.errors['dt'], ["2011-10-30 02:30:00 couldn't be interpreted in time zone " "Europe/Paris; it may be ambiguous or it may not exist."]) @requires_tz_support def test_split_form(self): form = EventSplitForm({'dt_0': '2011-09-01', 'dt_1': '13:20:30'}) self.assertTrue(form.is_valid()) self.assertEqual(form.cleaned_data['dt'], datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) @requires_tz_support def test_localized_form(self): form = EventLocalizedForm(initial={'dt': datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT)}) with timezone.override(ICT): self.assertIn("2011-09-01 17:20:30", str(form)) @requires_tz_support def test_model_form(self): EventModelForm({'dt': '2011-09-01 13:20:30'}).save() e = Event.objects.get() self.assertEqual(e.dt, datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) @requires_tz_support def test_localized_model_form(self): form = EventLocalizedModelForm(instance=Event(dt=datetime.datetime(2011, 9, 1, 13, 20, 30, tzinfo=EAT))) with timezone.override(ICT): self.assertIn("2011-09-01 17:20:30", str(form)) @override_settings(DATETIME_FORMAT='c', TIME_ZONE='Africa/Nairobi', USE_L10N=False, USE_TZ=True, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF='timezones.urls') class AdminTests(TestCase): fixtures = ['tz_users.xml'] def setUp(self): self.client.login(username='super', password='secret') @requires_tz_support def test_changelist(self): e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) response = self.client.get(reverse('admin:timezones_event_changelist')) self.assertContains(response, e.dt.astimezone(EAT).isoformat()) def test_changelist_in_other_timezone(self): e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) with timezone.override(ICT): response = self.client.get(reverse('admin:timezones_event_changelist')) self.assertContains(response, e.dt.astimezone(ICT).isoformat()) @requires_tz_support def test_change_editable(self): e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) response = self.client.get(reverse('admin:timezones_event_change', args=(e.pk,))) self.assertContains(response, e.dt.astimezone(EAT).date().isoformat()) self.assertContains(response, e.dt.astimezone(EAT).time().isoformat()) def test_change_editable_in_other_timezone(self): e = Event.objects.create(dt=datetime.datetime(2011, 9, 1, 10, 20, 30, tzinfo=UTC)) with timezone.override(ICT): response = self.client.get(reverse('admin:timezones_event_change', args=(e.pk,))) self.assertContains(response, e.dt.astimezone(ICT).date().isoformat()) self.assertContains(response, e.dt.astimezone(ICT).time().isoformat()) @requires_tz_support def test_change_readonly(self): Timestamp.objects.create() # re-fetch the object for backends that lose microseconds (MySQL) t = Timestamp.objects.get() response = self.client.get(reverse('admin:timezones_timestamp_change', args=(t.pk,))) self.assertContains(response, t.created.astimezone(EAT).isoformat()) def test_change_readonly_in_other_timezone(self): Timestamp.objects.create() # re-fetch the object for backends that lose microseconds (MySQL) t = Timestamp.objects.get() with timezone.override(ICT): response = self.client.get(reverse('admin:timezones_timestamp_change', args=(t.pk,))) self.assertContains(response, t.created.astimezone(ICT).isoformat()) Django-1.8.7/tests/timezones/fixtures/0000775000175000017500000000000012625116243017371 5ustar timtim00000000000000Django-1.8.7/tests/timezones/fixtures/tz_users.xml0000664000175000017500000000202612625113144021766 0ustar timtim00000000000000 super Super User super@example.com sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158 True True True 2001-01-01 00:00:00+00:00 2001-01-01 00:00:00+00:00 Django-1.8.7/tests/timezones/__init__.py0000664000175000017500000000000012603513307017615 0ustar timtim00000000000000Django-1.8.7/tests/timezones/forms.py0000664000175000017500000000102012603513307017207 0ustar timtim00000000000000from 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 fields = '__all__' localized_fields = '__all__' Django-1.8.7/tests/timezones/models.py0000664000175000017500000000111012625113144017343 0ustar timtim00000000000000from 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() Django-1.8.7/tests/timezones/admin.py0000664000175000017500000000046712625113144017166 0ustar timtim00000000000000from 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') admin.site.register(Timestamp, TimestampAdmin) Django-1.8.7/tests/template_tests/0000775000175000017500000000000012625116243016540 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/alternate_urls.py0000664000175000017500000000042112625116214022131 0ustar timtim00000000000000from django.conf.urls import url from . import views urlpatterns = [ # View returning a template response url(r'^template_response_view/$', views.template_response_view), # A view that can be hard to find... url(r'^snark/', views.snark, name='snark'), ] Django-1.8.7/tests/template_tests/syntax_tests/0000775000175000017500000000000012625116243021310 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/syntax_tests/test_basic.py0000664000175000017500000002617312625116214024011 0ustar timtim00000000000000from django.template.base import Context, TemplateSyntaxError from django.test import SimpleTestCase from ..utils import SilentAttrClass, SilentGetItemClass, SomeClass, setup basic_templates = { 'basic-syntax01': 'something cool', 'basic-syntax02': '{{ headline }}', 'basic-syntax03': '{{ first }} --- {{ second }}', } class BasicSyntaxTests(SimpleTestCase): @setup(basic_templates) def test_basic_syntax01(self): """ Plain text should go through the template parser untouched. """ output = self.engine.render_to_string('basic-syntax01') self.assertEqual(output, "something cool") @setup(basic_templates) def test_basic_syntax02(self): """ Variables should be replaced with their value in the current context """ output = self.engine.render_to_string('basic-syntax02', {'headline': 'Success'}) self.assertEqual(output, 'Success') @setup(basic_templates) def test_basic_syntax03(self): """ More than one replacement variable is allowed in a template """ output = self.engine.render_to_string('basic-syntax03', {"first": 1, "second": 2}) self.assertEqual(output, '1 --- 2') @setup({'basic-syntax04': 'as{{ missing }}df'}) def test_basic_syntax04(self): """ Fail silently when a variable is not found in the current context """ output = self.engine.render_to_string('basic-syntax04') if self.engine.string_if_invalid: self.assertEqual(output, 'asINVALIDdf') else: self.assertEqual(output, 'asdf') @setup({'basic-syntax06': '{{ multi word variable }}'}) def test_basic_syntax06(self): """ A variable may not contain more than one word """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax06') @setup({'basic-syntax07': '{{ }}'}) def test_basic_syntax07(self): """ Raise TemplateSyntaxError for empty variable tags. """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax07') @setup({'basic-syntax08': '{{ }}'}) def test_basic_syntax08(self): """ Raise TemplateSyntaxError for empty variable tags. """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax08') @setup({'basic-syntax09': '{{ var.method }}'}) def test_basic_syntax09(self): """ Attribute syntax allows a template to call an object's attribute """ output = self.engine.render_to_string('basic-syntax09', {'var': SomeClass()}) self.assertEqual(output, 'SomeClass.method') @setup({'basic-syntax10': '{{ var.otherclass.method }}'}) def test_basic_syntax10(self): """ Multiple levels of attribute access are allowed. """ output = self.engine.render_to_string('basic-syntax10', {'var': SomeClass()}) self.assertEqual(output, 'OtherClass.method') @setup({'basic-syntax11': '{{ var.blech }}'}) def test_basic_syntax11(self): """ Fail silently when a variable's attribute isn't found. """ output = self.engine.render_to_string('basic-syntax11', {'var': SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax12': '{{ var.__dict__ }}'}) def test_basic_syntax12(self): """ Raise TemplateSyntaxError when trying to access a variable beginning with an underscore. """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax12') # Raise TemplateSyntaxError when trying to access a variable # containing an illegal character. @setup({'basic-syntax13': "{{ va>r }}"}) def test_basic_syntax13(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax13') @setup({'basic-syntax14': "{{ (var.r) }}"}) def test_basic_syntax14(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax14') @setup({'basic-syntax15': "{{ sp%am }}"}) def test_basic_syntax15(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax15') @setup({'basic-syntax16': "{{ eggs! }}"}) def test_basic_syntax16(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax16') @setup({'basic-syntax17': "{{ moo? }}"}) def test_basic_syntax17(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax17') @setup({'basic-syntax18': "{{ foo.bar }}"}) def test_basic_syntax18(self): """ Attribute syntax allows a template to call a dictionary key's value. """ output = self.engine.render_to_string('basic-syntax18', {"foo": {"bar": "baz"}}) self.assertEqual(output, "baz") @setup({'basic-syntax19': "{{ foo.spam }}"}) def test_basic_syntax19(self): """ Fail silently when a variable's dictionary key isn't found. """ output = self.engine.render_to_string('basic-syntax19', {"foo": {"bar": "baz"}}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax20': "{{ var.method2 }}"}) def test_basic_syntax20(self): """ Fail silently when accessing a non-simple method """ output = self.engine.render_to_string('basic-syntax20', {'var': SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax20b': "{{ var.method5 }}"}) def test_basic_syntax20b(self): """ Don't silence a TypeError if it was raised inside a callable. """ template = self.engine.get_template('basic-syntax20b') with self.assertRaises(TypeError): template.render(Context({'var': SomeClass()})) # Don't get confused when parsing something that is almost, but not # quite, a template tag. @setup({'basic-syntax21': "a {{ moo %} b"}) def test_basic_syntax21(self): output = self.engine.render_to_string('basic-syntax21') self.assertEqual(output, "a {{ moo %} b") @setup({'basic-syntax22': "{{ moo #}"}) def test_basic_syntax22(self): output = self.engine.render_to_string('basic-syntax22') self.assertEqual(output, "{{ moo #}") @setup({'basic-syntax23': "{{ moo #} {{ cow }}"}) def test_basic_syntax23(self): """ Treat "moo #} {{ cow" as the variable. Not ideal, but costly to work around, so this triggers an error. """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('basic-syntax23') @setup({'basic-syntax24': "{{ moo\n }}"}) def test_basic_syntax24(self): """ Embedded newlines make it not-a-tag. """ output = self.engine.render_to_string('basic-syntax24') self.assertEqual(output, "{{ moo\n }}") # Literal strings are permitted inside variables, mostly for i18n # purposes. @setup({'basic-syntax25': '{{ "fred" }}'}) def test_basic_syntax25(self): output = self.engine.render_to_string('basic-syntax25') self.assertEqual(output, "fred") @setup({'basic-syntax26': r'{{ "\"fred\"" }}'}) def test_basic_syntax26(self): output = self.engine.render_to_string('basic-syntax26') self.assertEqual(output, "\"fred\"") @setup({'basic-syntax27': r'{{ _("\"fred\"") }}'}) def test_basic_syntax27(self): output = self.engine.render_to_string('basic-syntax27') self.assertEqual(output, "\"fred\"") # #12554 -- Make sure a silent_variable_failure Exception is # suppressed on dictionary and attribute lookup. @setup({'basic-syntax28': "{{ a.b }}"}) def test_basic_syntax28(self): output = self.engine.render_to_string('basic-syntax28', {'a': SilentGetItemClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'basic-syntax29': "{{ a.b }}"}) def test_basic_syntax29(self): output = self.engine.render_to_string('basic-syntax29', {'a': SilentAttrClass()}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') # Something that starts like a number but has an extra lookup works # as a lookup. @setup({'basic-syntax30': "{{ 1.2.3 }}"}) def test_basic_syntax30(self): output = self.engine.render_to_string( 'basic-syntax30', {"1": {"2": {"3": "d"}}} ) self.assertEqual(output, 'd') @setup({'basic-syntax31': "{{ 1.2.3 }}"}) def test_basic_syntax31(self): output = self.engine.render_to_string( 'basic-syntax31', {"1": {"2": ("a", "b", "c", "d")}}, ) self.assertEqual(output, 'd') @setup({'basic-syntax32': "{{ 1.2.3 }}"}) def test_basic_syntax32(self): output = self.engine.render_to_string( 'basic-syntax32', {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, ) self.assertEqual(output, 'd') @setup({'basic-syntax33': "{{ 1.2.3 }}"}) def test_basic_syntax33(self): output = self.engine.render_to_string( 'basic-syntax33', {"1": ("xxxx", "yyyy", "abcd")}, ) self.assertEqual(output, 'd') @setup({'basic-syntax34': "{{ 1.2.3 }}"}) def test_basic_syntax34(self): output = self.engine.render_to_string( 'basic-syntax34', {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})} ) self.assertEqual(output, 'd') # Numbers are numbers even if their digits are in the context. @setup({'basic-syntax35': "{{ 1 }}"}) def test_basic_syntax35(self): output = self.engine.render_to_string('basic-syntax35', {"1": "abc"}) self.assertEqual(output, '1') @setup({'basic-syntax36': "{{ 1.2 }}"}) def test_basic_syntax36(self): output = self.engine.render_to_string('basic-syntax36', {"1": "abc"}) self.assertEqual(output, '1.2') @setup({'basic-syntax37': '{{ callable }}'}) def test_basic_syntax37(self): """ Call methods in the top level of the context. """ output = self.engine.render_to_string('basic-syntax37', {"callable": lambda: "foo bar"}) self.assertEqual(output, 'foo bar') @setup({'basic-syntax38': '{{ var.callable }}'}) def test_basic_syntax38(self): """ Call methods returned from dictionary lookups. """ output = self.engine.render_to_string('basic-syntax38', {"var": {"callable": lambda: "foo bar"}}) self.assertEqual(output, 'foo bar') Django-1.8.7/tests/template_tests/syntax_tests/test_width_ratio.py0000664000175000017500000001361612625116214025243 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from django.utils import six from ..utils import setup class WidthRatioTagTests(SimpleTestCase): @setup({'widthratio01': '{% widthratio a b 0 %}'}) def test_widthratio01(self): output = self.engine.render_to_string('widthratio01', {'a': 50, 'b': 100}) self.assertEqual(output, '0') @setup({'widthratio02': '{% widthratio a b 100 %}'}) def test_widthratio02(self): output = self.engine.render_to_string('widthratio02', {'a': 0, 'b': 0}) self.assertEqual(output, '0') @setup({'widthratio03': '{% widthratio a b 100 %}'}) def test_widthratio03(self): output = self.engine.render_to_string('widthratio03', {'a': 0, 'b': 100}) self.assertEqual(output, '0') @setup({'widthratio04': '{% widthratio a b 100 %}'}) def test_widthratio04(self): output = self.engine.render_to_string('widthratio04', {'a': 50, 'b': 100}) self.assertEqual(output, '50') @setup({'widthratio05': '{% widthratio a b 100 %}'}) def test_widthratio05(self): output = self.engine.render_to_string('widthratio05', {'a': 100, 'b': 100}) self.assertEqual(output, '100') @setup({'widthratio06': '{% widthratio a b 100 %}'}) def test_widthratio06(self): """ 62.5 should round to 63 on Python 2 and 62 on Python 3 See http://docs.python.org/py3k/whatsnew/3.0.html """ output = self.engine.render_to_string('widthratio06', {'a': 50, 'b': 80}) self.assertEqual(output, '62' if six.PY3 else '63') @setup({'widthratio07': '{% widthratio a b 100 %}'}) def test_widthratio07(self): """ 71.4 should round to 71 """ output = self.engine.render_to_string('widthratio07', {'a': 50, 'b': 70}) self.assertEqual(output, '71') # Raise exception if we don't have 3 args, last one an integer @setup({'widthratio08': '{% widthratio %}'}) def test_widthratio08(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('widthratio08') @setup({'widthratio09': '{% widthratio a b %}'}) def test_widthratio09(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('widthratio09', {'a': 50, 'b': 100}) @setup({'widthratio10': '{% widthratio a b 100.0 %}'}) def test_widthratio10(self): output = self.engine.render_to_string('widthratio10', {'a': 50, 'b': 100}) self.assertEqual(output, '50') @setup({'widthratio11': '{% widthratio a b c %}'}) def test_widthratio11(self): """ #10043: widthratio should allow max_width to be a variable """ output = self.engine.render_to_string('widthratio11', {'a': 50, 'c': 100, 'b': 100}) self.assertEqual(output, '50') # #18739: widthratio should handle None args consistently with # non-numerics @setup({'widthratio12a': '{% widthratio a b c %}'}) def test_widthratio12a(self): output = self.engine.render_to_string('widthratio12a', {'a': 'a', 'c': 100, 'b': 100}) self.assertEqual(output, '') @setup({'widthratio12b': '{% widthratio a b c %}'}) def test_widthratio12b(self): output = self.engine.render_to_string('widthratio12b', {'a': None, 'c': 100, 'b': 100}) self.assertEqual(output, '') @setup({'widthratio13a': '{% widthratio a b c %}'}) def test_widthratio13a(self): output = self.engine.render_to_string('widthratio13a', {'a': 0, 'c': 100, 'b': 'b'}) self.assertEqual(output, '') @setup({'widthratio13b': '{% widthratio a b c %}'}) def test_widthratio13b(self): output = self.engine.render_to_string('widthratio13b', {'a': 0, 'c': 100, 'b': None}) self.assertEqual(output, '') @setup({'widthratio14a': '{% widthratio a b c %}'}) def test_widthratio14a(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('widthratio14a', {'a': 0, 'c': 'c', 'b': 100}) @setup({'widthratio14b': '{% widthratio a b c %}'}) def test_widthratio14b(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('widthratio14b', {'a': 0, 'c': None, 'b': 100}) @setup({'widthratio15': '{% load custom %}{% widthratio a|noop:"x y" b 0 %}'}) def test_widthratio15(self): """ Test whitespace in filter argument """ output = self.engine.render_to_string('widthratio15', {'a': 50, 'b': 100}) self.assertEqual(output, '0') # Widthratio with variable assignment @setup({'widthratio16': '{% widthratio a b 100 as variable %}-{{ variable }}-'}) def test_widthratio16(self): output = self.engine.render_to_string('widthratio16', {'a': 50, 'b': 100}) self.assertEqual(output, '-50-') @setup({'widthratio17': '{% widthratio a b 100 as variable %}-{{ variable }}-'}) def test_widthratio17(self): output = self.engine.render_to_string('widthratio17', {'a': 100, 'b': 100}) self.assertEqual(output, '-100-') @setup({'widthratio18': '{% widthratio a b 100 as %}'}) def test_widthratio18(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('widthratio18') @setup({'widthratio19': '{% widthratio a b 100 not_as variable %}'}) def test_widthratio19(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('widthratio19') @setup({'widthratio20': '{% widthratio a b 100 %}'}) def test_widthratio20(self): output = self.engine.render_to_string('widthratio20', {'a': float('inf'), 'b': float('inf')}) self.assertEqual(output, '') @setup({'widthratio21': '{% widthratio a b 100 %}'}) def test_widthratio21(self): output = self.engine.render_to_string('widthratio21', {'a': float('inf'), 'b': 2}) self.assertEqual(output, '') Django-1.8.7/tests/template_tests/syntax_tests/test_cycle.py0000664000175000017500000001551112625116214024021 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import RemovedInDjango110Warning from ..utils import setup class CycleTagTests(SimpleTestCase): @setup({'cycle01': '{% cycle a %}'}) def test_cycle01(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cycle01') @setup({'cycle02': '{% cycle a,b,c as abc %}{% cycle abc %}'}) def test_cycle02(self): output = self.engine.render_to_string('cycle02') self.assertEqual(output, 'ab') @setup({'cycle03': '{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}'}) def test_cycle03(self): output = self.engine.render_to_string('cycle03') self.assertEqual(output, 'abc') @setup({'cycle04': '{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}'}) def test_cycle04(self): output = self.engine.render_to_string('cycle04') self.assertEqual(output, 'abca') @setup({'cycle05': '{% cycle %}'}) def test_cycle05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cycle05') @setup({'cycle06': '{% cycle a %}'}) def test_cycle06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cycle06') @setup({'cycle07': '{% cycle a,b,c as foo %}{% cycle bar %}'}) def test_cycle07(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cycle07') @setup({'cycle08': '{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}'}) def test_cycle08(self): output = self.engine.render_to_string('cycle08') self.assertEqual(output, 'abbbcc') @setup({'cycle09': '{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}'}) def test_cycle09(self): output = self.engine.render_to_string('cycle09', {'test': list(range(5))}) self.assertEqual(output, 'a0,b1,a2,b3,a4,') @setup({'cycle10': "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}"}) def test_cycle10(self): output = self.engine.render_to_string('cycle10') self.assertEqual(output, 'ab') @setup({'cycle11': "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}"}) def test_cycle11(self): output = self.engine.render_to_string('cycle11') self.assertEqual(output, 'abc') @setup({'cycle12': "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}"}) def test_cycle12(self): output = self.engine.render_to_string('cycle12') self.assertEqual(output, 'abca') @setup({'cycle13': "{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}"}) def test_cycle13(self): output = self.engine.render_to_string('cycle13', {'test': list(range(5))}) self.assertEqual(output, 'a0,b1,a2,b3,a4,') @setup({'cycle14': '{% cycle one two as foo %}{% cycle foo %}'}) def test_cycle14(self): output = self.engine.render_to_string('cycle14', {'one': '1', 'two': '2'}) self.assertEqual(output, '12') @setup({'cycle15': '{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}'}) def test_cycle15(self): output = self.engine.render_to_string('cycle15', {'test': list(range(5)), 'aye': 'a', 'bee': 'b'}) self.assertEqual(output, 'a0,b1,a2,b3,a4,') @setup({'cycle16': '{% cycle one|lower two as foo %}{% cycle foo %}'}) def test_cycle16(self): output = self.engine.render_to_string('cycle16', {'one': 'A', 'two': '2'}) self.assertEqual(output, 'a2') @setup({'cycle17': "{% cycle 'a' 'b' 'c' as abc silent %}" "{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}"}) def test_cycle17(self): output = self.engine.render_to_string('cycle17') self.assertEqual(output, '') @setup({'cycle18': "{% cycle 'a' 'b' 'c' as foo invalid_flag %}"}) def test_cycle18(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cycle18') @setup({'cycle19': "{% cycle 'a' 'b' as silent %}{% cycle silent %}"}) def test_cycle19(self): output = self.engine.render_to_string('cycle19') self.assertEqual(output, 'ab') @setup({'cycle20': '{% cycle one two as foo %} & {% cycle foo %}'}) def test_cycle20(self): output = self.engine.render_to_string('cycle20', {'two': 'C & D', 'one': 'A & B'}) self.assertEqual(output, 'A & B & C & D') @setup({'cycle21': '{% filter force_escape %}' '{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}'}) def test_cycle21(self): output = self.engine.render_to_string('cycle21', {'two': 'C & D', 'one': 'A & B'}) self.assertEqual(output, 'A &amp; B & C &amp; D') @setup({'cycle22': "{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ x }}{% endfor %}"}) def test_cycle22(self): output = self.engine.render_to_string('cycle22', {'values': [1, 2, 3, 4]}) self.assertEqual(output, '1234') @setup({'cycle23': "{% for x in values %}" "{% cycle 'a' 'b' 'c' as abc silent %}{{ abc }}{{ x }}{% endfor %}"}) def test_cycle23(self): output = self.engine.render_to_string('cycle23', {'values': [1, 2, 3, 4]}) self.assertEqual(output, 'a1b2c3a4') @setup({ 'cycle24': "{% for x in values %}" "{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", 'included-cycle': '{{ abc }}', }) def test_cycle24(self): output = self.engine.render_to_string('cycle24', {'values': [1, 2, 3, 4]}) self.assertEqual(output, 'abca') @setup({'cycle25': '{% cycle a as abc %}'}) def test_cycle25(self): output = self.engine.render_to_string('cycle25', {'a': '<'}) self.assertEqual(output, '<') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'cycle26': '{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}'}) def test_cycle26(self): output = self.engine.render_to_string('cycle26', {'a': '<', 'b': '>'}) self.assertEqual(output, '<>') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'cycle27': '{% load cycle from future %}' '{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}'}) def test_cycle27(self): output = self.engine.render_to_string('cycle27', {'a': '<', 'b': '>'}) self.assertEqual(output, '<>') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'cycle28': '{% load cycle from future %}{% cycle a|safe b as ab %}{% cycle ab %}'}) def test_cycle28(self): output = self.engine.render_to_string('cycle28', {'a': '<', 'b': '>'}) self.assertEqual(output, '<>') Django-1.8.7/tests/template_tests/syntax_tests/test_now.py0000664000175000017500000000376512625116214023535 0ustar timtim00000000000000from datetime import datetime from django.test import SimpleTestCase from django.utils.formats import date_format from ..utils import setup class NowTagTests(SimpleTestCase): @setup({'now01': '{% now "j n Y" %}'}) def test_now01(self): """ Simple case """ output = self.engine.render_to_string('now01') self.assertEqual(output, "%d %d %d" % ( datetime.now().day, datetime.now().month, datetime.now().year, )) # Check parsing of locale strings @setup({'now02': '{% now "DATE_FORMAT" %}'}) def test_now02(self): output = self.engine.render_to_string('now02') self.assertEqual(output, date_format(datetime.now())) @setup({'now03': '{% now \'j n Y\' %}'}) def test_now03(self): """ #15092 - Also accept simple quotes """ output = self.engine.render_to_string('now03') self.assertEqual(output, "%d %d %d" % ( datetime.now().day, datetime.now().month, datetime.now().year, )) @setup({'now04': '{% now \'DATE_FORMAT\' %}'}) def test_now04(self): output = self.engine.render_to_string('now04') self.assertEqual(output, date_format(datetime.now())) @setup({'now05': '{% now \'j "n" Y\'%}'}) def test_now05(self): output = self.engine.render_to_string('now05') self.assertEqual(output, '%d "%d" %d' % ( datetime.now().day, datetime.now().month, datetime.now().year, )) @setup({'now06': '{% now "j \'n\' Y"%}'}) def test_now06(self): output = self.engine.render_to_string('now06') self.assertEqual(output, "%d '%d' %d" % ( datetime.now().day, datetime.now().month, datetime.now().year, )) @setup({'now07': '{% now "j n Y" as N %}-{{N}}-'}) def test_now07(self): output = self.engine.render_to_string('now07') self.assertEqual(output, '-%d %d %d-' % ( datetime.now().day, datetime.now().month, datetime.now().year, )) Django-1.8.7/tests/template_tests/syntax_tests/test_numpy.py0000664000175000017500000000262512625116214024074 0ustar timtim00000000000000import warnings from unittest import skipIf from django.test import SimpleTestCase from ..utils import setup try: import numpy except ImportError: numpy = False @skipIf(numpy is False, "Numpy must be installed to run these tests.") class NumpyTests(SimpleTestCase): # Ignore numpy deprecation warnings (#23890) warnings.filterwarnings( "ignore", "Using a non-integer number instead of an " "integer will result in an error in the future", DeprecationWarning ) @setup({'numpy-array-index01': '{{ var.1 }}'}) def test_numpy_array_index01(self): """ Numpy's array-index syntax allows a template to access a certain item of a subscriptable object. """ output = self.engine.render_to_string( 'numpy-array-index01', {'var': numpy.array(["first item", "second item"])}, ) self.assertEqual(output, 'second item') @setup({'numpy-array-index02': '{{ var.5 }}'}) def test_numpy_array_index02(self): """ Fail silently when the array index is out of range. """ output = self.engine.render_to_string( 'numpy-array-index02', {'var': numpy.array(["first item", "second item"])}, ) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') Django-1.8.7/tests/template_tests/syntax_tests/test_autoescape.py0000664000175000017500000001270712625116214025057 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import SafeClass, UnsafeClass, setup class AutoescapeTagTests(SimpleTestCase): @setup({'autoescape-tag01': '{% autoescape off %}hello{% endautoescape %}'}) def test_autoescape_tag01(self): output = self.engine.render_to_string('autoescape-tag01') self.assertEqual(output, 'hello') @setup({'autoescape-tag02': '{% autoescape off %}{{ first }}{% endautoescape %}'}) def test_autoescape_tag02(self): output = self.engine.render_to_string('autoescape-tag02', {'first': 'hello'}) self.assertEqual(output, 'hello') @setup({'autoescape-tag03': '{% autoescape on %}{{ first }}{% endautoescape %}'}) def test_autoescape_tag03(self): output = self.engine.render_to_string('autoescape-tag03', {'first': 'hello'}) self.assertEqual(output, '<b>hello</b>') # Autoescape disabling and enabling nest in a predictable way. @setup({'autoescape-tag04': '{% autoescape off %}' '{{ first }} {% autoescape on %}{{ first }}{% endautoescape %}{% endautoescape %}'}) def test_autoescape_tag04(self): output = self.engine.render_to_string('autoescape-tag04', {'first': ''}) self.assertEqual(output, ' <a>') @setup({'autoescape-tag05': '{% autoescape on %}{{ first }}{% endautoescape %}'}) def test_autoescape_tag05(self): output = self.engine.render_to_string('autoescape-tag05', {'first': 'first'}) self.assertEqual(output, '<b>first</b>') # Strings (ASCII or unicode) already marked as "safe" are not # auto-escaped @setup({'autoescape-tag06': '{{ first }}'}) def test_autoescape_tag06(self): output = self.engine.render_to_string('autoescape-tag06', {'first': mark_safe('first')}) self.assertEqual(output, 'first') @setup({'autoescape-tag07': '{% autoescape on %}{{ first }}{% endautoescape %}'}) def test_autoescape_tag07(self): output = self.engine.render_to_string('autoescape-tag07', {'first': mark_safe('Apple')}) self.assertEqual(output, 'Apple') @setup({'autoescape-tag08': r'{% autoescape on %}' r'{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}'}) def test_autoescape_tag08(self): """ Literal string arguments to filters, if used in the result, are safe. """ output = self.engine.render_to_string('autoescape-tag08', {"var": None}) self.assertEqual(output, ' endquote" hah') # Objects which return safe strings as their __str__ method # won't get double-escaped. @setup({'autoescape-tag09': r'{{ unsafe }}'}) def test_autoescape_tag09(self): output = self.engine.render_to_string('autoescape-tag09', {'unsafe': UnsafeClass()}) self.assertEqual(output, 'you & me') @setup({'autoescape-tag10': r'{{ safe }}'}) def test_autoescape_tag10(self): output = self.engine.render_to_string('autoescape-tag10', {'safe': SafeClass()}) self.assertEqual(output, 'you > me') @setup({'autoescape-filtertag01': '{{ first }}{% filter safe %}{{ first }} x'}) @setup({'autoescape-ifequal01': '{% ifequal var "this & that" %}yes{% endifequal %}'}) def test_autoescape_ifequal01(self): """ ifequal compares unescaped vales. """ output = self.engine.render_to_string('autoescape-ifequal01', {'var': 'this & that'}) self.assertEqual(output, 'yes') # Arguments to filters are 'safe' and manipulate their input unescaped. @setup({'autoescape-filters01': '{{ var|cut:"&" }}'}) def test_autoescape_filters01(self): output = self.engine.render_to_string('autoescape-filters01', {'var': 'this & that'}) self.assertEqual(output, 'this that') @setup({'autoescape-filters02': '{{ var|join:" & " }}'}) def test_autoescape_filters02(self): output = self.engine.render_to_string('autoescape-filters02', {'var': ('Tom', 'Dick', 'Harry')}) self.assertEqual(output, 'Tom & Dick & Harry') @setup({'autoescape-literals01': '{{ "this & that" }}'}) def test_autoescape_literals01(self): """ Literal strings are safe. """ output = self.engine.render_to_string('autoescape-literals01') self.assertEqual(output, 'this & that') @setup({'autoescape-stringiterations01': '{% for l in var %}{{ l }},{% endfor %}'}) def test_autoescape_stringiterations01(self): """ Iterating over strings outputs safe characters. """ output = self.engine.render_to_string('autoescape-stringiterations01', {'var': 'K&R'}) self.assertEqual(output, 'K,&,R,') @setup({'autoescape-lookup01': '{{ var.key }}'}) def test_autoescape_lookup01(self): """ Escape requirement survives lookup. """ output = self.engine.render_to_string('autoescape-lookup01', {'var': {'key': 'this & that'}}) self.assertEqual(output, 'this & that') Django-1.8.7/tests/template_tests/syntax_tests/test_if.py0000664000175000017500000006143712625116214023330 0ustar timtim00000000000000import warnings from django.template import TemplateSyntaxError from django.test import SimpleTestCase, ignore_warnings from django.test.utils import reset_warning_registry from django.utils.deprecation import RemovedInDjango110Warning from ..utils import TestObj, setup class IfTagTests(SimpleTestCase): @setup({'if-tag01': '{% if foo %}yes{% else %}no{% endif %}'}) def test_if_tag01(self): output = self.engine.render_to_string('if-tag01', {'foo': True}) self.assertEqual(output, 'yes') @setup({'if-tag02': '{% if foo %}yes{% else %}no{% endif %}'}) def test_if_tag02(self): output = self.engine.render_to_string('if-tag02', {'foo': False}) self.assertEqual(output, 'no') @setup({'if-tag03': '{% if foo %}yes{% else %}no{% endif %}'}) def test_if_tag03(self): output = self.engine.render_to_string('if-tag03') self.assertEqual(output, 'no') @setup({'if-tag04': '{% if foo %}foo{% elif bar %}bar{% endif %}'}) def test_if_tag04(self): output = self.engine.render_to_string('if-tag04', {'foo': True}) self.assertEqual(output, 'foo') @setup({'if-tag05': '{% if foo %}foo{% elif bar %}bar{% endif %}'}) def test_if_tag05(self): output = self.engine.render_to_string('if-tag05', {'bar': True}) self.assertEqual(output, 'bar') @setup({'if-tag06': '{% if foo %}foo{% elif bar %}bar{% endif %}'}) def test_if_tag06(self): output = self.engine.render_to_string('if-tag06') self.assertEqual(output, '') @setup({'if-tag07': '{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}'}) def test_if_tag07(self): output = self.engine.render_to_string('if-tag07', {'foo': True}) self.assertEqual(output, 'foo') @setup({'if-tag08': '{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}'}) def test_if_tag08(self): output = self.engine.render_to_string('if-tag08', {'bar': True}) self.assertEqual(output, 'bar') @setup({'if-tag09': '{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}'}) def test_if_tag09(self): output = self.engine.render_to_string('if-tag09') self.assertEqual(output, 'nothing') @setup({'if-tag10': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) def test_if_tag10(self): output = self.engine.render_to_string('if-tag10', {'foo': True}) self.assertEqual(output, 'foo') @setup({'if-tag11': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) def test_if_tag11(self): output = self.engine.render_to_string('if-tag11', {'bar': True}) self.assertEqual(output, 'bar') @setup({'if-tag12': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) def test_if_tag12(self): output = self.engine.render_to_string('if-tag12', {'baz': True}) self.assertEqual(output, 'baz') @setup({'if-tag13': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) def test_if_tag13(self): output = self.engine.render_to_string('if-tag13') self.assertEqual(output, 'nothing') # Filters @setup({'if-tag-filter01': '{% if foo|length == 5 %}yes{% else %}no{% endif %}'}) def test_if_tag_filter01(self): output = self.engine.render_to_string('if-tag-filter01', {'foo': 'abcde'}) self.assertEqual(output, 'yes') @setup({'if-tag-filter02': '{% if foo|upper == \'ABC\' %}yes{% else %}no{% endif %}'}) def test_if_tag_filter02(self): output = self.engine.render_to_string('if-tag-filter02') self.assertEqual(output, 'no') # Equality @setup({'if-tag-eq01': '{% if foo == bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq01(self): output = self.engine.render_to_string('if-tag-eq01') self.assertEqual(output, 'yes') @setup({'if-tag-eq02': '{% if foo == bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq02(self): output = self.engine.render_to_string('if-tag-eq02', {'foo': 1}) self.assertEqual(output, 'no') @setup({'if-tag-eq03': '{% if foo == bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq03(self): output = self.engine.render_to_string('if-tag-eq03', {'foo': 1, 'bar': 1}) self.assertEqual(output, 'yes') @setup({'if-tag-eq04': '{% if foo == bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq04(self): output = self.engine.render_to_string('if-tag-eq04', {'foo': 1, 'bar': 2}) self.assertEqual(output, 'no') @setup({'if-tag-eq05': '{% if foo == \'\' %}yes{% else %}no{% endif %}'}) def test_if_tag_eq05(self): output = self.engine.render_to_string('if-tag-eq05') self.assertEqual(output, 'no') # Comparison @setup({'if-tag-gt-01': '{% if 2 > 1 %}yes{% else %}no{% endif %}'}) def test_if_tag_gt_01(self): output = self.engine.render_to_string('if-tag-gt-01') self.assertEqual(output, 'yes') @setup({'if-tag-gt-02': '{% if 1 > 1 %}yes{% else %}no{% endif %}'}) def test_if_tag_gt_02(self): output = self.engine.render_to_string('if-tag-gt-02') self.assertEqual(output, 'no') @setup({'if-tag-gte-01': '{% if 1 >= 1 %}yes{% else %}no{% endif %}'}) def test_if_tag_gte_01(self): output = self.engine.render_to_string('if-tag-gte-01') self.assertEqual(output, 'yes') @setup({'if-tag-gte-02': '{% if 1 >= 2 %}yes{% else %}no{% endif %}'}) def test_if_tag_gte_02(self): output = self.engine.render_to_string('if-tag-gte-02') self.assertEqual(output, 'no') @setup({'if-tag-lt-01': '{% if 1 < 2 %}yes{% else %}no{% endif %}'}) def test_if_tag_lt_01(self): output = self.engine.render_to_string('if-tag-lt-01') self.assertEqual(output, 'yes') @setup({'if-tag-lt-02': '{% if 1 < 1 %}yes{% else %}no{% endif %}'}) def test_if_tag_lt_02(self): output = self.engine.render_to_string('if-tag-lt-02') self.assertEqual(output, 'no') @setup({'if-tag-lte-01': '{% if 1 <= 1 %}yes{% else %}no{% endif %}'}) def test_if_tag_lte_01(self): output = self.engine.render_to_string('if-tag-lte-01') self.assertEqual(output, 'yes') @setup({'if-tag-lte-02': '{% if 2 <= 1 %}yes{% else %}no{% endif %}'}) def test_if_tag_lte_02(self): output = self.engine.render_to_string('if-tag-lte-02') self.assertEqual(output, 'no') # Contains @setup({'if-tag-in-01': '{% if 1 in x %}yes{% else %}no{% endif %}'}) def test_if_tag_in_01(self): output = self.engine.render_to_string('if-tag-in-01', {'x': [1]}) self.assertEqual(output, 'yes') @setup({'if-tag-in-02': '{% if 2 in x %}yes{% else %}no{% endif %}'}) def test_if_tag_in_02(self): output = self.engine.render_to_string('if-tag-in-02', {'x': [1]}) self.assertEqual(output, 'no') @setup({'if-tag-not-in-01': '{% if 1 not in x %}yes{% else %}no{% endif %}'}) def test_if_tag_not_in_01(self): output = self.engine.render_to_string('if-tag-not-in-01', {'x': [1]}) self.assertEqual(output, 'no') @setup({'if-tag-not-in-02': '{% if 2 not in x %}yes{% else %}no{% endif %}'}) def test_if_tag_not_in_02(self): output = self.engine.render_to_string('if-tag-not-in-02', {'x': [1]}) self.assertEqual(output, 'yes') # AND @setup({'if-tag-and01': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and01(self): output = self.engine.render_to_string('if-tag-and01', {'foo': True, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-and02': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and02(self): output = self.engine.render_to_string('if-tag-and02', {'foo': True, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-and03': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and03(self): output = self.engine.render_to_string('if-tag-and03', {'foo': False, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-and04': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and04(self): output = self.engine.render_to_string('if-tag-and04', {'foo': False, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-and05': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and05(self): output = self.engine.render_to_string('if-tag-and05', {'foo': False}) self.assertEqual(output, 'no') @setup({'if-tag-and06': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and06(self): output = self.engine.render_to_string('if-tag-and06', {'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-and07': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and07(self): output = self.engine.render_to_string('if-tag-and07', {'foo': True}) self.assertEqual(output, 'no') @setup({'if-tag-and08': '{% if foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_and08(self): output = self.engine.render_to_string('if-tag-and08', {'bar': True}) self.assertEqual(output, 'no') # OR @setup({'if-tag-or01': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or01(self): output = self.engine.render_to_string('if-tag-or01', {'foo': True, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-or02': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or02(self): output = self.engine.render_to_string('if-tag-or02', {'foo': True, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-or03': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or03(self): output = self.engine.render_to_string('if-tag-or03', {'foo': False, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-or04': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or04(self): output = self.engine.render_to_string('if-tag-or04', {'foo': False, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-or05': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or05(self): output = self.engine.render_to_string('if-tag-or05', {'foo': False}) self.assertEqual(output, 'no') @setup({'if-tag-or06': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or06(self): output = self.engine.render_to_string('if-tag-or06', {'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-or07': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or07(self): output = self.engine.render_to_string('if-tag-or07', {'foo': True}) self.assertEqual(output, 'yes') @setup({'if-tag-or08': '{% if foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_or08(self): output = self.engine.render_to_string('if-tag-or08', {'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-or09': '{% if foo or bar or baz %}yes{% else %}no{% endif %}'}) def test_if_tag_or09(self): """ multiple ORs """ output = self.engine.render_to_string('if-tag-or09', {'baz': True}) self.assertEqual(output, 'yes') # NOT @setup({'if-tag-not01': '{% if not foo %}no{% else %}yes{% endif %}'}) def test_if_tag_not01(self): output = self.engine.render_to_string('if-tag-not01', {'foo': True}) self.assertEqual(output, 'yes') @setup({'if-tag-not02': '{% if not not foo %}no{% else %}yes{% endif %}'}) def test_if_tag_not02(self): output = self.engine.render_to_string('if-tag-not02', {'foo': True}) self.assertEqual(output, 'no') @setup({'if-tag-not06': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not06(self): output = self.engine.render_to_string('if-tag-not06') self.assertEqual(output, 'no') @setup({'if-tag-not07': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not07(self): output = self.engine.render_to_string('if-tag-not07', {'foo': True, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not08': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not08(self): output = self.engine.render_to_string('if-tag-not08', {'foo': True, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-not09': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not09(self): output = self.engine.render_to_string('if-tag-not09', {'foo': False, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not10': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not10(self): output = self.engine.render_to_string('if-tag-not10', {'foo': False, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-not11': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not11(self): output = self.engine.render_to_string('if-tag-not11') self.assertEqual(output, 'no') @setup({'if-tag-not12': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not12(self): output = self.engine.render_to_string('if-tag-not12', {'foo': True, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not13': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not13(self): output = self.engine.render_to_string('if-tag-not13', {'foo': True, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-not14': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not14(self): output = self.engine.render_to_string('if-tag-not14', {'foo': False, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-not15': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not15(self): output = self.engine.render_to_string('if-tag-not15', {'foo': False, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-not16': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not16(self): output = self.engine.render_to_string('if-tag-not16') self.assertEqual(output, 'yes') @setup({'if-tag-not17': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not17(self): output = self.engine.render_to_string('if-tag-not17', {'foo': True, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-not18': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not18(self): output = self.engine.render_to_string('if-tag-not18', {'foo': True, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-not19': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not19(self): output = self.engine.render_to_string('if-tag-not19', {'foo': False, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not20': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not20(self): output = self.engine.render_to_string('if-tag-not20', {'foo': False, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-not21': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not21(self): output = self.engine.render_to_string('if-tag-not21') self.assertEqual(output, 'yes') @setup({'if-tag-not22': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not22(self): output = self.engine.render_to_string('if-tag-not22', {'foo': True, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-not23': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not23(self): output = self.engine.render_to_string('if-tag-not23', {'foo': True, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-not24': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not24(self): output = self.engine.render_to_string('if-tag-not24', {'foo': False, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-not25': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not25(self): output = self.engine.render_to_string('if-tag-not25', {'foo': False, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-not26': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not26(self): output = self.engine.render_to_string('if-tag-not26') self.assertEqual(output, 'yes') @setup({'if-tag-not27': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not27(self): output = self.engine.render_to_string('if-tag-not27', {'foo': True, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not28': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not28(self): output = self.engine.render_to_string('if-tag-not28', {'foo': True, 'bar': False}) self.assertEqual(output, 'no') @setup({'if-tag-not29': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not29(self): output = self.engine.render_to_string('if-tag-not29', {'foo': False, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not30': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not30(self): output = self.engine.render_to_string('if-tag-not30', {'foo': False, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-not31': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not31(self): output = self.engine.render_to_string('if-tag-not31') self.assertEqual(output, 'yes') @setup({'if-tag-not32': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not32(self): output = self.engine.render_to_string('if-tag-not32', {'foo': True, 'bar': True}) self.assertEqual(output, 'no') @setup({'if-tag-not33': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not33(self): output = self.engine.render_to_string('if-tag-not33', {'foo': True, 'bar': False}) self.assertEqual(output, 'yes') @setup({'if-tag-not34': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not34(self): output = self.engine.render_to_string('if-tag-not34', {'foo': False, 'bar': True}) self.assertEqual(output, 'yes') @setup({'if-tag-not35': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) def test_if_tag_not35(self): output = self.engine.render_to_string('if-tag-not35', {'foo': False, 'bar': False}) self.assertEqual(output, 'yes') # Various syntax errors @setup({'if-tag-error01': '{% if %}yes{% endif %}'}) def test_if_tag_error01(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error01') @setup({'if-tag-error02': '{% if foo and %}yes{% else %}no{% endif %}'}) def test_if_tag_error02(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('if-tag-error02', {'foo': True}) @setup({'if-tag-error03': '{% if foo or %}yes{% else %}no{% endif %}'}) def test_if_tag_error03(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('if-tag-error03', {'foo': True}) @setup({'if-tag-error04': '{% if not foo and %}yes{% else %}no{% endif %}'}) def test_if_tag_error04(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('if-tag-error04', {'foo': True}) @setup({'if-tag-error05': '{% if not foo or %}yes{% else %}no{% endif %}'}) def test_if_tag_error05(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('if-tag-error05', {'foo': True}) @setup({'if-tag-error06': '{% if abc def %}yes{% endif %}'}) def test_if_tag_error06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error06') @setup({'if-tag-error07': '{% if not %}yes{% endif %}'}) def test_if_tag_error07(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error07') @setup({'if-tag-error08': '{% if and %}yes{% endif %}'}) def test_if_tag_error08(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error08') @setup({'if-tag-error09': '{% if or %}yes{% endif %}'}) def test_if_tag_error09(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error09') @setup({'if-tag-error10': '{% if == %}yes{% endif %}'}) def test_if_tag_error10(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error10') @setup({'if-tag-error11': '{% if 1 == %}yes{% endif %}'}) def test_if_tag_error11(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error11') @setup({'if-tag-error12': '{% if a not b %}yes{% endif %}'}) def test_if_tag_error12(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('if-tag-error12') @setup({'if-tag-shortcircuit01': '{% if x.is_true or x.is_bad %}yes{% else %}no{% endif %}'}) def test_if_tag_shortcircuit01(self): """ If evaluations are shortcircuited where possible """ output = self.engine.render_to_string('if-tag-shortcircuit01', {'x': TestObj()}) self.assertEqual(output, 'yes') @setup({'if-tag-shortcircuit02': '{% if x.is_false and x.is_bad %}yes{% else %}no{% endif %}'}) def test_if_tag_shortcircuit02(self): """ The is_bad() function should not be evaluated. If it is, an exception is raised. """ output = self.engine.render_to_string('if-tag-shortcircuit02', {'x': TestObj()}) self.assertEqual(output, 'no') @setup({'if-tag-badarg01': '{% if x|default_if_none:y %}yes{% endif %}'}) def test_if_tag_badarg01(self): """ Non-existent args """ output = self.engine.render_to_string('if-tag-badarg01') self.assertEqual(output, '') @setup({'if-tag-badarg02': '{% if x|default_if_none:y %}yes{% endif %}'}) def test_if_tag_badarg02(self): output = self.engine.render_to_string('if-tag-badarg02', {'y': 0}) self.assertEqual(output, '') @setup({'if-tag-badarg03': '{% if x|default_if_none:y %}yes{% endif %}'}) def test_if_tag_badarg03(self): output = self.engine.render_to_string('if-tag-badarg03', {'y': 1}) self.assertEqual(output, 'yes') @setup({'if-tag-badarg04': '{% if x|default_if_none:y %}yes{% else %}no{% endif %}'}) def test_if_tag_badarg04(self): output = self.engine.render_to_string('if-tag-badarg04') self.assertEqual(output, 'no') @setup({'if-tag-eq-deprecated': '{% if foo = bar %}yes{% else %}no{% endif %}'}, test_once=True) def test_if_tag_eq_deprecated(self): reset_warning_registry() with warnings.catch_warnings(record=True) as warns: warnings.simplefilter('always') output = self.engine.render_to_string('if-tag-eq-deprecated') self.assertEqual(output, 'yes') self.assertEqual(len(warns), 1) self.assertEqual( str(warns[0].message), "Operator '=' is deprecated and will be removed in Django 1.10. " "Use '==' instead." ) @ignore_warnings(category=RemovedInDjango110Warning) class TestEqualitySingleEqualsSign(SimpleTestCase): # The following tests should be changed to template.TemplateSyntaxError # (or simply removed) when the deprecation path ends in Django 1.10. @setup({'if-tag-eq01': '{% if foo = bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq01(self): output = self.engine.render_to_string('if-tag-eq01', {'foo': 1}) self.assertEqual(output, 'no') @setup({'if-tag-eq02': '{% if foo = bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq02(self): output = self.engine.render_to_string('if-tag-eq02', {'foo': 1, 'bar': 1}) self.assertEqual(output, 'yes') @setup({'if-tag-eq03': '{% if foo = bar %}yes{% else %}no{% endif %}'}) def test_if_tag_eq03(self): output = self.engine.render_to_string('if-tag-eq03', {'foo': 1, 'bar': 2}) self.assertEqual(output, 'no') @setup({'if-tag-eq04': '{% if foo == \'\' %}yes{% else %}no{% endif %}'}) def test_if_tag_eq04(self): output = self.engine.render_to_string('if-tag-eq04') self.assertEqual(output, 'no') Django-1.8.7/tests/template_tests/syntax_tests/test_template_tag.py0000664000175000017500000000504212625116214025366 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class TemplateTagTests(SimpleTestCase): @setup({'templatetag01': '{% templatetag openblock %}'}) def test_templatetag01(self): output = self.engine.render_to_string('templatetag01') self.assertEqual(output, '{%') @setup({'templatetag02': '{% templatetag closeblock %}'}) def test_templatetag02(self): output = self.engine.render_to_string('templatetag02') self.assertEqual(output, '%}') @setup({'templatetag03': '{% templatetag openvariable %}'}) def test_templatetag03(self): output = self.engine.render_to_string('templatetag03') self.assertEqual(output, '{{') @setup({'templatetag04': '{% templatetag closevariable %}'}) def test_templatetag04(self): output = self.engine.render_to_string('templatetag04') self.assertEqual(output, '}}') @setup({'templatetag05': '{% templatetag %}'}) def test_templatetag05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('templatetag05') @setup({'templatetag06': '{% templatetag foo %}'}) def test_templatetag06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('templatetag06') @setup({'templatetag07': '{% templatetag openbrace %}'}) def test_templatetag07(self): output = self.engine.render_to_string('templatetag07') self.assertEqual(output, '{') @setup({'templatetag08': '{% templatetag closebrace %}'}) def test_templatetag08(self): output = self.engine.render_to_string('templatetag08') self.assertEqual(output, '}') @setup({'templatetag09': '{% templatetag openbrace %}{% templatetag openbrace %}'}) def test_templatetag09(self): output = self.engine.render_to_string('templatetag09') self.assertEqual(output, '{{') @setup({'templatetag10': '{% templatetag closebrace %}{% templatetag closebrace %}'}) def test_templatetag10(self): output = self.engine.render_to_string('templatetag10') self.assertEqual(output, '}}') @setup({'templatetag11': '{% templatetag opencomment %}'}) def test_templatetag11(self): output = self.engine.render_to_string('templatetag11') self.assertEqual(output, '{#') @setup({'templatetag12': '{% templatetag closecomment %}'}) def test_templatetag12(self): output = self.engine.render_to_string('templatetag12') self.assertEqual(output, '#}') Django-1.8.7/tests/template_tests/syntax_tests/test_exceptions.py0000664000175000017500000000406312625116214025103 0ustar timtim00000000000000from django.template import TemplateDoesNotExist, TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup from .test_extends import inheritance_templates class ExceptionsTests(SimpleTestCase): @setup({'exception01': "{% extends 'nonexistent' %}"}) def test_exception01(self): """ Raise exception for invalid template name """ with self.assertRaises(TemplateDoesNotExist): self.engine.render_to_string('exception01') @setup({'exception02': '{% extends nonexistent %}'}) def test_exception02(self): """ Raise exception for invalid variable template name """ if self.engine.string_if_invalid: with self.assertRaises(TemplateDoesNotExist): self.engine.render_to_string('exception02') else: with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('exception02') @setup( {'exception03': "{% extends 'inheritance01' %}" "{% block first %}2{% endblock %}{% extends 'inheritance16' %}"}, inheritance_templates, ) def test_exception03(self): """ Raise exception for extra {% extends %} tags """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('exception03') @setup( {'exception04': "{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}"}, inheritance_templates, ) def test_exception04(self): """ Raise exception for custom tags used in child with {% load %} tag in parent, not in child """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('exception04') @setup({'exception05': '{% block first %}{{ block.super }}{% endblock %}'}) def test_exception05(self): """ Raise exception for block.super used in base template """ with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('exception05') Django-1.8.7/tests/template_tests/syntax_tests/test_filter_tag.py0000664000175000017500000000340312625116214025037 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class FilterTagTests(SimpleTestCase): @setup({'filter01': '{% filter upper %}{% endfilter %}'}) def test_filter01(self): output = self.engine.render_to_string('filter01') self.assertEqual(output, '') @setup({'filter02': '{% filter upper %}django{% endfilter %}'}) def test_filter02(self): output = self.engine.render_to_string('filter02') self.assertEqual(output, 'DJANGO') @setup({'filter03': '{% filter upper|lower %}django{% endfilter %}'}) def test_filter03(self): output = self.engine.render_to_string('filter03') self.assertEqual(output, 'django') @setup({'filter04': '{% filter cut:remove %}djangospam{% endfilter %}'}) def test_filter04(self): output = self.engine.render_to_string('filter04', {'remove': 'spam'}) self.assertEqual(output, 'django') @setup({'filter05': '{% filter safe %}fail{% endfilter %}'}) def test_filter05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter05') @setup({'filter05bis': '{% filter upper|safe %}fail{% endfilter %}'}) def test_filter05bis(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter05bis') @setup({'filter06': '{% filter escape %}fail{% endfilter %}'}) def test_filter06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter06') @setup({'filter06bis': '{% filter upper|escape %}fail{% endfilter %}'}) def test_filter06bis(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter06bis') Django-1.8.7/tests/template_tests/syntax_tests/test_for.py0000664000175000017500000002035512625116214023512 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import RemovedInDjango110Warning from ..utils import setup class ForTagTests(SimpleTestCase): @setup({'for-tag01': '{% for val in values %}{{ val }}{% endfor %}'}) def test_for_tag01(self): output = self.engine.render_to_string('for-tag01', {'values': [1, 2, 3]}) self.assertEqual(output, '123') @setup({'for-tag02': '{% for val in values reversed %}{{ val }}{% endfor %}'}) def test_for_tag02(self): output = self.engine.render_to_string('for-tag02', {'values': [1, 2, 3]}) self.assertEqual(output, '321') @setup({'for-tag-vars01': '{% for val in values %}{{ forloop.counter }}{% endfor %}'}) def test_for_tag_vars01(self): output = self.engine.render_to_string('for-tag-vars01', {'values': [6, 6, 6]}) self.assertEqual(output, '123') @setup({'for-tag-vars02': '{% for val in values %}{{ forloop.counter0 }}{% endfor %}'}) def test_for_tag_vars02(self): output = self.engine.render_to_string('for-tag-vars02', {'values': [6, 6, 6]}) self.assertEqual(output, '012') @setup({'for-tag-vars03': '{% for val in values %}{{ forloop.revcounter }}{% endfor %}'}) def test_for_tag_vars03(self): output = self.engine.render_to_string('for-tag-vars03', {'values': [6, 6, 6]}) self.assertEqual(output, '321') @setup({'for-tag-vars04': '{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}'}) def test_for_tag_vars04(self): output = self.engine.render_to_string('for-tag-vars04', {'values': [6, 6, 6]}) self.assertEqual(output, '210') @setup({'for-tag-vars05': '{% for val in values %}' '{% if forloop.first %}f{% else %}x{% endif %}{% endfor %}'}) def test_for_tag_vars05(self): output = self.engine.render_to_string('for-tag-vars05', {'values': [6, 6, 6]}) self.assertEqual(output, 'fxx') @setup({'for-tag-vars06': '{% for val in values %}' '{% if forloop.last %}l{% else %}x{% endif %}{% endfor %}'}) def test_for_tag_vars06(self): output = self.engine.render_to_string('for-tag-vars06', {'values': [6, 6, 6]}) self.assertEqual(output, 'xxl') @setup({'for-tag-unpack01': '{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack01(self): output = self.engine.render_to_string('for-tag-unpack01', {'items': (('one', 1), ('two', 2))}) self.assertEqual(output, 'one:1/two:2/') @setup({'for-tag-unpack03': '{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack03(self): output = self.engine.render_to_string('for-tag-unpack03', {'items': (('one', 1), ('two', 2))}) self.assertEqual(output, 'one:1/two:2/') @setup({'for-tag-unpack04': '{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack04(self): output = self.engine.render_to_string('for-tag-unpack04', {'items': (('one', 1), ('two', 2))}) self.assertEqual(output, 'one:1/two:2/') @setup({'for-tag-unpack05': '{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack05(self): output = self.engine.render_to_string('for-tag-unpack05', {'items': (('one', 1), ('two', 2))}) self.assertEqual(output, 'one:1/two:2/') @setup({'for-tag-unpack06': '{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack06(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('for-tag-unpack06', {'items': (('one', 1), ('two', 2))}) @setup({'for-tag-unpack07': '{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack07(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('for-tag-unpack07', {'items': (('one', 1), ('two', 2))}) @setup({'for-tag-unpack08': '{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}'}) def test_for_tag_unpack08(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('for-tag-unpack08', {'items': (('one', 1), ('two', 2))}) @setup({'for-tag-unpack09': '{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}'}) def test_for_tag_unpack09(self): """ Ensure that a single loopvar doesn't truncate the list in val. """ output = self.engine.render_to_string('for-tag-unpack09', {'items': (('one', 1), ('two', 2))}) self.assertEqual(output, 'one:1/two:2/') @setup({'for-tag-unpack13': '{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}'}) def test_for_tag_unpack13(self): output = self.engine.render_to_string('for-tag-unpack13', {'items': (('one', 1, 'carrot'), ('two', 2, 'cheese'))}) if self.engine.string_if_invalid: self.assertEqual(output, 'one:1,carrot/two:2,cheese/') else: self.assertEqual(output, 'one:1,carrot/two:2,cheese/') @setup({'for-tag-empty01': '{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}'}) def test_for_tag_empty01(self): output = self.engine.render_to_string('for-tag-empty01', {'values': [1, 2, 3]}) self.assertEqual(output, '123') @setup({'for-tag-empty02': '{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}'}) def test_for_tag_empty02(self): output = self.engine.render_to_string('for-tag-empty02', {'values': []}) self.assertEqual(output, 'values array empty') @setup({'for-tag-empty03': '{% for val in values %}' '{{ val }}{% empty %}values array not found{% endfor %}'}) def test_for_tag_empty03(self): output = self.engine.render_to_string('for-tag-empty03') self.assertEqual(output, 'values array not found') @setup({'for-tag-filter-ws': "{% load custom %}{% for x in s|noop:'x y' %}{{ x }}{% endfor %}"}) def test_for_tag_filter_ws(self): """ #19882 """ output = self.engine.render_to_string('for-tag-filter-ws', {'s': 'abc'}) self.assertEqual(output, 'abc') # These tests raise deprecation warnings and will raise an exception # in Django 1.10. The existing behavior is silent truncation if the # length of loopvars differs to the length of each set of items. @ignore_warnings(category=RemovedInDjango110Warning) @setup({'for-tag-unpack10': '{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}'}) def test_for_tag_unpack10(self): output = self.engine.render_to_string( 'for-tag-unpack10', {'items': (('one', 1, 'carrot'), ('two', 2, 'orange'))}, ) self.assertEqual(output, 'one:1/two:2/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'for-tag-unpack11': '{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}'}) def test_for_tag_unpack11(self): output = self.engine.render_to_string( 'for-tag-unpack11', {'items': (('one', 1), ('two', 2))}, ) if self.engine.string_if_invalid: self.assertEqual(output, 'one:1,INVALID/two:2,INVALID/') else: self.assertEqual(output, 'one:1,/two:2,/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'for-tag-unpack12': '{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}'}) def test_for_tag_unpack12(self): output = self.engine.render_to_string( 'for-tag-unpack12', {'items': (('one', 1, 'carrot'), ('two', 2))} ) if self.engine.string_if_invalid: self.assertEqual(output, 'one:1,carrot/two:2,INVALID/') else: self.assertEqual(output, 'one:1,carrot/two:2,/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'for-tag-unpack14': '{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}'}) def test_for_tag_unpack14(self): output = self.engine.render_to_string('for-tag-unpack14', {'items': (1, 2)}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID:INVALID/INVALID:INVALID/') else: self.assertEqual(output, ':/:/') Django-1.8.7/tests/template_tests/syntax_tests/test_invalid_string.py0000664000175000017500000000432112625116214025733 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class InvalidStringTests(SimpleTestCase): @setup({'invalidstr01': '{{ var|default:"Foo" }}'}) def test_invalidstr01(self): output = self.engine.render_to_string('invalidstr01') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, 'Foo') @setup({'invalidstr02': '{{ var|default_if_none:"Foo" }}'}) def test_invalidstr02(self): output = self.engine.render_to_string('invalidstr02') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'invalidstr03': '{% for v in var %}({{ v }}){% endfor %}'}) def test_invalidstr03(self): output = self.engine.render_to_string('invalidstr03') self.assertEqual(output, '') @setup({'invalidstr04': '{% if var %}Yes{% else %}No{% endif %}'}) def test_invalidstr04(self): output = self.engine.render_to_string('invalidstr04') self.assertEqual(output, 'No') @setup({'invalidstr04_2': '{% if var|default:"Foo" %}Yes{% else %}No{% endif %}'}) def test_invalidstr04_2(self): output = self.engine.render_to_string('invalidstr04_2') self.assertEqual(output, 'Yes') @setup({'invalidstr05': '{{ var }}'}) def test_invalidstr05(self): output = self.engine.render_to_string('invalidstr05') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'invalidstr06': '{{ var.prop }}'}) def test_invalidstr06(self): output = self.engine.render_to_string('invalidstr06') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'invalidstr07': '{% load i18n %}{% blocktrans %}{{ var }}{% endblocktrans %}'}) def test_invalidstr07(self): output = self.engine.render_to_string('invalidstr07') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') Django-1.8.7/tests/template_tests/syntax_tests/test_if_equal.py0000664000175000017500000002324412625116214024511 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class IfEqualTagTests(SimpleTestCase): @setup({'ifequal01': '{% ifequal a b %}yes{% endifequal %}'}) def test_ifequal01(self): output = self.engine.render_to_string('ifequal01', {'a': 1, 'b': 2}) self.assertEqual(output, '') @setup({'ifequal02': '{% ifequal a b %}yes{% endifequal %}'}) def test_ifequal02(self): output = self.engine.render_to_string('ifequal02', {'a': 1, 'b': 1}) self.assertEqual(output, 'yes') @setup({'ifequal03': '{% ifequal a b %}yes{% else %}no{% endifequal %}'}) def test_ifequal03(self): output = self.engine.render_to_string('ifequal03', {'a': 1, 'b': 2}) self.assertEqual(output, 'no') @setup({'ifequal04': '{% ifequal a b %}yes{% else %}no{% endifequal %}'}) def test_ifequal04(self): output = self.engine.render_to_string('ifequal04', {'a': 1, 'b': 1}) self.assertEqual(output, 'yes') @setup({'ifequal05': '{% ifequal a \'test\' %}yes{% else %}no{% endifequal %}'}) def test_ifequal05(self): output = self.engine.render_to_string('ifequal05', {'a': 'test'}) self.assertEqual(output, 'yes') @setup({'ifequal06': '{% ifequal a \'test\' %}yes{% else %}no{% endifequal %}'}) def test_ifequal06(self): output = self.engine.render_to_string('ifequal06', {'a': 'no'}) self.assertEqual(output, 'no') @setup({'ifequal07': '{% ifequal a "test" %}yes{% else %}no{% endifequal %}'}) def test_ifequal07(self): output = self.engine.render_to_string('ifequal07', {'a': 'test'}) self.assertEqual(output, 'yes') @setup({'ifequal08': '{% ifequal a "test" %}yes{% else %}no{% endifequal %}'}) def test_ifequal08(self): output = self.engine.render_to_string('ifequal08', {'a': 'no'}) self.assertEqual(output, 'no') @setup({'ifequal09': '{% ifequal a "test" %}yes{% else %}no{% endifequal %}'}) def test_ifequal09(self): output = self.engine.render_to_string('ifequal09') self.assertEqual(output, 'no') @setup({'ifequal10': '{% ifequal a b %}yes{% else %}no{% endifequal %}'}) def test_ifequal10(self): output = self.engine.render_to_string('ifequal10') self.assertEqual(output, 'yes') # SMART SPLITTING @setup({'ifequal-split01': '{% ifequal a "test man" %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split01(self): output = self.engine.render_to_string('ifequal-split01') self.assertEqual(output, 'no') @setup({'ifequal-split02': '{% ifequal a "test man" %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split02(self): output = self.engine.render_to_string('ifequal-split02', {'a': 'foo'}) self.assertEqual(output, 'no') @setup({'ifequal-split03': '{% ifequal a "test man" %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split03(self): output = self.engine.render_to_string('ifequal-split03', {'a': 'test man'}) self.assertEqual(output, 'yes') @setup({'ifequal-split04': '{% ifequal a \'test man\' %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split04(self): output = self.engine.render_to_string('ifequal-split04', {'a': 'test man'}) self.assertEqual(output, 'yes') @setup({'ifequal-split05': '{% ifequal a \'i "love" you\' %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split05(self): output = self.engine.render_to_string('ifequal-split05', {'a': ''}) self.assertEqual(output, 'no') @setup({'ifequal-split06': '{% ifequal a \'i "love" you\' %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split06(self): output = self.engine.render_to_string('ifequal-split06', {'a': 'i "love" you'}) self.assertEqual(output, 'yes') @setup({'ifequal-split07': '{% ifequal a \'i "love" you\' %}yes{% else %}no{% endifequal %}'}) def test_ifequal_split07(self): output = self.engine.render_to_string('ifequal-split07', {'a': 'i love you'}) self.assertEqual(output, 'no') @setup({'ifequal-split08': r"{% ifequal a 'I\'m happy' %}yes{% else %}no{% endifequal %}"}) def test_ifequal_split08(self): output = self.engine.render_to_string('ifequal-split08', {'a': "I'm happy"}) self.assertEqual(output, 'yes') @setup({'ifequal-split09': r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}"}) def test_ifequal_split09(self): output = self.engine.render_to_string('ifequal-split09', {'a': 'slash\man'}) self.assertEqual(output, 'yes') @setup({'ifequal-split10': r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}"}) def test_ifequal_split10(self): output = self.engine.render_to_string('ifequal-split10', {'a': 'slashman'}) self.assertEqual(output, 'no') # NUMERIC RESOLUTION @setup({'ifequal-numeric01': '{% ifequal x 5 %}yes{% endifequal %}'}) def test_ifequal_numeric01(self): output = self.engine.render_to_string('ifequal-numeric01', {'x': '5'}) self.assertEqual(output, '') @setup({'ifequal-numeric02': '{% ifequal x 5 %}yes{% endifequal %}'}) def test_ifequal_numeric02(self): output = self.engine.render_to_string('ifequal-numeric02', {'x': 5}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric03': '{% ifequal x 5.2 %}yes{% endifequal %}'}) def test_ifequal_numeric03(self): output = self.engine.render_to_string('ifequal-numeric03', {'x': 5}) self.assertEqual(output, '') @setup({'ifequal-numeric04': '{% ifequal x 5.2 %}yes{% endifequal %}'}) def test_ifequal_numeric04(self): output = self.engine.render_to_string('ifequal-numeric04', {'x': 5.2}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric05': '{% ifequal x 0.2 %}yes{% endifequal %}'}) def test_ifequal_numeric05(self): output = self.engine.render_to_string('ifequal-numeric05', {'x': 0.2}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric06': '{% ifequal x .2 %}yes{% endifequal %}'}) def test_ifequal_numeric06(self): output = self.engine.render_to_string('ifequal-numeric06', {'x': 0.2}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric07': '{% ifequal x 2. %}yes{% endifequal %}'}) def test_ifequal_numeric07(self): output = self.engine.render_to_string('ifequal-numeric07', {'x': 2}) self.assertEqual(output, '') @setup({'ifequal-numeric08': '{% ifequal x "5" %}yes{% endifequal %}'}) def test_ifequal_numeric08(self): output = self.engine.render_to_string('ifequal-numeric08', {'x': 5}) self.assertEqual(output, '') @setup({'ifequal-numeric09': '{% ifequal x "5" %}yes{% endifequal %}'}) def test_ifequal_numeric09(self): output = self.engine.render_to_string('ifequal-numeric09', {'x': '5'}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric10': '{% ifequal x -5 %}yes{% endifequal %}'}) def test_ifequal_numeric10(self): output = self.engine.render_to_string('ifequal-numeric10', {'x': -5}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric11': '{% ifequal x -5.2 %}yes{% endifequal %}'}) def test_ifequal_numeric11(self): output = self.engine.render_to_string('ifequal-numeric11', {'x': -5.2}) self.assertEqual(output, 'yes') @setup({'ifequal-numeric12': '{% ifequal x +5 %}yes{% endifequal %}'}) def test_ifequal_numeric12(self): output = self.engine.render_to_string('ifequal-numeric12', {'x': 5}) self.assertEqual(output, 'yes') # FILTER EXPRESSIONS AS ARGUMENTS @setup({'ifequal-filter01': '{% ifequal a|upper "A" %}x{% endifequal %}'}) def test_ifequal_filter01(self): output = self.engine.render_to_string('ifequal-filter01', {'a': 'a'}) self.assertEqual(output, 'x') @setup({'ifequal-filter02': '{% ifequal "A" a|upper %}x{% endifequal %}'}) def test_ifequal_filter02(self): output = self.engine.render_to_string('ifequal-filter02', {'a': 'a'}) self.assertEqual(output, 'x') @setup({'ifequal-filter03': '{% ifequal a|upper b|upper %}x{% endifequal %}'}) def test_ifequal_filter03(self): output = self.engine.render_to_string('ifequal-filter03', {'a': 'x', 'b': 'X'}) self.assertEqual(output, 'x') @setup({'ifequal-filter04': '{% ifequal x|slice:"1" "a" %}x{% endifequal %}'}) def test_ifequal_filter04(self): output = self.engine.render_to_string('ifequal-filter04', {'x': 'aaa'}) self.assertEqual(output, 'x') @setup({'ifequal-filter05': '{% ifequal x|slice:"1"|upper "A" %}x{% endifequal %}'}) def test_ifequal_filter05(self): output = self.engine.render_to_string('ifequal-filter05', {'x': 'aaa'}) self.assertEqual(output, 'x') class IfNotEqualTagTests(SimpleTestCase): @setup({'ifnotequal01': '{% ifnotequal a b %}yes{% endifnotequal %}'}) def test_ifnotequal01(self): output = self.engine.render_to_string('ifnotequal01', {'a': 1, 'b': 2}) self.assertEqual(output, 'yes') @setup({'ifnotequal02': '{% ifnotequal a b %}yes{% endifnotequal %}'}) def test_ifnotequal02(self): output = self.engine.render_to_string('ifnotequal02', {'a': 1, 'b': 1}) self.assertEqual(output, '') @setup({'ifnotequal03': '{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}'}) def test_ifnotequal03(self): output = self.engine.render_to_string('ifnotequal03', {'a': 1, 'b': 2}) self.assertEqual(output, 'yes') @setup({'ifnotequal04': '{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}'}) def test_ifnotequal04(self): output = self.engine.render_to_string('ifnotequal04', {'a': 1, 'b': 1}) self.assertEqual(output, 'no') Django-1.8.7/tests/template_tests/syntax_tests/test_i18n.py0000664000175000017500000004330012625116214023476 0ustar timtim00000000000000# coding: utf-8 from __future__ import unicode_literals from django.test import SimpleTestCase from django.utils import translation from django.utils.safestring import mark_safe from ..utils import setup class I18nTagTests(SimpleTestCase): @setup({'i18n01': '{% load i18n %}{% trans \'xxxyyyxxx\' %}'}) def test_i18n01(self): """ simple translation of a string delimited by ' """ output = self.engine.render_to_string('i18n01') self.assertEqual(output, 'xxxyyyxxx') @setup({'i18n02': '{% load i18n %}{% trans "xxxyyyxxx" %}'}) def test_i18n02(self): """ simple translation of a string delimited by " """ output = self.engine.render_to_string('i18n02') self.assertEqual(output, 'xxxyyyxxx') @setup({'i18n03': '{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}'}) def test_i18n03(self): """ simple translation of a variable """ output = self.engine.render_to_string('i18n03', {'anton': b'\xc3\x85'}) self.assertEqual(output, 'Ã…') @setup({'i18n04': '{% load i18n %}{% blocktrans with berta=anton|lower %}{{ berta }}{% endblocktrans %}'}) def test_i18n04(self): """ simple translation of a variable and filter """ output = self.engine.render_to_string('i18n04', {'anton': b'\xc3\x85'}) self.assertEqual(output, 'Ã¥') @setup({'legacyi18n04': '{% load i18n %}' '{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}'}) def test_legacyi18n04(self): """ simple translation of a variable and filter """ output = self.engine.render_to_string('legacyi18n04', {'anton': b'\xc3\x85'}) self.assertEqual(output, 'Ã¥') @setup({'i18n05': '{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}'}) def test_i18n05(self): """ simple translation of a string with interpolation """ output = self.engine.render_to_string('i18n05', {'anton': 'yyy'}) self.assertEqual(output, 'xxxyyyxxx') @setup({'i18n06': '{% load i18n %}{% trans "Page not found" %}'}) def test_i18n06(self): """ simple translation of a string to german """ with translation.override('de'): output = self.engine.render_to_string('i18n06') self.assertEqual(output, 'Seite nicht gefunden') @setup({'i18n07': '{% load i18n %}' '{% blocktrans count counter=number %}singular{% plural %}' '{{ counter }} plural{% endblocktrans %}'}) def test_i18n07(self): """ translation of singular form """ output = self.engine.render_to_string('i18n07', {'number': 1}) self.assertEqual(output, 'singular') @setup({'legacyi18n07': '{% load i18n %}' '{% blocktrans count number as counter %}singular{% plural %}' '{{ counter }} plural{% endblocktrans %}'}) def test_legacyi18n07(self): """ translation of singular form """ output = self.engine.render_to_string('legacyi18n07', {'number': 1}) self.assertEqual(output, 'singular') @setup({'i18n08': '{% load i18n %}' '{% blocktrans count number as counter %}singular{% plural %}' '{{ counter }} plural{% endblocktrans %}'}) def test_i18n08(self): """ translation of plural form """ output = self.engine.render_to_string('i18n08', {'number': 2}) self.assertEqual(output, '2 plural') @setup({'legacyi18n08': '{% load i18n %}' '{% blocktrans count counter=number %}singular{% plural %}' '{{ counter }} plural{% endblocktrans %}'}) def test_legacyi18n08(self): """ translation of plural form """ output = self.engine.render_to_string('legacyi18n08', {'number': 2}) self.assertEqual(output, '2 plural') @setup({'i18n09': '{% load i18n %}{% trans "Page not found" noop %}'}) def test_i18n09(self): """ simple non-translation (only marking) of a string to german """ with translation.override('de'): output = self.engine.render_to_string('i18n09') self.assertEqual(output, 'Page not found') @setup({'i18n10': '{{ bool|yesno:_("yes,no,maybe") }}'}) def test_i18n10(self): """ translation of a variable with a translated filter """ with translation.override('de'): output = self.engine.render_to_string('i18n10', {'bool': True}) self.assertEqual(output, 'Ja') @setup({'i18n11': '{{ bool|yesno:"ja,nein" }}'}) def test_i18n11(self): """ translation of a variable with a non-translated filter """ output = self.engine.render_to_string('i18n11', {'bool': True}) self.assertEqual(output, 'ja') @setup({'i18n12': '{% load i18n %}' '{% get_available_languages as langs %}{% for lang in langs %}' '{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}'}) def test_i18n12(self): """ usage of the get_available_languages tag """ output = self.engine.render_to_string('i18n12') self.assertEqual(output, 'de') @setup({'i18n13': '{{ _("Password") }}'}) def test_i18n13(self): """ translation of constant strings """ with translation.override('de'): output = self.engine.render_to_string('i18n13') self.assertEqual(output, 'Passwort') @setup({'i18n14': '{% cycle "foo" _("Password") _(\'Password\') as c %} {% cycle c %} {% cycle c %}'}) def test_i18n14(self): """ translation of constant strings """ with translation.override('de'): output = self.engine.render_to_string('i18n14') self.assertEqual(output, 'foo Passwort Passwort') @setup({'i18n15': '{{ absent|default:_("Password") }}'}) def test_i18n15(self): """ translation of constant strings """ with translation.override('de'): output = self.engine.render_to_string('i18n15', {'absent': ''}) self.assertEqual(output, 'Passwort') @setup({'i18n16': '{{ _("<") }}'}) def test_i18n16(self): """ translation of constant strings """ with translation.override('de'): output = self.engine.render_to_string('i18n16') self.assertEqual(output, '<') @setup({'i18n17': '{% load i18n %}' '{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}'}) def test_i18n17(self): """ Escaping inside blocktrans and trans works as if it was directly in the template. """ output = self.engine.render_to_string('i18n17', {'anton': 'α & β'}) self.assertEqual(output, 'α & β') @setup({'i18n18': '{% load i18n %}' '{% blocktrans with berta=anton|force_escape %}{{ berta }}{% endblocktrans %}'}) def test_i18n18(self): output = self.engine.render_to_string('i18n18', {'anton': 'α & β'}) self.assertEqual(output, 'α & β') @setup({'i18n19': '{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}'}) def test_i18n19(self): output = self.engine.render_to_string('i18n19', {'andrew': 'a & b'}) self.assertEqual(output, 'a & b') @setup({'i18n20': '{% load i18n %}{% trans andrew %}'}) def test_i18n20(self): output = self.engine.render_to_string('i18n20', {'andrew': 'a & b'}) self.assertEqual(output, 'a & b') @setup({'i18n21': '{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}'}) def test_i18n21(self): output = self.engine.render_to_string('i18n21', {'andrew': mark_safe('a & b')}) self.assertEqual(output, 'a & b') @setup({'i18n22': '{% load i18n %}{% trans andrew %}'}) def test_i18n22(self): output = self.engine.render_to_string('i18n22', {'andrew': mark_safe('a & b')}) self.assertEqual(output, 'a & b') @setup({'legacyi18n17': '{% load i18n %}' '{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}'}) def test_legacyi18n17(self): output = self.engine.render_to_string('legacyi18n17', {'anton': 'α & β'}) self.assertEqual(output, 'α & β') @setup({'legacyi18n18': '{% load i18n %}' '{% blocktrans with anton|force_escape as berta %}' '{{ berta }}{% endblocktrans %}'}) def test_legacyi18n18(self): output = self.engine.render_to_string('legacyi18n18', {'anton': 'α & β'}) self.assertEqual(output, 'α & β') @setup({'i18n23': '{% load i18n %}{% trans "Page not found"|capfirst|slice:"6:" %}'}) def test_i18n23(self): """ #5972 - Use filters with the {% trans %} tag """ with translation.override('de'): output = self.engine.render_to_string('i18n23') self.assertEqual(output, 'nicht gefunden') @setup({'i18n24': '{% load i18n %}{% trans \'Page not found\'|upper %}'}) def test_i18n24(self): with translation.override('de'): output = self.engine.render_to_string('i18n24') self.assertEqual(output, 'SEITE NICHT GEFUNDEN') @setup({'i18n25': '{% load i18n %}{% trans somevar|upper %}'}) def test_i18n25(self): with translation.override('de'): output = self.engine.render_to_string('i18n25', {'somevar': 'Page not found'}) self.assertEqual(output, 'SEITE NICHT GEFUNDEN') @setup({'i18n26': '{% load i18n %}' '{% blocktrans with extra_field=myextra_field count counter=number %}' 'singular {{ extra_field }}{% plural %}plural{% endblocktrans %}'}) def test_i18n26(self): """ translation of plural form with extra field in singular form (#13568) """ output = self.engine.render_to_string('i18n26', {'myextra_field': 'test', 'number': 1}) self.assertEqual(output, 'singular test') @setup({'legacyi18n26': '{% load i18n %}' '{% blocktrans with myextra_field as extra_field count number as counter %}' 'singular {{ extra_field }}{% plural %}plural{% endblocktrans %}'}) def test_legacyi18n26(self): output = self.engine.render_to_string('legacyi18n26', {'myextra_field': 'test', 'number': 1}) self.assertEqual(output, 'singular test') @setup({'i18n27': '{% load i18n %}{% blocktrans count counter=number %}' '{{ counter }} result{% plural %}{{ counter }} results' '{% endblocktrans %}'}) def test_i18n27(self): """ translation of singular form in russian (#14126) """ with translation.override('ru'): output = self.engine.render_to_string('i18n27', {'number': 1}) self.assertEqual(output, '1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442') @setup({'legacyi18n27': '{% load i18n %}' '{% blocktrans count number as counter %}{{ counter }} result' '{% plural %}{{ counter }} results{% endblocktrans %}'}) def test_legacyi18n27(self): with translation.override('ru'): output = self.engine.render_to_string('legacyi18n27', {'number': 1}) self.assertEqual(output, '1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442') @setup({'i18n28': '{% load i18n %}' '{% blocktrans with a=anton b=berta %}{{ a }} + {{ b }}{% endblocktrans %}'}) def test_i18n28(self): """ simple translation of multiple variables """ output = self.engine.render_to_string('i18n28', {'anton': 'α', 'berta': 'β'}) self.assertEqual(output, 'α + β') @setup({'legacyi18n28': '{% load i18n %}' '{% blocktrans with anton as a and berta as b %}' '{{ a }} + {{ b }}{% endblocktrans %}'}) def test_legacyi18n28(self): output = self.engine.render_to_string('legacyi18n28', {'anton': 'α', 'berta': 'β'}) self.assertEqual(output, 'α + β') # retrieving language information @setup({'i18n28_2': '{% load i18n %}' '{% get_language_info for "de" as l %}' '{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}'}) def test_i18n28_2(self): output = self.engine.render_to_string('i18n28_2') self.assertEqual(output, 'de: German/Deutsch bidi=False') @setup({'i18n29': '{% load i18n %}' '{% get_language_info for LANGUAGE_CODE as l %}' '{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}'}) def test_i18n29(self): output = self.engine.render_to_string('i18n29', {'LANGUAGE_CODE': 'fi'}) self.assertEqual(output, 'fi: Finnish/suomi bidi=False') @setup({'i18n30': '{% load i18n %}' '{% get_language_info_list for langcodes as langs %}' '{% for l in langs %}{{ l.code }}: {{ l.name }}/' '{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}'}) def test_i18n30(self): output = self.engine.render_to_string('i18n30', {'langcodes': ['it', 'no']}) self.assertEqual(output, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; ') @setup({'i18n31': '{% load i18n %}' '{% get_language_info_list for langcodes as langs %}' '{% for l in langs %}{{ l.code }}: {{ l.name }}/' '{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}'}) def test_i18n31(self): output = self.engine.render_to_string('i18n31', {'langcodes': (('sl', 'Slovenian'), ('fa', 'Persian'))}) self.assertEqual( output, 'sl: Slovenian/Sloven\u0161\u010dina bidi=False; ' 'fa: Persian/\u0641\u0627\u0631\u0633\u06cc bidi=True; ' ) @setup({'i18n32': '{% load i18n %}{{ "hu"|language_name }} ' '{{ "hu"|language_name_local }} {{ "hu"|language_bidi }}'}) def test_i18n32(self): output = self.engine.render_to_string('i18n32') self.assertEqual(output, 'Hungarian Magyar False') @setup({'i18n33': '{% load i18n %}' '{{ langcode|language_name }} {{ langcode|language_name_local }} ' '{{ langcode|language_bidi }}'}) def test_i18n33(self): output = self.engine.render_to_string('i18n33', {'langcode': 'nl'}) self.assertEqual(output, 'Dutch Nederlands False') # blocktrans handling of variables which are not in the context. # this should work as if blocktrans was not there (#19915) @setup({'i18n34': '{% load i18n %}{% blocktrans %}{{ missing }}{% endblocktrans %}'}) def test_i18n34(self): output = self.engine.render_to_string('i18n34') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'i18n34_2': '{% load i18n %}{% blocktrans with a=\'α\' %}{{ missing }}{% endblocktrans %}'}) def test_i18n34_2(self): output = self.engine.render_to_string('i18n34_2') if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'i18n34_3': '{% load i18n %}{% blocktrans with a=anton %}{{ missing }}{% endblocktrans %}'}) def test_i18n34_3(self): output = self.engine.render_to_string('i18n34_3', {'anton': '\xce\xb1'}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') # trans tag with as var @setup({'i18n35': '{% load i18n %}{% trans "Page not found" as page_not_found %}{{ page_not_found }}'}) def test_i18n35(self): with translation.override('de'): output = self.engine.render_to_string('i18n35') self.assertEqual(output, 'Seite nicht gefunden') @setup({'i18n36': '{% load i18n %}' '{% trans "Page not found" noop as page_not_found %}{{ page_not_found }}'}) def test_i18n36(self): with translation.override('de'): output = self.engine.render_to_string('i18n36') self.assertEqual(output, 'Page not found') @setup({'i18n37': '{% load i18n %}' '{% trans "Page not found" as page_not_found %}' '{% blocktrans %}Error: {{ page_not_found }}{% endblocktrans %}'}) def test_i18n37(self): with translation.override('de'): output = self.engine.render_to_string('i18n37') self.assertEqual(output, 'Error: Seite nicht gefunden') # Test whitespace in filter arguments @setup({'i18n38': '{% load i18n custom %}' '{% get_language_info for "de"|noop:"x y" as l %}' '{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}'}) def test_i18n38(self): output = self.engine.render_to_string('i18n38') self.assertEqual(output, 'de: German/Deutsch bidi=False') @setup({'i18n38_2': '{% load i18n custom %}' '{% get_language_info_list for langcodes|noop:"x y" as langs %}' '{% for l in langs %}{{ l.code }}: {{ l.name }}/' '{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}'}) def test_i18n38_2(self): output = self.engine.render_to_string('i18n38_2', {'langcodes': ['it', 'no']}) self.assertEqual(output, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; ') Django-1.8.7/tests/template_tests/syntax_tests/test_list_index.py0000664000175000017500000000520612625116214025064 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class ListIndexTests(SimpleTestCase): @setup({'list-index01': '{{ var.1 }}'}) def test_list_index01(self): """ List-index syntax allows a template to access a certain item of a subscriptable object. """ output = self.engine.render_to_string('list-index01', {'var': ['first item', 'second item']}) self.assertEqual(output, 'second item') @setup({'list-index02': '{{ var.5 }}'}) def test_list_index02(self): """ Fail silently when the list index is out of range. """ output = self.engine.render_to_string('list-index02', {'var': ['first item', 'second item']}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'list-index03': '{{ var.1 }}'}) def test_list_index03(self): """ Fail silently when the list index is out of range. """ output = self.engine.render_to_string('list-index03', {'var': None}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'list-index04': '{{ var.1 }}'}) def test_list_index04(self): """ Fail silently when variable is a dict without the specified key. """ output = self.engine.render_to_string('list-index04', {'var': {}}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID') else: self.assertEqual(output, '') @setup({'list-index05': '{{ var.1 }}'}) def test_list_index05(self): """ Dictionary lookup wins out when dict's key is a string. """ output = self.engine.render_to_string('list-index05', {'var': {'1': "hello"}}) self.assertEqual(output, 'hello') @setup({'list-index06': '{{ var.1 }}'}) def test_list_index06(self): """ But list-index lookup wins out when dict's key is an int, which behind the scenes is really a dictionary lookup (for a dict) after converting the key to an int. """ output = self.engine.render_to_string('list-index06', {"var": {1: "hello"}}) self.assertEqual(output, 'hello') @setup({'list-index07': '{{ var.1 }}'}) def test_list_index07(self): """ Dictionary lookup wins out when there is a string and int version of the key. """ output = self.engine.render_to_string('list-index07', {"var": {'1': "hello", 1: "world"}}) self.assertEqual(output, 'hello') Django-1.8.7/tests/template_tests/syntax_tests/test_static.py0000664000175000017500000000477512625116214024223 0ustar timtim00000000000000from django.conf import settings from django.test import SimpleTestCase, override_settings from django.utils.six.moves.urllib.parse import urljoin from ..utils import setup @override_settings(MEDIA_URL="/media/", STATIC_URL="/static/") class StaticTagTests(SimpleTestCase): @setup({'static-prefixtag01': '{% load static %}{% get_static_prefix %}'}) def test_static_prefixtag01(self): output = self.engine.render_to_string('static-prefixtag01') self.assertEqual(output, settings.STATIC_URL) @setup({'static-prefixtag02': '{% load static %}' '{% get_static_prefix as static_prefix %}{{ static_prefix }}'}) def test_static_prefixtag02(self): output = self.engine.render_to_string('static-prefixtag02') self.assertEqual(output, settings.STATIC_URL) @setup({'static-prefixtag03': '{% load static %}{% get_media_prefix %}'}) def test_static_prefixtag03(self): output = self.engine.render_to_string('static-prefixtag03') self.assertEqual(output, settings.MEDIA_URL) @setup({'static-prefixtag04': '{% load static %}' '{% get_media_prefix as media_prefix %}{{ media_prefix }}'}) def test_static_prefixtag04(self): output = self.engine.render_to_string('static-prefixtag04') self.assertEqual(output, settings.MEDIA_URL) @setup({'static-statictag01': '{% load static %}{% static "admin/base.css" %}'}) def test_static_statictag01(self): output = self.engine.render_to_string('static-statictag01') self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) @setup({'static-statictag02': '{% load static %}{% static base_css %}'}) def test_static_statictag02(self): output = self.engine.render_to_string('static-statictag02', {'base_css': 'admin/base.css'}) self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) @setup({'static-statictag03': '{% load static %}{% static "admin/base.css" as foo %}{{ foo }}'}) def test_static_statictag03(self): output = self.engine.render_to_string('static-statictag03') self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) @setup({'static-statictag04': '{% load static %}{% static base_css as foo %}{{ foo }}'}) def test_static_statictag04(self): output = self.engine.render_to_string('static-statictag04', {'base_css': 'admin/base.css'}) self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) Django-1.8.7/tests/template_tests/syntax_tests/test_named_endblock.py0000664000175000017500000000441012625116214025643 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class NamedEndblockTests(SimpleTestCase): @setup({'namedendblocks01': '1{% block first %}_{% block second %}' '2{% endblock second %}_{% endblock first %}3'}) def test_namedendblocks01(self): output = self.engine.render_to_string('namedendblocks01') self.assertEqual(output, '1_2_3') # Unbalanced blocks @setup({'namedendblocks02': '1{% block first %}_{% block second %}' '2{% endblock first %}_{% endblock second %}3'}) def test_namedendblocks02(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('namedendblocks02') @setup({'namedendblocks03': '1{% block first %}_{% block second %}' '2{% endblock %}_{% endblock second %}3'}) def test_namedendblocks03(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('namedendblocks03') @setup({'namedendblocks04': '1{% block first %}_{% block second %}' '2{% endblock second %}_{% endblock third %}3'}) def test_namedendblocks04(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('namedendblocks04') @setup({'namedendblocks05': '1{% block first %}_{% block second %}2{% endblock first %}'}) def test_namedendblocks05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('namedendblocks05') # Mixed named and unnamed endblocks @setup({'namedendblocks06': '1{% block first %}_{% block second %}' '2{% endblock %}_{% endblock first %}3'}) def test_namedendblocks06(self): """ Mixed named and unnamed endblocks """ output = self.engine.render_to_string('namedendblocks06') self.assertEqual(output, '1_2_3') @setup({'namedendblocks07': '1{% block first %}_{% block second %}' '2{% endblock second %}_{% endblock %}3'}) def test_namedendblocks07(self): output = self.engine.render_to_string('namedendblocks07') self.assertEqual(output, '1_2_3') Django-1.8.7/tests/template_tests/syntax_tests/test_multiline.py0000664000175000017500000000055212625116214024723 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup multiline_string = """ Hello, boys. How are you gentlemen. """ class MultilineTests(SimpleTestCase): @setup({'multiline01': multiline_string}) def test_multiline01(self): output = self.engine.render_to_string('multiline01') self.assertEqual(output, multiline_string) Django-1.8.7/tests/template_tests/syntax_tests/test_comment.py0000664000175000017500000000712312625116214024364 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class CommentSyntaxTests(SimpleTestCase): @setup({'comment-syntax01': '{# this is hidden #}hello'}) def test_comment_syntax01(self): output = self.engine.render_to_string('comment-syntax01') self.assertEqual(output, 'hello') @setup({'comment-syntax02': '{# this is hidden #}hello{# foo #}'}) def test_comment_syntax02(self): output = self.engine.render_to_string('comment-syntax02') self.assertEqual(output, 'hello') @setup({'comment-syntax03': 'foo{# {% if %} #}'}) def test_comment_syntax03(self): output = self.engine.render_to_string('comment-syntax03') self.assertEqual(output, 'foo') @setup({'comment-syntax04': 'foo{# {% endblock %} #}'}) def test_comment_syntax04(self): output = self.engine.render_to_string('comment-syntax04') self.assertEqual(output, 'foo') @setup({'comment-syntax05': 'foo{# {% somerandomtag %} #}'}) def test_comment_syntax05(self): output = self.engine.render_to_string('comment-syntax05') self.assertEqual(output, 'foo') @setup({'comment-syntax06': 'foo{# {% #}'}) def test_comment_syntax06(self): output = self.engine.render_to_string('comment-syntax06') self.assertEqual(output, 'foo') @setup({'comment-syntax07': 'foo{# %} #}'}) def test_comment_syntax07(self): output = self.engine.render_to_string('comment-syntax07') self.assertEqual(output, 'foo') @setup({'comment-syntax08': 'foo{# %} #}bar'}) def test_comment_syntax08(self): output = self.engine.render_to_string('comment-syntax08') self.assertEqual(output, 'foobar') @setup({'comment-syntax09': 'foo{# {{ #}'}) def test_comment_syntax09(self): output = self.engine.render_to_string('comment-syntax09') self.assertEqual(output, 'foo') @setup({'comment-syntax10': 'foo{# }} #}'}) def test_comment_syntax10(self): output = self.engine.render_to_string('comment-syntax10') self.assertEqual(output, 'foo') @setup({'comment-syntax11': 'foo{# { #}'}) def test_comment_syntax11(self): output = self.engine.render_to_string('comment-syntax11') self.assertEqual(output, 'foo') @setup({'comment-syntax12': 'foo{# } #}'}) def test_comment_syntax12(self): output = self.engine.render_to_string('comment-syntax12') self.assertEqual(output, 'foo') @setup({'comment-tag01': '{% comment %}this is hidden{% endcomment %}hello'}) def test_comment_tag01(self): output = self.engine.render_to_string('comment-tag01') self.assertEqual(output, 'hello') @setup({'comment-tag02': '{% comment %}this is hidden{% endcomment %}' 'hello{% comment %}foo{% endcomment %}'}) def test_comment_tag02(self): output = self.engine.render_to_string('comment-tag02') self.assertEqual(output, 'hello') @setup({'comment-tag03': 'foo{% comment %} {% if %} {% endcomment %}'}) def test_comment_tag03(self): output = self.engine.render_to_string('comment-tag03') self.assertEqual(output, 'foo') @setup({'comment-tag04': 'foo{% comment %} {% endblock %} {% endcomment %}'}) def test_comment_tag04(self): output = self.engine.render_to_string('comment-tag04') self.assertEqual(output, 'foo') @setup({'comment-tag05': 'foo{% comment %} {% somerandomtag %} {% endcomment %}'}) def test_comment_tag05(self): output = self.engine.render_to_string('comment-tag05') self.assertEqual(output, 'foo') Django-1.8.7/tests/template_tests/syntax_tests/test_lorem.py0000664000175000017500000000043512625116214024037 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class LoremTagTests(SimpleTestCase): @setup({'lorem1': '{% lorem 3 w %}'}) def test_lorem1(self): output = self.engine.render_to_string('lorem1') self.assertEqual(output, 'lorem ipsum dolor') Django-1.8.7/tests/template_tests/syntax_tests/test_regroup.py0000664000175000017500000000762012625116214024407 0ustar timtim00000000000000from datetime import date from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class RegroupTagTests(SimpleTestCase): @setup({'regroup01': '' '{% regroup data by bar as grouped %}' '{% for group in grouped %}' '{{ group.grouper }}:' '{% for item in group.list %}' '{{ item.foo }}' '{% endfor %},' '{% endfor %}'}) def test_regroup01(self): output = self.engine.render_to_string('regroup01', { 'data': [{'foo': 'c', 'bar': 1}, {'foo': 'd', 'bar': 1}, {'foo': 'a', 'bar': 2}, {'foo': 'b', 'bar': 2}, {'foo': 'x', 'bar': 3}], }) self.assertEqual(output, '1:cd,2:ab,3:x,') @setup({'regroup02': '' '{% regroup data by bar as grouped %}' '{% for group in grouped %}' '{{ group.grouper }}:' '{% for item in group.list %}' '{{ item.foo }}' '{% endfor %}' '{% endfor %}'}) def test_regroup02(self): """ Test for silent failure when target variable isn't found """ output = self.engine.render_to_string('regroup02', {}) self.assertEqual(output, '') @setup({'regroup03': '' '{% regroup data by at|date:"m" as grouped %}' '{% for group in grouped %}' '{{ group.grouper }}:' '{% for item in group.list %}' '{{ item.at|date:"d" }}' '{% endfor %},' '{% endfor %}'}) def test_regroup03(self): """ Regression tests for #17675 The date template filter has expects_localtime = True """ output = self.engine.render_to_string('regroup03', { 'data': [{'at': date(2012, 2, 14)}, {'at': date(2012, 2, 28)}, {'at': date(2012, 7, 4)}], }) self.assertEqual(output, '02:1428,07:04,') @setup({'regroup04': '' '{% regroup data by bar|join:"" as grouped %}' '{% for group in grouped %}' '{{ group.grouper }}:' '{% for item in group.list %}' '{{ item.foo|first }}' '{% endfor %},' '{% endfor %}'}) def test_regroup04(self): """ The join template filter has needs_autoescape = True """ output = self.engine.render_to_string('regroup04', { 'data': [{'foo': 'x', 'bar': ['ab', 'c']}, {'foo': 'y', 'bar': ['a', 'bc']}, {'foo': 'z', 'bar': ['a', 'd']}], }) self.assertEqual(output, 'abc:xy,ad:z,') # Test syntax errors @setup({'regroup05': '{% regroup data by bar as %}'}) def test_regroup05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('regroup05') @setup({'regroup06': '{% regroup data by bar thisaintright grouped %}'}) def test_regroup06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('regroup06') @setup({'regroup07': '{% regroup data thisaintright bar as grouped %}'}) def test_regroup07(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('regroup07') @setup({'regroup08': '{% regroup data by bar as grouped toomanyargs %}'}) def test_regroup08(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('regroup08') Django-1.8.7/tests/template_tests/syntax_tests/test_filter_syntax.py0000664000175000017500000002121412625116214025612 0ustar timtim00000000000000# coding: utf-8 from __future__ import unicode_literals from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import SomeClass, SomeOtherException, UTF8Class, setup class FilterSyntaxTests(SimpleTestCase): @setup({'filter-syntax01': '{{ var|upper }}'}) def test_filter_syntax01(self): """ Basic filter usage """ output = self.engine.render_to_string('filter-syntax01', {"var": "Django is the greatest!"}) self.assertEqual(output, "DJANGO IS THE GREATEST!") @setup({'filter-syntax02': '{{ var|upper|lower }}'}) def test_filter_syntax02(self): """ Chained filters """ output = self.engine.render_to_string('filter-syntax02', {"var": "Django is the greatest!"}) self.assertEqual(output, "django is the greatest!") @setup({'filter-syntax03': '{{ var |upper }}'}) def test_filter_syntax03(self): """ Allow spaces before the filter pipe """ output = self.engine.render_to_string('filter-syntax03', {'var': 'Django is the greatest!'}) self.assertEqual(output, 'DJANGO IS THE GREATEST!') @setup({'filter-syntax04': '{{ var| upper }}'}) def test_filter_syntax04(self): """ Allow spaces after the filter pipe """ output = self.engine.render_to_string('filter-syntax04', {'var': 'Django is the greatest!'}) self.assertEqual(output, 'DJANGO IS THE GREATEST!') @setup({'filter-syntax05': '{{ var|does_not_exist }}'}) def test_filter_syntax05(self): """ Raise TemplateSyntaxError for a nonexistent filter """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter-syntax05') @setup({'filter-syntax06': '{{ var|fil(ter) }}'}) def test_filter_syntax06(self): """ Raise TemplateSyntaxError when trying to access a filter containing an illegal character """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter-syntax06') @setup({'filter-syntax07': "{% nothing_to_see_here %}"}) def test_filter_syntax07(self): """ Raise TemplateSyntaxError for invalid block tags """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter-syntax07') @setup({'filter-syntax08': "{% %}"}) def test_filter_syntax08(self): """ Raise TemplateSyntaxError for empty block tags """ with self.assertRaises(TemplateSyntaxError): self.engine.get_template('filter-syntax08') @setup({'filter-syntax09': '{{ var|cut:"o"|upper|lower }}'}) def test_filter_syntax09(self): """ Chained filters, with an argument to the first one """ output = self.engine.render_to_string('filter-syntax09', {'var': 'Foo'}) self.assertEqual(output, 'f') @setup({'filter-syntax10': r'{{ var|default_if_none:" endquote\" hah" }}'}) def test_filter_syntax10(self): """ Literal string as argument is always "safe" from auto-escaping. """ output = self.engine.render_to_string('filter-syntax10', {"var": None}) self.assertEqual(output, ' endquote" hah') @setup({'filter-syntax11': r'{{ var|default_if_none:var2 }}'}) def test_filter_syntax11(self): """ Variable as argument """ output = self.engine.render_to_string('filter-syntax11', {"var": None, "var2": "happy"}) self.assertEqual(output, 'happy') @setup({'filter-syntax12': r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}'}) def test_filter_syntax12(self): """ Default argument testing """ output = self.engine.render_to_string('filter-syntax12', {"var": True}) self.assertEqual(output, 'yup yes') @setup({'filter-syntax13': r'1{{ var.method3 }}2'}) def test_filter_syntax13(self): """ Fail silently for methods that raise an exception with a `silent_variable_failure` attribute """ output = self.engine.render_to_string('filter-syntax13', {"var": SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, "1INVALID2") else: self.assertEqual(output, "12") @setup({'filter-syntax14': r'1{{ var.method4 }}2'}) def test_filter_syntax14(self): """ In methods that raise an exception without a `silent_variable_attribute` set to True, the exception propagates """ with self.assertRaises(SomeOtherException): self.engine.render_to_string('filter-syntax14', {"var": SomeClass()}) @setup({'filter-syntax15': r'{{ var|default_if_none:"foo\bar" }}'}) def test_filter_syntax15(self): """ Escaped backslash in argument """ output = self.engine.render_to_string('filter-syntax15', {"var": None}) self.assertEqual(output, r'foo\bar') @setup({'filter-syntax16': r'{{ var|default_if_none:"foo\now" }}'}) def test_filter_syntax16(self): """ Escaped backslash using known escape char """ output = self.engine.render_to_string('filter-syntax16', {"var": None}) self.assertEqual(output, r'foo\now') @setup({'filter-syntax17': r'{{ var|join:"" }}'}) def test_filter_syntax17(self): """ Empty strings can be passed as arguments to filters """ output = self.engine.render_to_string('filter-syntax17', {'var': ['a', 'b', 'c']}) self.assertEqual(output, 'abc') @setup({'filter-syntax18': r'{{ var }}'}) def test_filter_syntax18(self): """ Make sure that any unicode strings are converted to bytestrings in the final output. """ output = self.engine.render_to_string('filter-syntax18', {'var': UTF8Class()}) self.assertEqual(output, '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') @setup({'filter-syntax19': '{{ var|truncatewords:1 }}'}) def test_filter_syntax19(self): """ Numbers as filter arguments should work """ output = self.engine.render_to_string('filter-syntax19', {"var": "hello world"}) self.assertEqual(output, "hello ...") @setup({'filter-syntax20': '{{ ""|default_if_none:"was none" }}'}) def test_filter_syntax20(self): """ Filters should accept empty string constants """ output = self.engine.render_to_string('filter-syntax20') self.assertEqual(output, "") @setup({'filter-syntax21': r'1{{ var.silent_fail_key }}2'}) def test_filter_syntax21(self): """ Fail silently for non-callable attribute and dict lookups which raise an exception with a "silent_variable_failure" attribute """ output = self.engine.render_to_string('filter-syntax21', {"var": SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, "1INVALID2") else: self.assertEqual(output, "12") @setup({'filter-syntax22': r'1{{ var.silent_fail_attribute }}2'}) def test_filter_syntax22(self): """ Fail silently for non-callable attribute and dict lookups which raise an exception with a `silent_variable_failure` attribute """ output = self.engine.render_to_string('filter-syntax22', {"var": SomeClass()}) if self.engine.string_if_invalid: self.assertEqual(output, "1INVALID2") else: self.assertEqual(output, "12") @setup({'filter-syntax23': r'1{{ var.noisy_fail_key }}2'}) def test_filter_syntax23(self): """ In attribute and dict lookups that raise an unexpected exception without a `silent_variable_attribute` set to True, the exception propagates """ with self.assertRaises(SomeOtherException): self.engine.render_to_string('filter-syntax23', {"var": SomeClass()}) @setup({'filter-syntax24': r'1{{ var.noisy_fail_attribute }}2'}) def test_filter_syntax24(self): """ In attribute and dict lookups that raise an unexpected exception without a `silent_variable_attribute` set to True, the exception propagates """ with self.assertRaises(SomeOtherException): self.engine.render_to_string('filter-syntax24', {"var": SomeClass()}) @setup({'filter-syntax25': '{{ var.attribute_error_attribute }}'}) def test_filter_syntax25(self): """ #16383 - Attribute errors from an @property value should be reraised. """ with self.assertRaises(AttributeError): self.engine.render_to_string('filter-syntax25', {'var': SomeClass()}) Django-1.8.7/tests/template_tests/syntax_tests/__init__.py0000664000175000017500000000000012625116214023405 0ustar timtim00000000000000Django-1.8.7/tests/template_tests/syntax_tests/test_verbatim.py0000664000175000017500000000317212625116214024533 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class VerbatimTagTests(SimpleTestCase): @setup({'verbatim-tag01': '{% verbatim %}{{bare }}{% endverbatim %}'}) def test_verbatim_tag01(self): output = self.engine.render_to_string('verbatim-tag01') self.assertEqual(output, '{{bare }}') @setup({'verbatim-tag02': '{% verbatim %}{% endif %}{% endverbatim %}'}) def test_verbatim_tag02(self): output = self.engine.render_to_string('verbatim-tag02') self.assertEqual(output, '{% endif %}') @setup({'verbatim-tag03': '{% verbatim %}It\'s the {% verbatim %} tag{% endverbatim %}'}) def test_verbatim_tag03(self): output = self.engine.render_to_string('verbatim-tag03') self.assertEqual(output, 'It\'s the {% verbatim %} tag') @setup({'verbatim-tag04': '{% verbatim %}{% verbatim %}{% endverbatim %}{% endverbatim %}'}) def test_verbatim_tag04(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('verbatim-tag04') @setup({'verbatim-tag05': '{% verbatim %}{% endverbatim %}{% verbatim %}{% endverbatim %}'}) def test_verbatim_tag05(self): output = self.engine.render_to_string('verbatim-tag05') self.assertEqual(output, '') @setup({'verbatim-tag06': '{% verbatim special %}' 'Don\'t {% endverbatim %} just yet{% endverbatim special %}'}) def test_verbatim_tag06(self): output = self.engine.render_to_string('verbatim-tag06') self.assertEqual(output, 'Don\'t {% endverbatim %} just yet') Django-1.8.7/tests/template_tests/syntax_tests/test_setup.py0000664000175000017500000000133312625116214024057 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class SetupTests(SimpleTestCase): def test_setup(self): """ Let's just make sure setup runs cases in the right order. """ cases = [] @setup({}) def method(self): cases.append([ self.engine.string_if_invalid, self.engine.debug, ]) method(self) self.assertEqual(cases[0], ['', False]) self.assertEqual(cases[1], ['', False]) self.assertEqual(cases[2], ['INVALID', False]) self.assertEqual(cases[3], ['INVALID', False]) self.assertEqual(cases[4], ['', True]) self.assertEqual(cases[5], ['', True]) Django-1.8.7/tests/template_tests/syntax_tests/test_spaceless.py0000664000175000017500000000334612625116214024707 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class SpacelessTagTests(SimpleTestCase): @setup({'spaceless01': "{% spaceless %} text {% endspaceless %}"}) def test_spaceless01(self): output = self.engine.render_to_string('spaceless01') self.assertEqual(output, " text ") @setup({'spaceless02': "{% spaceless %} \n text \n {% endspaceless %}"}) def test_spaceless02(self): output = self.engine.render_to_string('spaceless02') self.assertEqual(output, " text ") @setup({'spaceless03': "{% spaceless %}text{% endspaceless %}"}) def test_spaceless03(self): output = self.engine.render_to_string('spaceless03') self.assertEqual(output, "text") @setup({'spaceless04': "{% spaceless %} {{ text }} {% endspaceless %}"}) def test_spaceless04(self): output = self.engine.render_to_string('spaceless04', {'text': 'This & that'}) self.assertEqual(output, "This & that") @setup({'spaceless05': "{% autoescape off %}{% spaceless %}" " {{ text }} {% endspaceless %}" "{% endautoescape %}"}) def test_spaceless05(self): output = self.engine.render_to_string('spaceless05', {'text': 'This & that'}) self.assertEqual(output, "This & that") @setup({'spaceless06': "{% spaceless %} {{ text|safe }} {% endspaceless %}"}) def test_spaceless06(self): output = self.engine.render_to_string('spaceless06', {'text': 'This & that'}) self.assertEqual(output, "This & that") Django-1.8.7/tests/template_tests/syntax_tests/test_extends.py0000664000175000017500000003611212625116214024374 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup inheritance_templates = { 'inheritance01': "1{% block first %}&{% endblock %}3{% block second %}_{% endblock %}", 'inheritance02': "{% extends 'inheritance01' %}" "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", 'inheritance03': "{% extends 'inheritance02' %}", 'inheritance04': "{% extends 'inheritance01' %}", 'inheritance05': "{% extends 'inheritance02' %}", 'inheritance06': "{% extends foo %}", 'inheritance07': "{% extends 'inheritance01' %}{% block second %}5{% endblock %}", 'inheritance08': "{% extends 'inheritance02' %}{% block second %}5{% endblock %}", 'inheritance09': "{% extends 'inheritance04' %}", 'inheritance10': "{% extends 'inheritance04' %} ", 'inheritance11': "{% extends 'inheritance04' %}" "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", 'inheritance12': "{% extends 'inheritance07' %}{% block first %}2{% endblock %}", 'inheritance13': "{% extends 'inheritance02' %}" "{% block first %}a{% endblock %}{% block second %}b{% endblock %}", 'inheritance14': "{% extends 'inheritance01' %}{% block newblock %}NO DISPLAY{% endblock %}", 'inheritance15': "{% extends 'inheritance01' %}" "{% block first %}2{% block inner %}inner{% endblock %}{% endblock %}", 'inheritance16': "{% extends 'inheritance15' %}{% block inner %}out{% endblock %}", 'inheritance17': "{% load testtags %}{% block first %}1234{% endblock %}", 'inheritance18': "{% load testtags %}{% echo this that theother %}5678", 'inheritance19': "{% extends 'inheritance01' %}" "{% block first %}{% load testtags %}{% echo 400 %}5678{% endblock %}", 'inheritance20': "{% extends 'inheritance01' %}{% block first %}{{ block.super }}a{% endblock %}", 'inheritance21': "{% extends 'inheritance02' %}{% block first %}{{ block.super }}a{% endblock %}", 'inheritance22': "{% extends 'inheritance04' %}{% block first %}{{ block.super }}a{% endblock %}", 'inheritance23': "{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", 'inheritance24': "{% extends context_template %}" "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", 'inheritance25': "{% extends context_template.1 %}" "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", 'inheritance26': "no tags", 'inheritance27': "{% extends 'inheritance26' %}", 'inheritance 28': "{% block first %}!{% endblock %}", 'inheritance29': "{% extends 'inheritance 28' %}", 'inheritance30': "1{% if optional %}{% block opt %}2{% endblock %}{% endif %}3", 'inheritance31': "{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", 'inheritance32': "{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", 'inheritance33': "1{% ifequal optional 1 %}{% block opt %}2{% endblock %}{% endifequal %}3", 'inheritance34': "{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", 'inheritance35': "{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", 'inheritance36': "{% for n in numbers %}_{% block opt %}{{ n }}{% endblock %}{% endfor %}_", 'inheritance37': "{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", 'inheritance38': "{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", 'inheritance39': "{% extends 'inheritance30' %}{% block opt %}new{{ block.super }}{% endblock %}", 'inheritance40': "{% extends 'inheritance33' %}{% block opt %}new{{ block.super }}{% endblock %}", 'inheritance41': "{% extends 'inheritance36' %}{% block opt %}new{{ block.super }}{% endblock %}", 'inheritance42': "{% extends 'inheritance02'|cut:' ' %}", } class InheritanceTests(SimpleTestCase): @setup(inheritance_templates) def test_inheritance01(self): """ Standard template with no inheritance """ output = self.engine.render_to_string('inheritance01') self.assertEqual(output, '1&3_') @setup(inheritance_templates) def test_inheritance02(self): """ Standard two-level inheritance """ output = self.engine.render_to_string('inheritance02') self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance03(self): """ Three-level with no redefinitions on third level """ output = self.engine.render_to_string('inheritance03') self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance04(self): """ Two-level with no redefinitions on second level """ output = self.engine.render_to_string('inheritance04') self.assertEqual(output, '1&3_') @setup(inheritance_templates) def test_inheritance05(self): """ Two-level with double quotes instead of single quotes """ output = self.engine.render_to_string('inheritance05') self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance06(self): """ Three-level with variable parent-template name """ output = self.engine.render_to_string('inheritance06', {'foo': 'inheritance02'}) self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance07(self): """ Two-level with one block defined, one block not defined """ output = self.engine.render_to_string('inheritance07') self.assertEqual(output, '1&35') @setup(inheritance_templates) def test_inheritance08(self): """ Three-level with one block defined on this level, two blocks defined next level """ output = self.engine.render_to_string('inheritance08') self.assertEqual(output, '1235') @setup(inheritance_templates) def test_inheritance09(self): """ Three-level with second and third levels blank """ output = self.engine.render_to_string('inheritance09') self.assertEqual(output, '1&3_') @setup(inheritance_templates) def test_inheritance10(self): """ Three-level with space NOT in a block -- should be ignored """ output = self.engine.render_to_string('inheritance10') self.assertEqual(output, '1&3_') @setup(inheritance_templates) def test_inheritance11(self): """ Three-level with both blocks defined on this level, but none on second level """ output = self.engine.render_to_string('inheritance11') self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance12(self): """ Three-level with this level providing one and second level providing the other """ output = self.engine.render_to_string('inheritance12') self.assertEqual(output, '1235') @setup(inheritance_templates) def test_inheritance13(self): """ Three-level with this level overriding second level """ output = self.engine.render_to_string('inheritance13') self.assertEqual(output, '1a3b') @setup(inheritance_templates) def test_inheritance14(self): """ A block defined only in a child template shouldn't be displayed """ output = self.engine.render_to_string('inheritance14') self.assertEqual(output, '1&3_') @setup(inheritance_templates) def test_inheritance15(self): """ A block within another block """ output = self.engine.render_to_string('inheritance15') self.assertEqual(output, '12inner3_') @setup(inheritance_templates) def test_inheritance16(self): """ A block within another block (level 2) """ output = self.engine.render_to_string('inheritance16') self.assertEqual(output, '12out3_') @setup(inheritance_templates) def test_inheritance17(self): """ {% load %} tag (parent -- setup for exception04) """ output = self.engine.render_to_string('inheritance17') self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance18(self): """ {% load %} tag (standard usage, without inheritance) """ output = self.engine.render_to_string('inheritance18') self.assertEqual(output, 'this that theother5678') @setup(inheritance_templates) def test_inheritance19(self): """ {% load %} tag (within a child template) """ output = self.engine.render_to_string('inheritance19') self.assertEqual(output, '140056783_') @setup(inheritance_templates) def test_inheritance20(self): """ Two-level inheritance with {{ block.super }} """ output = self.engine.render_to_string('inheritance20') self.assertEqual(output, '1&a3_') @setup(inheritance_templates) def test_inheritance21(self): """ Three-level inheritance with {{ block.super }} from parent """ output = self.engine.render_to_string('inheritance21') self.assertEqual(output, '12a34') @setup(inheritance_templates) def test_inheritance22(self): """ Three-level inheritance with {{ block.super }} from grandparent """ output = self.engine.render_to_string('inheritance22') self.assertEqual(output, '1&a3_') @setup(inheritance_templates) def test_inheritance23(self): """ Three-level inheritance with {{ block.super }} from parent and grandparent """ output = self.engine.render_to_string('inheritance23') self.assertEqual(output, '1&ab3_') @setup(inheritance_templates) def test_inheritance24(self): """ Inheritance from local context without use of template loader """ context_template = self.engine.from_string("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}") output = self.engine.render_to_string('inheritance24', {'context_template': context_template}) self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance25(self): """ Inheritance from local context with variable parent template """ context_template = [ self.engine.from_string("Wrong"), self.engine.from_string("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}"), ] output = self.engine.render_to_string('inheritance25', {'context_template': context_template}) self.assertEqual(output, '1234') @setup(inheritance_templates) def test_inheritance26(self): """ Set up a base template to extend """ output = self.engine.render_to_string('inheritance26') self.assertEqual(output, 'no tags') @setup(inheritance_templates) def test_inheritance27(self): """ Inheritance from a template that doesn't have any blocks """ output = self.engine.render_to_string('inheritance27') self.assertEqual(output, 'no tags') @setup(inheritance_templates) def test_inheritance_28(self): """ Set up a base template with a space in it. """ output = self.engine.render_to_string('inheritance 28') self.assertEqual(output, '!') @setup(inheritance_templates) def test_inheritance29(self): """ Inheritance from a template with a space in its name should work. """ output = self.engine.render_to_string('inheritance29') self.assertEqual(output, '!') @setup(inheritance_templates) def test_inheritance30(self): """ Base template, putting block in a conditional {% if %} tag """ output = self.engine.render_to_string('inheritance30', {'optional': True}) self.assertEqual(output, '123') # Inherit from a template with block wrapped in an {% if %} tag # (in parent), still gets overridden @setup(inheritance_templates) def test_inheritance31(self): output = self.engine.render_to_string('inheritance31', {'optional': True}) self.assertEqual(output, '1two3') @setup(inheritance_templates) def test_inheritance32(self): output = self.engine.render_to_string('inheritance32') self.assertEqual(output, '13') @setup(inheritance_templates) def test_inheritance33(self): """ Base template, putting block in a conditional {% ifequal %} tag """ output = self.engine.render_to_string('inheritance33', {'optional': 1}) self.assertEqual(output, '123') @setup(inheritance_templates) def test_inheritance34(self): """ Inherit from a template with block wrapped in an {% ifequal %} tag (in parent), still gets overridden """ output = self.engine.render_to_string('inheritance34', {'optional': 1}) self.assertEqual(output, '1two3') @setup(inheritance_templates) def test_inheritance35(self): """ Inherit from a template with block wrapped in an {% ifequal %} tag (in parent), still gets overridden """ output = self.engine.render_to_string('inheritance35', {'optional': 2}) self.assertEqual(output, '13') @setup(inheritance_templates) def test_inheritance36(self): """ Base template, putting block in a {% for %} tag """ output = self.engine.render_to_string('inheritance36', {'numbers': '123'}) self.assertEqual(output, '_1_2_3_') @setup(inheritance_templates) def test_inheritance37(self): """ Inherit from a template with block wrapped in an {% for %} tag (in parent), still gets overridden """ output = self.engine.render_to_string('inheritance37', {'numbers': '123'}) self.assertEqual(output, '_X_X_X_') @setup(inheritance_templates) def test_inheritance38(self): """ Inherit from a template with block wrapped in an {% for %} tag (in parent), still gets overridden """ output = self.engine.render_to_string('inheritance38') self.assertEqual(output, '_') # The super block will still be found. @setup(inheritance_templates) def test_inheritance39(self): output = self.engine.render_to_string('inheritance39', {'optional': True}) self.assertEqual(output, '1new23') @setup(inheritance_templates) def test_inheritance40(self): output = self.engine.render_to_string('inheritance40', {'optional': 1}) self.assertEqual(output, '1new23') @setup(inheritance_templates) def test_inheritance41(self): output = self.engine.render_to_string('inheritance41', {'numbers': '123'}) self.assertEqual(output, '_new1_new2_new3_') @setup(inheritance_templates) def test_inheritance42(self): """ Expression starting and ending with a quote """ output = self.engine.render_to_string('inheritance42') self.assertEqual(output, '1234') Django-1.8.7/tests/template_tests/syntax_tests/test_firstof.py0000664000175000017500000000650612625116214024402 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import RemovedInDjango110Warning from ..utils import setup class FirstOfTagTests(SimpleTestCase): @setup({'firstof01': '{% firstof a b c %}'}) def test_firstof01(self): output = self.engine.render_to_string('firstof01', {'a': 0, 'c': 0, 'b': 0}) self.assertEqual(output, '') @setup({'firstof02': '{% firstof a b c %}'}) def test_firstof02(self): output = self.engine.render_to_string('firstof02', {'a': 1, 'c': 0, 'b': 0}) self.assertEqual(output, '1') @setup({'firstof03': '{% firstof a b c %}'}) def test_firstof03(self): output = self.engine.render_to_string('firstof03', {'a': 0, 'c': 0, 'b': 2}) self.assertEqual(output, '2') @setup({'firstof04': '{% firstof a b c %}'}) def test_firstof04(self): output = self.engine.render_to_string('firstof04', {'a': 0, 'c': 3, 'b': 0}) self.assertEqual(output, '3') @setup({'firstof05': '{% firstof a b c %}'}) def test_firstof05(self): output = self.engine.render_to_string('firstof05', {'a': 1, 'c': 3, 'b': 2}) self.assertEqual(output, '1') @setup({'firstof06': '{% firstof a b c %}'}) def test_firstof06(self): output = self.engine.render_to_string('firstof06', {'c': 3, 'b': 0}) self.assertEqual(output, '3') @setup({'firstof07': '{% firstof a b "c" %}'}) def test_firstof07(self): output = self.engine.render_to_string('firstof07', {'a': 0}) self.assertEqual(output, 'c') @setup({'firstof08': '{% firstof a b "c and d" %}'}) def test_firstof08(self): output = self.engine.render_to_string('firstof08', {'a': 0, 'b': 0}) self.assertEqual(output, 'c and d') @setup({'firstof09': '{% firstof %}'}) def test_firstof09(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('firstof09') @setup({'firstof10': '{% firstof a %}'}) def test_firstof10(self): output = self.engine.render_to_string('firstof10', {'a': '<'}) self.assertEqual(output, '<') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'firstof11': '{% load firstof from future %}{% firstof a b %}'}) def test_firstof11(self): output = self.engine.render_to_string('firstof11', {'a': '<', 'b': '>'}) self.assertEqual(output, '<') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'firstof12': '{% load firstof from future %}{% firstof a b %}'}) def test_firstof12(self): output = self.engine.render_to_string('firstof12', {'a': '', 'b': '>'}) self.assertEqual(output, '>') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'firstof13': '{% load firstof from future %}' '{% autoescape off %}{% firstof a %}{% endautoescape %}'}) def test_firstof13(self): output = self.engine.render_to_string('firstof13', {'a': '<'}) self.assertEqual(output, '<') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'firstof14': '{% load firstof from future %}{% firstof a|safe b %}'}) def test_firstof14(self): output = self.engine.render_to_string('firstof14', {'a': '<'}) self.assertEqual(output, '<') Django-1.8.7/tests/template_tests/syntax_tests/test_cache.py0000664000175000017500000001443412625116214023770 0ustar timtim00000000000000from django.core.cache import cache from django.template import Context, Template, TemplateSyntaxError from django.test import SimpleTestCase, override_settings from ..utils import setup class CacheTagTests(SimpleTestCase): def tearDown(self): cache.clear() @setup({'cache03': '{% load cache %}{% cache 2 test %}cache03{% endcache %}'}) def test_cache03(self): output = self.engine.render_to_string('cache03') self.assertEqual(output, 'cache03') @setup({ 'cache03': '{% load cache %}{% cache 2 test %}cache03{% endcache %}', 'cache04': '{% load cache %}{% cache 2 test %}cache04{% endcache %}', }) def test_cache04(self): self.engine.render_to_string('cache03') output = self.engine.render_to_string('cache04') self.assertEqual(output, 'cache03') @setup({'cache05': '{% load cache %}{% cache 2 test foo %}cache05{% endcache %}'}) def test_cache05(self): output = self.engine.render_to_string('cache05', {'foo': 1}) self.assertEqual(output, 'cache05') @setup({'cache06': '{% load cache %}{% cache 2 test foo %}cache06{% endcache %}'}) def test_cache06(self): output = self.engine.render_to_string('cache06', {'foo': 2}) self.assertEqual(output, 'cache06') @setup({ 'cache05': '{% load cache %}{% cache 2 test foo %}cache05{% endcache %}', 'cache07': '{% load cache %}{% cache 2 test foo %}cache07{% endcache %}', }) def test_cache07(self): context = {'foo': 1} self.engine.render_to_string('cache05', context) output = self.engine.render_to_string('cache07', context) self.assertEqual(output, 'cache05') @setup({ 'cache06': '{% load cache %}{% cache 2 test foo %}cache06{% endcache %}', 'cache08': '{% load cache %}{% cache time test foo %}cache08{% endcache %}', }) def test_cache08(self): """ Allow first argument to be a variable. """ context = {'foo': 2, 'time': 2} self.engine.render_to_string('cache06', context) output = self.engine.render_to_string('cache08', context) self.assertEqual(output, 'cache06') # Raise exception if we don't have at least 2 args, first one integer. @setup({'cache11': '{% load cache %}{% cache %}{% endcache %}'}) def test_cache11(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cache11') @setup({'cache12': '{% load cache %}{% cache 1 %}{% endcache %}'}) def test_cache12(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('cache12') @setup({'cache13': '{% load cache %}{% cache foo bar %}{% endcache %}'}) def test_cache13(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('cache13') @setup({'cache14': '{% load cache %}{% cache foo bar %}{% endcache %}'}) def test_cache14(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('cache14', {'foo': 'fail'}) @setup({'cache15': '{% load cache %}{% cache foo bar %}{% endcache %}'}) def test_cache15(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('cache15', {'foo': []}) @setup({'cache16': '{% load cache %}{% cache 1 foo bar %}{% endcache %}'}) def test_cache16(self): """ Regression test for #7460. """ output = self.engine.render_to_string('cache16', {'foo': 'foo', 'bar': 'with spaces'}) self.assertEqual(output, '') @setup({'cache17': '{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}'}) def test_cache17(self): """ Regression test for #11270. """ output = self.engine.render_to_string('cache17', {'poem': 'Oh freddled gruntbuggly/' 'Thy micturations are to me/' 'As plurdled gabbleblotchits/' 'On a lurgid bee/' 'That mordiously hath bitled out/' 'Its earted jurtles/' 'Into a rancid festering/' 'Or else I shall rend thee in the gobberwarts' 'with my blurglecruncheon/' 'See if I dont.'}) self.assertEqual(output, 'Some Content') @setup({'cache18': '{% load cache custom %}{% cache 2|noop:"x y" cache18 %}cache18{% endcache %}'}) def test_cache18(self): """ Test whitespace in filter arguments """ output = self.engine.render_to_string('cache18') self.assertEqual(output, 'cache18') class CacheTests(SimpleTestCase): def test_cache_regression_20130(self): t = Template('{% load cache %}{% cache 1 regression_20130 %}foo{% endcache %}') cachenode = t.nodelist[1] self.assertEqual(cachenode.fragment_name, 'regression_20130') @override_settings(CACHES={ 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'default', }, 'template_fragments': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'fragments', }, }) def test_cache_fragment_cache(self): """ When a cache called "template_fragments" is present, the cache tag will use it in preference to 'default' """ t1 = Template('{% load cache %}{% cache 1 fragment %}foo{% endcache %}') t2 = Template('{% load cache %}{% cache 1 fragment using="default" %}bar{% endcache %}') ctx = Context() o1 = t1.render(ctx) o2 = t2.render(ctx) self.assertEqual(o1, 'foo') self.assertEqual(o2, 'bar') def test_cache_missing_backend(self): """ When a cache that doesn't exist is specified, the cache tag will raise a TemplateSyntaxError '""" t = Template('{% load cache %}{% cache 1 backend using="unknown" %}bar{% endcache %}') ctx = Context() with self.assertRaises(TemplateSyntaxError): t.render(ctx) Django-1.8.7/tests/template_tests/syntax_tests/test_with.py0000664000175000017500000000430512625116214023674 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class WithTagTests(SimpleTestCase): @setup({'with01': '{% with key=dict.key %}{{ key }}{% endwith %}'}) def test_with01(self): output = self.engine.render_to_string('with01', {'dict': {'key': 50}}) self.assertEqual(output, '50') @setup({'legacywith01': '{% with dict.key as key %}{{ key }}{% endwith %}'}) def test_legacywith01(self): output = self.engine.render_to_string('legacywith01', {'dict': {'key': 50}}) self.assertEqual(output, '50') @setup({'with02': '{{ key }}{% with key=dict.key %}' '{{ key }}-{{ dict.key }}-{{ key }}' '{% endwith %}{{ key }}'}) def test_with02(self): output = self.engine.render_to_string('with02', {'dict': {'key': 50}}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID50-50-50INVALID') else: self.assertEqual(output, '50-50-50') @setup({'legacywith02': '{{ key }}{% with dict.key as key %}' '{{ key }}-{{ dict.key }}-{{ key }}' '{% endwith %}{{ key }}'}) def test_legacywith02(self): output = self.engine.render_to_string('legacywith02', {'dict': {'key': 50}}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID50-50-50INVALID') else: self.assertEqual(output, '50-50-50') @setup({'with03': '{% with a=alpha b=beta %}{{ a }}{{ b }}{% endwith %}'}) def test_with03(self): output = self.engine.render_to_string('with03', {'alpha': 'A', 'beta': 'B'}) self.assertEqual(output, 'AB') @setup({'with-error01': '{% with dict.key xx key %}{{ key }}{% endwith %}'}) def test_with_error01(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('with-error01', {'dict': {'key': 50}}) @setup({'with-error02': '{% with dict.key as %}{{ key }}{% endwith %}'}) def test_with_error02(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('with-error02', {'dict': {'key': 50}}) Django-1.8.7/tests/template_tests/syntax_tests/test_include.py0000664000175000017500000003105112625116214024342 0ustar timtim00000000000000from django.template import ( Context, Template, TemplateDoesNotExist, TemplateSyntaxError, engines, ) from django.test import SimpleTestCase, override_settings from ..utils import setup from .test_basic import basic_templates include_fail_templates = { 'include-fail1': '{% load bad_tag %}{% badtag %}', 'include-fail2': '{% load broken_tag %}', } class IncludeTagTests(SimpleTestCase): @setup({'include01': '{% include "basic-syntax01" %}'}, basic_templates) def test_include01(self): output = self.engine.render_to_string('include01') self.assertEqual(output, 'something cool') @setup({'include02': '{% include "basic-syntax02" %}'}, basic_templates) def test_include02(self): output = self.engine.render_to_string('include02', {'headline': 'Included'}) self.assertEqual(output, 'Included') @setup({'include03': '{% include template_name %}'}, basic_templates) def test_include03(self): output = self.engine.render_to_string( 'include03', {'template_name': 'basic-syntax02', 'headline': 'Included'}, ) self.assertEqual(output, 'Included') @setup({'include04': 'a{% include "nonexistent" %}b'}) def test_include04(self): template = self.engine.get_template('include04') if self.engine.debug: with self.assertRaises(TemplateDoesNotExist): template.render(Context({})) else: output = template.render(Context({})) self.assertEqual(output, "ab") @setup({ 'include 05': 'template with a space', 'include06': '{% include "include 05"%}', }) def test_include06(self): output = self.engine.render_to_string('include06') self.assertEqual(output, "template with a space") @setup({'include07': '{% include "basic-syntax02" with headline="Inline" %}'}, basic_templates) def test_include07(self): output = self.engine.render_to_string('include07', {'headline': 'Included'}) self.assertEqual(output, 'Inline') @setup({'include08': '{% include headline with headline="Dynamic" %}'}, basic_templates) def test_include08(self): output = self.engine.render_to_string('include08', {'headline': 'basic-syntax02'}) self.assertEqual(output, 'Dynamic') @setup( {'include09': '{{ first }}--' '{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}' '--{{ second }}'}, basic_templates, ) def test_include09(self): output = self.engine.render_to_string('include09', {'first': 'Ul', 'second': 'lU'}) self.assertEqual(output, 'Ul--LU --- UL--lU') @setup({'include10': '{% include "basic-syntax03" only %}'}, basic_templates) def test_include10(self): output = self.engine.render_to_string('include10', {'first': '1'}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID --- INVALID') else: self.assertEqual(output, ' --- ') @setup({'include11': '{% include "basic-syntax03" only with second=2 %}'}, basic_templates) def test_include11(self): output = self.engine.render_to_string('include11', {'first': '1'}) if self.engine.string_if_invalid: self.assertEqual(output, 'INVALID --- 2') else: self.assertEqual(output, ' --- 2') @setup({'include12': '{% include "basic-syntax03" with first=1 only %}'}, basic_templates) def test_include12(self): output = self.engine.render_to_string('include12', {'second': '2'}) if self.engine.string_if_invalid: self.assertEqual(output, '1 --- INVALID') else: self.assertEqual(output, '1 --- ') @setup( {'include13': '{% autoescape off %}{% include "basic-syntax03" %}{% endautoescape %}'}, basic_templates, ) def test_include13(self): output = self.engine.render_to_string('include13', {'first': '&'}) if self.engine.string_if_invalid: self.assertEqual(output, '& --- INVALID') else: self.assertEqual(output, '& --- ') @setup( {'include14': '{% autoescape off %}' '{% include "basic-syntax03" with first=var1 only %}' '{% endautoescape %}'}, basic_templates, ) def test_include14(self): output = self.engine.render_to_string('include14', {'var1': '&'}) if self.engine.string_if_invalid: self.assertEqual(output, '& --- INVALID') else: self.assertEqual(output, '& --- ') # Include syntax errors @setup({'include-error01': '{% include "basic-syntax01" with %}'}) def test_include_error01(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-error01') @setup({'include-error02': '{% include "basic-syntax01" with "no key" %}'}) def test_include_error02(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-error02') @setup({'include-error03': '{% include "basic-syntax01" with dotted.arg="error" %}'}) def test_include_error03(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-error03') @setup({'include-error04': '{% include "basic-syntax01" something_random %}'}) def test_include_error04(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-error04') @setup({'include-error05': '{% include "basic-syntax01" foo="duplicate" foo="key" %}'}) def test_include_error05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-error05') @setup({'include-error06': '{% include "basic-syntax01" only only %}'}) def test_include_error06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-error06') @setup(include_fail_templates) def test_include_fail1(self): with self.assertRaises(RuntimeError): self.engine.get_template('include-fail1') @setup(include_fail_templates) def test_include_fail2(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('include-fail2') @setup({'include-error07': '{% include "include-fail1" %}'}, include_fail_templates) def test_include_error07(self): template = self.engine.get_template('include-error07') if self.engine.debug: with self.assertRaises(RuntimeError): template.render(Context()) else: self.assertEqual(template.render(Context()), '') @setup({'include-error08': '{% include "include-fail2" %}'}, include_fail_templates) def test_include_error08(self): template = self.engine.get_template('include-error08') if self.engine.debug: with self.assertRaises(TemplateSyntaxError): template.render(Context()) else: self.assertEqual(template.render(Context()), '') @setup({'include-error09': '{% include failed_include %}'}, include_fail_templates) def test_include_error09(self): context = Context({'failed_include': 'include-fail1'}) template = self.engine.get_template('include-error09') if self.engine.debug: with self.assertRaises(RuntimeError): template.render(context) else: self.assertEqual(template.render(context), '') @setup({'include-error10': '{% include failed_include %}'}, include_fail_templates) def test_include_error10(self): context = Context({'failed_include': 'include-fail2'}) template = self.engine.get_template('include-error10') if self.engine.debug: with self.assertRaises(TemplateSyntaxError): template.render(context) else: self.assertEqual(template.render(context), '') class IncludeTests(SimpleTestCase): # Test the base loader class via the app loader. load_template # from base is used by all shipped loaders excepting cached, # which has its own test. @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, 'OPTIONS': { # Enable debug, otherwise the exception raised during # {% include %} processing will be suppressed. 'debug': True, } }]) def test_include_missing_template(self): """ Tests that the correct template is identified as not existing when {% include %} specifies a template that does not exist. """ template = engines['django'].get_template('test_include_error.html') with self.assertRaises(TemplateDoesNotExist) as e: template.render() self.assertEqual(e.exception.args[0], 'missing.html') # Test the base loader class via the app loader. load_template # from base is used by all shipped loaders excepting cached, # which has its own test. @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, 'OPTIONS': { # Enable debug, otherwise the exception raised during # {% include %} processing will be suppressed. 'debug': True, } }]) def test_extends_include_missing_baseloader(self): """ #12787 -- Tests that the correct template is identified as not existing when {% extends %} specifies a template that does exist, but that template has an {% include %} of something that does not exist. """ template = engines['django'].get_template('test_extends_error.html') with self.assertRaises(TemplateDoesNotExist) as e: template.render() self.assertEqual(e.exception.args[0], 'missing.html') @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'OPTIONS': { 'debug': True, 'loaders': [ ('django.template.loaders.cached.Loader', [ 'django.template.loaders.app_directories.Loader', ]), ], }, }]) def test_extends_include_missing_cachedloader(self): """ Test the cache loader separately since it overrides load_template. """ template = engines['django'].get_template('test_extends_error.html') with self.assertRaises(TemplateDoesNotExist) as e: template.render() self.assertEqual(e.exception.args[0], 'missing.html') # Repeat to ensure it still works when loading from the cache template = engines['django'].get_template('test_extends_error.html') with self.assertRaises(TemplateDoesNotExist) as e: template.render() self.assertEqual(e.exception.args[0], 'missing.html') def test_include_template_argument(self): """ Support any render() supporting object """ ctx = Context({ 'tmpl': Template('This worked!'), }) outer_tmpl = Template('{% include tmpl %}') output = outer_tmpl.render(ctx) self.assertEqual(output, 'This worked!') @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'OPTIONS': { 'debug': True, }, }]) def test_include_immediate_missing(self): """ #16417 -- Include tags pointing to missing templates should not raise an error at parsing time. """ template = Template('{% include "this_does_not_exist.html" %}') self.assertIsInstance(template, Template) @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, 'OPTIONS': { 'debug': True, }, }]) def test_include_recursive(self): comments = [ { 'comment': 'A1', 'children': [ {'comment': 'B1', 'children': []}, {'comment': 'B2', 'children': []}, {'comment': 'B3', 'children': [ {'comment': 'C1', 'children': []} ]}, ] } ] t = engines['django'].get_template('recursive_include.html') self.assertEqual( "Recursion! A1 Recursion! B1 B2 B3 Recursion! C1", t.render({'comments': comments}).replace(' ', '').replace('\n', ' ').strip(), ) Django-1.8.7/tests/template_tests/syntax_tests/test_builtins.py0000664000175000017500000000116512625116214024553 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class BuiltinsTests(SimpleTestCase): @setup({'builtins01': '{{ True }}'}) def test_builtins01(self): output = self.engine.render_to_string('builtins01') self.assertEqual(output, 'True') @setup({'builtins02': '{{ False }}'}) def test_builtins02(self): output = self.engine.render_to_string('builtins02') self.assertEqual(output, 'False') @setup({'builtins03': '{{ None }}'}) def test_builtins03(self): output = self.engine.render_to_string('builtins03') self.assertEqual(output, 'None') Django-1.8.7/tests/template_tests/syntax_tests/test_url.py0000664000175000017500000002545512625116214023534 0ustar timtim00000000000000# coding: utf-8 from django.core.urlresolvers import NoReverseMatch from django.template import TemplateSyntaxError from django.test import SimpleTestCase, ignore_warnings, override_settings from django.utils.deprecation import RemovedInDjango110Warning from ..utils import setup @override_settings(ROOT_URLCONF='template_tests.urls') class UrlTagTests(SimpleTestCase): # Successes @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url01': '{% url "template_tests.views.client" client.id %}'}) def test_url01(self): output = self.engine.render_to_string('url01', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url02': '{% url "template_tests.views.client_action" id=client.id action="update" %}'}) def test_url02(self): output = self.engine.render_to_string('url02', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/update/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url02a': '{% url "template_tests.views.client_action" client.id "update" %}'}) def test_url02a(self): output = self.engine.render_to_string('url02a', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/update/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url02b': "{% url 'template_tests.views.client_action' id=client.id action='update' %}"}) def test_url02b(self): output = self.engine.render_to_string('url02b', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/update/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url02c': "{% url 'template_tests.views.client_action' client.id 'update' %}"}) def test_url02c(self): output = self.engine.render_to_string('url02c', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/update/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url03': '{% url "template_tests.views.index" %}'}) def test_url03(self): output = self.engine.render_to_string('url03') self.assertEqual(output, '/') @setup({'url04': '{% url "named.client" client.id %}'}) def test_url04(self): output = self.engine.render_to_string('url04', {'client': {'id': 1}}) self.assertEqual(output, '/named-client/1/') @setup({'url05': '{% url "метка_оператора" v %}'}) def test_url05(self): output = self.engine.render_to_string('url05', {'v': 'Ω'}) self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') @setup({'url06': '{% url "метка_оператора_2" tag=v %}'}) def test_url06(self): output = self.engine.render_to_string('url06', {'v': 'Ω'}) self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url07': '{% url "template_tests.views.client2" tag=v %}'}) def test_url07(self): output = self.engine.render_to_string('url07', {'v': 'Ω'}) self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') @setup({'url08': '{% url "метка_оператора" v %}'}) def test_url08(self): output = self.engine.render_to_string('url08', {'v': 'Ω'}) self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') @setup({'url09': '{% url "метка_оператора_2" tag=v %}'}) def test_url09(self): output = self.engine.render_to_string('url09', {'v': 'Ω'}) self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url10': '{% url "template_tests.views.client_action" id=client.id action="two words" %}'}) def test_url10(self): output = self.engine.render_to_string('url10', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/two%20words/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url11': '{% url "template_tests.views.client_action" id=client.id action="==" %}'}) def test_url11(self): output = self.engine.render_to_string('url11', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/==/') @setup({'url12': '{% url "template_tests.views.client_action" ' 'id=client.id action="!$&\'()*+,;=~:@," %}'}) @ignore_warnings(category=RemovedInDjango110Warning) def test_url12(self): output = self.engine.render_to_string('url12', {'client': {'id': 1}}) self.assertEqual(output, '/client/1/!$&\'()*+,;=~:@,/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url13': '{% url "template_tests.views.client_action" ' 'id=client.id action=arg|join:"-" %}'}) def test_url13(self): output = self.engine.render_to_string('url13', {'client': {'id': 1}, 'arg': ['a', 'b']}) self.assertEqual(output, '/client/1/a-b/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url14': '{% url "template_tests.views.client_action" client.id arg|join:"-" %}'}) def test_url14(self): output = self.engine.render_to_string('url14', {'client': {'id': 1}, 'arg': ['a', 'b']}) self.assertEqual(output, '/client/1/a-b/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url15': '{% url "template_tests.views.client_action" 12 "test" %}'}) def test_url15(self): output = self.engine.render_to_string('url15') self.assertEqual(output, '/client/12/test/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url18': '{% url "template_tests.views.client" "1,2" %}'}) def test_url18(self): output = self.engine.render_to_string('url18') self.assertEqual(output, '/client/1,2/') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url19': '{% url named_url client.id %}'}) def test_url19(self): output = self.engine.render_to_string('url19', {'client': {'id': 1}, 'named_url': 'template_tests.views.client'}) self.assertEqual(output, '/client/1/') @setup({'url20': '{% url url_name_in_var client.id %}'}) def test_url20(self): output = self.engine.render_to_string('url20', {'client': {'id': 1}, 'url_name_in_var': 'named.client'}) self.assertEqual(output, '/named-client/1/') # Failures @setup({'url-fail01': '{% url %}'}) def test_url_fail01(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail01') @setup({'url-fail02': '{% url "no_such_view" %}'}) def test_url_fail02(self): with self.assertRaises(NoReverseMatch): self.engine.render_to_string('url-fail02') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url-fail03': '{% url "template_tests.views.client" %}'}) def test_url_fail03(self): with self.assertRaises(NoReverseMatch): self.engine.render_to_string('url-fail03') @setup({'url-fail04': '{% url "view" id, %}'}) def test_url_fail04(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail04') @setup({'url-fail05': '{% url "view" id= %}'}) def test_url_fail05(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail05') @setup({'url-fail06': '{% url "view" a.id=id %}'}) def test_url_fail06(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail06') @setup({'url-fail07': '{% url "view" a.id!id %}'}) def test_url_fail07(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail07') @setup({'url-fail08': '{% url "view" id="unterminatedstring %}'}) def test_url_fail08(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail08') @setup({'url-fail09': '{% url "view" id=", %}'}) def test_url_fail09(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('url-fail09') @setup({'url-fail11': '{% url named_url %}'}) def test_url_fail11(self): with self.assertRaises(NoReverseMatch): self.engine.render_to_string('url-fail11') @setup({'url-fail12': '{% url named_url %}'}) def test_url_fail12(self): with self.assertRaises(NoReverseMatch): self.engine.render_to_string('url-fail12', {'named_url': 'no_such_view'}) @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url-fail13': '{% url named_url %}'}) def test_url_fail13(self): with self.assertRaises(NoReverseMatch): self.engine.render_to_string('url-fail13', {'named_url': 'template_tests.views.client'}) @setup({'url-fail14': '{% url named_url id, %}'}) def test_url_fail14(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('url-fail14', {'named_url': 'view'}) @setup({'url-fail15': '{% url named_url id= %}'}) def test_url_fail15(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('url-fail15', {'named_url': 'view'}) @setup({'url-fail16': '{% url named_url a.id=id %}'}) def test_url_fail16(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('url-fail16', {'named_url': 'view'}) @setup({'url-fail17': '{% url named_url a.id!id %}'}) def test_url_fail17(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('url-fail17', {'named_url': 'view'}) @setup({'url-fail18': '{% url named_url id="unterminatedstring %}'}) def test_url_fail18(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('url-fail18', {'named_url': 'view'}) @setup({'url-fail19': '{% url named_url id=", %}'}) def test_url_fail19(self): with self.assertRaises(TemplateSyntaxError): self.engine.render_to_string('url-fail19', {'named_url': 'view'}) # {% url ... as var %} @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url-asvar01': '{% url "template_tests.views.index" as url %}'}) def test_url_asvar01(self): output = self.engine.render_to_string('url-asvar01') self.assertEqual(output, '') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'url-asvar02': '{% url "template_tests.views.index" as url %}{{ url }}'}) def test_url_asvar02(self): output = self.engine.render_to_string('url-asvar02') self.assertEqual(output, '/') @setup({'url-asvar03': '{% url "no_such_view" as url %}{{ url }}'}) def test_url_asvar03(self): output = self.engine.render_to_string('url-asvar03') self.assertEqual(output, '') Django-1.8.7/tests/template_tests/syntax_tests/test_load.py0000664000175000017500000000544312625116214023644 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class LoadTagTests(SimpleTestCase): @setup({'load01': '{% load testtags subpackage.echo %}{% echo test %} {% echo2 "test" %}'}) def test_load01(self): output = self.engine.render_to_string('load01') self.assertEqual(output, 'test test') @setup({'load02': '{% load subpackage.echo %}{% echo2 "test" %}'}) def test_load02(self): output = self.engine.render_to_string('load02') self.assertEqual(output, 'test') # {% load %} tag, importing individual tags @setup({'load03': '{% load echo from testtags %}{% echo this that theother %}'}) def test_load03(self): output = self.engine.render_to_string('load03') self.assertEqual(output, 'this that theother') @setup({'load04': '{% load echo other_echo from testtags %}' '{% echo this that theother %} {% other_echo and another thing %}'}) def test_load04(self): output = self.engine.render_to_string('load04') self.assertEqual(output, 'this that theother and another thing') @setup({'load05': '{% load echo upper from testtags %}' '{% echo this that theother %} {{ statement|upper }}'}) def test_load05(self): output = self.engine.render_to_string('load05', {'statement': 'not shouting'}) self.assertEqual(output, 'this that theother NOT SHOUTING') @setup({'load06': '{% load echo2 from subpackage.echo %}{% echo2 "test" %}'}) def test_load06(self): output = self.engine.render_to_string('load06') self.assertEqual(output, 'test') # {% load %} tag errors @setup({'load07': '{% load echo other_echo bad_tag from testtags %}'}) def test_load07(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('load07') @setup({'load08': '{% load echo other_echo bad_tag from %}'}) def test_load08(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('load08') @setup({'load09': '{% load from testtags %}'}) def test_load09(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('load09') @setup({'load10': '{% load echo from bad_library %}'}) def test_load10(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('load10') @setup({'load11': '{% load subpackage.echo_invalid %}'}) def test_load11(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('load11') @setup({'load12': '{% load subpackage.missing %}'}) def test_load12(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('load12') Django-1.8.7/tests/template_tests/syntax_tests/test_if_changed.py0000664000175000017500000002254712625116214025000 0ustar timtim00000000000000from django.template import Context, Template from django.test import SimpleTestCase from ..utils import setup class IfChangedTagTests(SimpleTestCase): @setup({'ifchanged01': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}'}) def test_ifchanged01(self): output = self.engine.render_to_string('ifchanged01', {'num': (1, 2, 3)}) self.assertEqual(output, '123') @setup({'ifchanged02': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}'}) def test_ifchanged02(self): output = self.engine.render_to_string('ifchanged02', {'num': (1, 1, 3)}) self.assertEqual(output, '13') @setup({'ifchanged03': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}'}) def test_ifchanged03(self): output = self.engine.render_to_string('ifchanged03', {'num': (1, 1, 1)}) self.assertEqual(output, '1') @setup({'ifchanged04': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' '{% endfor %}{% endfor %}'}) def test_ifchanged04(self): output = self.engine.render_to_string('ifchanged04', {'num': (1, 2, 3), 'numx': (2, 2, 2)}) self.assertEqual(output, '122232') @setup({'ifchanged05': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' '{% endfor %}{% endfor %}'}) def test_ifchanged05(self): output = self.engine.render_to_string('ifchanged05', {'num': (1, 1, 1), 'numx': (1, 2, 3)}) self.assertEqual(output, '1123123123') @setup({'ifchanged06': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' '{% endfor %}{% endfor %}'}) def test_ifchanged06(self): output = self.engine.render_to_string('ifchanged06', {'num': (1, 1, 1), 'numx': (2, 2, 2)}) self.assertEqual(output, '1222') @setup({'ifchanged07': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' '{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}' '{% endfor %}{% endfor %}{% endfor %}'}) def test_ifchanged07(self): output = self.engine.render_to_string('ifchanged07', {'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}) self.assertEqual(output, '1233323332333') @setup({'ifchanged08': '{% for data in datalist %}{% for c,d in data %}' '{% if c %}{% ifchanged %}{{ d }}{% endifchanged %}' '{% endif %}{% endfor %}{% endfor %}'}) def test_ifchanged08(self): output = self.engine.render_to_string('ifchanged08', {'datalist': [ [(1, 'a'), (1, 'a'), (0, 'b'), (1, 'c')], [(0, 'a'), (1, 'c'), (1, 'd'), (1, 'd'), (0, 'e')] ]}) self.assertEqual(output, 'accd') @setup({'ifchanged-param01': '{% for n in num %}{% ifchanged n %}..{% endifchanged %}' '{{ n }}{% endfor %}'}) def test_ifchanged_param01(self): """ Test one parameter given to ifchanged. """ output = self.engine.render_to_string('ifchanged-param01', {'num': (1, 2, 3)}) self.assertEqual(output, '..1..2..3') @setup({'ifchanged-param02': '{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}' '{{ x }}{% endfor %}{% endfor %}'}) def test_ifchanged_param02(self): output = self.engine.render_to_string('ifchanged-param02', {'num': (1, 2, 3), 'numx': (5, 6, 7)}) self.assertEqual(output, '..567..567..567') @setup({'ifchanged-param03': '{% for n in num %}{{ n }}{% for x in numx %}' '{% ifchanged x n %}{{ x }}{% endifchanged %}' '{% endfor %}{% endfor %}'}) def test_ifchanged_param03(self): """ Test multiple parameters to ifchanged. """ output = self.engine.render_to_string('ifchanged-param03', {'num': (1, 1, 2), 'numx': (5, 6, 6)}) self.assertEqual(output, '156156256') @setup({'ifchanged-param04': '{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}' '{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}' '{% endfor %}{% endfor %}'}) def test_ifchanged_param04(self): """ Test a date+hour like construct, where the hour of the last day is the same but the date had changed, so print the hour anyway. """ output = self.engine.render_to_string( 'ifchanged-param04', {'days': [{'hours': [1, 2, 3], 'day': 1}, {'hours': [3], 'day': 2}]}, ) self.assertEqual(output, '112323') @setup({'ifchanged-param05': '{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}' '{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}' '{% endfor %}{% endfor %}'}) def test_ifchanged_param05(self): """ Logically the same as above, just written with explicit ifchanged for the day. """ output = self.engine.render_to_string( 'ifchanged-param05', {'days': [{'hours': [1, 2, 3], 'day': 1}, {'hours': [3], 'day': 2}]}, ) self.assertEqual(output, '112323') @setup({'ifchanged-else01': '{% for id in ids %}{{ id }}' '{% ifchanged id %}-first{% else %}-other{% endifchanged %}' ',{% endfor %}'}) def test_ifchanged_else01(self): """ Test the else clause of ifchanged. """ output = self.engine.render_to_string('ifchanged-else01', {'ids': [1, 1, 2, 2, 2, 3]}) self.assertEqual(output, '1-first,1-other,2-first,2-other,2-other,3-first,') @setup({'ifchanged-else02': '{% for id in ids %}{{ id }}-' '{% ifchanged id %}{% cycle red,blue %}{% else %}grey{% endifchanged %}' ',{% endfor %}'}) def test_ifchanged_else02(self): output = self.engine.render_to_string('ifchanged-else02', {'ids': [1, 1, 2, 2, 2, 3]}) self.assertEqual(output, '1-red,1-grey,2-blue,2-grey,2-grey,3-red,') @setup({'ifchanged-else03': '{% for id in ids %}{{ id }}' '{% ifchanged id %}-{% cycle red,blue %}{% else %}{% endifchanged %}' ',{% endfor %}'}) def test_ifchanged_else03(self): output = self.engine.render_to_string('ifchanged-else03', {'ids': [1, 1, 2, 2, 2, 3]}) self.assertEqual(output, '1-red,1,2-blue,2,2,3-red,') @setup({'ifchanged-else04': '{% for id in ids %}' '{% ifchanged %}***{{ id }}*{% else %}...{% endifchanged %}' '{{ forloop.counter }}{% endfor %}'}) def test_ifchanged_else04(self): output = self.engine.render_to_string('ifchanged-else04', {'ids': [1, 1, 2, 2, 2, 3, 4]}) self.assertEqual(output, '***1*1...2***2*3...4...5***3*6***4*7') @setup({'ifchanged-filter-ws': '{% load custom %}{% for n in num %}' '{% ifchanged n|noop:"x y" %}..{% endifchanged %}{{ n }}' '{% endfor %}'}) def test_ifchanged_filter_ws(self): """ Test whitespace in filter arguments """ output = self.engine.render_to_string('ifchanged-filter-ws', {'num': (1, 2, 3)}) self.assertEqual(output, '..1..2..3') class IfChangedTests(SimpleTestCase): def test_ifchanged_concurrency(self): """ #15849 -- ifchanged should be thread-safe. """ template = Template('[0{% for x in foo %},{% with var=get_value %}{% ifchanged %}{{ var }}{% endifchanged %}{% endwith %}{% endfor %}]') # Using generator to mimic concurrency. # The generator is not passed to the 'for' loop, because it does a list(values) # instead, call gen.next() in the template to control the generator. def gen(): yield 1 yield 2 # Simulate that another thread is now rendering. # When the IfChangeNode stores state at 'self' it stays at '3' and skip the last yielded value below. iter2 = iter([1, 2, 3]) output2 = template.render(Context({'foo': range(3), 'get_value': lambda: next(iter2)})) self.assertEqual(output2, '[0,1,2,3]', 'Expected [0,1,2,3] in second parallel template, got {}'.format(output2)) yield 3 gen1 = gen() output1 = template.render(Context({'foo': range(3), 'get_value': lambda: next(gen1)})) self.assertEqual(output1, '[0,1,2,3]', 'Expected [0,1,2,3] in first template, got {}'.format(output1)) def test_ifchanged_render_once(self): """ #19890. The content of ifchanged template tag was rendered twice. """ template = Template('{% ifchanged %}{% cycle "1st time" "2nd time" %}{% endifchanged %}') output = template.render(Context({})) self.assertEqual(output, '1st time') Django-1.8.7/tests/template_tests/syntax_tests/test_simple_tag.py0000664000175000017500000000150712625116214025046 0ustar timtim00000000000000from django.template import TemplateSyntaxError from django.test import SimpleTestCase from ..utils import setup class SimpleTagTests(SimpleTestCase): @setup({'simpletag-renamed01': '{% load custom %}{% minusone 7 %}'}) def test_simpletag_renamed01(self): output = self.engine.render_to_string('simpletag-renamed01') self.assertEqual(output, '6') @setup({'simpletag-renamed02': '{% load custom %}{% minustwo 7 %}'}) def test_simpletag_renamed02(self): output = self.engine.render_to_string('simpletag-renamed02') self.assertEqual(output, '5') @setup({'simpletag-renamed03': '{% load custom %}{% minustwo_overridden_name 7 %}'}) def test_simpletag_renamed03(self): with self.assertRaises(TemplateSyntaxError): self.engine.get_template('simpletag-renamed03') Django-1.8.7/tests/template_tests/syntax_tests/test_ssi.py0000664000175000017500000001051112625116214023513 0ustar timtim00000000000000from __future__ import unicode_literals import os from django.template import Context, Engine from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import ( RemovedInDjango19Warning, RemovedInDjango110Warning, ) from ..utils import ROOT, setup @ignore_warnings(category=RemovedInDjango110Warning) class SsiTagTests(SimpleTestCase): # Test normal behavior @setup({'ssi01': '{%% ssi "%s" %%}' % os.path.join( ROOT, 'templates', 'ssi_include.html', )}) def test_ssi01(self): output = self.engine.render_to_string('ssi01') self.assertEqual(output, 'This is for testing an ssi include. {{ test }}\n') @setup({'ssi02': '{%% ssi "%s" %%}' % os.path.join( ROOT, 'not_here', )}) def test_ssi02(self): output = self.engine.render_to_string('ssi02') self.assertEqual(output, ''), @setup({'ssi03': "{%% ssi '%s' %%}" % os.path.join( ROOT, 'not_here', )}) def test_ssi03(self): output = self.engine.render_to_string('ssi03') self.assertEqual(output, ''), # Test passing as a variable @ignore_warnings(category=RemovedInDjango19Warning) @setup({'ssi04': '{% load ssi from future %}{% ssi ssi_file %}'}) def test_ssi04(self): output = self.engine.render_to_string('ssi04', { 'ssi_file': os.path.join(ROOT, 'templates', 'ssi_include.html') }) self.assertEqual(output, 'This is for testing an ssi include. {{ test }}\n') @ignore_warnings(category=RemovedInDjango19Warning) @setup({'ssi05': '{% load ssi from future %}{% ssi ssi_file %}'}) def test_ssi05(self): output = self.engine.render_to_string('ssi05', {'ssi_file': 'no_file'}) self.assertEqual(output, '') # Test parsed output @setup({'ssi06': '{%% ssi "%s" parsed %%}' % os.path.join( ROOT, 'templates', 'ssi_include.html', )}) def test_ssi06(self): output = self.engine.render_to_string('ssi06', {'test': 'Look ma! It parsed!'}) self.assertEqual(output, 'This is for testing an ssi include. ' 'Look ma! It parsed!\n') @setup({'ssi07': '{%% ssi "%s" parsed %%}' % os.path.join( ROOT, 'not_here', )}) def test_ssi07(self): output = self.engine.render_to_string('ssi07', {'test': 'Look ma! It parsed!'}) self.assertEqual(output, '') # Test space in file name @setup({'ssi08': '{%% ssi "%s" %%}' % os.path.join( ROOT, 'templates', 'ssi include with spaces.html', )}) def test_ssi08(self): output = self.engine.render_to_string('ssi08') self.assertEqual(output, 'This is for testing an ssi include ' 'with spaces in its name. {{ test }}\n') @setup({'ssi09': '{%% ssi "%s" parsed %%}' % os.path.join( ROOT, 'templates', 'ssi include with spaces.html', )}) def test_ssi09(self): output = self.engine.render_to_string('ssi09', {'test': 'Look ma! It parsed!'}) self.assertEqual(output, 'This is for testing an ssi include ' 'with spaces in its name. Look ma! It parsed!\n') @ignore_warnings(category=RemovedInDjango110Warning) class SSISecurityTests(SimpleTestCase): def setUp(self): self.ssi_dir = os.path.join(ROOT, "templates", "first") self.engine = Engine(allowed_include_roots=(self.ssi_dir,)) def render_ssi(self, path): # the path must exist for the test to be reliable self.assertTrue(os.path.exists(path)) return self.engine.from_string('{%% ssi "%s" %%}' % path).render(Context({})) def test_allowed_paths(self): acceptable_path = os.path.join(self.ssi_dir, "..", "first", "test.html") self.assertEqual(self.render_ssi(acceptable_path), 'First template\n') def test_relative_include_exploit(self): """ May not bypass allowed_include_roots with relative paths e.g. if allowed_include_roots = ("/var/www",), it should not be possible to do {% ssi "/var/www/../../etc/passwd" %} """ disallowed_paths = [ os.path.join(self.ssi_dir, "..", "ssi_include.html"), os.path.join(self.ssi_dir, "..", "second", "test.html"), ] for disallowed_path in disallowed_paths: self.assertEqual(self.render_ssi(disallowed_path), '') Django-1.8.7/tests/template_tests/test_unicode.py0000664000175000017500000000253612625116214021603 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from unittest import TestCase from django.template import Context, Template from django.template.base import TemplateEncodingError from django.utils import six from django.utils.safestring import SafeData class UnicodeTests(TestCase): def test_template(self): # Templates can be created from unicode strings. t1 = Template('Å ÄĆŽćžšđ {{ var }}') # Templates can also be created from bytestrings. These are assumed to # be encoded using UTF-8. s = b'\xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91 {{ var }}' t2 = Template(s) s = b'\x80\xc5\xc0' self.assertRaises(TemplateEncodingError, Template, s) # Contexts can be constructed from unicode or UTF-8 bytestrings. Context({b"var": b"foo"}) Context({"var": b"foo"}) c3 = Context({b"var": "ÄÄ‘"}) Context({"var": b"\xc4\x90\xc4\x91"}) # Since both templates and all four contexts represent the same thing, # they all render the same (and are returned as unicode objects and # "safe" objects as well, for auto-escaping purposes). self.assertEqual(t1.render(c3), t2.render(c3)) self.assertIsInstance(t1.render(c3), six.text_type) self.assertIsInstance(t1.render(c3), SafeData) Django-1.8.7/tests/template_tests/views.py0000664000175000017500000000070012603513307020242 0ustar timtim00000000000000# Fake views for testing url reverse lookup from django.http import HttpResponse 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!') Django-1.8.7/tests/template_tests/other_templates/0000775000175000017500000000000012625116243021737 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/other_templates/test_dirs.html0000664000175000017500000000002312603513307024616 0ustar timtim00000000000000spam eggs{{ obj }} Django-1.8.7/tests/template_tests/other_templates/priority/0000775000175000017500000000000012625116243023620 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/other_templates/priority/foo.html0000664000175000017500000000001112625116214025257 0ustar timtim00000000000000priority Django-1.8.7/tests/template_tests/utils.py0000664000175000017500000001146312625116214020255 0ustar timtim00000000000000# coding: utf-8 from __future__ import unicode_literals import functools import os from django import template from django.template import Library from django.template.base import libraries from django.template.engine import Engine from django.test.utils import override_settings from django.utils._os import upath from django.utils.encoding import python_2_unicode_compatible from django.utils.safestring import mark_safe ROOT = os.path.dirname(os.path.abspath(upath(__file__))) TEMPLATE_DIR = os.path.join(ROOT, 'templates') def setup(templates, *args, **kwargs): """ Runs test method multiple times in the following order: debug cached string_if_invalid ----- ------ ----------------- False False False True False False INVALID False True INVALID True False True True """ # when testing deprecation warnings, it's useful to run just one test since # the message won't be displayed multiple times test_once = kwargs.get('test_once', False) for arg in args: templates.update(arg) # numerous tests make use of an inclusion tag # add this in here for simplicity templates["inclusion.html"] = "{{ result }}" loaders = [ ('django.template.loaders.cached.Loader', [ ('django.template.loaders.locmem.Loader', templates), ]), ] def decorator(func): @register_test_tags # Make Engine.get_default() raise an exception to ensure that tests # are properly isolated from Django's global settings. @override_settings(TEMPLATES=None) @functools.wraps(func) def inner(self): self.engine = Engine( allowed_include_roots=[ROOT], loaders=loaders, ) func(self) if test_once: return func(self) self.engine = Engine( allowed_include_roots=[ROOT], loaders=loaders, string_if_invalid='INVALID', ) func(self) func(self) self.engine = Engine( allowed_include_roots=[ROOT], debug=True, loaders=loaders, ) func(self) func(self) return inner return decorator # Custom template tag for tests register = Library() class EchoNode(template.Node): def __init__(self, contents): self.contents = contents def render(self, context): return ' '.join(self.contents) @register.tag def echo(parser, token): return EchoNode(token.contents.split()[1:]) register.tag('other_echo', echo) @register.filter def upper(value): return value.upper() def register_test_tags(func): @functools.wraps(func) def inner(self): libraries['testtags'] = register try: func(self) finally: del libraries['testtags'] return inner # Helper objects class SomeException(Exception): silent_variable_failure = True class SomeOtherException(Exception): pass class ShouldNotExecuteException(Exception): pass class SomeClass: def __init__(self): self.otherclass = OtherClass() def method(self): return 'SomeClass.method' def method2(self, o): return o def method3(self): raise SomeException def method4(self): raise SomeOtherException def method5(self): raise TypeError def __getitem__(self, key): if key == 'silent_fail_key': raise SomeException elif key == 'noisy_fail_key': raise SomeOtherException raise KeyError @property def silent_fail_attribute(self): raise SomeException @property def noisy_fail_attribute(self): raise SomeOtherException @property def attribute_error_attribute(self): raise AttributeError class OtherClass: def method(self): return 'OtherClass.method' class TestObj(object): def is_true(self): return True def is_false(self): return False 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" def __str__(self): return 'Å ÄĆŽćžšđ' # 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): return mark_safe('you > me') Django-1.8.7/tests/template_tests/templatetags/0000775000175000017500000000000012625116243021232 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templatetags/inclusion.py0000664000175000017500000002035112625116214023606 0ustar timtim00000000000000import operator from django.template import Library from django.template.loader import get_template from django.utils import six register = Library() @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__""" # Sort the dictionary by key to guarantee the order for testing. sorted_kwarg = sorted(six.iteritems(kwargs), key=operator.itemgetter(0)) return {"result": "inclusion_unlimited_args_kwargs - Expected result: %s / %s" % ( ', '.join(six.text_type(arg) for arg in [one, two] + list(args)), ', '.join('%s=%s' % (k, v) for (k, v) in sorted_kwarg) )} 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.inclusion_tag('inclusion_extends1.html') def inclusion_extends1(): return {} @register.inclusion_tag('inclusion_extends2.html') def inclusion_extends2(): return {} Django-1.8.7/tests/template_tests/templatetags/subpackage/0000775000175000017500000000000012625116243023337 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templatetags/subpackage/echo.py0000664000175000017500000000016112603513307024623 0ustar timtim00000000000000from django import template register = template.Library() @register.simple_tag def echo2(arg): return arg Django-1.8.7/tests/template_tests/templatetags/subpackage/__init__.py0000664000175000017500000000000012603513307025434 0ustar timtim00000000000000Django-1.8.7/tests/template_tests/templatetags/subpackage/echo_invalid.py0000664000175000017500000000004212625113144026326 0ustar timtim00000000000000import nonexistent.module # NOQA Django-1.8.7/tests/template_tests/templatetags/custom.py0000664000175000017500000001717112625116214023123 0ustar timtim00000000000000import operator from django import template from django.template.defaultfilters import stringfilter 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 its second (optional) one. Useful for testing out whitespace in filter arguments (see #19882).""" return value @register.simple_tag(takes_context=True) def context_stack_length(context): return len(context.dicts) @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__""" # Sort the dictionary by key to guarantee the order for testing. sorted_kwarg = sorted(six.iteritems(kwargs), key=operator.itemgetter(0)) return "simple_unlimited_args_kwargs - Expected result: %s / %s" % ( ', '.join(six.text_type(arg) for arg in [one, two] + list(args)), ', '.join('%s=%s' % (k, v) for (k, v) in sorted_kwarg) ) 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.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__""" # Sort the dictionary by key to guarantee the order for testing. sorted_kwarg = sorted(six.iteritems(kwargs), key=operator.itemgetter(0)) return "assignment_unlimited_args_kwargs - Expected result: %s / %s" % ( ', '.join(six.text_type(arg) for arg in [one, two] + list(args)), ', '.join('%s=%s' % (k, v) for (k, v) in sorted_kwarg) ) 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__""" return "Expected result" assignment_tag_without_context_parameter.anything = "Expected assignment_tag_without_context_parameter __dict__" Django-1.8.7/tests/template_tests/templatetags/broken_tag.py0000664000175000017500000000004512625113144023713 0ustar timtim00000000000000from django import Xtemplate # NOQA Django-1.8.7/tests/template_tests/templatetags/bad_tag.py0000664000175000017500000000035112625116214023162 0ustar timtim00000000000000from 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") Django-1.8.7/tests/template_tests/templatetags/__init__.py0000664000175000017500000000000012603513307023327 0ustar timtim00000000000000Django-1.8.7/tests/template_tests/test_smartif.py0000664000175000017500000000420212603513307021612 0ustar timtim00000000000000import unittest from django.template.smartif import IfParser class SmartIfTests(unittest.TestCase): def assertCalcEqual(self, expected, tokens): self.assertEqual(expected, IfParser(tokens).parse().eval({})) # We only test things here that are difficult to test elsewhere # Many other tests are found in the main tests for builtin template tags # Test parsing via the printed parse tree def test_not(self): var = IfParser(["not", False]).parse() self.assertEqual("(not (literal False))", repr(var)) self.assertTrue(var.eval({})) self.assertFalse(IfParser(["not", True]).parse().eval({})) def test_or(self): var = IfParser([True, "or", False]).parse() self.assertEqual("(or (literal True) (literal False))", repr(var)) self.assertTrue(var.eval({})) def test_in(self): list_ = [1, 2, 3] self.assertCalcEqual(True, [1, 'in', list_]) self.assertCalcEqual(False, [1, 'in', None]) self.assertCalcEqual(False, [None, 'in', list_]) def test_not_in(self): list_ = [1, 2, 3] self.assertCalcEqual(False, [1, 'not', 'in', list_]) self.assertCalcEqual(True, [4, 'not', 'in', list_]) self.assertCalcEqual(False, [1, 'not', 'in', None]) self.assertCalcEqual(True, [None, 'not', 'in', list_]) def test_precedence(self): # (False and False) or True == True <- we want this one, like Python # False and (False or True) == False self.assertCalcEqual(True, [False, 'and', False, 'or', True]) # True or (False and False) == True <- we want this one, like Python # (True or False) and False == False self.assertCalcEqual(True, [True, 'or', False, 'and', False]) # (1 or 1) == 2 -> False # 1 or (1 == 2) -> True <- we want this one self.assertCalcEqual(True, [1, 'or', 1, '==', 2]) self.assertCalcEqual(True, [True, '==', True, 'or', True, '==', False]) self.assertEqual("(or (and (== (literal 1) (literal 2)) (literal 3)) (literal 4))", repr(IfParser([1, '==', 2, 'and', 3, 'or', 4]).parse())) Django-1.8.7/tests/template_tests/test_engine.py0000664000175000017500000001041412625116214021414 0ustar timtim00000000000000import os from django.template import Context from django.template.engine import Engine from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import RemovedInDjango110Warning from .utils import ROOT, TEMPLATE_DIR OTHER_DIR = os.path.join(ROOT, 'other_templates') @ignore_warnings(category=RemovedInDjango110Warning) class DeprecatedRenderToStringTest(SimpleTestCase): def setUp(self): self.engine = Engine(dirs=[TEMPLATE_DIR]) def test_basic_context(self): self.assertEqual( self.engine.render_to_string('test_context.html', {'obj': 'test'}), 'obj:test\n', ) def test_existing_context_kept_clean(self): context = Context({'obj': 'before'}) output = self.engine.render_to_string( 'test_context.html', {'obj': 'after'}, context_instance=context, ) self.assertEqual(output, 'obj:after\n') self.assertEqual(context['obj'], 'before') def test_no_empty_dict_pushed_to_stack(self): """ #21741 -- An empty dict should not be pushed to the context stack when render_to_string is called without a context argument. """ # The stack should have a length of 1, corresponding to the builtins self.assertEqual( '1', self.engine.render_to_string('test_context_stack.html').strip(), ) self.assertEqual( '1', self.engine.render_to_string( 'test_context_stack.html', context_instance=Context() ).strip(), ) class LoaderTests(SimpleTestCase): def test_debug_nodelist_name(self): engine = Engine(dirs=[TEMPLATE_DIR], debug=True) template_name = 'index.html' template = engine.get_template(template_name) name = template.nodelist[0].source[0].name self.assertTrue(name.endswith(template_name)) def test_origin(self): engine = Engine(dirs=[TEMPLATE_DIR], debug=True) template = engine.get_template('index.html') self.assertEqual(template.origin.loadname, 'index.html') def test_origin_debug_false(self): engine = Engine(dirs=[TEMPLATE_DIR], debug=False) template = engine.get_template('index.html') self.assertEqual(template.origin, None) def test_loader_priority(self): """ #21460 -- Check that the order of template loader works. """ loaders = [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ] engine = Engine(dirs=[OTHER_DIR, TEMPLATE_DIR], loaders=loaders) template = engine.get_template('priority/foo.html') self.assertEqual(template.render(Context()), 'priority\n') def test_cached_loader_priority(self): """ Check that the order of template loader works. Refs #21460. """ loaders = [ ('django.template.loaders.cached.Loader', [ 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', ]), ] engine = Engine(dirs=[OTHER_DIR, TEMPLATE_DIR], loaders=loaders) template = engine.get_template('priority/foo.html') self.assertEqual(template.render(Context()), 'priority\n') template = engine.get_template('priority/foo.html') self.assertEqual(template.render(Context()), 'priority\n') @ignore_warnings(category=RemovedInDjango110Warning) class TemplateDirsOverrideTests(SimpleTestCase): DIRS = ((OTHER_DIR, ), [OTHER_DIR]) def setUp(self): self.engine = Engine() def test_render_to_string(self): for dirs in self.DIRS: self.assertEqual( self.engine.render_to_string('test_dirs.html', dirs=dirs), 'spam eggs\n', ) def test_get_template(self): for dirs in self.DIRS: template = self.engine.get_template('test_dirs.html', dirs=dirs) self.assertEqual(template.render(Context()), 'spam eggs\n') def test_select_template(self): for dirs in self.DIRS: template = self.engine.select_template(['test_dirs.html'], dirs=dirs) self.assertEqual(template.render(Context()), 'spam eggs\n') Django-1.8.7/tests/template_tests/urls.py0000664000175000017500000000132712625116214020100 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.conf.urls import url from . import views urlpatterns = [ # Test urls for testing reverse lookups url(r'^$', views.index), url(r'^client/([0-9,]+)/$', views.client), url(r'^client/(?P[0-9]+)/(?P[^/]+)/$', views.client_action), url(r'^client/(?P[0-9]+)/(?P[^/]+)/$', views.client_action), url(r'^named-client/([0-9]+)/$', views.client2, name="named.client"), # Unicode strings are permitted everywhere. url(r'^Юникод/(\w+)/$', views.client2, name="метка_оператора"), url(r'^Юникод/(?P\S+)/$', views.client2, name="метка_оператора_2"), ] Django-1.8.7/tests/template_tests/templates/0000775000175000017500000000000012625116243020536 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templates/broken_base.html0000664000175000017500000000003512603513307023672 0ustar timtim00000000000000{% include "missing.html" %} Django-1.8.7/tests/template_tests/templates/template_tests/0000775000175000017500000000000012625116243023573 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templates/template_tests/using.html0000664000175000017500000000000412625116214025576 0ustar timtim00000000000000DTL Django-1.8.7/tests/template_tests/templates/test_incl_tag_current_app.html0000664000175000017500000000004312625113144026637 0ustar timtim00000000000000{% load custom %}{% current_app %} Django-1.8.7/tests/template_tests/templates/second/0000775000175000017500000000000012625116243022011 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templates/second/test.html0000664000175000017500000000002012603513307023644 0ustar timtim00000000000000Second template Django-1.8.7/tests/template_tests/templates/included_content.html0000664000175000017500000000027012603513307024742 0ustar timtim00000000000000{% extends "included_base.html" %} {% block content %} content {{ block.super }} {% endblock %} {% block error_here %} error here {% url "non_existing_url" %} {% endblock %} Django-1.8.7/tests/template_tests/templates/inclusion_extends1.html0000664000175000017500000000011112625116214025231 0ustar timtim00000000000000{% extends 'inclusion_base.html' %} {% block content %}one{% endblock %} Django-1.8.7/tests/template_tests/templates/index.html0000664000175000017500000000000612625116214022525 0ustar timtim00000000000000index Django-1.8.7/tests/template_tests/templates/inclusion_extends2.html0000664000175000017500000000011112625116214025232 0ustar timtim00000000000000{% extends 'inclusion_base.html' %} {% block content %}two{% endblock %} Django-1.8.7/tests/template_tests/templates/included_base.html0000664000175000017500000000011212603513307024175 0ustar timtim00000000000000{% block content %} {% block error_here %}{% endblock %} {% endblock %} Django-1.8.7/tests/template_tests/templates/ssi include with spaces.html0000664000175000017500000000010712603513307026015 0ustar timtim00000000000000This is for testing an ssi include with spaces in its name. {{ test }} Django-1.8.7/tests/template_tests/templates/test_incl_tag_use_l10n.html0000664000175000017500000000004012603513307025741 0ustar timtim00000000000000{% load custom %}{% use_l10n %} Django-1.8.7/tests/template_tests/templates/test_context_stack.html0000664000175000017500000000005512603513307025332 0ustar timtim00000000000000{% load custom %} {% context_stack_length %} Django-1.8.7/tests/template_tests/templates/test_extends_error.html0000664000175000017500000000004112603513307025337 0ustar timtim00000000000000{% extends "broken_base.html" %} Django-1.8.7/tests/template_tests/templates/priority/0000775000175000017500000000000012625116243022417 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templates/priority/foo.html0000664000175000017500000000001412603513307024061 0ustar timtim00000000000000no priority Django-1.8.7/tests/template_tests/templates/recursive_include.html0000664000175000017500000000031412603513307025132 0ustar timtim00000000000000Recursion! {% for comment in comments %} {{ comment.comment }} {% if comment.children %} {% include "recursive_include.html" with comments=comment.children %} {% endif %} {% endfor %} Django-1.8.7/tests/template_tests/templates/test_context.html0000664000175000017500000000001612625116214024142 0ustar timtim00000000000000obj:{{ obj }} Django-1.8.7/tests/template_tests/templates/inclusion_base.html0000664000175000017500000000004612625116214024417 0ustar timtim00000000000000{% block content %}base{% endblock %} Django-1.8.7/tests/template_tests/templates/response.html0000664000175000017500000000011012603513307023250 0ustar timtim00000000000000This is where you can find the snark: {% url "snark" %} {% now "U.u" %} Django-1.8.7/tests/template_tests/templates/first/0000775000175000017500000000000012625116243021665 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/templates/first/test.html0000664000175000017500000000001712603513307023526 0ustar timtim00000000000000First template Django-1.8.7/tests/template_tests/templates/ssi_include.html0000664000175000017500000000005712603513307023725 0ustar timtim00000000000000This is for testing an ssi include. {{ test }} Django-1.8.7/tests/template_tests/templates/inclusion.html0000664000175000017500000000001512603513307023421 0ustar timtim00000000000000{{ result }} Django-1.8.7/tests/template_tests/templates/test_include_error.html0000664000175000017500000000003512625116214025313 0ustar timtim00000000000000{% include "missing.html" %} Django-1.8.7/tests/template_tests/eggs/0000775000175000017500000000000012625116243017465 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/eggs/tagsegg.egg0000664000175000017500000000502512603513307021572 0ustar timtim00000000000000PK†¹Ž<“×2EGG-INFO/dependency_links.txtãPK†¹Ž>3/³$>^¯ ²„¨Ú&7?¥4'Õdf1È PKÑ­Ž<.:ž"tagsegg/templatetags/broken_egg.pyK+ÊÏUHÉJÌKÏWÈÌ-È/*Qˆ(IÍ-ÈI,IåPK†¹Ž<"þ͉”Ç#tagsegg/templatetags/broken_egg.pyc»ø‰—ëWþ1ïd(`b .)@ÄÈÍÀÃÈÅÈÀ˜ÂĬR‘ù4¬N Q’š[“X’ê§ÁcéÍJÌKÏ™©J[‰¤ÒÌœ”˜¤”Ìâ½òÌEGG-INFO/PKG-INFOPK†¹Ž<ÖNë|¶âEGG-INFO/SOURCES.txtPK†¹Žescaping'}) self.assertEqual(output, 'testing\\u000D\\u000Ajavascript ' '\\u0027string\\u0022 \\u003Cb\\u003E' 'escaping\\u003C/b\\u003E') @setup({'escapejs02': '{% autoescape off %}{{ a|escapejs }}{% endautoescape %}'}) def test_escapejs02(self): output = self.engine.render_to_string('escapejs02', {'a': 'testing\r\njavascript \'string" escaping'}) self.assertEqual(output, 'testing\\u000D\\u000Ajavascript ' '\\u0027string\\u0022 \\u003Cb\\u003E' 'escaping\\u003C/b\\u003E') class FunctionTests(SimpleTestCase): def test_quotes(self): self.assertEqual( escapejs_filter('"double quotes" and \'single quotes\''), '\\u0022double quotes\\u0022 and \\u0027single quotes\\u0027', ) def test_backslashes(self): self.assertEqual(escapejs_filter(r'\ : backslashes, too'), '\\u005C : backslashes, too') def test_whitespace(self): self.assertEqual( escapejs_filter('and lots of whitespace: \r\n\t\v\f\b'), 'and lots of whitespace: \\u000D\\u000A\\u0009\\u000B\\u000C\\u0008', ) def test_script(self): self.assertEqual( escapejs_filter(r''), '\\u003Cscript\\u003Eand this\\u003C/script\\u003E', ) def test_paragraph_separator(self): self.assertEqual( escapejs_filter('paragraph separator:\u2029and line separator:\u2028'), 'paragraph separator:\\u2029and line separator:\\u2028', ) Django-1.8.7/tests/template_tests/filter_tests/test_get_digit.py0000664000175000017500000000073512625116214024622 0ustar timtim00000000000000from django.template.defaultfilters import get_digit from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_values(self): self.assertEqual(get_digit(123, 1), 3) self.assertEqual(get_digit(123, 2), 2) self.assertEqual(get_digit(123, 3), 1) self.assertEqual(get_digit(123, 4), 0) self.assertEqual(get_digit(123, 0), 123) def test_string(self): self.assertEqual(get_digit('xyz', 0), 'xyz') Django-1.8.7/tests/template_tests/filter_tests/test_make_list.py0000664000175000017500000000311312625116214024624 0ustar timtim00000000000000from django.template.defaultfilters import make_list from django.test import SimpleTestCase from django.test.utils import str_prefix from django.utils.safestring import mark_safe from ..utils import setup class MakeListTests(SimpleTestCase): """ The make_list filter can destroy existing escaping, so the results are escaped. """ @setup({'make_list01': '{% autoescape off %}{{ a|make_list }}{% endautoescape %}'}) def test_make_list01(self): output = self.engine.render_to_string('make_list01', {"a": mark_safe("&")}) self.assertEqual(output, str_prefix("[%(_)s'&']")) @setup({'make_list02': '{{ a|make_list }}'}) def test_make_list02(self): output = self.engine.render_to_string('make_list02', {"a": mark_safe("&")}) self.assertEqual(output, str_prefix("[%(_)s'&']")) @setup({'make_list03': '{% autoescape off %}{{ a|make_list|stringformat:"s"|safe }}{% endautoescape %}'}) def test_make_list03(self): output = self.engine.render_to_string('make_list03', {"a": mark_safe("&")}) self.assertEqual(output, str_prefix("[%(_)s'&']")) @setup({'make_list04': '{{ a|make_list|stringformat:"s"|safe }}'}) def test_make_list04(self): output = self.engine.render_to_string('make_list04', {"a": mark_safe("&")}) self.assertEqual(output, str_prefix("[%(_)s'&']")) class FunctionTests(SimpleTestCase): def test_string(self): self.assertEqual(make_list('abc'), ['a', 'b', 'c']) def test_integer(self): self.assertEqual(make_list(1234), ['1', '2', '3', '4']) Django-1.8.7/tests/template_tests/filter_tests/test_urlencode.py0000664000175000017500000000164512625116214024644 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import urlencode from django.test import SimpleTestCase from ..utils import setup class UrlencodeTests(SimpleTestCase): @setup({'urlencode01': '{{ url|urlencode }}'}) def test_urlencode01(self): output = self.engine.render_to_string('urlencode01', {'url': '/test&"/me?/'}) self.assertEqual(output, '/test%26%22/me%3F/') @setup({'urlencode02': '/test/{{ urlbit|urlencode:"" }}/'}) def test_urlencode02(self): output = self.engine.render_to_string('urlencode02', {'urlbit': 'escape/slash'}) self.assertEqual(output, '/test/escape%2Fslash/') class FunctionTests(SimpleTestCase): def test_urlencode(self): self.assertEqual(urlencode('fran\xe7ois & jill'), 'fran%C3%A7ois%20%26%20jill') def test_non_string_input(self): self.assertEqual(urlencode(1), '1') Django-1.8.7/tests/template_tests/filter_tests/test_dictsort.py0000664000175000017500000000270512625116214024515 0ustar timtim00000000000000from django.template.defaultfilters import dictsort from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_sort(self): sorted_dicts = dictsort( [{'age': 23, 'name': 'Barbara-Ann'}, {'age': 63, 'name': 'Ra Ra Rasputin'}, {'name': 'Jonny B Goode', 'age': 18}], 'age', ) self.assertEqual( [sorted(dict.items()) for dict in sorted_dicts], [[('age', 18), ('name', 'Jonny B Goode')], [('age', 23), ('name', 'Barbara-Ann')], [('age', 63), ('name', 'Ra Ra Rasputin')]], ) def test_dictsort_complex_sorting_key(self): """ Since dictsort uses template.Variable under the hood, it can sort on keys like 'foo.bar'. """ data = [ {'foo': {'bar': 1, 'baz': 'c'}}, {'foo': {'bar': 2, 'baz': 'b'}}, {'foo': {'bar': 3, 'baz': 'a'}}, ] sorted_data = dictsort(data, 'foo.baz') self.assertEqual([d['foo']['bar'] for d in sorted_data], [3, 2, 1]) def test_invalid_values(self): """ If dictsort is passed something other than a list of dictionaries, fail silently. """ self.assertEqual(dictsort([1, 2, 3], 'age'), '') self.assertEqual(dictsort('Hello!', 'age'), '') self.assertEqual(dictsort({'a': 1}, 'age'), '') self.assertEqual(dictsort(1, 'age'), '') Django-1.8.7/tests/template_tests/filter_tests/test_timeuntil.py0000664000175000017500000001051212625116214024667 0ustar timtim00000000000000from __future__ import unicode_literals from datetime import datetime, timedelta from django.template.defaultfilters import timeuntil_filter from django.test import SimpleTestCase from django.test.utils import requires_tz_support from ..utils import setup from .timezone_utils import TimezoneTestCase class TimeuntilTests(TimezoneTestCase): # Default compare with datetime.now() @setup({'timeuntil01': '{{ a|timeuntil }}'}) def test_timeuntil01(self): output = self.engine.render_to_string('timeuntil01', {'a': datetime.now() + timedelta(minutes=2, seconds=10)}) self.assertEqual(output, '2\xa0minutes') @setup({'timeuntil02': '{{ a|timeuntil }}'}) def test_timeuntil02(self): output = self.engine.render_to_string('timeuntil02', {'a': (datetime.now() + timedelta(days=1, seconds=10))}) self.assertEqual(output, '1\xa0day') @setup({'timeuntil03': '{{ a|timeuntil }}'}) def test_timeuntil03(self): output = self.engine.render_to_string('timeuntil03', {'a': (datetime.now() + timedelta(hours=8, minutes=10, seconds=10))}) self.assertEqual(output, '8\xa0hours, 10\xa0minutes') # Compare to a given parameter @setup({'timeuntil04': '{{ a|timeuntil:b }}'}) def test_timeuntil04(self): output = self.engine.render_to_string( 'timeuntil04', {'a': self.now - timedelta(days=1), 'b': self.now - timedelta(days=2)}, ) self.assertEqual(output, '1\xa0day') @setup({'timeuntil05': '{{ a|timeuntil:b }}'}) def test_timeuntil05(self): output = self.engine.render_to_string( 'timeuntil05', {'a': self.now - timedelta(days=2), 'b': self.now - timedelta(days=2, minutes=1)}, ) self.assertEqual(output, '1\xa0minute') # Regression for #7443 @setup({'timeuntil06': '{{ earlier|timeuntil }}'}) def test_timeuntil06(self): output = self.engine.render_to_string('timeuntil06', {'earlier': self.now - timedelta(days=7)}) self.assertEqual(output, '0\xa0minutes') @setup({'timeuntil07': '{{ earlier|timeuntil:now }}'}) def test_timeuntil07(self): output = self.engine.render_to_string('timeuntil07', {'now': self.now, 'earlier': self.now - timedelta(days=7)}) self.assertEqual(output, '0\xa0minutes') @setup({'timeuntil08': '{{ later|timeuntil }}'}) def test_timeuntil08(self): output = self.engine.render_to_string('timeuntil08', {'later': self.now + timedelta(days=7, hours=1)}) self.assertEqual(output, '1\xa0week') @setup({'timeuntil09': '{{ later|timeuntil:now }}'}) def test_timeuntil09(self): output = self.engine.render_to_string('timeuntil09', {'now': self.now, 'later': self.now + timedelta(days=7)}) self.assertEqual(output, '1\xa0week') # Ensures that differing timezones are calculated correctly. @requires_tz_support @setup({'timeuntil10': '{{ a|timeuntil }}'}) def test_timeuntil10(self): output = self.engine.render_to_string('timeuntil10', {'a': self.now_tz}) self.assertEqual(output, '0\xa0minutes') @requires_tz_support @setup({'timeuntil11': '{{ a|timeuntil }}'}) def test_timeuntil11(self): output = self.engine.render_to_string('timeuntil11', {'a': self.now_tz_i}) self.assertEqual(output, '0\xa0minutes') @setup({'timeuntil12': '{{ a|timeuntil:b }}'}) def test_timeuntil12(self): output = self.engine.render_to_string('timeuntil12', {'a': self.now_tz_i, 'b': self.now_tz}) self.assertEqual(output, '0\xa0minutes') # Regression for #9065 (two date objects). @setup({'timeuntil13': '{{ a|timeuntil:b }}'}) def test_timeuntil13(self): output = self.engine.render_to_string('timeuntil13', {'a': self.today, 'b': self.today}) self.assertEqual(output, '0\xa0minutes') @setup({'timeuntil14': '{{ a|timeuntil:b }}'}) def test_timeuntil14(self): output = self.engine.render_to_string('timeuntil14', {'a': self.today, 'b': self.today - timedelta(hours=24)}) self.assertEqual(output, '1\xa0day') class FunctionTests(SimpleTestCase): def test_until_now(self): self.assertEqual(timeuntil_filter(datetime.now() + timedelta(1, 1)), '1\xa0day') def test_explicit_date(self): self.assertEqual(timeuntil_filter(datetime(2005, 12, 30), datetime(2005, 12, 29)), '1\xa0day') Django-1.8.7/tests/template_tests/filter_tests/test_yesno.py0000664000175000017500000000152512625116214024016 0ustar timtim00000000000000from django.template.defaultfilters import yesno from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_true(self): self.assertEqual(yesno(True), 'yes') def test_false(self): self.assertEqual(yesno(False), 'no') def test_none(self): self.assertEqual(yesno(None), 'maybe') def test_true_arguments(self): self.assertEqual(yesno(True, 'certainly,get out of town,perhaps'), 'certainly') def test_false_arguments(self): self.assertEqual(yesno(False, 'certainly,get out of town,perhaps'), 'get out of town') def test_none_two_arguments(self): self.assertEqual(yesno(None, 'certainly,get out of town'), 'get out of town') def test_none_three_arguments(self): self.assertEqual(yesno(None, 'certainly,get out of town,perhaps'), 'perhaps') Django-1.8.7/tests/template_tests/filter_tests/test_center.py0000664000175000017500000000167112625116214024143 0ustar timtim00000000000000from django.template.defaultfilters import center from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class CenterTests(SimpleTestCase): @setup({'center01': '{% autoescape off %}.{{ a|center:"5" }}. .{{ b|center:"5" }}.{% endautoescape %}'}) def test_center01(self): output = self.engine.render_to_string('center01', {"a": "a&b", "b": mark_safe("a&b")}) self.assertEqual(output, ". a&b . . a&b .") @setup({'center02': '.{{ a|center:"5" }}. .{{ b|center:"5" }}.'}) def test_center02(self): output = self.engine.render_to_string('center02', {"a": "a&b", "b": mark_safe("a&b")}) self.assertEqual(output, ". a&b . . a&b .") class FunctionTests(SimpleTestCase): def test_center(self): self.assertEqual(center('test', 6), ' test ') def test_non_string_input(self): self.assertEqual(center(123, 5), ' 123 ') Django-1.8.7/tests/template_tests/filter_tests/test_chaining.py0000664000175000017500000000754412625116214024450 0ustar timtim00000000000000from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class ChainingTests(SimpleTestCase): """ Chaining safeness-preserving filters should not alter the safe status. """ @setup({'chaining01': '{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}'}) def test_chaining01(self): output = self.engine.render_to_string('chaining01', {'a': 'a < b', 'b': mark_safe('a < b')}) self.assertEqual(output, ' A < b . A < b ') @setup({'chaining02': '{% autoescape off %}{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}{% endautoescape %}'}) def test_chaining02(self): output = self.engine.render_to_string('chaining02', {'a': 'a < b', 'b': mark_safe('a < b')}) self.assertEqual(output, ' A < b . A < b ') # Using a filter that forces a string back to unsafe: @setup({'chaining03': '{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}'}) def test_chaining03(self): output = self.engine.render_to_string('chaining03', {'a': 'a < b', 'b': mark_safe('a < b')}) self.assertEqual(output, 'A < .A < ') @setup({'chaining04': '{% autoescape off %}{{ a|cut:"b"|capfirst }}.{{ b|cut:"b"|capfirst }}{% endautoescape %}'}) def test_chaining04(self): output = self.engine.render_to_string('chaining04', {'a': 'a < b', 'b': mark_safe('a < b')}) self.assertEqual(output, 'A < .A < ') # Using a filter that forces safeness does not lead to double-escaping @setup({'chaining05': '{{ a|escape|capfirst }}'}) def test_chaining05(self): output = self.engine.render_to_string('chaining05', {'a': 'a < b'}) self.assertEqual(output, 'A < b') @setup({'chaining06': '{% autoescape off %}{{ a|escape|capfirst }}{% endautoescape %}'}) def test_chaining06(self): output = self.engine.render_to_string('chaining06', {'a': 'a < b'}) self.assertEqual(output, 'A < b') # Force to safe, then back (also showing why using force_escape too # early in a chain can lead to unexpected results). @setup({'chaining07': '{{ a|force_escape|cut:";" }}'}) def test_chaining07(self): output = self.engine.render_to_string('chaining07', {'a': 'a < b'}) self.assertEqual(output, 'a &lt b') @setup({'chaining08': '{% autoescape off %}{{ a|force_escape|cut:";" }}{% endautoescape %}'}) def test_chaining08(self): output = self.engine.render_to_string('chaining08', {'a': 'a < b'}) self.assertEqual(output, 'a < b') @setup({'chaining09': '{{ a|cut:";"|force_escape }}'}) def test_chaining09(self): output = self.engine.render_to_string('chaining09', {'a': 'a < b'}) self.assertEqual(output, 'a < b') @setup({'chaining10': '{% autoescape off %}{{ a|cut:";"|force_escape }}{% endautoescape %}'}) def test_chaining10(self): output = self.engine.render_to_string('chaining10', {'a': 'a < b'}) self.assertEqual(output, 'a < b') @setup({'chaining11': '{{ a|cut:"b"|safe }}'}) def test_chaining11(self): output = self.engine.render_to_string('chaining11', {'a': 'a < b'}) self.assertEqual(output, 'a < ') @setup({'chaining12': '{% autoescape off %}{{ a|cut:"b"|safe }}{% endautoescape %}'}) def test_chaining12(self): output = self.engine.render_to_string('chaining12', {'a': 'a < b'}) self.assertEqual(output, 'a < ') @setup({'chaining13': '{{ a|safe|force_escape }}'}) def test_chaining13(self): output = self.engine.render_to_string('chaining13', {"a": "a < b"}) self.assertEqual(output, 'a < b') @setup({'chaining14': '{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}'}) def test_chaining14(self): output = self.engine.render_to_string('chaining14', {"a": "a < b"}) self.assertEqual(output, 'a < b') Django-1.8.7/tests/template_tests/filter_tests/test_linebreaksbr.py0000664000175000017500000000331612625116214025324 0ustar timtim00000000000000from django.template.defaultfilters import linebreaksbr from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LinebreaksbrTests(SimpleTestCase): """ The contents in "linebreaksbr" are escaped according to the current autoescape setting. """ @setup({'linebreaksbr01': '{{ a|linebreaksbr }} {{ b|linebreaksbr }}'}) def test_linebreaksbr01(self): output = self.engine.render_to_string('linebreaksbr01', {"a": "x&\ny", "b": mark_safe("x&\ny")}) self.assertEqual(output, "x&
y x&
y") @setup({'linebreaksbr02': '{% autoescape off %}{{ a|linebreaksbr }} {{ b|linebreaksbr }}{% endautoescape %}'}) def test_linebreaksbr02(self): output = self.engine.render_to_string('linebreaksbr02', {"a": "x&\ny", "b": mark_safe("x&\ny")}) self.assertEqual(output, "x&
y x&
y") class FunctionTests(SimpleTestCase): def test_newline(self): self.assertEqual(linebreaksbr('line 1\nline 2'), 'line 1
line 2') def test_carriage(self): self.assertEqual(linebreaksbr('line 1\rline 2'), 'line 1
line 2') def test_carriage_newline(self): self.assertEqual(linebreaksbr('line 1\r\nline 2'), 'line 1
line 2') def test_non_string_input(self): self.assertEqual(linebreaksbr(123), '123') def test_autoescape(self): self.assertEqual( linebreaksbr('foo\n
bar\nbuz'), 'foo
<a>bar</a>
buz', ) def test_autoescape_off(self): self.assertEqual( linebreaksbr('foo\nbar\nbuz', autoescape=False), 'foo
bar
buz', ) Django-1.8.7/tests/template_tests/filter_tests/test_truncatewords.py0000664000175000017500000000325112625116214025563 0ustar timtim00000000000000from django.template.defaultfilters import truncatewords from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class TruncatewordsTests(SimpleTestCase): @setup({'truncatewords01': '{% autoescape off %}{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}{% endautoescape %}'}) def test_truncatewords01(self): output = self.engine.render_to_string('truncatewords01', {'a': 'alpha & bravo', 'b': mark_safe('alpha & bravo')}) self.assertEqual(output, 'alpha & ... alpha & ...') @setup({'truncatewords02': '{{ a|truncatewords:"2" }} {{ b|truncatewords:"2"}}'}) def test_truncatewords02(self): output = self.engine.render_to_string('truncatewords02', {'a': 'alpha & bravo', 'b': mark_safe('alpha & bravo')}) self.assertEqual(output, 'alpha & ... alpha & ...') class FunctionTests(SimpleTestCase): def test_truncate(self): self.assertEqual(truncatewords('A sentence with a few words in it', 1), 'A ...') def test_truncate2(self): self.assertEqual( truncatewords('A sentence with a few words in it', 5), 'A sentence with a few ...', ) def test_overtruncate(self): self.assertEqual( truncatewords('A sentence with a few words in it', 100), 'A sentence with a few words in it', ) def test_invalid_number(self): self.assertEqual( truncatewords('A sentence with a few words in it', 'not a number'), 'A sentence with a few words in it', ) def test_non_string_input(self): self.assertEqual(truncatewords(123, 2), '123') Django-1.8.7/tests/template_tests/filter_tests/test_urlizetrunc.py0000664000175000017500000000642412625116214025252 0ustar timtim00000000000000from django.template.defaultfilters import urlizetrunc from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class UrlizetruncTests(SimpleTestCase): @setup({'urlizetrunc01': '{% autoescape off %}{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}{% endautoescape %}'}) def test_urlizetrunc01(self): output = self.engine.render_to_string( 'urlizetrunc01', { 'a': '"Unsafe" http://example.com/x=&y=', 'b': mark_safe('"Safe" http://example.com?x=&y='), }, ) self.assertEqual( output, '"Unsafe" http:... ' '"Safe" http:...' ) @setup({'urlizetrunc02': '{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}'}) def test_urlizetrunc02(self): output = self.engine.render_to_string( 'urlizetrunc02', { 'a': '"Unsafe" http://example.com/x=&y=', 'b': mark_safe('"Safe" http://example.com?x=&y='), }, ) self.assertEqual( output, '"Unsafe" http:... ' '"Safe" http:...' ) class FunctionTests(SimpleTestCase): def test_truncate(self): uri = 'http://31characteruri.com/test/' self.assertEqual(len(uri), 31) self.assertEqual( urlizetrunc(uri, 31), '' 'http://31characteruri.com/test/', ) self.assertEqual( urlizetrunc(uri, 30), '' 'http://31characteruri.com/t...', ) self.assertEqual( urlizetrunc(uri, 2), '...', ) def test_overtruncate(self): self.assertEqual( urlizetrunc('http://short.com/', 20), 'http://short.com/', ) def test_query_string(self): self.assertEqual( urlizetrunc('http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20), 'http://www.google...', ) def test_non_string_input(self): self.assertEqual(urlizetrunc(123, 1), '123') def test_autoescape(self): self.assertEqual( urlizetrunc('foobarbuz', 10), 'foo<a href=" google.com ">bar</a>buz', ) def test_autoescape_off(self): self.assertEqual( urlizetrunc('foobarbuz', 9, autoescape=False), 'foogoogle... ">barbuz', ) Django-1.8.7/tests/template_tests/filter_tests/test_length_is.py0000664000175000017500000000620412625116214024634 0ustar timtim00000000000000from django.template.defaultfilters import length_is from django.test import SimpleTestCase from ..utils import setup class LengthIsTests(SimpleTestCase): @setup({'length_is01': '{% if some_list|length_is:"4" %}Four{% endif %}'}) def test_length_is01(self): output = self.engine.render_to_string('length_is01', {'some_list': ['4', None, True, {}]}) self.assertEqual(output, 'Four') @setup({'length_is02': '{% if some_list|length_is:"4" %}Four{% else %}Not Four{% endif %}'}) def test_length_is02(self): output = self.engine.render_to_string('length_is02', {'some_list': ['4', None, True, {}, 17]}) self.assertEqual(output, 'Not Four') @setup({'length_is03': '{% if mystring|length_is:"4" %}Four{% endif %}'}) def test_length_is03(self): output = self.engine.render_to_string('length_is03', {'mystring': 'word'}) self.assertEqual(output, 'Four') @setup({'length_is04': '{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}'}) def test_length_is04(self): output = self.engine.render_to_string('length_is04', {'mystring': 'Python'}) self.assertEqual(output, 'Not Four') @setup({'length_is05': '{% if mystring|length_is:"4" %}Four{% else %}Not Four{% endif %}'}) def test_length_is05(self): output = self.engine.render_to_string('length_is05', {'mystring': ''}) self.assertEqual(output, 'Not Four') @setup({'length_is06': '{% with var|length as my_length %}{{ my_length }}{% endwith %}'}) def test_length_is06(self): output = self.engine.render_to_string('length_is06', {'var': 'django'}) self.assertEqual(output, '6') # Boolean return value from length_is should not be coerced to a string @setup({'length_is07': '{% if "X"|length_is:0 %}Length is 0{% else %}Length not 0{% endif %}'}) def test_length_is07(self): output = self.engine.render_to_string('length_is07', {}) self.assertEqual(output, 'Length not 0') @setup({'length_is08': '{% if "X"|length_is:1 %}Length is 1{% else %}Length not 1{% endif %}'}) def test_length_is08(self): output = self.engine.render_to_string('length_is08', {}) self.assertEqual(output, 'Length is 1') # Invalid uses that should fail silently. @setup({'length_is09': '{{ var|length_is:"fish" }}'}) def test_length_is09(self): output = self.engine.render_to_string('length_is09', {'var': 'django'}) self.assertEqual(output, '') @setup({'length_is10': '{{ int|length_is:"1" }}'}) def test_length_is10(self): output = self.engine.render_to_string('length_is10', {'int': 7}) self.assertEqual(output, '') @setup({'length_is11': '{{ none|length_is:"1" }}'}) def test_length_is11(self): output = self.engine.render_to_string('length_is11', {'none': None}) self.assertEqual(output, '') class FunctionTests(SimpleTestCase): def test_empty_list(self): self.assertEqual(length_is([], 0), True) self.assertEqual(length_is([], 1), False) def test_string(self): self.assertEqual(length_is('a', 1), True) self.assertEqual(length_is('a', 10), False) Django-1.8.7/tests/template_tests/filter_tests/test_date.py0000664000175000017500000000474612625116214023606 0ustar timtim00000000000000from datetime import datetime, time from django.template.defaultfilters import date from django.test import SimpleTestCase from django.utils import timezone from ..utils import setup from .timezone_utils import TimezoneTestCase class DateTests(TimezoneTestCase): @setup({'date01': '{{ d|date:"m" }}'}) def test_date01(self): output = self.engine.render_to_string('date01', {'d': datetime(2008, 1, 1)}) self.assertEqual(output, '01') @setup({'date02': '{{ d|date }}'}) def test_date02(self): output = self.engine.render_to_string('date02', {'d': datetime(2008, 1, 1)}) self.assertEqual(output, 'Jan. 1, 2008') @setup({'date03': '{{ d|date:"m" }}'}) def test_date03(self): """ #9520: Make sure |date doesn't blow up on non-dates """ output = self.engine.render_to_string('date03', {'d': 'fail_string'}) self.assertEqual(output, '') # ISO date formats @setup({'date04': '{{ d|date:"o" }}'}) def test_date04(self): output = self.engine.render_to_string('date04', {'d': datetime(2008, 12, 29)}) self.assertEqual(output, '2009') @setup({'date05': '{{ d|date:"o" }}'}) def test_date05(self): output = self.engine.render_to_string('date05', {'d': datetime(2010, 1, 3)}) self.assertEqual(output, '2009') # Timezone name @setup({'date06': '{{ d|date:"e" }}'}) def test_date06(self): output = self.engine.render_to_string('date06', {'d': datetime(2009, 3, 12, tzinfo=timezone.get_fixed_timezone(30))}) self.assertEqual(output, '+0030') @setup({'date07': '{{ d|date:"e" }}'}) def test_date07(self): output = self.engine.render_to_string('date07', {'d': datetime(2009, 3, 12)}) self.assertEqual(output, '') # #19370: Make sure |date doesn't blow up on a midnight time object @setup({'date08': '{{ t|date:"H:i" }}'}) def test_date08(self): output = self.engine.render_to_string('date08', {'t': time(0, 1)}) self.assertEqual(output, '00:01') @setup({'date09': '{{ t|date:"H:i" }}'}) def test_date09(self): output = self.engine.render_to_string('date09', {'t': time(0, 0)}) self.assertEqual(output, '00:00') class FunctionTests(SimpleTestCase): def test_date(self): self.assertEqual(date(datetime(2005, 12, 29), "d F Y"), '29 December 2005') def test_escape_characters(self): self.assertEqual(date(datetime(2005, 12, 29), r'jS \o\f F'), '29th of December') Django-1.8.7/tests/template_tests/filter_tests/test_autoescape.py0000664000175000017500000000247612625116214025020 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import SafeClass, UnsafeClass, setup class AutoescapeStringfilterTests(SimpleTestCase): """ Filters decorated with stringfilter still respect is_safe. """ @setup({'autoescape-stringfilter01': '{{ unsafe|capfirst }}'}) def test_autoescape_stringfilter01(self): output = self.engine.render_to_string('autoescape-stringfilter01', {'unsafe': UnsafeClass()}) self.assertEqual(output, 'You & me') @setup({'autoescape-stringfilter02': '{% autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}'}) def test_autoescape_stringfilter02(self): output = self.engine.render_to_string('autoescape-stringfilter02', {'unsafe': UnsafeClass()}) self.assertEqual(output, 'You & me') @setup({'autoescape-stringfilter03': '{{ safe|capfirst }}'}) def test_autoescape_stringfilter03(self): output = self.engine.render_to_string('autoescape-stringfilter03', {'safe': SafeClass()}) self.assertEqual(output, 'You > me') @setup({'autoescape-stringfilter04': '{% autoescape off %}{{ safe|capfirst }}{% endautoescape %}'}) def test_autoescape_stringfilter04(self): output = self.engine.render_to_string('autoescape-stringfilter04', {'safe': SafeClass()}) self.assertEqual(output, 'You > me') Django-1.8.7/tests/template_tests/filter_tests/test_truncatechars_html.py0000664000175000017500000000231512625116214026551 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import truncatechars_html from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_truncate_zero(self): self.assertEqual(truncatechars_html('

one two - three
four
five

', 0), '...') def test_truncate(self): self.assertEqual( truncatechars_html('

one two - three
four
five

', 6), '

one...

', ) def test_truncate2(self): self.assertEqual( truncatechars_html('

one two - three
four
five

', 11), '

one two ...

', ) def test_truncate3(self): self.assertEqual( truncatechars_html('

one two - three
four
five

', 100), '

one two - three
four
five

', ) def test_truncate_unicode(self): self.assertEqual(truncatechars_html('\xc5ngstr\xf6m was here', 5), '\xc5n...') def test_truncate_something(self): self.assertEqual(truncatechars_html('abc', 3), 'abc') Django-1.8.7/tests/template_tests/filter_tests/test_first.py0000664000175000017500000000171212625116214024006 0ustar timtim00000000000000from django.template.defaultfilters import first from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class FirstTests(SimpleTestCase): @setup({'first01': '{{ a|first }} {{ b|first }}'}) def test_first01(self): output = self.engine.render_to_string('first01', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]}) self.assertEqual(output, "a&b a&b") @setup({'first02': '{% autoescape off %}{{ a|first }} {{ b|first }}{% endautoescape %}'}) def test_first02(self): output = self.engine.render_to_string('first02', {"a": ["a&b", "x"], "b": [mark_safe("a&b"), "x"]}) self.assertEqual(output, "a&b a&b") class FunctionTests(SimpleTestCase): def test_list(self): self.assertEqual(first([0, 1, 2]), 0) def test_empty_string(self): self.assertEqual(first(''), '') def test_string(self): self.assertEqual(first('test'), 't') Django-1.8.7/tests/template_tests/filter_tests/test_floatformat.py0000664000175000017500000001060012625116214025171 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from decimal import Decimal, localcontext from unittest import expectedFailure from django.template.defaultfilters import floatformat from django.test import SimpleTestCase from django.utils import six from django.utils.safestring import mark_safe from ..utils import setup class FloatformatTests(SimpleTestCase): @setup({'floatformat01': '{% autoescape off %}{{ a|floatformat }} {{ b|floatformat }}{% endautoescape %}'}) def test_floatformat01(self): output = self.engine.render_to_string('floatformat01', {"a": "1.42", "b": mark_safe("1.42")}) self.assertEqual(output, "1.4 1.4") @setup({'floatformat02': '{{ a|floatformat }} {{ b|floatformat }}'}) def test_floatformat02(self): output = self.engine.render_to_string('floatformat02', {"a": "1.42", "b": mark_safe("1.42")}) self.assertEqual(output, "1.4 1.4") class FunctionTests(SimpleTestCase): def test_inputs(self): self.assertEqual(floatformat(7.7), '7.7') self.assertEqual(floatformat(7.0), '7') self.assertEqual(floatformat(0.7), '0.7') self.assertEqual(floatformat(0.07), '0.1') self.assertEqual(floatformat(0.007), '0.0') self.assertEqual(floatformat(0.0), '0') self.assertEqual(floatformat(7.7, 3), '7.700') self.assertEqual(floatformat(6.000000, 3), '6.000') self.assertEqual(floatformat(6.200000, 3), '6.200') self.assertEqual(floatformat(6.200000, -3), '6.200') self.assertEqual(floatformat(13.1031, -3), '13.103') self.assertEqual(floatformat(11.1197, -2), '11.12') self.assertEqual(floatformat(11.0000, -2), '11') self.assertEqual(floatformat(11.000001, -2), '11.00') self.assertEqual(floatformat(8.2798, 3), '8.280') self.assertEqual(floatformat(5555.555, 2), '5555.56') self.assertEqual(floatformat(001.3000, 2), '1.30') self.assertEqual(floatformat(0.12345, 2), '0.12') self.assertEqual(floatformat(Decimal('555.555'), 2), '555.56') self.assertEqual(floatformat(Decimal('09.000')), '9') self.assertEqual(floatformat('foo'), '') self.assertEqual(floatformat(13.1031, 'bar'), '13.1031') self.assertEqual(floatformat(18.125, 2), '18.13') self.assertEqual(floatformat('foo', 'bar'), '') self.assertEqual(floatformat('¿Cómo esta usted?'), '') self.assertEqual(floatformat(None), '') def test_zero_values(self): """ Check that we're not converting to scientific notation. """ self.assertEqual(floatformat(0, 6), '0.000000') self.assertEqual(floatformat(0, 7), '0.0000000') self.assertEqual(floatformat(0, 10), '0.0000000000') self.assertEqual(floatformat(0.000000000000000000015, 20), '0.00000000000000000002') def test_infinity(self): pos_inf = float(1e30000) self.assertEqual(floatformat(pos_inf), six.text_type(pos_inf)) neg_inf = float(-1e30000) self.assertEqual(floatformat(neg_inf), six.text_type(neg_inf)) nan = pos_inf / pos_inf self.assertEqual(floatformat(nan), six.text_type(nan)) def test_float_dunder_method(self): class FloatWrapper(object): def __init__(self, value): self.value = value def __float__(self): return self.value self.assertEqual(floatformat(FloatWrapper(11.000001), -2), '11.00') def test_low_decimal_precision(self): """ #15789 """ with localcontext() as ctx: ctx.prec = 2 self.assertEqual(floatformat(1.2345, 2), '1.23') self.assertEqual(floatformat(15.2042, -3), '15.204') self.assertEqual(floatformat(1.2345, '2'), '1.23') self.assertEqual(floatformat(15.2042, '-3'), '15.204') self.assertEqual(floatformat(Decimal('1.2345'), 2), '1.23') self.assertEqual(floatformat(Decimal('15.2042'), -3), '15.204') def test_many_zeroes(self): self.assertEqual(floatformat(1.00000000000000015, 16), '1.0000000000000002') if six.PY2: # The above test fails because of Python 2's float handling. Floats # with many zeroes after the decimal point should be passed in as # another type such as unicode or Decimal. test_many_zeroes = expectedFailure(test_many_zeroes) Django-1.8.7/tests/template_tests/filter_tests/test_timesince.py0000664000175000017500000001245612625116214024646 0ustar timtim00000000000000from __future__ import unicode_literals from datetime import datetime, timedelta from django.template.defaultfilters import timesince_filter from django.test import SimpleTestCase from django.test.utils import requires_tz_support from ..utils import setup from .timezone_utils import TimezoneTestCase class TimesinceTests(TimezoneTestCase): """ #20246 - \xa0 in output avoids line-breaks between value and unit """ # Default compare with datetime.now() @setup({'timesince01': '{{ a|timesince }}'}) def test_timesince01(self): output = self.engine.render_to_string('timesince01', {'a': datetime.now() + timedelta(minutes=-1, seconds=-10)}) self.assertEqual(output, '1\xa0minute') @setup({'timesince02': '{{ a|timesince }}'}) def test_timesince02(self): output = self.engine.render_to_string('timesince02', {'a': datetime.now() - timedelta(days=1, minutes=1)}) self.assertEqual(output, '1\xa0day') @setup({'timesince03': '{{ a|timesince }}'}) def test_timesince03(self): output = self.engine.render_to_string('timesince03', {'a': datetime.now() - timedelta(hours=1, minutes=25, seconds=10)}) self.assertEqual(output, '1\xa0hour, 25\xa0minutes') # Compare to a given parameter @setup({'timesince04': '{{ a|timesince:b }}'}) def test_timesince04(self): output = self.engine.render_to_string( 'timesince04', {'a': self.now - timedelta(days=2), 'b': self.now - timedelta(days=1)}, ) self.assertEqual(output, '1\xa0day') @setup({'timesince05': '{{ a|timesince:b }}'}) def test_timesince05(self): output = self.engine.render_to_string( 'timesince05', {'a': self.now - timedelta(days=2, minutes=1), 'b': self.now - timedelta(days=2)}, ) self.assertEqual(output, '1\xa0minute') # Check that timezone is respected @setup({'timesince06': '{{ a|timesince:b }}'}) def test_timesince06(self): output = self.engine.render_to_string('timesince06', {'a': self.now_tz - timedelta(hours=8), 'b': self.now_tz}) self.assertEqual(output, '8\xa0hours') # Tests for #7443 @setup({'timesince07': '{{ earlier|timesince }}'}) def test_timesince07(self): output = self.engine.render_to_string('timesince07', {'earlier': self.now - timedelta(days=7)}) self.assertEqual(output, '1\xa0week') @setup({'timesince08': '{{ earlier|timesince:now }}'}) def test_timesince08(self): output = self.engine.render_to_string('timesince08', {'now': self.now, 'earlier': self.now - timedelta(days=7)}) self.assertEqual(output, '1\xa0week') @setup({'timesince09': '{{ later|timesince }}'}) def test_timesince09(self): output = self.engine.render_to_string('timesince09', {'later': self.now + timedelta(days=7)}) self.assertEqual(output, '0\xa0minutes') @setup({'timesince10': '{{ later|timesince:now }}'}) def test_timesince10(self): output = self.engine.render_to_string('timesince10', {'now': self.now, 'later': self.now + timedelta(days=7)}) self.assertEqual(output, '0\xa0minutes') # Ensures that differing timezones are calculated correctly. @setup({'timesince11': '{{ a|timesince }}'}) def test_timesince11(self): output = self.engine.render_to_string('timesince11', {'a': self.now}) self.assertEqual(output, '0\xa0minutes') @requires_tz_support @setup({'timesince12': '{{ a|timesince }}'}) def test_timesince12(self): output = self.engine.render_to_string('timesince12', {'a': self.now_tz}) self.assertEqual(output, '0\xa0minutes') @requires_tz_support @setup({'timesince13': '{{ a|timesince }}'}) def test_timesince13(self): output = self.engine.render_to_string('timesince13', {'a': self.now_tz_i}) self.assertEqual(output, '0\xa0minutes') @setup({'timesince14': '{{ a|timesince:b }}'}) def test_timesince14(self): output = self.engine.render_to_string('timesince14', {'a': self.now_tz, 'b': self.now_tz_i}) self.assertEqual(output, '0\xa0minutes') @setup({'timesince15': '{{ a|timesince:b }}'}) def test_timesince15(self): output = self.engine.render_to_string('timesince15', {'a': self.now, 'b': self.now_tz_i}) self.assertEqual(output, '') @setup({'timesince16': '{{ a|timesince:b }}'}) def test_timesince16(self): output = self.engine.render_to_string('timesince16', {'a': self.now_tz_i, 'b': self.now}) self.assertEqual(output, '') # Tests for #9065 (two date objects). @setup({'timesince17': '{{ a|timesince:b }}'}) def test_timesince17(self): output = self.engine.render_to_string('timesince17', {'a': self.today, 'b': self.today}) self.assertEqual(output, '0\xa0minutes') @setup({'timesince18': '{{ a|timesince:b }}'}) def test_timesince18(self): output = self.engine.render_to_string('timesince18', {'a': self.today, 'b': self.today + timedelta(hours=24)}) self.assertEqual(output, '1\xa0day') class FunctionTests(SimpleTestCase): def test_since_now(self): self.assertEqual(timesince_filter(datetime.now() - timedelta(1)), '1\xa0day') def test_explicit_date(self): self.assertEqual(timesince_filter(datetime(2005, 12, 29), datetime(2005, 12, 30)), '1\xa0day') Django-1.8.7/tests/template_tests/filter_tests/test_force_escape.py0000664000175000017500000000554512625116214025305 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import force_escape from django.test import SimpleTestCase from django.utils.safestring import SafeData from ..utils import setup class ForceEscapeTests(SimpleTestCase): """ Force_escape is applied immediately. It can be used to provide double-escaping, for example. """ @setup({'force-escape01': '{% autoescape off %}{{ a|force_escape }}{% endautoescape %}'}) def test_force_escape01(self): output = self.engine.render_to_string('force-escape01', {"a": "x&y"}) self.assertEqual(output, "x&y") @setup({'force-escape02': '{{ a|force_escape }}'}) def test_force_escape02(self): output = self.engine.render_to_string('force-escape02', {"a": "x&y"}) self.assertEqual(output, "x&y") @setup({'force-escape03': '{% autoescape off %}{{ a|force_escape|force_escape }}{% endautoescape %}'}) def test_force_escape03(self): output = self.engine.render_to_string('force-escape03', {"a": "x&y"}) self.assertEqual(output, "x&amp;y") @setup({'force-escape04': '{{ a|force_escape|force_escape }}'}) def test_force_escape04(self): output = self.engine.render_to_string('force-escape04', {"a": "x&y"}) self.assertEqual(output, "x&amp;y") # Because the result of force_escape is "safe", an additional # escape filter has no effect. @setup({'force-escape05': '{% autoescape off %}{{ a|force_escape|escape }}{% endautoescape %}'}) def test_force_escape05(self): output = self.engine.render_to_string('force-escape05', {"a": "x&y"}) self.assertEqual(output, "x&y") @setup({'force-escape06': '{{ a|force_escape|escape }}'}) def test_force_escape06(self): output = self.engine.render_to_string('force-escape06', {"a": "x&y"}) self.assertEqual(output, "x&y") @setup({'force-escape07': '{% autoescape off %}{{ a|escape|force_escape }}{% endautoescape %}'}) def test_force_escape07(self): output = self.engine.render_to_string('force-escape07', {"a": "x&y"}) self.assertEqual(output, "x&y") @setup({'force-escape08': '{{ a|escape|force_escape }}'}) def test_force_escape08(self): output = self.engine.render_to_string('force-escape08', {"a": "x&y"}) self.assertEqual(output, "x&y") class FunctionTests(SimpleTestCase): def test_escape(self): escaped = force_escape(' here') self.assertEqual(escaped, '<some html & special characters > here') self.assertIsInstance(escaped, SafeData) def test_unicode(self): self.assertEqual( force_escape(' here ÄÅ€£'), '<some html & special characters > here \u0110\xc5\u20ac\xa3', ) Django-1.8.7/tests/template_tests/filter_tests/test_stringformat.py0000664000175000017500000000223112625116214025373 0ustar timtim00000000000000from django.template.defaultfilters import stringformat from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class StringformatTests(SimpleTestCase): """ Notice that escaping is applied *after* any filters, so the string formatting here only needs to deal with pre-escaped characters. """ @setup({'stringformat01': '{% autoescape off %}.{{ a|stringformat:"5s" }}. .{{ b|stringformat:"5s" }}.{% endautoescape %}'}) def test_stringformat01(self): output = self.engine.render_to_string('stringformat01', {'a': 'a', 'b': mark_safe('fred>')}) self.assertEqual(output, 'Fred> Fred>') @setup({'capfirst02': '{{ a|capfirst }} {{ b|capfirst }}'}) def test_capfirst02(self): output = self.engine.render_to_string('capfirst02', {'a': 'fred>', 'b': mark_safe('fred>')}) self.assertEqual(output, 'Fred> Fred>') class FunctionTests(SimpleTestCase): def test_capfirst(self): self.assertEqual(capfirst('hello world'), 'Hello world') Django-1.8.7/tests/template_tests/filter_tests/test_filesizeformat.py0000664000175000017500000000467712625116214025717 0ustar timtim00000000000000from __future__ import unicode_literals from django.template.defaultfilters import filesizeformat from django.test import SimpleTestCase from django.utils import translation class FunctionTests(SimpleTestCase): def test_formats(self): self.assertEqual(filesizeformat(1023), '1023\xa0bytes') self.assertEqual(filesizeformat(1024), '1.0\xa0KB') self.assertEqual(filesizeformat(10 * 1024), '10.0\xa0KB') self.assertEqual(filesizeformat(1024 * 1024 - 1), '1024.0\xa0KB') self.assertEqual(filesizeformat(1024 * 1024), '1.0\xa0MB') self.assertEqual(filesizeformat(1024 * 1024 * 50), '50.0\xa0MB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 - 1), '1024.0\xa0MB') self.assertEqual(filesizeformat(1024 * 1024 * 1024), '1.0\xa0GB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 1024), '1.0\xa0TB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 1024 * 1024), '1.0\xa0PB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 1024 * 1024 * 2000), '2000.0\xa0PB') self.assertEqual(filesizeformat(complex(1, -1)), '0\xa0bytes') self.assertEqual(filesizeformat(""), '0\xa0bytes') self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"), '0\xa0bytes') def test_localized_formats(self): with self.settings(USE_L10N=True), translation.override('de'): self.assertEqual(filesizeformat(1023), '1023\xa0Bytes') self.assertEqual(filesizeformat(1024), '1,0\xa0KB') self.assertEqual(filesizeformat(10 * 1024), '10,0\xa0KB') self.assertEqual(filesizeformat(1024 * 1024 - 1), '1024,0\xa0KB') self.assertEqual(filesizeformat(1024 * 1024), '1,0\xa0MB') self.assertEqual(filesizeformat(1024 * 1024 * 50), '50,0\xa0MB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 - 1), '1024,0\xa0MB') self.assertEqual(filesizeformat(1024 * 1024 * 1024), '1,0\xa0GB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 1024), '1,0\xa0TB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 1024 * 1024), '1,0\xa0PB') self.assertEqual(filesizeformat(1024 * 1024 * 1024 * 1024 * 1024 * 2000), '2000,0\xa0PB') self.assertEqual(filesizeformat(complex(1, -1)), '0\xa0Bytes') self.assertEqual(filesizeformat(""), '0\xa0Bytes') self.assertEqual(filesizeformat("\N{GREEK SMALL LETTER ALPHA}"), '0\xa0Bytes') Django-1.8.7/tests/template_tests/filter_tests/test_upper.py0000664000175000017500000000233512625116214024014 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import upper from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class UpperTests(SimpleTestCase): """ The "upper" filter messes up entities (which are case-sensitive), so it's not safe for non-escaping purposes. """ @setup({'upper01': '{% autoescape off %}{{ a|upper }} {{ b|upper }}{% endautoescape %}'}) def test_upper01(self): output = self.engine.render_to_string('upper01', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, 'A & B A & B') @setup({'upper02': '{{ a|upper }} {{ b|upper }}'}) def test_upper02(self): output = self.engine.render_to_string('upper02', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, 'A & B A &AMP; B') class FunctionTests(SimpleTestCase): def test_upper(self): self.assertEqual(upper('Mixed case input'), 'MIXED CASE INPUT') def test_unicode(self): # lowercase e umlaut self.assertEqual(upper('\xeb'), '\xcb') def test_non_string_input(self): self.assertEqual(upper(123), '123') Django-1.8.7/tests/template_tests/filter_tests/test_urlize.py0000664000175000017500000003226712625116214024202 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import urlize from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class UrlizeTests(SimpleTestCase): @setup({'urlize01': '{% autoescape off %}{{ a|urlize }} {{ b|urlize }}{% endautoescape %}'}) def test_urlize01(self): output = self.engine.render_to_string( 'urlize01', {'a': 'http://example.com/?x=&y=', 'b': mark_safe('http://example.com?x=&y=<2>')}, ) self.assertEqual( output, 'http://example.com/?x=&y= ' 'http://example.com?x=&y=<2>' ) @setup({'urlize02': '{{ a|urlize }} {{ b|urlize }}'}) def test_urlize02(self): output = self.engine.render_to_string( 'urlize02', {'a': "http://example.com/?x=&y=", 'b': mark_safe("http://example.com?x=&y=")}, ) self.assertEqual( output, 'http://example.com/?x=&y= ' 'http://example.com?x=&y=' ) @setup({'urlize03': '{% autoescape off %}{{ a|urlize }}{% endautoescape %}'}) def test_urlize03(self): output = self.engine.render_to_string('urlize03', {'a': mark_safe("a & b")}) self.assertEqual(output, 'a & b') @setup({'urlize04': '{{ a|urlize }}'}) def test_urlize04(self): output = self.engine.render_to_string('urlize04', {'a': mark_safe("a & b")}) self.assertEqual(output, 'a & b') # This will lead to a nonsense result, but at least it won't be # exploitable for XSS purposes when auto-escaping is on. @setup({'urlize05': '{% autoescape off %}{{ a|urlize }}{% endautoescape %}'}) def test_urlize05(self): output = self.engine.render_to_string('urlize05', {'a': ""}) self.assertEqual(output, "") @setup({'urlize06': '{{ a|urlize }}'}) def test_urlize06(self): output = self.engine.render_to_string('urlize06', {'a': ""}) self.assertEqual(output, '<script>alert('foo')</script>') # mailto: testing for urlize @setup({'urlize07': '{{ a|urlize }}'}) def test_urlize07(self): output = self.engine.render_to_string('urlize07', {'a': "Email me at me@example.com"}) self.assertEqual( output, 'Email me at me@example.com', ) @setup({'urlize08': '{{ a|urlize }}'}) def test_urlize08(self): output = self.engine.render_to_string('urlize08', {'a': "Email me at "}) self.assertEqual( output, 'Email me at <me@example.com>', ) @setup({'urlize09': '{% autoescape off %}{{ a|urlize }}{% endautoescape %}'}) def test_urlize09(self): output = self.engine.render_to_string('urlize09', {'a': "http://example.com/?x=&y=<2>"}) self.assertEqual( output, 'http://example.com/?x=&y=<2>', ) class FunctionTests(SimpleTestCase): def test_urls(self): self.assertEqual( urlize('http://google.com'), 'http://google.com', ) self.assertEqual( urlize('http://google.com/'), 'http://google.com/', ) self.assertEqual( urlize('www.google.com'), 'www.google.com', ) self.assertEqual( urlize('djangoproject.org'), 'djangoproject.org', ) self.assertEqual( urlize('djangoproject.org/'), 'djangoproject.org/', ) def test_email(self): self.assertEqual( urlize('info@djangoproject.org'), 'info@djangoproject.org', ) def test_word_with_dot(self): self.assertEqual(urlize('some.organization'), 'some.organization'), def test_https(self): self.assertEqual( urlize('https://google.com'), 'https://google.com', ) def test_quoting(self): """ #9655 - Check urlize doesn't overquote already quoted urls. The teststring is the urlquoted version of 'http://hi.baidu.com/釿–°å¼€å§‹' """ self.assertEqual( urlize('http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B'), '' 'http://hi.baidu.com/%E9%87%8D%E6%96%B0%E5%BC%80%E5%A7%8B', ) def test_urlencoded(self): self.assertEqual( urlize('www.mystore.com/30%OffCoupons!'), '' 'www.mystore.com/30%OffCoupons!', ) self.assertEqual( urlize('https://en.wikipedia.org/wiki/Caf%C3%A9'), '' 'https://en.wikipedia.org/wiki/Caf%C3%A9', ) def test_unicode(self): self.assertEqual( urlize('https://en.wikipedia.org/wiki/Café'), '' 'https://en.wikipedia.org/wiki/Café', ) def test_parenthesis(self): """ #11911 - Check urlize keeps balanced parentheses """ self.assertEqual( urlize('https://en.wikipedia.org/wiki/Django_(web_framework)'), '' 'https://en.wikipedia.org/wiki/Django_(web_framework)', ) self.assertEqual( urlize('(see https://en.wikipedia.org/wiki/Django_(web_framework))'), '(see ' 'https://en.wikipedia.org/wiki/Django_(web_framework))', ) def test_nofollow(self): """ #12183 - Check urlize adds nofollow properly - see #12183 """ self.assertEqual( urlize('foo@bar.com or www.bar.com'), 'foo@bar.com or ' 'www.bar.com', ) def test_idn(self): """ #13704 - Check urlize handles IDN correctly """ self.assertEqual(urlize('http://c✶.ws'), 'http://c✶.ws') self.assertEqual(urlize('www.c✶.ws'), 'www.c✶.ws') self.assertEqual(urlize('c✶.org'), 'c✶.org') self.assertEqual(urlize('info@c✶.org'), 'info@c✶.org') def test_malformed(self): """ #16395 - Check urlize doesn't highlight malformed URIs """ self.assertEqual(urlize('http:///www.google.com'), 'http:///www.google.com') self.assertEqual(urlize('http://.google.com'), 'http://.google.com') self.assertEqual(urlize('http://@foo.com'), 'http://@foo.com') def test_tlds(self): """ #16656 - Check urlize accepts more TLDs """ self.assertEqual(urlize('usa.gov'), 'usa.gov') def test_invalid_email(self): """ #17592 - Check urlize don't crash on invalid email with dot-starting domain """ self.assertEqual(urlize('email@.stream.ru'), 'email@.stream.ru') def test_uppercase(self): """ #18071 - Check urlize accepts uppercased URL schemes """ self.assertEqual( urlize('HTTPS://github.com/'), 'HTTPS://github.com/', ) def test_trailing_period(self): """ #18644 - Check urlize trims trailing period when followed by parenthesis """ self.assertEqual( urlize('(Go to http://www.example.com/foo.)'), '(Go to http://www.example.com/foo.)', ) def test_brackets(self): """ #19070 - Check urlize handles brackets properly """ self.assertEqual( urlize('[see www.example.com]'), '[see www.example.com]', ) self.assertEqual( urlize('see test[at[example.com'), 'see test[at[example.com', ) self.assertEqual( urlize('[http://168.192.0.1](http://168.192.0.1)'), '[' 'http://168.192.0.1](http://168.192.0.1)', ) def test_ipv4(self): self.assertEqual( urlize('http://192.168.0.15/api/9'), 'http://192.168.0.15/api/9', ) def test_ipv6(self): self.assertEqual( urlize('http://[2001:db8:cafe::2]/api/9'), 'http://[2001:db8:cafe::2]/api/9', ) def test_quotation_marks(self): """ #20364 - Check urlize correctly include quotation marks in links """ self.assertEqual( urlize('before "hi@example.com" afterwards', autoescape=False), 'before "hi@example.com" afterwards', ) self.assertEqual( urlize('before hi@example.com" afterwards', autoescape=False), 'before hi@example.com" afterwards', ) self.assertEqual( urlize('before "hi@example.com afterwards', autoescape=False), 'before "hi@example.com afterwards', ) self.assertEqual( urlize('before \'hi@example.com\' afterwards', autoescape=False), 'before \'hi@example.com\' afterwards', ) self.assertEqual( urlize('before hi@example.com\' afterwards', autoescape=False), 'before hi@example.com\' afterwards', ) self.assertEqual( urlize('before \'hi@example.com afterwards', autoescape=False), 'before \'hi@example.com afterwards', ) def test_quote_commas(self): """ #20364 - Check urlize copes with commas following URLs in quotes """ self.assertEqual( urlize('Email us at "hi@example.com", or phone us at +xx.yy', autoescape=False), 'Email us at "hi@example.com", or phone us at +xx.yy', ) def test_exclamation_marks(self): """ #23715 - Check urlize correctly handles exclamation marks after TLDs or query string """ self.assertEqual( urlize('Go to djangoproject.com! and enjoy.'), 'Go to djangoproject.com! and enjoy.', ) self.assertEqual( urlize('Search for google.com/?q=! and see.'), 'Search for google.com/?q=! and see.', ) self.assertEqual( urlize('Search for google.com/?q=dj!`? and see.'), 'Search for google.com/?q=dj!`? and see.', ) self.assertEqual( urlize('Search for google.com/?q=dj!`?! and see.'), 'Search for google.com/?q=dj!`?! and see.', ) def test_non_string_input(self): self.assertEqual(urlize(123), '123') def test_autoescape(self): self.assertEqual( urlize('foobarbuz'), 'foo<a href=" google.com ">bar</a>buz', ) def test_autoescape_off(self): self.assertEqual( urlize('foobarbuz', autoescape=False), 'foogoogle.com ">barbuz', ) Django-1.8.7/tests/template_tests/filter_tests/test_default.py0000664000175000017500000000415712625116214024311 0ustar timtim00000000000000from django.template.defaultfilters import default from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class DefaultTests(SimpleTestCase): """ Literal string arguments to the default filter are always treated as safe strings, regardless of the auto-escaping state. Note: we have to use {"a": ""} here, otherwise the invalid template variable string interferes with the test result. """ @setup({'default01': '{{ a|default:"x<" }}'}) def test_default01(self): output = self.engine.render_to_string('default01', {"a": ""}) self.assertEqual(output, "x<") @setup({'default02': '{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}'}) def test_default02(self): output = self.engine.render_to_string('default02', {"a": ""}) self.assertEqual(output, "x<") @setup({'default03': '{{ a|default:"x<" }}'}) def test_default03(self): output = self.engine.render_to_string('default03', {"a": mark_safe("x>")}) self.assertEqual(output, "x>") @setup({'default04': '{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}'}) def test_default04(self): output = self.engine.render_to_string('default04', {"a": mark_safe("x>")}) self.assertEqual(output, "x>") class DefaultIfNoneTests(SimpleTestCase): @setup({'default_if_none01': '{{ a|default:"x<" }}'}) def test_default_if_none01(self): output = self.engine.render_to_string('default_if_none01', {"a": None}) self.assertEqual(output, "x<") @setup({'default_if_none02': '{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}'}) def test_default_if_none02(self): output = self.engine.render_to_string('default_if_none02', {"a": None}) self.assertEqual(output, "x<") class FunctionTests(SimpleTestCase): def test_value(self): self.assertEqual(default('val', 'default'), 'val') def test_none(self): self.assertEqual(default(None, 'default'), 'default') def test_empty_string(self): self.assertEqual(default('', 'default'), 'default') Django-1.8.7/tests/template_tests/filter_tests/test_add.py0000664000175000017500000000323012625116214023404 0ustar timtim00000000000000from datetime import date, timedelta from django.template.defaultfilters import add from django.test import SimpleTestCase from ..utils import setup class AddTests(SimpleTestCase): """ Tests for #11687 and #16676 """ @setup({'add01': '{{ i|add:"5" }}'}) def test_add01(self): output = self.engine.render_to_string('add01', {'i': 2000}) self.assertEqual(output, '2005') @setup({'add02': '{{ i|add:"napis" }}'}) def test_add02(self): output = self.engine.render_to_string('add02', {'i': 2000}) self.assertEqual(output, '') @setup({'add03': '{{ i|add:16 }}'}) def test_add03(self): output = self.engine.render_to_string('add03', {'i': 'not_an_int'}) self.assertEqual(output, '') @setup({'add04': '{{ i|add:"16" }}'}) def test_add04(self): output = self.engine.render_to_string('add04', {'i': 'not_an_int'}) self.assertEqual(output, 'not_an_int16') @setup({'add05': '{{ l1|add:l2 }}'}) def test_add05(self): output = self.engine.render_to_string('add05', {'l1': [1, 2], 'l2': [3, 4]}) self.assertEqual(output, '[1, 2, 3, 4]') @setup({'add06': '{{ t1|add:t2 }}'}) def test_add06(self): output = self.engine.render_to_string('add06', {'t1': (3, 4), 't2': (1, 2)}) self.assertEqual(output, '(3, 4, 1, 2)') @setup({'add07': '{{ d|add:t }}'}) def test_add07(self): output = self.engine.render_to_string('add07', {'d': date(2000, 1, 1), 't': timedelta(10)}) self.assertEqual(output, 'Jan. 11, 2000') class FunctionTests(SimpleTestCase): def test_add(self): self.assertEqual(add('1', '2'), 3) Django-1.8.7/tests/template_tests/filter_tests/__init__.py0000664000175000017500000000000012625116214023344 0ustar timtim00000000000000Django-1.8.7/tests/template_tests/filter_tests/test_safeseq.py0000664000175000017500000000116212625116214024305 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class SafeseqTests(SimpleTestCase): @setup({'safeseq01': '{{ a|join:", " }} -- {{ a|safeseq|join:", " }}'}) def test_safeseq01(self): output = self.engine.render_to_string('safeseq01', {'a': ['&', '<']}) self.assertEqual(output, '&, < -- &, <') @setup({'safeseq02': '{% autoescape off %}{{ a|join:", " }} -- {{ a|safeseq|join:", " }}{% endautoescape %}'}) def test_safeseq02(self): output = self.engine.render_to_string('safeseq02', {'a': ['&', '<']}) self.assertEqual(output, '&, < -- &, <') Django-1.8.7/tests/template_tests/filter_tests/test_join.py0000664000175000017500000000522312625116214023617 0ustar timtim00000000000000from django.template.defaultfilters import join from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class JoinTests(SimpleTestCase): @setup({'join01': '{{ a|join:", " }}'}) def test_join01(self): output = self.engine.render_to_string('join01', {'a': ['alpha', 'beta & me']}) self.assertEqual(output, 'alpha, beta & me') @setup({'join02': '{% autoescape off %}{{ a|join:", " }}{% endautoescape %}'}) def test_join02(self): output = self.engine.render_to_string('join02', {'a': ['alpha', 'beta & me']}) self.assertEqual(output, 'alpha, beta & me') @setup({'join03': '{{ a|join:" & " }}'}) def test_join03(self): output = self.engine.render_to_string('join03', {'a': ['alpha', 'beta & me']}) self.assertEqual(output, 'alpha & beta & me') @setup({'join04': '{% autoescape off %}{{ a|join:" & " }}{% endautoescape %}'}) def test_join04(self): output = self.engine.render_to_string('join04', {'a': ['alpha', 'beta & me']}) self.assertEqual(output, 'alpha & beta & me') # #11377 Test that joining with unsafe joiners doesn't result in # unsafe strings @setup({'join05': '{{ a|join:var }}'}) def test_join05(self): output = self.engine.render_to_string('join05', {'a': ['alpha', 'beta & me'], 'var': ' & '}) self.assertEqual(output, 'alpha & beta & me') @setup({'join06': '{{ a|join:var }}'}) def test_join06(self): output = self.engine.render_to_string('join06', {'a': ['alpha', 'beta & me'], 'var': mark_safe(' & ')}) self.assertEqual(output, 'alpha & beta & me') @setup({'join07': '{{ a|join:var|lower }}'}) def test_join07(self): output = self.engine.render_to_string('join07', {'a': ['Alpha', 'Beta & me'], 'var': ' & '}) self.assertEqual(output, 'alpha & beta & me') @setup({'join08': '{{ a|join:var|lower }}'}) def test_join08(self): output = self.engine.render_to_string('join08', {'a': ['Alpha', 'Beta & me'], 'var': mark_safe(' & ')}) self.assertEqual(output, 'alpha & beta & me') class FunctionTests(SimpleTestCase): def test_list(self): self.assertEqual(join([0, 1, 2], 'glue'), '0glue1glue2') def test_autoescape(self): self.assertEqual( join(['', '', ''], '
'), '<a><br><img><br></a>', ) def test_autoescape_off(self): self.assertEqual( join(['', '', ''], '
', autoescape=False), '<br><br>', ) Django-1.8.7/tests/template_tests/filter_tests/test_wordcount.py0000664000175000017500000000212312625116214024700 0ustar timtim00000000000000from django.template.defaultfilters import wordcount from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class WordcountTests(SimpleTestCase): @setup({'wordcount01': '{% autoescape off %}{{ a|wordcount }} {{ b|wordcount }}{% endautoescape %}'}) def test_wordcount01(self): output = self.engine.render_to_string('wordcount01', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, '3 3') @setup({'wordcount02': '{{ a|wordcount }} {{ b|wordcount }}'}) def test_wordcount02(self): output = self.engine.render_to_string('wordcount02', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, '3 3') class FunctionTests(SimpleTestCase): def test_empty_string(self): self.assertEqual(wordcount(''), 0) def test_count_one(self): self.assertEqual(wordcount('oneword'), 1) def test_count_multiple(self): self.assertEqual(wordcount('lots of words'), 3) def test_non_string_input(self): self.assertEqual(wordcount(123), 1) Django-1.8.7/tests/template_tests/filter_tests/timezone_utils.py0000664000175000017500000000071712625116214024676 0ustar timtim00000000000000from datetime import date, datetime from django.test import SimpleTestCase from django.utils import timezone class TimezoneTestCase(SimpleTestCase): def setUp(self): self.now = datetime.now() self.now_tz = timezone.make_aware( self.now, timezone.get_default_timezone(), ) self.now_tz_i = timezone.localtime( self.now_tz, timezone.get_fixed_timezone(195), ) self.today = date.today() Django-1.8.7/tests/template_tests/filter_tests/test_linenumbers.py0000664000175000017500000000371012625116214025202 0ustar timtim00000000000000from django.template.defaultfilters import linenumbers from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LinenumbersTests(SimpleTestCase): """ The contents of "linenumbers" is escaped according to the current autoescape setting. """ @setup({'linenumbers01': '{{ a|linenumbers }} {{ b|linenumbers }}'}) def test_linenumbers01(self): output = self.engine.render_to_string( 'linenumbers01', {'a': 'one\n\nthree', 'b': mark_safe('one\n<two>\nthree')}, ) self.assertEqual(output, '1. one\n2. <two>\n3. three ' '1. one\n2. <two>\n3. three') @setup({'linenumbers02': '{% autoescape off %}{{ a|linenumbers }} {{ b|linenumbers }}{% endautoescape %}'}) def test_linenumbers02(self): output = self.engine.render_to_string( 'linenumbers02', {'a': 'one\n\nthree', 'b': mark_safe('one\n<two>\nthree')}, ) self.assertEqual(output, '1. one\n2. \n3. three ' '1. one\n2. <two>\n3. three') class FunctionTests(SimpleTestCase): def test_linenumbers(self): self.assertEqual(linenumbers('line 1\nline 2'), '1. line 1\n2. line 2') def test_linenumbers2(self): self.assertEqual( linenumbers('\n'.join(['x'] * 10)), '01. x\n02. x\n03. x\n04. x\n05. x\n06. x\n07. x\n08. x\n09. x\n10. x', ) def test_non_string_input(self): self.assertEqual(linenumbers(123), '1. 123') def test_autoescape(self): self.assertEqual( linenumbers('foo\nbar\nbuz'), '1. foo\n2. <a>bar</a>\n3. buz', ) def test_autoescape_off(self): self.assertEqual( linenumbers('foo\nbar\nbuz', autoescape=False), '1. foo\n2. bar\n3. buz' ) Django-1.8.7/tests/template_tests/filter_tests/test_linebreaks.py0000664000175000017500000000360012625116214024774 0ustar timtim00000000000000from django.template.defaultfilters import linebreaks_filter from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LinebreaksTests(SimpleTestCase): """ The contents in "linebreaks" are escaped according to the current autoescape setting. """ @setup({'linebreaks01': '{{ a|linebreaks }} {{ b|linebreaks }}'}) def test_linebreaks01(self): output = self.engine.render_to_string('linebreaks01', {"a": "x&\ny", "b": mark_safe("x&\ny")}) self.assertEqual(output, "

x&
y

x&
y

") @setup({'linebreaks02': '{% autoescape off %}{{ a|linebreaks }} {{ b|linebreaks }}{% endautoescape %}'}) def test_linebreaks02(self): output = self.engine.render_to_string('linebreaks02', {"a": "x&\ny", "b": mark_safe("x&\ny")}) self.assertEqual(output, "

x&
y

x&
y

") class FunctionTests(SimpleTestCase): def test_line(self): self.assertEqual(linebreaks_filter('line 1'), '

line 1

') def test_newline(self): self.assertEqual(linebreaks_filter('line 1\nline 2'), '

line 1
line 2

') def test_carriage(self): self.assertEqual(linebreaks_filter('line 1\rline 2'), '

line 1
line 2

') def test_carriage_newline(self): self.assertEqual(linebreaks_filter('line 1\r\nline 2'), '

line 1
line 2

') def test_non_string_input(self): self.assertEqual(linebreaks_filter(123), '

123

') def test_autoescape(self): self.assertEqual( linebreaks_filter('foo\nbar\nbuz'), '

foo
<a>bar</a>
buz

', ) def test_autoescape_off(self): self.assertEqual( linebreaks_filter('foo\nbar\nbuz', autoescape=False), '

foo
bar
buz

', ) Django-1.8.7/tests/template_tests/filter_tests/test_lower.py0000664000175000017500000000220312625116214024003 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import lower from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LowerTests(SimpleTestCase): @setup({'lower01': '{% autoescape off %}{{ a|lower }} {{ b|lower }}{% endautoescape %}'}) def test_lower01(self): output = self.engine.render_to_string('lower01', {"a": "Apple & banana", "b": mark_safe("Apple & banana")}) self.assertEqual(output, "apple & banana apple & banana") @setup({'lower02': '{{ a|lower }} {{ b|lower }}'}) def test_lower02(self): output = self.engine.render_to_string('lower02', {"a": "Apple & banana", "b": mark_safe("Apple & banana")}) self.assertEqual(output, "apple & banana apple & banana") class FunctionTests(SimpleTestCase): def test_lower(self): self.assertEqual(lower('TEST'), 'test') def test_unicode(self): # uppercase E umlaut self.assertEqual(lower('\xcb'), '\xeb') def test_non_string_input(self): self.assertEqual(lower(123), '123') Django-1.8.7/tests/template_tests/filter_tests/test_last.py0000664000175000017500000000121412625116214023617 0ustar timtim00000000000000from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LastTests(SimpleTestCase): @setup({'last01': '{{ a|last }} {{ b|last }}'}) def test_last01(self): output = self.engine.render_to_string('last01', {"a": ["x", "a&b"], "b": ["x", mark_safe("a&b")]}) self.assertEqual(output, "a&b a&b") @setup({'last02': '{% autoescape off %}{{ a|last }} {{ b|last }}{% endautoescape %}'}) def test_last02(self): output = self.engine.render_to_string('last02', {"a": ["x", "a&b"], "b": ["x", mark_safe("a&b")]}) self.assertEqual(output, "a&b a&b") Django-1.8.7/tests/template_tests/filter_tests/test_wordwrap.py0000664000175000017500000000320212625116214024520 0ustar timtim00000000000000from django.template.defaultfilters import wordwrap from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class WordwrapTests(SimpleTestCase): @setup({'wordwrap01': '{% autoescape off %}{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}{% endautoescape %}'}) def test_wordwrap01(self): output = self.engine.render_to_string('wordwrap01', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, 'a &\nb a &\nb') @setup({'wordwrap02': '{{ a|wordwrap:"3" }} {{ b|wordwrap:"3" }}'}) def test_wordwrap02(self): output = self.engine.render_to_string('wordwrap02', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, 'a &\nb a &\nb') class FunctionTests(SimpleTestCase): def test_wrap(self): self.assertEqual( wordwrap('this is a long paragraph of text that really needs to be wrapped I\'m afraid', 14), 'this is a long\nparagraph of\ntext that\nreally needs\nto be wrapped\nI\'m afraid', ) def test_indent(self): self.assertEqual( wordwrap('this is a short paragraph of text.\n But this line should be indented', 14), 'this is a\nshort\nparagraph of\ntext.\n But this\nline should be\nindented', ) def test_indent2(self): self.assertEqual( wordwrap('this is a short paragraph of text.\n But this line should be indented', 15), 'this is a short\nparagraph of\ntext.\n But this line\nshould be\nindented', ) def test_non_string_input(self): self.assertEqual(wordwrap(123, 2), '123') Django-1.8.7/tests/template_tests/filter_tests/test_unordered_list.py0000664000175000017500000001776312625116214025716 0ustar timtim00000000000000from django.template.defaultfilters import unordered_list from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import RemovedInDjango110Warning from django.utils.encoding import python_2_unicode_compatible from django.utils.safestring import mark_safe from ..utils import setup class UnorderedListTests(SimpleTestCase): @setup({'unordered_list01': '{{ a|unordered_list }}'}) def test_unordered_list01(self): output = self.engine.render_to_string('unordered_list01', {'a': ['x>', ['x>\n\t
    \n\t\t
  • <y
  • \n\t
\n\t') @ignore_warnings(category=RemovedInDjango110Warning) @setup({'unordered_list02': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'}) def test_unordered_list02(self): output = self.engine.render_to_string('unordered_list02', {'a': ['x>', ['x>\n\t
    \n\t\t
  • \n\t
\n\t') @setup({'unordered_list03': '{{ a|unordered_list }}'}) def test_unordered_list03(self): output = self.engine.render_to_string('unordered_list03', {'a': ['x>', [mark_safe('x>\n\t
    \n\t\t
  • \n\t
\n\t') @setup({'unordered_list04': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'}) def test_unordered_list04(self): output = self.engine.render_to_string('unordered_list04', {'a': ['x>', [mark_safe('x>\n\t
    \n\t\t
  • \n\t
\n\t') @setup({'unordered_list05': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'}) def test_unordered_list05(self): output = self.engine.render_to_string('unordered_list05', {'a': ['x>', ['x>\n\t
    \n\t\t
  • \n\t
\n\t') @ignore_warnings(category=RemovedInDjango110Warning) class DeprecatedUnorderedListSyntaxTests(SimpleTestCase): @setup({'unordered_list01': '{{ a|unordered_list }}'}) def test_unordered_list01(self): output = self.engine.render_to_string('unordered_list01', {'a': ['x>', [['x>\n\t
    \n\t\t
  • <y
  • \n\t
\n\t') @setup({'unordered_list02': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'}) def test_unordered_list02(self): output = self.engine.render_to_string('unordered_list02', {'a': ['x>', [['x>\n\t
    \n\t\t
  • \n\t
\n\t') @setup({'unordered_list03': '{{ a|unordered_list }}'}) def test_unordered_list03(self): output = self.engine.render_to_string('unordered_list03', {'a': ['x>', [[mark_safe('x>\n\t
    \n\t\t
  • \n\t
\n\t') @setup({'unordered_list04': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'}) def test_unordered_list04(self): output = self.engine.render_to_string('unordered_list04', {'a': ['x>', [[mark_safe('x>\n\t
    \n\t\t
  • \n\t
\n\t') @setup({'unordered_list05': '{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}'}) def test_unordered_list05(self): output = self.engine.render_to_string('unordered_list05', {'a': ['x>', [['x>\n\t
    \n\t\t
  • \n\t
\n\t') class FunctionTests(SimpleTestCase): def test_list(self): self.assertEqual(unordered_list(['item 1', 'item 2']), '\t
  • item 1
  • \n\t
  • item 2
  • ') def test_nested(self): self.assertEqual( unordered_list(['item 1', ['item 1.1']]), '\t
  • item 1\n\t
      \n\t\t
    • item 1.1
    • \n\t
    \n\t
  • ', ) def test_nested2(self): self.assertEqual( unordered_list(['item 1', ['item 1.1', 'item1.2'], 'item 2']), '\t
  • item 1\n\t
      \n\t\t
    • item 1.1
    • \n\t\t
    • item1.2' '
    • \n\t
    \n\t
  • \n\t
  • item 2
  • ', ) def test_nested3(self): self.assertEqual( unordered_list(['item 1', 'item 2', ['item 2.1']]), '\t
  • item 1
  • \n\t
  • item 2\n\t
      \n\t\t
    • item 2.1' '
    • \n\t
    \n\t
  • ', ) def test_nested_multiple(self): self.assertEqual( unordered_list(['item 1', ['item 1.1', ['item 1.1.1', ['item 1.1.1.1']]]]), '\t
  • item 1\n\t
      \n\t\t
    • item 1.1\n\t\t
        \n\t\t\t
      • ' 'item 1.1.1\n\t\t\t
          \n\t\t\t\t
        • item 1.1.1.1
        • \n\t\t\t' '
        \n\t\t\t
      • \n\t\t
      \n\t\t
    • \n\t
    \n\t
  • ', ) def test_nested_multiple2(self): self.assertEqual( unordered_list(['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]), '\t
  • States\n\t
      \n\t\t
    • Kansas\n\t\t
        \n\t\t\t
      • ' 'Lawrence
      • \n\t\t\t
      • Topeka
      • \n\t\t
      \n\t\t
    • ' '\n\t\t
    • Illinois
    • \n\t
    \n\t
  • ', ) def test_autoescape(self): self.assertEqual( unordered_list(['item 1', 'item 2']), '\t
  • <a>item 1</a>
  • \n\t
  • item 2
  • ', ) def test_autoescape_off(self): self.assertEqual( unordered_list(['item 1', 'item 2'], autoescape=False), '\t
  • item 1
  • \n\t
  • item 2
  • ', ) def test_ulitem(self): @python_2_unicode_compatible class ULItem(object): def __init__(self, title): self.title = title def __str__(self): return 'ulitem-%s' % str(self.title) a = ULItem('a') b = ULItem('b') c = ULItem('c') self.assertEqual( unordered_list([a, b, c]), '\t
  • ulitem-a
  • \n\t
  • ulitem-b
  • \n\t
  • ulitem-<a>c</a>
  • ', ) def item_generator(): yield a yield b yield c self.assertEqual( unordered_list(item_generator()), '\t
  • ulitem-a
  • \n\t
  • ulitem-b
  • \n\t
  • ulitem-<a>c</a>
  • ', ) def test_ulitem_autoescape_off(self): @python_2_unicode_compatible class ULItem(object): def __init__(self, title): self.title = title def __str__(self): return 'ulitem-%s' % str(self.title) a = ULItem('a') b = ULItem('b') c = ULItem('c') self.assertEqual( unordered_list([a, b, c], autoescape=False), '\t
  • ulitem-a
  • \n\t
  • ulitem-b
  • \n\t
  • ulitem-c
  • ', ) def item_generator(): yield a yield b yield c self.assertEqual( unordered_list(item_generator(), autoescape=False), '\t
  • ulitem-a
  • \n\t
  • ulitem-b
  • \n\t
  • ulitem-c
  • ', ) @ignore_warnings(category=RemovedInDjango110Warning) def test_legacy(self): """ Old format for unordered lists should still work """ self.assertEqual(unordered_list(['item 1', []]), '\t
  • item 1
  • ') self.assertEqual( unordered_list(['item 1', [['item 1.1', []]]]), '\t
  • item 1\n\t
      \n\t\t
    • item 1.1
    • \n\t
    \n\t
  • ', ) self.assertEqual( unordered_list(['item 1', [['item 1.1', []], ['item 1.2', []]]]), '\t
  • item 1\n\t
      \n\t\t
    • item 1.1' '
    • \n\t\t
    • item 1.2
    • \n\t
    \n\t
  • ', ) self.assertEqual( unordered_list(['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]), '\t
  • States\n\t
      \n\t\t
    • Kansas\n\t\t
        \n\t\t\t
      • Lawrence
      • ' '\n\t\t\t
      • Topeka
      • \n\t\t
      \n\t\t
    • \n\t\t
    • Illinois
    • \n\t
    \n\t
  • ', ) Django-1.8.7/tests/template_tests/filter_tests/test_striptags.py0000664000175000017500000000247212625116214024703 0ustar timtim00000000000000from django.template.defaultfilters import striptags from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class StriptagsTests(SimpleTestCase): @setup({'striptags01': '{{ a|striptags }} {{ b|striptags }}'}) def test_striptags01(self): output = self.engine.render_to_string( 'striptags01', { 'a': 'x

    y

    ', 'b': mark_safe('x

    y

    '), }, ) self.assertEqual(output, 'x y x y') @setup({'striptags02': '{% autoescape off %}{{ a|striptags }} {{ b|striptags }}{% endautoescape %}'}) def test_striptags02(self): output = self.engine.render_to_string( 'striptags02', { 'a': 'x

    y

    ', 'b': mark_safe('x

    y

    '), }, ) self.assertEqual(output, 'x y x y') class FunctionTests(SimpleTestCase): def test_strip(self): self.assertEqual( striptags('some html with disallowed tags'), 'some html with alert("You smell") disallowed tags', ) def test_non_string_input(self): self.assertEqual(striptags(123), '123') Django-1.8.7/tests/template_tests/filter_tests/test_slice.py0000664000175000017500000000244512625116214023762 0ustar timtim00000000000000from django.template.defaultfilters import slice_filter from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class SliceTests(SimpleTestCase): @setup({'slice01': '{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}'}) def test_slice01(self): output = self.engine.render_to_string('slice01', {'a': 'a&b', 'b': mark_safe('a&b')}) self.assertEqual(output, '&b &b') @setup({'slice02': '{% autoescape off %}{{ a|slice:"1:3" }} {{ b|slice:"1:3" }}{% endautoescape %}'}) def test_slice02(self): output = self.engine.render_to_string('slice02', {'a': 'a&b', 'b': mark_safe('a&b')}) self.assertEqual(output, '&b &b') class FunctionTests(SimpleTestCase): def test_zero_length(self): self.assertEqual(slice_filter('abcdefg', '0'), '') def test_index(self): self.assertEqual(slice_filter('abcdefg', '1'), 'a') def test_negative_index(self): self.assertEqual(slice_filter('abcdefg', '-1'), 'abcdef') def test_range(self): self.assertEqual(slice_filter('abcdefg', '1:2'), 'b') def test_range_multiple(self): self.assertEqual(slice_filter('abcdefg', '1:3'), 'bc') def test_range_step(self): self.assertEqual(slice_filter('abcdefg', '0::2'), 'aceg') Django-1.8.7/tests/template_tests/filter_tests/test_removetags.py0000664000175000017500000000323012625116214025030 0ustar timtim00000000000000from django.template.defaultfilters import removetags from django.test import SimpleTestCase, ignore_warnings from django.utils.deprecation import RemovedInDjango110Warning from django.utils.safestring import mark_safe from ..utils import setup @ignore_warnings(category=RemovedInDjango110Warning) class RemovetagsTests(SimpleTestCase): @setup({'removetags01': '{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}'}) def test_removetags01(self): output = self.engine.render_to_string( 'removetags01', { 'a': 'x

    y

    ', 'b': mark_safe('x

    y

    '), }, ) self.assertEqual(output, 'x <p>y</p> x

    y

    ') @setup({'removetags02': '{% autoescape off %}{{ a|removetags:"a b" }} {{ b|removetags:"a b" }}{% endautoescape %}'}) def test_removetags02(self): output = self.engine.render_to_string( 'removetags02', { 'a': 'x

    y

    ', 'b': mark_safe('x

    y

    '), }, ) self.assertEqual(output, 'x

    y

    x

    y

    ') @ignore_warnings(category=RemovedInDjango110Warning) class FunctionTests(SimpleTestCase): def test_removetags(self): self.assertEqual( removetags( 'some html with disallowed tags', 'script img', ), 'some html with alert("You smell") disallowed tags', ) def test_non_string_input(self): self.assertEqual(removetags(123, 'a'), '123') Django-1.8.7/tests/template_tests/filter_tests/test_slugify.py0000664000175000017500000000262612625116214024346 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import slugify from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class SlugifyTests(SimpleTestCase): """ Running slugify on a pre-escaped string leads to odd behavior, but the result is still safe. """ @setup({'slugify01': '{% autoescape off %}{{ a|slugify }} {{ b|slugify }}{% endautoescape %}'}) def test_slugify01(self): output = self.engine.render_to_string('slugify01', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, 'a-b a-amp-b') @setup({'slugify02': '{{ a|slugify }} {{ b|slugify }}'}) def test_slugify02(self): output = self.engine.render_to_string('slugify02', {'a': 'a & b', 'b': mark_safe('a & b')}) self.assertEqual(output, 'a-b a-amp-b') class FunctionTests(SimpleTestCase): def test_slugify(self): self.assertEqual( slugify(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/'), 'jack-jill-like-numbers-123-and-4-and-silly-characters', ) def test_unicode(self): self.assertEqual( slugify("Un \xe9l\xe9phant \xe0 l'or\xe9e du bois"), 'un-elephant-a-loree-du-bois', ) def test_non_string_input(self): self.assertEqual(slugify(123), '123') Django-1.8.7/tests/template_tests/filter_tests/test_random.py0000664000175000017500000000130012625116214024130 0ustar timtim00000000000000from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class RandomTests(SimpleTestCase): @setup({'random01': '{{ a|random }} {{ b|random }}'}) def test_random01(self): output = self.engine.render_to_string('random01', {'a': ['a&b', 'a&b'], 'b': [mark_safe('a&b'), mark_safe('a&b')]}) self.assertEqual(output, 'a&b a&b') @setup({'random02': '{% autoescape off %}{{ a|random }} {{ b|random }}{% endautoescape %}'}) def test_random02(self): output = self.engine.render_to_string('random02', {'a': ['a&b', 'a&b'], 'b': [mark_safe('a&b'), mark_safe('a&b')]}) self.assertEqual(output, 'a&b a&b') Django-1.8.7/tests/template_tests/filter_tests/test_truncatechars.py0000664000175000017500000000106312625116214025524 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class TruncatecharsTests(SimpleTestCase): @setup({'truncatechars01': '{{ a|truncatechars:5 }}'}) def test_truncatechars01(self): output = self.engine.render_to_string('truncatechars01', {'a': 'Testing, testing'}) self.assertEqual(output, 'Te...') @setup({'truncatechars02': '{{ a|truncatechars:7 }}'}) def test_truncatechars02(self): output = self.engine.render_to_string('truncatechars02', {'a': 'Testing'}) self.assertEqual(output, 'Testing') Django-1.8.7/tests/template_tests/filter_tests/test_rjust.py0000664000175000017500000000200612625116214024023 0ustar timtim00000000000000from django.template.defaultfilters import rjust from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class RjustTests(SimpleTestCase): @setup({'rjust01': '{% autoescape off %}.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.{% endautoescape %}'}) def test_rjust01(self): output = self.engine.render_to_string('rjust01', {"a": "a&b", "b": mark_safe("a&b")}) self.assertEqual(output, ". a&b. . a&b.") @setup({'rjust02': '.{{ a|rjust:"5" }}. .{{ b|rjust:"5" }}.'}) def test_rjust02(self): output = self.engine.render_to_string('rjust02', {"a": "a&b", "b": mark_safe("a&b")}) self.assertEqual(output, ". a&b. . a&b.") class FunctionTests(SimpleTestCase): def test_rjust(self): self.assertEqual(rjust('test', 10), ' test') def test_less_than_string_length(self): self.assertEqual(rjust('test', 3), 'test') def test_non_string_input(self): self.assertEqual(rjust(123, 4), ' 123') Django-1.8.7/tests/template_tests/filter_tests/test_dictsortreversed.py0000664000175000017500000000205212625116214026250 0ustar timtim00000000000000from django.template.defaultfilters import dictsortreversed from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_sort(self): sorted_dicts = dictsortreversed( [{'age': 23, 'name': 'Barbara-Ann'}, {'age': 63, 'name': 'Ra Ra Rasputin'}, {'name': 'Jonny B Goode', 'age': 18}], 'age', ) self.assertEqual( [sorted(dict.items()) for dict in sorted_dicts], [[('age', 63), ('name', 'Ra Ra Rasputin')], [('age', 23), ('name', 'Barbara-Ann')], [('age', 18), ('name', 'Jonny B Goode')]], ) def test_invalid_values(self): """ If dictsortreversed is passed something other than a list of dictionaries, fail silently. """ self.assertEqual(dictsortreversed([1, 2, 3], 'age'), '') self.assertEqual(dictsortreversed('Hello!', 'age'), '') self.assertEqual(dictsortreversed({'a': 1}, 'age'), '') self.assertEqual(dictsortreversed(1, 'age'), '') Django-1.8.7/tests/template_tests/filter_tests/test_phone2numeric.py0000664000175000017500000000274412625116214025443 0ustar timtim00000000000000from django.template.defaultfilters import phone2numeric_filter from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class Phone2numericTests(SimpleTestCase): @setup({'phone2numeric01': '{{ a|phone2numeric }} {{ b|phone2numeric }}'}) def test_phone2numeric01(self): output = self.engine.render_to_string( 'phone2numeric01', {'a': '<1-800-call-me>', 'b': mark_safe('<1-800-call-me>')}, ) self.assertEqual(output, '<1-800-2255-63> <1-800-2255-63>') @setup({'phone2numeric02': '{% autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{% endautoescape %}'}) def test_phone2numeric02(self): output = self.engine.render_to_string( 'phone2numeric02', {'a': '<1-800-call-me>', 'b': mark_safe('<1-800-call-me>')}, ) self.assertEqual(output, '<1-800-2255-63> <1-800-2255-63>') @setup({'phone2numeric03': '{{ a|phone2numeric }}'}) def test_phone2numeric03(self): output = self.engine.render_to_string( 'phone2numeric03', {'a': 'How razorback-jumping frogs can level six piqued gymnasts!'}, ) self.assertEqual( output, '469 729672225-5867464 37647 226 53835 749 747833 49662787!' ) class FunctionTests(SimpleTestCase): def test_phone2numeric(self): self.assertEqual(phone2numeric_filter('0800 flowers'), '0800 3569377') Django-1.8.7/tests/template_tests/filter_tests/test_time.py0000664000175000017500000000342412625116214023617 0ustar timtim00000000000000from datetime import time from django.template.defaultfilters import time as time_filter from django.test import SimpleTestCase from django.utils import timezone from ..utils import setup from .timezone_utils import TimezoneTestCase class TimeTests(TimezoneTestCase): """ #20693: Timezone support for the time template filter """ @setup({'time01': '{{ dt|time:"e:O:T:Z" }}'}) def test_time01(self): output = self.engine.render_to_string('time01', {'dt': self.now_tz_i}) self.assertEqual(output, '+0315:+0315:+0315:11700') @setup({'time02': '{{ dt|time:"e:T" }}'}) def test_time02(self): output = self.engine.render_to_string('time02', {'dt': self.now}) self.assertEqual(output, ':' + self.now_tz.tzinfo.tzname(self.now_tz)) @setup({'time03': '{{ t|time:"P:e:O:T:Z" }}'}) def test_time03(self): output = self.engine.render_to_string('time03', {'t': time(4, 0, tzinfo=timezone.get_fixed_timezone(30))}) self.assertEqual(output, '4 a.m.::::') @setup({'time04': '{{ t|time:"P:e:O:T:Z" }}'}) def test_time04(self): output = self.engine.render_to_string('time04', {'t': time(4, 0)}) self.assertEqual(output, '4 a.m.::::') @setup({'time05': '{{ d|time:"P:e:O:T:Z" }}'}) def test_time05(self): output = self.engine.render_to_string('time05', {'d': self.today}) self.assertEqual(output, '') @setup({'time06': '{{ obj|time:"P:e:O:T:Z" }}'}) def test_time06(self): output = self.engine.render_to_string('time06', {'obj': 'non-datetime-value'}) self.assertEqual(output, '') class FunctionTests(SimpleTestCase): def test_inputs(self): self.assertEqual(time_filter(time(13), 'h'), '01') self.assertEqual(time_filter(time(0), 'h'), '12') Django-1.8.7/tests/template_tests/filter_tests/test_pluralize.py0000664000175000017500000000226012625116214024665 0ustar timtim00000000000000from decimal import Decimal from django.template.defaultfilters import pluralize from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_integers(self): self.assertEqual(pluralize(1), '') self.assertEqual(pluralize(0), 's') self.assertEqual(pluralize(2), 's') def test_floats(self): self.assertEqual(pluralize(0.5), 's') self.assertEqual(pluralize(1.5), 's') def test_decimals(self): self.assertEqual(pluralize(Decimal(1)), '') self.assertEqual(pluralize(Decimal(0)), 's') self.assertEqual(pluralize(Decimal(2)), 's') def test_lists(self): self.assertEqual(pluralize([1]), '') self.assertEqual(pluralize([]), 's') self.assertEqual(pluralize([1, 2, 3]), 's') def test_suffixes(self): self.assertEqual(pluralize(1, 'es'), '') self.assertEqual(pluralize(0, 'es'), 'es') self.assertEqual(pluralize(2, 'es'), 'es') self.assertEqual(pluralize(1, 'y,ies'), 'y') self.assertEqual(pluralize(0, 'y,ies'), 'ies') self.assertEqual(pluralize(2, 'y,ies'), 'ies') self.assertEqual(pluralize(0, 'y,ies,error'), '') Django-1.8.7/tests/template_tests/filter_tests/test_title.py0000664000175000017500000000171112625116214023777 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import title from django.test import SimpleTestCase from ..utils import setup class TitleTests(SimpleTestCase): @setup({'title1': '{{ a|title }}'}) def test_title1(self): output = self.engine.render_to_string('title1', {'a': 'JOE\'S CRAB SHACK'}) self.assertEqual(output, 'Joe's Crab Shack') @setup({'title2': '{{ a|title }}'}) def test_title2(self): output = self.engine.render_to_string('title2', {'a': '555 WEST 53RD STREET'}) self.assertEqual(output, '555 West 53rd Street') class FunctionTests(SimpleTestCase): def test_title(self): self.assertEqual(title('a nice title, isn\'t it?'), "A Nice Title, Isn't It?") def test_unicode(self): self.assertEqual(title('discoth\xe8que'), 'Discoth\xe8que') def test_non_string_input(self): self.assertEqual(title(123), '123') Django-1.8.7/tests/template_tests/filter_tests/test_divisibleby.py0000664000175000017500000000043712625116214025167 0ustar timtim00000000000000from django.template.defaultfilters import divisibleby from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_true(self): self.assertEqual(divisibleby(4, 2), True) def test_false(self): self.assertEqual(divisibleby(4, 3), False) Django-1.8.7/tests/template_tests/filter_tests/test_safe.py0000664000175000017500000000112412625116214023572 0ustar timtim00000000000000from django.test import SimpleTestCase from ..utils import setup class SafeTests(SimpleTestCase): @setup({'safe01': '{{ a }} -- {{ a|safe }}'}) def test_safe01(self): output = self.engine.render_to_string('safe01', {'a': 'hello'}) self.assertEqual(output, '<b>hello</b> -- hello') @setup({'safe02': '{% autoescape off %}{{ a }} -- {{ a|safe }}{% endautoescape %}'}) def test_safe02(self): output = self.engine.render_to_string('safe02', {'a': 'hello'}) self.assertEqual(output, 'hello -- hello') Django-1.8.7/tests/template_tests/filter_tests/test_ljust.py0000664000175000017500000000207112625116214024017 0ustar timtim00000000000000from django.template.defaultfilters import ljust from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LjustTests(SimpleTestCase): @setup({'ljust01': '{% autoescape off %}.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.{% endautoescape %}'}) def test_ljust01(self): output = self.engine.render_to_string('ljust01', {"a": "a&b", "b": mark_safe("a&b")}) self.assertEqual(output, ".a&b . .a&b .") @setup({'ljust02': '.{{ a|ljust:"5" }}. .{{ b|ljust:"5" }}.'}) def test_ljust02(self): output = self.engine.render_to_string('ljust02', {"a": "a&b", "b": mark_safe("a&b")}) self.assertEqual(output, ".a&b . .a&b .") class FunctionTests(SimpleTestCase): def test_ljust(self): self.assertEqual(ljust('test', 10), 'test ') self.assertEqual(ljust('test', 3), 'test') def test_less_than_string_length(self): self.assertEqual(ljust('test', 3), 'test') def test_non_string_input(self): self.assertEqual(ljust(123, 4), '123 ') Django-1.8.7/tests/template_tests/filter_tests/test_truncatewords_html.py0000664000175000017500000000310712625116214026607 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import truncatewords_html from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_truncate_zero(self): self.assertEqual(truncatewords_html('

    one two - three
    four
    five

    ', 0), '') def test_truncate(self): self.assertEqual( truncatewords_html('

    one two - three
    four
    five

    ', 2), '

    one two ...

    ', ) def test_truncate2(self): self.assertEqual( truncatewords_html('

    one two - three
    four
    five

    ', 4), '

    one two - three
    four ...

    ', ) def test_truncate3(self): self.assertEqual( truncatewords_html('

    one two - three
    four
    five

    ', 5), '

    one two - three
    four
    five

    ', ) def test_truncate4(self): self.assertEqual( truncatewords_html('

    one two - three
    four
    five

    ', 100), '

    one two - three
    four
    five

    ', ) def test_truncate_unicode(self): self.assertEqual(truncatewords_html('\xc5ngstr\xf6m was here', 1), '\xc5ngstr\xf6m ...') def test_truncate_complex(self): self.assertEqual( truncatewords_html('Buenos días! ¿Cómo está?', 3), 'Buenos días! ¿Cómo ...', ) Django-1.8.7/tests/template_tests/filter_tests/test_iriencode.py0000664000175000017500000000310312625116214024614 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.template.defaultfilters import iriencode, urlencode from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class IriencodeTests(SimpleTestCase): """ Ensure iriencode keeps safe strings. """ @setup({'iriencode01': '{{ url|iriencode }}'}) def test_iriencode01(self): output = self.engine.render_to_string('iriencode01', {'url': '?test=1&me=2'}) self.assertEqual(output, '?test=1&me=2') @setup({'iriencode02': '{% autoescape off %}{{ url|iriencode }}{% endautoescape %}'}) def test_iriencode02(self): output = self.engine.render_to_string('iriencode02', {'url': '?test=1&me=2'}) self.assertEqual(output, '?test=1&me=2') @setup({'iriencode03': '{{ url|iriencode }}'}) def test_iriencode03(self): output = self.engine.render_to_string('iriencode03', {'url': mark_safe('?test=1&me=2')}) self.assertEqual(output, '?test=1&me=2') @setup({'iriencode04': '{% autoescape off %}{{ url|iriencode }}{% endautoescape %}'}) def test_iriencode04(self): output = self.engine.render_to_string('iriencode04', {'url': mark_safe('?test=1&me=2')}) self.assertEqual(output, '?test=1&me=2') class FunctionTests(SimpleTestCase): def test_unicode(self): self.assertEqual(iriencode('S\xf8r-Tr\xf8ndelag'), 'S%C3%B8r-Tr%C3%B8ndelag') def test_urlencoded(self): self.assertEqual(iriencode(urlencode('fran\xe7ois & jill')), 'fran%C3%A7ois%20%26%20jill') Django-1.8.7/tests/template_tests/filter_tests/test_addslashes.py0000664000175000017500000000226212625116214024773 0ustar timtim00000000000000from django.template.defaultfilters import addslashes from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class AddslashesTests(SimpleTestCase): @setup({'addslashes01': '{% autoescape off %}{{ a|addslashes }} {{ b|addslashes }}{% endautoescape %}'}) def test_addslashes01(self): output = self.engine.render_to_string('addslashes01', {"a": "'", "b": mark_safe("'")}) self.assertEqual(output, r"\' \'") @setup({'addslashes02': '{{ a|addslashes }} {{ b|addslashes }}'}) def test_addslashes02(self): output = self.engine.render_to_string('addslashes02', {"a": "'", "b": mark_safe("'")}) self.assertEqual(output, r"<a>\' \'") class FunctionTests(SimpleTestCase): def test_quotes(self): self.assertEqual( addslashes('"double quotes" and \'single quotes\''), '\\"double quotes\\" and \\\'single quotes\\\'', ) def test_backslashes(self): self.assertEqual(addslashes(r'\ : backslashes, too'), '\\\\ : backslashes, too') def test_non_string_input(self): self.assertEqual(addslashes(123), '123') Django-1.8.7/tests/template_tests/filter_tests/test_default_if_none.py0000664000175000017500000000064612625116214026005 0ustar timtim00000000000000from django.template.defaultfilters import default_if_none from django.test import SimpleTestCase class FunctionTests(SimpleTestCase): def test_value(self): self.assertEqual(default_if_none("val", 'default'), 'val') def test_none(self): self.assertEqual(default_if_none(None, 'default'), 'default') def test_empty_string(self): self.assertEqual(default_if_none('', 'default'), '') Django-1.8.7/tests/template_tests/filter_tests/test_cut.py0000664000175000017500000000433512625116214023456 0ustar timtim00000000000000from django.template.defaultfilters import cut from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class CutTests(SimpleTestCase): @setup({'cut01': '{% autoescape off %}{{ a|cut:"x" }} {{ b|cut:"x" }}{% endautoescape %}'}) def test_cut01(self): output = self.engine.render_to_string('cut01', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "&y &y") @setup({'cut02': '{{ a|cut:"x" }} {{ b|cut:"x" }}'}) def test_cut02(self): output = self.engine.render_to_string('cut02', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "&y &y") @setup({'cut03': '{% autoescape off %}{{ a|cut:"&" }} {{ b|cut:"&" }}{% endautoescape %}'}) def test_cut03(self): output = self.engine.render_to_string('cut03', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "xy xamp;y") @setup({'cut04': '{{ a|cut:"&" }} {{ b|cut:"&" }}'}) def test_cut04(self): output = self.engine.render_to_string('cut04', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "xy xamp;y") # Passing ';' to cut can break existing HTML entities, so those strings # are auto-escaped. @setup({'cut05': '{% autoescape off %}{{ a|cut:";" }} {{ b|cut:";" }}{% endautoescape %}'}) def test_cut05(self): output = self.engine.render_to_string('cut05', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "x&y x&y") @setup({'cut06': '{{ a|cut:";" }} {{ b|cut:";" }}'}) def test_cut06(self): output = self.engine.render_to_string('cut06', {"a": "x&y", "b": mark_safe("x&y")}) self.assertEqual(output, "x&y x&ampy") class FunctionTests(SimpleTestCase): def test_character(self): self.assertEqual(cut('a string to be mangled', 'a'), ' string to be mngled') def test_characters(self): self.assertEqual(cut('a string to be mangled', 'ng'), 'a stri to be maled') def test_non_matching_string(self): self.assertEqual(cut('a string to be mangled', 'strings'), 'a string to be mangled') def test_non_string_input(self): self.assertEqual(cut(123, '2'), '13') Django-1.8.7/tests/template_tests/filter_tests/test_length.py0000664000175000017500000000355412625116214024146 0ustar timtim00000000000000from django.template.defaultfilters import length from django.test import SimpleTestCase from django.utils.safestring import mark_safe from ..utils import setup class LengthTests(SimpleTestCase): @setup({'length01': '{{ list|length }}'}) def test_length01(self): output = self.engine.render_to_string('length01', {'list': ['4', None, True, {}]}) self.assertEqual(output, '4') @setup({'length02': '{{ list|length }}'}) def test_length02(self): output = self.engine.render_to_string('length02', {'list': []}) self.assertEqual(output, '0') @setup({'length03': '{{ string|length }}'}) def test_length03(self): output = self.engine.render_to_string('length03', {'string': ''}) self.assertEqual(output, '0') @setup({'length04': '{{ string|length }}'}) def test_length04(self): output = self.engine.render_to_string('length04', {'string': 'django'}) self.assertEqual(output, '6') @setup({'length05': '{% if string|length == 6 %}Pass{% endif %}'}) def test_length05(self): output = self.engine.render_to_string('length05', {'string': mark_safe('django')}) self.assertEqual(output, 'Pass') # Invalid uses that should fail silently. @setup({'length06': '{{ int|length }}'}) def test_length06(self): output = self.engine.render_to_string('length06', {'int': 7}) self.assertEqual(output, '0') @setup({'length07': '{{ None|length }}'}) def test_length07(self): output = self.engine.render_to_string('length07', {'None': None}) self.assertEqual(output, '0') class FunctionTests(SimpleTestCase): def test_string(self): self.assertEqual(length('1234'), 4) def test_safestring(self): self.assertEqual(length(mark_safe('1234')), 4) def test_list(self): self.assertEqual(length([1, 2, 3, 4]), 4) Django-1.8.7/tests/template_tests/test_loaders.py0000664000175000017500000002036112625116214021602 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import os.path import sys import types import unittest from contextlib import contextmanager from django.template import Context, TemplateDoesNotExist from django.template.engine import Engine from django.test import SimpleTestCase, override_settings from django.utils import six from .utils import TEMPLATE_DIR try: import pkg_resources except ImportError: pkg_resources = None class CachedLoaderTests(SimpleTestCase): def create_engine(self, **kwargs): return Engine( loaders=[ ('django.template.loaders.cached.Loader', [ 'django.template.loaders.filesystem.Loader', ]), ], ) def test_templatedir_caching(self): """ #13573 -- Template directories should be part of the cache key. """ engine = self.create_engine() # Retrieve a template specifying a template directory to check t1, name = engine.find_template('test.html', (os.path.join(TEMPLATE_DIR, 'first'),)) # Now retrieve the same template name, but from a different directory t2, name = engine.find_template('test.html', (os.path.join(TEMPLATE_DIR, 'second'),)) # The two templates should not have the same content self.assertNotEqual(t1.render(Context({})), t2.render(Context({}))) def test_missing_template_is_cached(self): """ #19949 -- TemplateDoesNotExist exceptions should be cached. """ engine = self.create_engine() loader = engine.template_loaders[0] self.assertFalse('missing.html' in loader.template_cache) with self.assertRaises(TemplateDoesNotExist): loader.load_template("missing.html") self.assertEqual( loader.template_cache["missing.html"], TemplateDoesNotExist, "Cached loader failed to cache the TemplateDoesNotExist exception", ) def test_debug_nodelist_name(self): template_name = 'index.html' engine = Engine(dirs=[TEMPLATE_DIR], debug=True) template = engine.get_template(template_name) name = template.nodelist[0].source[0].name self.assertTrue( name.endswith(template_name), 'Template loaded through cached loader has incorrect name for debug page: %s' % template_name, ) template = engine.get_template(template_name) name = template.nodelist[0].source[0].name self.assertTrue( name.endswith(template_name), 'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name, ) @unittest.skipUnless(pkg_resources, 'setuptools is not installed') class EggLoaderTests(SimpleTestCase): @contextmanager def create_egg(self, name, resources): """ Creates a mock egg with a list of resources. name: The name of the module. resources: A dictionary of template names mapped to file-like objects. """ if six.PY2: name = name.encode('utf-8') class MockLoader(object): pass class MockProvider(pkg_resources.NullProvider): def __init__(self, module): pkg_resources.NullProvider.__init__(self, module) self.module = module def _has(self, path): return path in self.module._resources def _isdir(self, path): return False def get_resource_stream(self, manager, resource_name): return self.module._resources[resource_name] def _get(self, path): return self.module._resources[path].read() def _fn(self, base, resource_name): return os.path.normcase(resource_name) egg = types.ModuleType(name) egg.__loader__ = MockLoader() egg.__path__ = ['/some/bogus/path/'] egg.__file__ = '/some/bogus/path/__init__.pyc' egg._resources = resources sys.modules[name] = egg pkg_resources._provider_factories[MockLoader] = MockProvider try: yield finally: del sys.modules[name] del pkg_resources._provider_factories[MockLoader] def setUp(self): engine = Engine(loaders=[ 'django.template.loaders.eggs.Loader', ]) self.loader = engine.template_loaders[0] def test_existing(self): templates = { os.path.normcase('templates/y.html'): six.StringIO("y"), } with self.create_egg('egg', templates): with override_settings(INSTALLED_APPS=['egg']): contents, template_name = self.loader.load_template_source("y.html") self.assertEqual(contents, "y") self.assertEqual(template_name, "egg:egg:templates/y.html") def test_non_existing(self): """ Template loading fails if the template is not in the egg. """ with self.create_egg('egg', {}): with override_settings(INSTALLED_APPS=['egg']): with self.assertRaises(TemplateDoesNotExist): self.loader.load_template_source("not-existing.html") def test_not_installed(self): """ Template loading fails if the egg is not in INSTALLED_APPS. """ templates = { os.path.normcase('templates/y.html'): six.StringIO("y"), } with self.create_egg('egg', templates): with self.assertRaises(TemplateDoesNotExist): self.loader.load_template_source("y.html") class FileSystemLoaderTests(SimpleTestCase): def setUp(self): self.engine = Engine() @contextmanager def source_checker(self, dirs): loader = self.engine.template_loaders[0] def check_sources(path, expected_sources): expected_sources = [os.path.abspath(s) for s in expected_sources] self.assertEqual( list(loader.get_template_sources(path, dirs)), expected_sources, ) yield check_sources def test_directory_security(self): with self.source_checker(['/dir1', '/dir2']) as check_sources: check_sources('index.html', ['/dir1/index.html', '/dir2/index.html']) check_sources('/etc/passwd', []) check_sources('etc/passwd', ['/dir1/etc/passwd', '/dir2/etc/passwd']) check_sources('../etc/passwd', []) check_sources('../../../etc/passwd', []) check_sources('/dir1/index.html', ['/dir1/index.html']) check_sources('../dir2/index.html', ['/dir2/index.html']) check_sources('/dir1blah', []) check_sources('../dir1blah', []) def test_unicode_template_name(self): with self.source_checker(['/dir1', '/dir2']) as check_sources: # UTF-8 bytestrings are permitted. check_sources(b'\xc3\x85ngstr\xc3\xb6m', ['/dir1/Ã…ngström', '/dir2/Ã…ngström']) # Unicode strings are permitted. check_sources('Ã…ngström', ['/dir1/Ã…ngström', '/dir2/Ã…ngström']) def test_utf8_bytestring(self): """ Invalid UTF-8 encoding in bytestrings should raise a useful error """ engine = Engine() loader = engine.template_loaders[0] with self.assertRaises(UnicodeDecodeError): list(loader.get_template_sources(b'\xc3\xc3', ['/dir1'])) def test_unicode_dir_name(self): with self.source_checker([b'/Stra\xc3\x9fe']) as check_sources: check_sources('Ã…ngström', ['/Straße/Ã…ngström']) check_sources(b'\xc3\x85ngstr\xc3\xb6m', ['/Straße/Ã…ngström']) @unittest.skipUnless( os.path.normcase('/TEST') == os.path.normpath('/test'), "This test only runs on case-sensitive file systems.", ) def test_case_sensitivity(self): with self.source_checker(['/dir1', '/DIR2']) as check_sources: check_sources('index.html', ['/dir1/index.html', '/DIR2/index.html']) check_sources('/DIR1/index.HTML', ['/DIR1/index.HTML']) class AppDirectoriesLoaderTest(FileSystemLoaderTests): def setUp(self): self.engine = Engine( loaders=['django.template.loaders.app_directories.Loader'], ) Django-1.8.7/tests/template_tests/test_parser.py0000664000175000017500000001243112625116214021444 0ustar timtim00000000000000""" Testing some internals of the template processing. These are *not* examples to be copied in user code. """ from __future__ import unicode_literals from unittest import TestCase from django.template import Library, Template, TemplateSyntaxError from django.template.base import ( TOKEN_BLOCK, FilterExpression, Parser, Token, TokenParser, Variable, ) from django.test import override_settings from django.utils import six class ParserTests(TestCase): def test_token_smart_split(self): """ #7027 -- _() syntax should work with spaces """ token = Token(TOKEN_BLOCK, 'sometag _("Page not found") value|yesno:_("yes,no")') split = token.split_contents() self.assertEqual(split, ["sometag", '_("Page not found")', 'value|yesno:_("yes,no")']) def test_token_parsing(self): # Tests for TokenParser behavior in the face of quoted strings with # spaces. p = TokenParser("tag thevar|filter sometag") self.assertEqual(p.tagname, "tag") self.assertEqual(p.value(), "thevar|filter") self.assertTrue(p.more()) self.assertEqual(p.tag(), "sometag") self.assertFalse(p.more()) p = TokenParser('tag "a value"|filter sometag') self.assertEqual(p.tagname, "tag") self.assertEqual(p.value(), '"a value"|filter') self.assertTrue(p.more()) self.assertEqual(p.tag(), "sometag") self.assertFalse(p.more()) p = TokenParser("tag 'a value'|filter sometag") self.assertEqual(p.tagname, "tag") self.assertEqual(p.value(), "'a value'|filter") self.assertTrue(p.more()) self.assertEqual(p.tag(), "sometag") self.assertFalse(p.more()) def test_filter_parsing(self): c = {"article": {"section": "News"}} p = Parser("") def fe_test(s, val): self.assertEqual(FilterExpression(s, p).resolve(c), val) fe_test("article.section", "News") fe_test("article.section|upper", "NEWS") fe_test('"News"', "News") fe_test("'News'", "News") fe_test(r'"Some \"Good\" News"', 'Some "Good" News') fe_test(r'"Some \"Good\" News"', 'Some "Good" News') fe_test(r"'Some \'Bad\' News'", "Some 'Bad' News") fe = FilterExpression(r'"Some \"Good\" News"', p) self.assertEqual(fe.filters, []) self.assertEqual(fe.var, 'Some "Good" News') # Filtered variables should reject access of attributes beginning with # underscores. self.assertRaises(TemplateSyntaxError, FilterExpression, "article._hidden|upper", p) def test_variable_parsing(self): c = {"article": {"section": "News"}} self.assertEqual(Variable("article.section").resolve(c), "News") self.assertEqual(Variable('"News"').resolve(c), "News") self.assertEqual(Variable("'News'").resolve(c), "News") # Translated strings are handled correctly. self.assertEqual(Variable("_(article.section)").resolve(c), "News") self.assertEqual(Variable('_("Good News")').resolve(c), "Good News") self.assertEqual(Variable("_('Better News')").resolve(c), "Better News") # Escaped quotes work correctly as well. self.assertEqual( Variable(r'"Some \"Good\" News"').resolve(c), 'Some "Good" News' ) self.assertEqual( Variable(r"'Some \'Better\' News'").resolve(c), "Some 'Better' News" ) # Variables should reject access of attributes beginning with # underscores. self.assertRaises(TemplateSyntaxError, Variable, "article._hidden") # Variables should raise on non string type with six.assertRaisesRegex(self, TypeError, "Variable must be a string or number, got <(class|type) 'dict'>"): Variable({}) @override_settings(DEBUG=True) def test_compile_filter_error(self): # regression test for #19819 msg = "Could not parse the remainder: '@bar' from 'foo@bar'" with six.assertRaisesRegex(self, TemplateSyntaxError, msg) as cm: Template("{% if 1 %}{{ foo@bar }}{% endif %}") self.assertEqual(cm.exception.django_template_source[1], (10, 23)) def test_filter_args_count(self): p = Parser("") l = Library() @l.filter def no_arguments(value): pass @l.filter def one_argument(value, arg): pass @l.filter def one_opt_argument(value, arg=False): pass @l.filter def two_arguments(value, arg, arg2): pass @l.filter def two_one_opt_arg(value, arg, arg2=False): pass p.add_library(l) for expr in ( '1|no_arguments:"1"', '1|two_arguments', '1|two_arguments:"1"', '1|two_one_opt_arg', ): with self.assertRaises(TemplateSyntaxError): FilterExpression(expr, p) for expr in ( # Correct number of arguments '1|no_arguments', '1|one_argument:"1"', # One optional '1|one_opt_argument', '1|one_opt_argument:"1"', # Not supplying all '1|two_one_opt_arg:"1"', ): FilterExpression(expr, p) Django-1.8.7/tests/template_tests/jinja2/0000775000175000017500000000000012625116242017714 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/jinja2/template_tests/0000775000175000017500000000000012625116243022752 5ustar timtim00000000000000Django-1.8.7/tests/template_tests/jinja2/template_tests/using.html0000664000175000017500000000000712625116214024760 0ustar timtim00000000000000Jinja2 Django-1.8.7/tests/template_tests/test_custom.py0000664000175000017500000006224112625116214021466 0ustar timtim00000000000000from __future__ import unicode_literals import os from django.template import Context, Engine, Template, TemplateSyntaxError from django.template.base import Node from django.test import SimpleTestCase, ignore_warnings from django.test.utils import extend_sys_path from django.utils import six from django.utils.deprecation import RemovedInDjango110Warning from .templatetags import custom, inclusion from .utils import ROOT class CustomFilterTests(SimpleTestCase): def test_filter(self): t = Template("{% load custom %}{{ string|trim:5 }}") self.assertEqual( t.render(Context({"string": "abcdefghijklmnopqrstuvwxyz"})), "abcde" ) class CustomTagTests(SimpleTestCase): def verify_tag(self, tag, name): self.assertEqual(tag.__name__, name) self.assertEqual(tag.__doc__, 'Expected %s __doc__' % name) self.assertEqual(tag.__dict__['anything'], 'Expected %s __dict__' % name) def test_simple_tags(self): c = Context({'value': 42}) t = Template('{% load custom %}{% no_params %}') self.assertEqual(t.render(c), 'no_params - Expected result') t = Template('{% load custom %}{% one_param 37 %}') self.assertEqual(t.render(c), 'one_param - Expected result: 37') t = Template('{% load custom %}{% explicit_no_context 37 %}') self.assertEqual(t.render(c), 'explicit_no_context - Expected result: 37') t = Template('{% load custom %}{% no_params_with_context %}') self.assertEqual(t.render(c), 'no_params_with_context - Expected result (context value: 42)') t = Template('{% load custom %}{% params_and_context 37 %}') self.assertEqual(t.render(c), 'params_and_context - Expected result (context value: 42): 37') t = Template('{% load custom %}{% simple_two_params 37 42 %}') self.assertEqual(t.render(c), 'simple_two_params - Expected result: 37, 42') t = Template('{% load custom %}{% simple_one_default 37 %}') self.assertEqual(t.render(c), 'simple_one_default - Expected result: 37, hi') t = Template('{% load custom %}{% simple_one_default 37 two="hello" %}') self.assertEqual(t.render(c), 'simple_one_default - Expected result: 37, hello') t = Template('{% load custom %}{% simple_one_default one=99 two="hello" %}') self.assertEqual(t.render(c), 'simple_one_default - Expected result: 99, hello') six.assertRaisesRegex(self, TemplateSyntaxError, "'simple_one_default' received unexpected keyword argument 'three'", Template, '{% load custom %}{% simple_one_default 99 two="hello" three="foo" %}') t = Template('{% load custom %}{% simple_one_default 37 42 %}') self.assertEqual(t.render(c), 'simple_one_default - Expected result: 37, 42') t = Template('{% load custom %}{% simple_unlimited_args 37 %}') self.assertEqual(t.render(c), 'simple_unlimited_args - Expected result: 37, hi') t = Template('{% load custom %}{% simple_unlimited_args 37 42 56 89 %}') self.assertEqual(t.render(c), 'simple_unlimited_args - Expected result: 37, 42, 56, 89') t = Template('{% load custom %}{% simple_only_unlimited_args %}') self.assertEqual(t.render(c), 'simple_only_unlimited_args - Expected result: ') t = Template('{% load custom %}{% simple_only_unlimited_args 37 42 56 89 %}') self.assertEqual(t.render(c), 'simple_only_unlimited_args - Expected result: 37, 42, 56, 89') six.assertRaisesRegex(self, TemplateSyntaxError, "'simple_two_params' received too many positional arguments", Template, '{% load custom %}{% simple_two_params 37 42 56 %}') six.assertRaisesRegex(self, TemplateSyntaxError, "'simple_one_default' received too many positional arguments", Template, '{% load custom %}{% simple_one_default 37 42 56 %}') t = Template('{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}') self.assertEqual(t.render(c), 'simple_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4') six.assertRaisesRegex(self, TemplateSyntaxError, "'simple_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}') six.assertRaisesRegex(self, TemplateSyntaxError, "'simple_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", Template, '{% load custom %}{% simple_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}') def test_simple_tag_registration(self): # Test that the decorators preserve the decorated function's docstring, name and attributes. self.verify_tag(custom.no_params, 'no_params') self.verify_tag(custom.one_param, 'one_param') self.verify_tag(custom.explicit_no_context, 'explicit_no_context') self.verify_tag(custom.no_params_with_context, 'no_params_with_context') self.verify_tag(custom.params_and_context, 'params_and_context') self.verify_tag(custom.simple_unlimited_args_kwargs, 'simple_unlimited_args_kwargs') self.verify_tag(custom.simple_tag_without_context_parameter, 'simple_tag_without_context_parameter') def test_simple_tag_missing_context(self): # The 'context' parameter must be present when takes_context is True six.assertRaisesRegex(self, TemplateSyntaxError, "'simple_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", Template, '{% load custom %}{% simple_tag_without_context_parameter 123 %}') def test_inclusion_tags(self): c = Context({'value': 42}) t = Template('{% load inclusion %}{% inclusion_no_params %}') self.assertEqual(t.render(c), 'inclusion_no_params - Expected result\n') t = Template('{% load inclusion %}{% inclusion_one_param 37 %}') self.assertEqual(t.render(c), 'inclusion_one_param - Expected result: 37\n') t = Template('{% load inclusion %}{% inclusion_explicit_no_context 37 %}') self.assertEqual(t.render(c), 'inclusion_explicit_no_context - Expected result: 37\n') t = Template('{% load inclusion %}{% inclusion_no_params_with_context %}') self.assertEqual(t.render(c), 'inclusion_no_params_with_context - Expected result (context value: 42)\n') t = Template('{% load inclusion %}{% inclusion_params_and_context 37 %}') self.assertEqual(t.render(c), 'inclusion_params_and_context - Expected result (context value: 42): 37\n') t = Template('{% load inclusion %}{% inclusion_two_params 37 42 %}') self.assertEqual(t.render(c), 'inclusion_two_params - Expected result: 37, 42\n') t = Template('{% load inclusion %}{% inclusion_one_default 37 %}') self.assertEqual(t.render(c), 'inclusion_one_default - Expected result: 37, hi\n') t = Template('{% load inclusion %}{% inclusion_one_default 37 two="hello" %}') self.assertEqual(t.render(c), 'inclusion_one_default - Expected result: 37, hello\n') t = Template('{% load inclusion %}{% inclusion_one_default one=99 two="hello" %}') self.assertEqual(t.render(c), 'inclusion_one_default - Expected result: 99, hello\n') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_one_default' received unexpected keyword argument 'three'", Template, '{% load inclusion %}{% inclusion_one_default 99 two="hello" three="foo" %}') t = Template('{% load inclusion %}{% inclusion_one_default 37 42 %}') self.assertEqual(t.render(c), 'inclusion_one_default - Expected result: 37, 42\n') t = Template('{% load inclusion %}{% inclusion_unlimited_args 37 %}') self.assertEqual(t.render(c), 'inclusion_unlimited_args - Expected result: 37, hi\n') t = Template('{% load inclusion %}{% inclusion_unlimited_args 37 42 56 89 %}') self.assertEqual(t.render(c), 'inclusion_unlimited_args - Expected result: 37, 42, 56, 89\n') t = Template('{% load inclusion %}{% inclusion_only_unlimited_args %}') self.assertEqual(t.render(c), 'inclusion_only_unlimited_args - Expected result: \n') t = Template('{% load inclusion %}{% inclusion_only_unlimited_args 37 42 56 89 %}') self.assertEqual(t.render(c), 'inclusion_only_unlimited_args - Expected result: 37, 42, 56, 89\n') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_two_params' received too many positional arguments", Template, '{% load inclusion %}{% inclusion_two_params 37 42 56 %}') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_one_default' received too many positional arguments", Template, '{% load inclusion %}{% inclusion_one_default 37 42 56 %}') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_one_default' did not receive value\(s\) for the argument\(s\): 'one'", Template, '{% load inclusion %}{% inclusion_one_default %}') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'", Template, '{% load inclusion %}{% inclusion_unlimited_args %}') t = Template('{% load inclusion %}{% inclusion_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 %}') self.assertEqual(t.render(c), 'inclusion_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4\n') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", Template, '{% load inclusion %}{% inclusion_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 %}') six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", Template, '{% load inclusion %}{% inclusion_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" %}') def test_include_tag_missing_context(self): # The 'context' parameter must be present when takes_context is True six.assertRaisesRegex(self, TemplateSyntaxError, "'inclusion_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", Template, '{% load inclusion %}{% inclusion_tag_without_context_parameter 123 %}') def test_inclusion_tags_from_template(self): c = Context({'value': 42}) t = Template('{% load inclusion %}{% inclusion_no_params_from_template %}') self.assertEqual(t.render(c), 'inclusion_no_params_from_template - Expected result\n') t = Template('{% load inclusion %}{% inclusion_one_param_from_template 37 %}') self.assertEqual(t.render(c), 'inclusion_one_param_from_template - Expected result: 37\n') t = Template('{% load inclusion %}{% inclusion_explicit_no_context_from_template 37 %}') self.assertEqual(t.render(c), 'inclusion_explicit_no_context_from_template - Expected result: 37\n') t = Template('{% load inclusion %}{% inclusion_no_params_with_context_from_template %}') self.assertEqual(t.render(c), 'inclusion_no_params_with_context_from_template - Expected result (context value: 42)\n') t = Template('{% load inclusion %}{% inclusion_params_and_context_from_template 37 %}') self.assertEqual(t.render(c), 'inclusion_params_and_context_from_template - Expected result (context value: 42): 37\n') t = Template('{% load inclusion %}{% inclusion_two_params_from_template 37 42 %}') self.assertEqual(t.render(c), 'inclusion_two_params_from_template - Expected result: 37, 42\n') t = Template('{% load inclusion %}{% inclusion_one_default_from_template 37 %}') self.assertEqual(t.render(c), 'inclusion_one_default_from_template - Expected result: 37, hi\n') t = Template('{% load inclusion %}{% inclusion_one_default_from_template 37 42 %}') self.assertEqual(t.render(c), 'inclusion_one_default_from_template - Expected result: 37, 42\n') t = Template('{% load inclusion %}{% inclusion_unlimited_args_from_template 37 %}') self.assertEqual(t.render(c), 'inclusion_unlimited_args_from_template - Expected result: 37, hi\n') t = Template('{% load inclusion %}{% inclusion_unlimited_args_from_template 37 42 56 89 %}') self.assertEqual(t.render(c), 'inclusion_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n') t = Template('{% load inclusion %}{% inclusion_only_unlimited_args_from_template %}') self.assertEqual(t.render(c), 'inclusion_only_unlimited_args_from_template - Expected result: \n') t = Template('{% load inclusion %}{% inclusion_only_unlimited_args_from_template 37 42 56 89 %}') self.assertEqual(t.render(c), 'inclusion_only_unlimited_args_from_template - Expected result: 37, 42, 56, 89\n') def test_inclusion_tag_registration(self): # Test that the decorators preserve the decorated function's docstring, name and attributes. self.verify_tag(inclusion.inclusion_no_params, 'inclusion_no_params') self.verify_tag(inclusion.inclusion_one_param, 'inclusion_one_param') self.verify_tag(inclusion.inclusion_explicit_no_context, 'inclusion_explicit_no_context') self.verify_tag(inclusion.inclusion_no_params_with_context, 'inclusion_no_params_with_context') self.verify_tag(inclusion.inclusion_params_and_context, 'inclusion_params_and_context') self.verify_tag(inclusion.inclusion_two_params, 'inclusion_two_params') self.verify_tag(inclusion.inclusion_one_default, 'inclusion_one_default') self.verify_tag(inclusion.inclusion_unlimited_args, 'inclusion_unlimited_args') self.verify_tag(inclusion.inclusion_only_unlimited_args, 'inclusion_only_unlimited_args') self.verify_tag(inclusion.inclusion_tag_without_context_parameter, 'inclusion_tag_without_context_parameter') self.verify_tag(inclusion.inclusion_tag_use_l10n, 'inclusion_tag_use_l10n') self.verify_tag(inclusion.inclusion_tag_current_app, 'inclusion_tag_current_app') self.verify_tag(inclusion.inclusion_unlimited_args_kwargs, 'inclusion_unlimited_args_kwargs') @ignore_warnings(category=RemovedInDjango110Warning) def test_15070_current_app(self): """ Test that inclusion tag passes down `current_app` of context to the Context of the included/rendered template as well. """ c = Context({}) t = Template('{% load inclusion %}{% inclusion_tag_current_app %}') self.assertEqual(t.render(c).strip(), 'None') # That part produces the deprecation warning c = Context({}, current_app='advanced') self.assertEqual(t.render(c).strip(), 'advanced') def test_15070_use_l10n(self): """ Test that inclusion tag passes down `use_l10n` of context to the Context of the included/rendered template as well. """ c = Context({}) t = Template('{% load inclusion %}{% inclusion_tag_use_l10n %}') self.assertEqual(t.render(c).strip(), 'None') c.use_l10n = True self.assertEqual(t.render(c).strip(), 'True') def test_no_render_side_effect(self): """ #23441 -- InclusionNode shouldn't modify its nodelist at render time. """ engine = Engine(app_dirs=True) template = engine.from_string('{% load inclusion %}{% inclusion_no_params %}') count = template.nodelist.get_nodes_by_type(Node) template.render(Context({})) self.assertEqual(template.nodelist.get_nodes_by_type(Node), count) def test_render_context_is_cleared(self): """ #24555 -- InclusionNode should push and pop the render_context stack when rendering. Otherwise, leftover values such as blocks from extending can interfere with subsequent rendering. """ engine = Engine(app_dirs=True) template = engine.from_string('{% load inclusion %}{% inclusion_extends1 %}{% inclusion_extends2 %}') self.assertEqual(template.render(Context({})).strip(), 'one\ntwo') def test_assignment_tags(self): c = Context({'value': 42}) t = Template('{% load custom %}{% assignment_no_params as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_no_params - Expected result') t = Template('{% load custom %}{% assignment_one_param 37 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_one_param - Expected result: 37') t = Template('{% load custom %}{% assignment_explicit_no_context 37 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_explicit_no_context - Expected result: 37') t = Template('{% load custom %}{% assignment_no_params_with_context as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_no_params_with_context - Expected result (context value: 42)') t = Template('{% load custom %}{% assignment_params_and_context 37 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_params_and_context - Expected result (context value: 42): 37') t = Template('{% load custom %}{% assignment_two_params 37 42 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_two_params - Expected result: 37, 42') t = Template('{% load custom %}{% assignment_one_default 37 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_one_default - Expected result: 37, hi') t = Template('{% load custom %}{% assignment_one_default 37 two="hello" as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_one_default - Expected result: 37, hello') t = Template('{% load custom %}{% assignment_one_default one=99 two="hello" as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_one_default - Expected result: 99, hello') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_one_default' received unexpected keyword argument 'three'", Template, '{% load custom %}{% assignment_one_default 99 two="hello" three="foo" as var %}') t = Template('{% load custom %}{% assignment_one_default 37 42 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_one_default - Expected result: 37, 42') t = Template('{% load custom %}{% assignment_unlimited_args 37 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_unlimited_args - Expected result: 37, hi') t = Template('{% load custom %}{% assignment_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_unlimited_args - Expected result: 37, 42, 56, 89') t = Template('{% load custom %}{% assignment_only_unlimited_args as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_only_unlimited_args - Expected result: ') t = Template('{% load custom %}{% assignment_only_unlimited_args 37 42 56 89 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_only_unlimited_args - Expected result: 37, 42, 56, 89') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", Template, '{% load custom %}{% assignment_one_param 37 %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", Template, '{% load custom %}{% assignment_one_param 37 as %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_one_param' tag takes at least 2 arguments and the second last argument must be 'as'", Template, '{% load custom %}{% assignment_one_param 37 ass var %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_two_params' received too many positional arguments", Template, '{% load custom %}{% assignment_two_params 37 42 56 as var %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_one_default' received too many positional arguments", Template, '{% load custom %}{% assignment_one_default 37 42 56 as var %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_one_default' did not receive value\(s\) for the argument\(s\): 'one'", Template, '{% load custom %}{% assignment_one_default as var %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_unlimited_args' did not receive value\(s\) for the argument\(s\): 'one'", Template, '{% load custom %}{% assignment_unlimited_args as var %}The result is: {{ var }}') t = Template('{% load custom %}{% assignment_unlimited_args_kwargs 37 40|add:2 56 eggs="scrambled" four=1|add:3 as var %}The result is: {{ var }}') self.assertEqual(t.render(c), 'The result is: assignment_unlimited_args_kwargs - Expected result: 37, 42, 56 / eggs=scrambled, four=4') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_unlimited_args_kwargs' received some positional argument\(s\) after some keyword argument\(s\)", Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 40|add:2 eggs="scrambled" 56 four=1|add:3 as var %}The result is: {{ var }}') six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_unlimited_args_kwargs' received multiple values for keyword argument 'eggs'", Template, '{% load custom %}{% assignment_unlimited_args_kwargs 37 eggs="scrambled" eggs="scrambled" as var %}The result is: {{ var }}') def test_assignment_tag_registration(self): # Test that the decorators preserve the decorated function's docstring, name and attributes. self.verify_tag(custom.assignment_no_params, 'assignment_no_params') self.verify_tag(custom.assignment_one_param, 'assignment_one_param') self.verify_tag(custom.assignment_explicit_no_context, 'assignment_explicit_no_context') self.verify_tag(custom.assignment_no_params_with_context, 'assignment_no_params_with_context') self.verify_tag(custom.assignment_params_and_context, 'assignment_params_and_context') self.verify_tag(custom.assignment_one_default, 'assignment_one_default') self.verify_tag(custom.assignment_two_params, 'assignment_two_params') self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args') self.verify_tag(custom.assignment_only_unlimited_args, 'assignment_only_unlimited_args') self.verify_tag(custom.assignment_unlimited_args, 'assignment_unlimited_args') self.verify_tag(custom.assignment_unlimited_args_kwargs, 'assignment_unlimited_args_kwargs') self.verify_tag(custom.assignment_tag_without_context_parameter, 'assignment_tag_without_context_parameter') def test_assignment_tag_missing_context(self): # The 'context' parameter must be present when takes_context is True six.assertRaisesRegex(self, TemplateSyntaxError, "'assignment_tag_without_context_parameter' is decorated with takes_context=True so it must have a first argument of 'context'", Template, '{% load custom %}{% assignment_tag_without_context_parameter 123 as var %}') class TemplateTagLoadingTests(SimpleTestCase): def setUp(self): self.egg_dir = os.path.join(ROOT, 'eggs') def test_load_error(self): ttext = "{% load broken_tag %}" with self.assertRaises(TemplateSyntaxError) as e: Template(ttext) self.assertIn('ImportError', e.exception.args[0]) self.assertIn('Xtemplate', e.exception.args[0]) def test_load_error_egg(self): ttext = "{% load broken_egg %}" egg_name = '%s/tagsegg.egg' % self.egg_dir with extend_sys_path(egg_name): with self.assertRaises(TemplateSyntaxError): with self.settings(INSTALLED_APPS=['tagsegg']): Template(ttext) try: with self.settings(INSTALLED_APPS=['tagsegg']): Template(ttext) except TemplateSyntaxError as e: self.assertIn('ImportError', e.args[0]) self.assertIn('Xtemplate', e.args[0]) def test_load_working_egg(self): ttext = "{% load working_egg %}" egg_name = '%s/tagsegg.egg' % self.egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['tagsegg']): Template(ttext) Django-1.8.7/tests/template_tests/test_response.py0000664000175000017500000003410012625116214022003 0ustar timtim00000000000000from __future__ import unicode_literals import pickle import time from datetime import datetime from django.conf import settings from django.template import Context, engines from django.template.response import ( ContentNotRenderedError, SimpleTemplateResponse, TemplateResponse, ) from django.test import ( RequestFactory, SimpleTestCase, ignore_warnings, override_settings, ) from django.test.utils import require_jinja2 from django.utils.deprecation import RemovedInDjango110Warning from .utils import TEMPLATE_DIR def test_processor(request): return {'processors': 'yes'} test_processor_name = 'template_tests.test_response.test_processor' # A test middleware that installs a temporary URLConf class CustomURLConfMiddleware(object): def process_request(self, request): request.urlconf = 'template_tests.alternate_urls' class SimpleTemplateResponseTest(SimpleTestCase): def _response(self, template='foo', *args, **kwargs): template = engines['django'].from_string(template) return SimpleTemplateResponse(template, *args, **kwargs) def test_template_resolving(self): response = SimpleTemplateResponse('first/test.html') response.render() self.assertEqual(response.content, b'First template\n') templates = ['foo.html', 'second/test.html', 'first/test.html'] response = SimpleTemplateResponse(templates) response.render() self.assertEqual(response.content, b'Second template\n') response = self._response() response.render() self.assertEqual(response.content, b'foo') def test_explicit_baking(self): # explicit baking response = self._response() self.assertFalse(response.is_rendered) response.render() self.assertTrue(response.is_rendered) def test_render(self): # response is not re-rendered without the render call response = self._response().render() self.assertEqual(response.content, b'foo') # rebaking doesn't change the rendered content template = engines['django'].from_string('bar{{ baz }}') response.template_name = template response.render() self.assertEqual(response.content, b'foo') # but rendered content can be overridden by manually # setting content response.content = 'bar' self.assertEqual(response.content, b'bar') def test_iteration_unrendered(self): # unrendered response raises an exception on iteration response = self._response() self.assertFalse(response.is_rendered) def iteration(): for x in response: pass self.assertRaises(ContentNotRenderedError, iteration) self.assertFalse(response.is_rendered) def test_iteration_rendered(self): # iteration works for rendered responses response = self._response().render() res = [x for x in response] self.assertEqual(res, [b'foo']) def test_content_access_unrendered(self): # unrendered response raises an exception when content is accessed response = self._response() self.assertFalse(response.is_rendered) self.assertRaises(ContentNotRenderedError, lambda: response.content) self.assertFalse(response.is_rendered) def test_content_access_rendered(self): # rendered response content can be accessed response = self._response().render() self.assertEqual(response.content, b'foo') def test_set_content(self): # content can be overridden response = self._response() self.assertFalse(response.is_rendered) response.content = 'spam' self.assertTrue(response.is_rendered) self.assertEqual(response.content, b'spam') response.content = 'baz' self.assertEqual(response.content, b'baz') def test_dict_context(self): response = self._response('{{ foo }}{{ processors }}', {'foo': 'bar'}) self.assertEqual(response.context_data, {'foo': 'bar'}) response.render() self.assertEqual(response.content, b'bar') @ignore_warnings(category=RemovedInDjango110Warning) def test_context_instance(self): response = self._response('{{ foo }}{{ processors }}', Context({'foo': 'bar'})) self.assertEqual(response.context_data.__class__, Context) response.render() self.assertEqual(response.content, b'bar') def test_kwargs(self): response = self._response(content_type='application/json', status=504) self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.status_code, 504) def test_args(self): response = SimpleTemplateResponse('', {}, 'application/json', 504) self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.status_code, 504) @require_jinja2 def test_using(self): response = SimpleTemplateResponse('template_tests/using.html').render() self.assertEqual(response.content, b'DTL\n') response = SimpleTemplateResponse('template_tests/using.html', using='django').render() self.assertEqual(response.content, b'DTL\n') response = SimpleTemplateResponse('template_tests/using.html', using='jinja2').render() self.assertEqual(response.content, b'Jinja2\n') def test_post_callbacks(self): "Rendering a template response triggers the post-render callbacks" post = [] def post1(obj): post.append('post1') def post2(obj): post.append('post2') response = SimpleTemplateResponse('first/test.html', {}) response.add_post_render_callback(post1) response.add_post_render_callback(post2) # When the content is rendered, all the callbacks are invoked, too. response.render() self.assertEqual(response.content, b'First template\n') self.assertEqual(post, ['post1', 'post2']) def test_pickling(self): # Create a template response. The context is # known to be unpickleable (e.g., a function). response = SimpleTemplateResponse('first/test.html', { 'value': 123, 'fn': datetime.now, }) self.assertRaises(ContentNotRenderedError, pickle.dumps, response) # But if we render the response, we can pickle it. response.render() pickled_response = pickle.dumps(response) unpickled_response = pickle.loads(pickled_response) self.assertEqual(unpickled_response.content, response.content) self.assertEqual(unpickled_response['content-type'], response['content-type']) self.assertEqual(unpickled_response.status_code, response.status_code) # ...and the unpickled response doesn't have the # template-related attributes, so it can't be re-rendered template_attrs = ('template_name', 'context_data', '_post_render_callbacks') for attr in template_attrs: self.assertFalse(hasattr(unpickled_response, attr)) # ...and requesting any of those attributes raises an exception for attr in template_attrs: with self.assertRaises(AttributeError): getattr(unpickled_response, attr) def test_repickling(self): response = SimpleTemplateResponse('first/test.html', { 'value': 123, 'fn': datetime.now, }) self.assertRaises(ContentNotRenderedError, pickle.dumps, response) response.render() pickled_response = pickle.dumps(response) unpickled_response = pickle.loads(pickled_response) pickle.dumps(unpickled_response) def test_pickling_cookie(self): response = SimpleTemplateResponse('first/test.html', { 'value': 123, 'fn': datetime.now, }) response.cookies['key'] = 'value' response.render() pickled_response = pickle.dumps(response, pickle.HIGHEST_PROTOCOL) unpickled_response = pickle.loads(pickled_response) self.assertEqual(unpickled_response.cookies['key'].value, 'value') @override_settings(TEMPLATES=[{ 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [TEMPLATE_DIR], 'OPTIONS': { 'context_processors': [test_processor_name], }, }]) class TemplateResponseTest(SimpleTestCase): def setUp(self): self.factory = RequestFactory() def _response(self, template='foo', *args, **kwargs): self._request = self.factory.get('/') template = engines['django'].from_string(template) return TemplateResponse(self._request, template, *args, **kwargs) def test_render(self): response = self._response('{{ foo }}{{ processors }}').render() self.assertEqual(response.content, b'yes') def test_render_with_requestcontext(self): response = self._response('{{ foo }}{{ processors }}', {'foo': 'bar'}).render() self.assertEqual(response.content, b'baryes') @ignore_warnings(category=RemovedInDjango110Warning) def test_render_with_context(self): response = self._response('{{ foo }}{{ processors }}', Context({'foo': 'bar'})).render() self.assertEqual(response.content, b'bar') def test_context_processor_priority(self): # context processors should be overridden by passed-in context response = self._response('{{ foo }}{{ processors }}', {'processors': 'no'}).render() self.assertEqual(response.content, b'no') def test_kwargs(self): response = self._response(content_type='application/json', status=504) self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.status_code, 504) def test_args(self): response = TemplateResponse(self.factory.get('/'), '', {}, 'application/json', 504) self.assertEqual(response['content-type'], 'application/json') self.assertEqual(response.status_code, 504) @require_jinja2 def test_using(self): request = self.factory.get('/') response = TemplateResponse(request, 'template_tests/using.html').render() self.assertEqual(response.content, b'DTL\n') response = TemplateResponse(request, 'template_tests/using.html', using='django').render() self.assertEqual(response.content, b'DTL\n') response = TemplateResponse(request, 'template_tests/using.html', using='jinja2').render() self.assertEqual(response.content, b'Jinja2\n') @ignore_warnings(category=RemovedInDjango110Warning) def test_custom_app(self): self._response('{{ foo }}', current_app="foobar") self.assertEqual(self._request.current_app, 'foobar') def test_pickling(self): # Create a template response. The context is # known to be unpickleable (e.g., a function). response = TemplateResponse(self.factory.get('/'), 'first/test.html', { 'value': 123, 'fn': datetime.now, } ) self.assertRaises(ContentNotRenderedError, pickle.dumps, response) # But if we render the response, we can pickle it. response.render() pickled_response = pickle.dumps(response) unpickled_response = pickle.loads(pickled_response) self.assertEqual(unpickled_response.content, response.content) self.assertEqual(unpickled_response['content-type'], response['content-type']) self.assertEqual(unpickled_response.status_code, response.status_code) # ...and the unpickled response doesn't have the # template-related attributes, so it can't be re-rendered template_attrs = ('template_name', 'context_data', '_post_render_callbacks', '_request', '_current_app') for attr in template_attrs: self.assertFalse(hasattr(unpickled_response, attr)) # ...and requesting any of those attributes raises an exception for attr in template_attrs: with self.assertRaises(AttributeError): getattr(unpickled_response, attr) def test_repickling(self): response = SimpleTemplateResponse('first/test.html', { 'value': 123, 'fn': datetime.now, }) self.assertRaises(ContentNotRenderedError, pickle.dumps, response) response.render() pickled_response = pickle.dumps(response) unpickled_response = pickle.loads(pickled_response) pickle.dumps(unpickled_response) @override_settings( MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) + [ 'template_tests.test_response.CustomURLConfMiddleware' ], ROOT_URLCONF='template_tests.urls', ) class CustomURLConfTest(SimpleTestCase): def test_custom_urlconf(self): response = self.client.get('/template_response_view/') self.assertEqual(response.status_code, 200) self.assertContains(response, 'This is where you can find the snark: /snark/') @override_settings( CACHE_MIDDLEWARE_SECONDS=2.0, MIDDLEWARE_CLASSES=list(settings.MIDDLEWARE_CLASSES) + [ 'django.middleware.cache.FetchFromCacheMiddleware', 'django.middleware.cache.UpdateCacheMiddleware', ], ROOT_URLCONF='template_tests.alternate_urls', ) class CacheMiddlewareTest(SimpleTestCase): def test_middleware_caching(self): response = self.client.get('/template_response_view/') self.assertEqual(response.status_code, 200) time.sleep(1.0) response2 = self.client.get('/template_response_view/') self.assertEqual(response2.status_code, 200) self.assertEqual(response.content, response2.content) time.sleep(2.0) # Let the cache expire and test again response2 = self.client.get('/template_response_view/') self.assertEqual(response2.status_code, 200) self.assertNotEqual(response.content, response2.content) Django-1.8.7/tests/template_tests/test_callables.py0000664000175000017500000001001412625116214022065 0ustar timtim00000000000000from __future__ import unicode_literals from unittest import TestCase from django import template class CallableVariablesTests(TestCase): def test_callable(self): class Doodad(object): def __init__(self, value): self.num_calls = 0 self.value = value def __call__(self): self.num_calls += 1 return {"the_value": self.value} my_doodad = Doodad(42) c = template.Context({"my_doodad": my_doodad}) # We can't access ``my_doodad.value`` in the template, because # ``my_doodad.__call__`` will be invoked first, yielding a dictionary # without a key ``value``. t = template.Template('{{ my_doodad.value }}') self.assertEqual(t.render(c), '') # We can confirm that the doodad has been called self.assertEqual(my_doodad.num_calls, 1) # But we can access keys on the dict that's returned # by ``__call__``, instead. t = template.Template('{{ my_doodad.the_value }}') self.assertEqual(t.render(c), '42') self.assertEqual(my_doodad.num_calls, 2) def test_alters_data(self): class Doodad(object): alters_data = True def __init__(self, value): self.num_calls = 0 self.value = value def __call__(self): self.num_calls += 1 return {"the_value": self.value} my_doodad = Doodad(42) c = template.Context({"my_doodad": my_doodad}) # Since ``my_doodad.alters_data`` is True, the template system will not # try to call our doodad but will use string_if_invalid t = template.Template('{{ my_doodad.value }}') self.assertEqual(t.render(c), '') t = template.Template('{{ my_doodad.the_value }}') self.assertEqual(t.render(c), '') # Double-check that the object was really never called during the # template rendering. self.assertEqual(my_doodad.num_calls, 0) def test_do_not_call(self): class Doodad(object): do_not_call_in_templates = True def __init__(self, value): self.num_calls = 0 self.value = value def __call__(self): self.num_calls += 1 return {"the_value": self.value} my_doodad = Doodad(42) c = template.Context({"my_doodad": my_doodad}) # Since ``my_doodad.do_not_call_in_templates`` is True, the template # system will not try to call our doodad. We can access its attributes # as normal, and we don't have access to the dict that it returns when # called. t = template.Template('{{ my_doodad.value }}') self.assertEqual(t.render(c), '42') t = template.Template('{{ my_doodad.the_value }}') self.assertEqual(t.render(c), '') # Double-check that the object was really never called during the # template rendering. self.assertEqual(my_doodad.num_calls, 0) def test_do_not_call_and_alters_data(self): # If we combine ``alters_data`` and ``do_not_call_in_templates``, the # ``alters_data`` attribute will not make any difference in the # template system's behavior. class Doodad(object): do_not_call_in_templates = True alters_data = True def __init__(self, value): self.num_calls = 0 self.value = value def __call__(self): self.num_calls += 1 return {"the_value": self.value} my_doodad = Doodad(42) c = template.Context({"my_doodad": my_doodad}) t = template.Template('{{ my_doodad.value }}') self.assertEqual(t.render(c), '42') t = template.Template('{{ my_doodad.the_value }}') self.assertEqual(t.render(c), '') # Double-check that the object was really never called during the # template rendering. self.assertEqual(my_doodad.num_calls, 0) Django-1.8.7/tests/defer_regress/0000775000175000017500000000000012625116243016322 5ustar timtim00000000000000Django-1.8.7/tests/defer_regress/tests.py0000664000175000017500000003160712625116213020042 0ustar timtim00000000000000from __future__ import unicode_literals from operator import attrgetter from django.apps import apps from django.apps.registry import Apps from django.contrib.contenttypes.models import ContentType from django.contrib.sessions.backends.db import SessionStore from django.db import models from django.db.models import Count from django.db.models.query_utils import ( DeferredAttribute, deferred_class_factory, ) from django.test import TestCase, override_settings from .models import ( Base, Child, Derived, Feature, Item, ItemAndSimpleItem, Leaf, Location, OneToOneItem, Proxy, ProxyRelated, RelatedItem, Request, ResolveThis, SimpleItem, SpecialFeature, ) class DeferRegressionTest(TestCase): def test_basic(self): # Deferred fields should really be deferred and not accidentally use # the field's default value just because they aren't passed to __init__ Item.objects.create(name="first", value=42) obj = Item.objects.only("name", "other_value").get(name="first") # Accessing "name" doesn't trigger a new database query. Accessing # "value" or "text" should. with self.assertNumQueries(0): self.assertEqual(obj.name, "first") self.assertEqual(obj.other_value, 0) with self.assertNumQueries(1): self.assertEqual(obj.value, 42) with self.assertNumQueries(1): self.assertEqual(obj.text, "xyzzy") with self.assertNumQueries(0): self.assertEqual(obj.text, "xyzzy") # Regression test for #10695. Make sure different instances don't # inadvertently share data in the deferred descriptor objects. i = Item.objects.create(name="no I'm first", value=37) items = Item.objects.only("value").order_by("-value") self.assertEqual(items[0].name, "first") self.assertEqual(items[1].name, "no I'm first") RelatedItem.objects.create(item=i) r = RelatedItem.objects.defer("item").get() self.assertEqual(r.item_id, i.id) self.assertEqual(r.item, i) # Some further checks for select_related() and inherited model # behavior (regression for #10710). c1 = Child.objects.create(name="c1", value=42) c2 = Child.objects.create(name="c2", value=37) Leaf.objects.create(name="l1", child=c1, second_child=c2) obj = Leaf.objects.only("name", "child").select_related()[0] self.assertEqual(obj.child.name, "c1") self.assertQuerysetEqual( Leaf.objects.select_related().only("child__name", "second_child__name"), [ "l1", ], attrgetter("name") ) # Models instances with deferred fields should still return the same # content types as their non-deferred versions (bug #10738). ctype = ContentType.objects.get_for_model c1 = ctype(Item.objects.all()[0]) c2 = ctype(Item.objects.defer("name")[0]) c3 = ctype(Item.objects.only("name")[0]) self.assertTrue(c1 is c2 is c3) # Regression for #10733 - only() can be used on a model with two # foreign keys. results = Leaf.objects.only("name", "child", "second_child").select_related() self.assertEqual(results[0].child.name, "c1") self.assertEqual(results[0].second_child.name, "c2") results = Leaf.objects.only( "name", "child", "second_child", "child__name", "second_child__name" ).select_related() self.assertEqual(results[0].child.name, "c1") self.assertEqual(results[0].second_child.name, "c2") # Regression for #16409 - make sure defer() and only() work with annotate() self.assertIsInstance( list(SimpleItem.objects.annotate(Count('feature')).defer('name')), list) self.assertIsInstance( list(SimpleItem.objects.annotate(Count('feature')).only('name')), list) def test_ticket_11936(self): app_config = apps.get_app_config("defer_regress") # Regression for #11936 - get_models should not return deferred models # by default. Run a couple of defer queries so that app registry must # contain some deferred classes. It might contain a lot more classes # depending on the order the tests are ran. list(Item.objects.defer("name")) list(Child.objects.defer("value")) klasses = {model.__name__ for model in app_config.get_models()} self.assertIn("Child", klasses) self.assertIn("Item", klasses) self.assertNotIn("Child_Deferred_value", klasses) self.assertNotIn("Item_Deferred_name", klasses) self.assertFalse(any(k._deferred for k in app_config.get_models())) klasses_with_deferred = {model.__name__ for model in app_config.get_models(include_deferred=True)} self.assertIn("Child", klasses_with_deferred) self.assertIn("Item", klasses_with_deferred) self.assertIn("Child_Deferred_value", klasses_with_deferred) self.assertIn("Item_Deferred_name", klasses_with_deferred) self.assertTrue(any(k._deferred for k in app_config.get_models(include_deferred=True))) @override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer') def test_ticket_12163(self): # Test for #12163 - Pickling error saving session with unsaved model # instances. SESSION_KEY = '2b1189a188b44ad18c35e1baac6ceead' item = Item() item._deferred = False s = SessionStore(SESSION_KEY) s.clear() s["item"] = item s.save() s = SessionStore(SESSION_KEY) s.modified = True s.save() i2 = s["item"] self.assertFalse(i2._deferred) def test_ticket_16409(self): # Regression for #16409 - make sure defer() and only() work with annotate() self.assertIsInstance( list(SimpleItem.objects.annotate(Count('feature')).defer('name')), list) self.assertIsInstance( list(SimpleItem.objects.annotate(Count('feature')).only('name')), list) def test_ticket_23270(self): Derived.objects.create(text="foo", other_text="bar") with self.assertNumQueries(1): obj = Base.objects.select_related("derived").defer("text")[0] self.assertIsInstance(obj.derived, Derived) self.assertEqual("bar", obj.derived.other_text) self.assertNotIn("text", obj.__dict__) self.assertEqual(1, obj.derived.base_ptr_id) def test_only_and_defer_usage_on_proxy_models(self): # Regression for #15790 - only() broken for proxy models proxy = Proxy.objects.create(name="proxy", value=42) msg = 'QuerySet.only() return bogus results with proxy models' dp = Proxy.objects.only('other_value').get(pk=proxy.pk) self.assertEqual(dp.name, proxy.name, msg=msg) self.assertEqual(dp.value, proxy.value, msg=msg) # also test things with .defer() msg = 'QuerySet.defer() return bogus results with proxy models' dp = Proxy.objects.defer('name', 'text', 'value').get(pk=proxy.pk) self.assertEqual(dp.name, proxy.name, msg=msg) self.assertEqual(dp.value, proxy.value, msg=msg) def test_resolve_columns(self): ResolveThis.objects.create(num=5.0, name='Foobar') qs = ResolveThis.objects.defer('num') self.assertEqual(1, qs.count()) self.assertEqual('Foobar', qs[0].name) def test_reverse_one_to_one_relations(self): # Refs #14694. Test reverse relations which are known unique (reverse # side has o2ofield or unique FK) - the o2o case item = Item.objects.create(name="first", value=42) o2o = OneToOneItem.objects.create(item=item, name="second") self.assertEqual(len(Item.objects.defer('one_to_one_item__name')), 1) self.assertEqual(len(Item.objects.select_related('one_to_one_item')), 1) self.assertEqual(len(Item.objects.select_related( 'one_to_one_item').defer('one_to_one_item__name')), 1) self.assertEqual(len(Item.objects.select_related('one_to_one_item').defer('value')), 1) # Make sure that `only()` doesn't break when we pass in a unique relation, # rather than a field on the relation. self.assertEqual(len(Item.objects.only('one_to_one_item')), 1) with self.assertNumQueries(1): i = Item.objects.select_related('one_to_one_item')[0] self.assertEqual(i.one_to_one_item.pk, o2o.pk) self.assertEqual(i.one_to_one_item.name, "second") with self.assertNumQueries(1): i = Item.objects.select_related('one_to_one_item').defer( 'value', 'one_to_one_item__name')[0] self.assertEqual(i.one_to_one_item.pk, o2o.pk) self.assertEqual(i.name, "first") with self.assertNumQueries(1): self.assertEqual(i.one_to_one_item.name, "second") with self.assertNumQueries(1): self.assertEqual(i.value, 42) def test_defer_with_select_related(self): item1 = Item.objects.create(name="first", value=47) item2 = Item.objects.create(name="second", value=42) simple = SimpleItem.objects.create(name="simple", value="23") ItemAndSimpleItem.objects.create(item=item1, simple=simple) obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get() self.assertEqual(obj.item, item1) self.assertEqual(obj.item_id, item1.id) obj.item = item2 obj.save() obj = ItemAndSimpleItem.objects.defer('item').select_related('simple').get() self.assertEqual(obj.item, item2) self.assertEqual(obj.item_id, item2.id) def test_proxy_model_defer_with_select_related(self): # Regression for #22050 item = Item.objects.create(name="first", value=47) RelatedItem.objects.create(item=item) # Defer fields with only() obj = ProxyRelated.objects.all().select_related().only('item__name')[0] with self.assertNumQueries(0): self.assertEqual(obj.item.name, "first") with self.assertNumQueries(1): self.assertEqual(obj.item.value, 47) def test_only_with_select_related(self): # Test for #17485. item = SimpleItem.objects.create(name='first', value=47) feature = Feature.objects.create(item=item) SpecialFeature.objects.create(feature=feature) qs = Feature.objects.only('item__name').select_related('item') self.assertEqual(len(qs), 1) qs = SpecialFeature.objects.only('feature__item__name').select_related('feature__item') self.assertEqual(len(qs), 1) def test_deferred_class_factory(self): new_class = deferred_class_factory( Item, ('this_is_some_very_long_attribute_name_so_modelname_truncation_is_triggered',)) self.assertEqual( new_class.__name__, 'Item_Deferred_this_is_some_very_long_attribute_nac34b1f495507dad6b02e2cb235c875e') def test_deferred_class_factory_already_deferred(self): deferred_item1 = deferred_class_factory(Item, ('name',)) deferred_item2 = deferred_class_factory(deferred_item1, ('value',)) self.assertIs(deferred_item2._meta.proxy_for_model, Item) self.assertNotIsInstance(deferred_item2.__dict__.get('name'), DeferredAttribute) self.assertIsInstance(deferred_item2.__dict__.get('value'), DeferredAttribute) def test_deferred_class_factory_no_attrs(self): deferred_cls = deferred_class_factory(Item, ()) self.assertFalse(deferred_cls._deferred) def test_deferred_class_factory_apps_reuse(self): """ #25563 - model._meta.apps should be used for caching and retrieval of the created proxy class. """ isolated_apps = Apps(['defer_regress']) class BaseModel(models.Model): field = models.BooleanField() class Meta: apps = isolated_apps app_label = 'defer_regress' deferred_model = deferred_class_factory(BaseModel, ['field']) self.assertIs(deferred_model._meta.apps, isolated_apps) self.assertIs(deferred_class_factory(BaseModel, ['field']), deferred_model) class DeferAnnotateSelectRelatedTest(TestCase): def test_defer_annotate_select_related(self): location = Location.objects.create() Request.objects.create(location=location) self.assertIsInstance(list(Request.objects .annotate(Count('items')).select_related('profile', 'location') .only('profile', 'location')), list) self.assertIsInstance(list(Request.objects .annotate(Count('items')).select_related('profile', 'location') .only('profile__profile1', 'location__location1')), list) self.assertIsInstance(list(Request.objects .annotate(Count('items')).select_related('profile', 'location') .defer('request1', 'request2', 'request3', 'request4')), list) Django-1.8.7/tests/defer_regress/__init__.py0000664000175000017500000000000012603513307020417 0ustar timtim00000000000000Django-1.8.7/tests/defer_regress/models.py0000664000175000017500000000474212625116213020163 0ustar timtim00000000000000""" Regression tests for defer() / only() behavior. """ from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=15) text = models.TextField(default="xyzzy") value = models.IntegerField() other_value = models.IntegerField(default=0) def __str__(self): return self.name class RelatedItem(models.Model): item = models.ForeignKey(Item) class ProxyRelated(RelatedItem): class Meta: proxy = True class Child(models.Model): name = models.CharField(max_length=10) value = models.IntegerField() @python_2_unicode_compatible class Leaf(models.Model): name = models.CharField(max_length=10) child = models.ForeignKey(Child) second_child = models.ForeignKey(Child, related_name="other", null=True) value = models.IntegerField(default=42) def __str__(self): return self.name class ResolveThis(models.Model): num = models.FloatField() name = models.CharField(max_length=16) class Proxy(Item): class Meta: proxy = True @python_2_unicode_compatible class SimpleItem(models.Model): name = models.CharField(max_length=15) value = models.IntegerField() def __str__(self): return self.name class Feature(models.Model): item = models.ForeignKey(SimpleItem) class SpecialFeature(models.Model): feature = models.ForeignKey(Feature) class OneToOneItem(models.Model): item = models.OneToOneField(Item, related_name="one_to_one_item") name = models.CharField(max_length=15) class ItemAndSimpleItem(models.Model): item = models.ForeignKey(Item) simple = models.ForeignKey(SimpleItem) class Profile(models.Model): profile1 = models.CharField(max_length=1000, default='profile1') class Location(models.Model): location1 = models.CharField(max_length=1000, default='location1') class Request(models.Model): profile = models.ForeignKey(Profile, null=True, blank=True) location = models.ForeignKey(Location) items = models.ManyToManyField(Item) request1 = models.CharField(default='request1', max_length=1000) request2 = models.CharField(default='request2', max_length=1000) request3 = models.CharField(default='request3', max_length=1000) request4 = models.CharField(default='request4', max_length=1000) class Base(models.Model): text = models.TextField() class Derived(Base): other_text = models.TextField() Django-1.8.7/tests/createsuperuser/0000775000175000017500000000000012625116243016724 5ustar timtim00000000000000Django-1.8.7/tests/createsuperuser/tests.py0000664000175000017500000000361012625116213020435 0ustar timtim00000000000000from django.contrib.auth import models from django.contrib.auth.management.commands import changepassword from django.core.management import call_command from django.test import TestCase from django.utils.six import StringIO class MultiDBChangepasswordManagementCommandTestCase(TestCase): multi_db = True def setUp(self): self.user = models.User.objects.db_manager('other').create_user(username='joe', password='qwerty') def test_that_changepassword_command_with_database_option_uses_given_db(self): """ Executing the changepassword management command with a database option should operate on the specified DB """ self.assertTrue(self.user.check_password('qwerty')) command = changepassword.Command() command._get_pass = lambda *args: 'not qwerty' out = StringIO() command.execute(username="joe", database='other', stdout=out) command_output = out.getvalue().strip() self.assertEqual(command_output, "Changing password for user 'joe'\nPassword changed successfully for user 'joe'") self.assertTrue(models.User.objects.using('other').get(username="joe").check_password("not qwerty")) class MultiDBCreatesuperuserTestCase(TestCase): multi_db = True def test_createsuperuser_command_with_database_option(self): " createsuperuser command should operate on specified DB" new_io = StringIO() call_command( "createsuperuser", interactive=False, username="joe", email="joe@somewhere.org", database='other', stdout=new_io ) command_output = new_io.getvalue().strip() self.assertEqual(command_output, 'Superuser created successfully.') u = models.User.objects.using('other').get(username="joe") self.assertEqual(u.email, 'joe@somewhere.org') new_io.close() Django-1.8.7/tests/createsuperuser/__init__.py0000664000175000017500000000000012603513307021021 0ustar timtim00000000000000Django-1.8.7/tests/field_defaults/0000775000175000017500000000000012625116243016455 5ustar timtim00000000000000Django-1.8.7/tests/field_defaults/tests.py0000664000175000017500000000065112625116213020170 0ustar timtim00000000000000from datetime import datetime from django.test import TestCase from django.utils import six from .models import Article class DefaultTests(TestCase): def test_field_defaults(self): a = Article() now = datetime.now() a.save() self.assertIsInstance(a.id, six.integer_types) self.assertEqual(a.headline, "Default headline") self.assertLess((now - a.pub_date).seconds, 5) Django-1.8.7/tests/field_defaults/__init__.py0000664000175000017500000000000012603513307020552 0ustar timtim00000000000000Django-1.8.7/tests/field_defaults/models.py0000664000175000017500000000127012625116213020307 0ustar timtim00000000000000# coding: utf-8 """ Callable defaults You can pass callable objects as the ``default`` parameter to a field. When the object is created without an explicit value passed in, Django will call the method to determine the default value. This example uses ``datetime.datetime.now`` as the default for the ``pub_date`` field. """ from datetime import datetime from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField(default=datetime.now) def __str__(self): return self.headline Django-1.8.7/tests/logging_tests/0000775000175000017500000000000012625116243016353 5ustar timtim00000000000000Django-1.8.7/tests/logging_tests/views.py0000664000175000017500000000037012625116214020060 0ustar timtim00000000000000from __future__ import unicode_literals from django.core.exceptions import DisallowedHost, SuspiciousOperation def suspicious(request): raise SuspiciousOperation('dubious') def suspicious_spec(request): raise DisallowedHost('dubious') Django-1.8.7/tests/logging_tests/urls.py0000664000175000017500000000032612625116214017711 0ustar timtim00000000000000from __future__ import unicode_literals from django.conf.urls import url from . import views urlpatterns = [ url(r'^suspicious/$', views.suspicious), url(r'^suspicious_spec/$', views.suspicious_spec), ] Django-1.8.7/tests/logging_tests/tests.py0000664000175000017500000003751612625116214020101 0ustar timtim00000000000000# -*- coding:utf-8 -*- from __future__ import unicode_literals import logging import warnings from admin_scripts.tests import AdminScriptTestCase from django.core import mail from django.core.files.temp import NamedTemporaryFile from django.test import RequestFactory, TestCase, override_settings from django.test.utils import patch_logger from django.utils.deprecation import RemovedInNextVersionWarning from django.utils.encoding import force_text from django.utils.log import ( AdminEmailHandler, CallbackFilter, RequireDebugFalse, RequireDebugTrue, ) from django.utils.six import StringIO from .logconfig import MyEmailBackend # logging config prior to using filter with mail_admins OLD_LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'mail_admins': { 'level': 'ERROR', 'class': 'django.utils.log.AdminEmailHandler' } }, 'loggers': { 'django.request': { 'handlers': ['mail_admins'], 'level': 'ERROR', 'propagate': True, }, } } class LoggingFiltersTest(TestCase): def test_require_debug_false_filter(self): """ Test the RequireDebugFalse filter class. """ filter_ = RequireDebugFalse() with self.settings(DEBUG=True): self.assertEqual(filter_.filter("record is not used"), False) with self.settings(DEBUG=False): self.assertEqual(filter_.filter("record is not used"), True) def test_require_debug_true_filter(self): """ Test the RequireDebugTrue filter class. """ filter_ = RequireDebugTrue() with self.settings(DEBUG=True): self.assertEqual(filter_.filter("record is not used"), True) with self.settings(DEBUG=False): self.assertEqual(filter_.filter("record is not used"), False) class DefaultLoggingTest(TestCase): def setUp(self): self.logger = logging.getLogger('django') self.old_stream = self.logger.handlers[0].stream def tearDown(self): self.logger.handlers[0].stream = self.old_stream def test_django_logger(self): """ The 'django' base logger only output anything when DEBUG=True. """ output = StringIO() self.logger.handlers[0].stream = output self.logger.error("Hey, this is an error.") self.assertEqual(output.getvalue(), '') with self.settings(DEBUG=True): self.logger.error("Hey, this is an error.") self.assertEqual(output.getvalue(), 'Hey, this is an error.\n') class WarningLoggerTests(TestCase): """ Tests that warnings output for RemovedInDjangoXXWarning (XX being the next Django version) is enabled and captured to the logging system """ def setUp(self): # If tests are invoke with "-Wall" (or any -W flag actually) then # warning logging gets disabled (see configure_logging in django/utils/log.py). # However, these tests expect warnings to be logged, so manually force warnings # to the logs. Use getattr() here because the logging capture state is # undocumented and (I assume) brittle. self._old_capture_state = bool(getattr(logging, '_warnings_showwarning', False)) logging.captureWarnings(True) # this convoluted setup is to avoid printing this deprecation to # stderr during test running - as the test runner forces deprecations # to be displayed at the global py.warnings level self.logger = logging.getLogger('py.warnings') self.outputs = [] self.old_streams = [] for handler in self.logger.handlers: self.old_streams.append(handler.stream) self.outputs.append(StringIO()) handler.stream = self.outputs[-1] def tearDown(self): for i, handler in enumerate(self.logger.handlers): self.logger.handlers[i].stream = self.old_streams[i] # Reset warnings state. logging.captureWarnings(self._old_capture_state) @override_settings(DEBUG=True) def test_warnings_capture(self): with warnings.catch_warnings(): warnings.filterwarnings('always') warnings.warn('Foo Deprecated', RemovedInNextVersionWarning) output = force_text(self.outputs[0].getvalue()) self.assertIn('Foo Deprecated', output) def test_warnings_capture_debug_false(self): with warnings.catch_warnings(): warnings.filterwarnings('always') warnings.warn('Foo Deprecated', RemovedInNextVersionWarning) output = force_text(self.outputs[0].getvalue()) self.assertNotIn('Foo Deprecated', output) @override_settings(DEBUG=True) def test_error_filter_still_raises(self): with warnings.catch_warnings(): warnings.filterwarnings( 'error', category=RemovedInNextVersionWarning ) with self.assertRaises(RemovedInNextVersionWarning): warnings.warn('Foo Deprecated', RemovedInNextVersionWarning) class CallbackFilterTest(TestCase): def test_sense(self): f_false = CallbackFilter(lambda r: False) f_true = CallbackFilter(lambda r: True) self.assertEqual(f_false.filter("record"), False) self.assertEqual(f_true.filter("record"), True) def test_passes_on_record(self): collector = [] def _callback(record): collector.append(record) return True f = CallbackFilter(_callback) f.filter("a record") self.assertEqual(collector, ["a record"]) class AdminEmailHandlerTest(TestCase): logger = logging.getLogger('django.request') def get_admin_email_handler(self, logger): # Inspired from views/views.py: send_log() # ensuring the AdminEmailHandler does not get filtered out # even with DEBUG=True. admin_email_handler = [ h for h in logger.handlers if h.__class__.__name__ == "AdminEmailHandler" ][0] return admin_email_handler def test_fail_silently(self): admin_email_handler = self.get_admin_email_handler(self.logger) self.assertTrue(admin_email_handler.connection().fail_silently) @override_settings( ADMINS=(('whatever admin', 'admin@example.com'),), EMAIL_SUBJECT_PREFIX='-SuperAwesomeSubject-' ) def test_accepts_args(self): """ Ensure that user-supplied arguments and the EMAIL_SUBJECT_PREFIX setting are used to compose the email subject. Refs #16736. """ message = "Custom message that says '%s' and '%s'" token1 = 'ping' token2 = 'pong' admin_email_handler = self.get_admin_email_handler(self.logger) # Backup then override original filters orig_filters = admin_email_handler.filters try: admin_email_handler.filters = [] self.logger.error(message, token1, token2) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ['admin@example.com']) self.assertEqual(mail.outbox[0].subject, "-SuperAwesomeSubject-ERROR: Custom message that says 'ping' and 'pong'") finally: # Restore original filters admin_email_handler.filters = orig_filters @override_settings( ADMINS=(('whatever admin', 'admin@example.com'),), EMAIL_SUBJECT_PREFIX='-SuperAwesomeSubject-', INTERNAL_IPS=('127.0.0.1',), ) def test_accepts_args_and_request(self): """ Ensure that the subject is also handled if being passed a request object. """ message = "Custom message that says '%s' and '%s'" token1 = 'ping' token2 = 'pong' admin_email_handler = self.get_admin_email_handler(self.logger) # Backup then override original filters orig_filters = admin_email_handler.filters try: admin_email_handler.filters = [] rf = RequestFactory() request = rf.get('/') self.logger.error(message, token1, token2, extra={ 'status_code': 403, 'request': request, } ) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ['admin@example.com']) self.assertEqual(mail.outbox[0].subject, "-SuperAwesomeSubject-ERROR (internal IP): Custom message that says 'ping' and 'pong'") finally: # Restore original filters admin_email_handler.filters = orig_filters @override_settings( ADMINS=(('admin', 'admin@example.com'),), EMAIL_SUBJECT_PREFIX='', DEBUG=False, ) def test_subject_accepts_newlines(self): """ Ensure that newlines in email reports' subjects are escaped to avoid AdminErrorHandler to fail. Refs #17281. """ message = 'Message \r\n with newlines' expected_subject = 'ERROR: Message \\r\\n with newlines' self.assertEqual(len(mail.outbox), 0) self.logger.error(message) self.assertEqual(len(mail.outbox), 1) self.assertNotIn('\n', mail.outbox[0].subject) self.assertNotIn('\r', mail.outbox[0].subject) self.assertEqual(mail.outbox[0].subject, expected_subject) @override_settings( ADMINS=(('admin', 'admin@example.com'),), EMAIL_SUBJECT_PREFIX='', DEBUG=False, ) def test_truncate_subject(self): """ RFC 2822's hard limit is 998 characters per line. So, minus "Subject: ", the actual subject must be no longer than 989 characters. Refs #17281. """ message = 'a' * 1000 expected_subject = 'ERROR: aa' + 'a' * 980 self.assertEqual(len(mail.outbox), 0) self.logger.error(message) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, expected_subject) @override_settings( ADMINS=(('admin', 'admin@example.com'),), DEBUG=False, ) def test_uses_custom_email_backend(self): """ Refs #19325 """ message = 'All work and no play makes Jack a dull boy' admin_email_handler = self.get_admin_email_handler(self.logger) mail_admins_called = {'called': False} def my_mail_admins(*args, **kwargs): connection = kwargs['connection'] self.assertIsInstance(connection, MyEmailBackend) mail_admins_called['called'] = True # Monkeypatches orig_mail_admins = mail.mail_admins orig_email_backend = admin_email_handler.email_backend mail.mail_admins = my_mail_admins admin_email_handler.email_backend = ( 'logging_tests.logconfig.MyEmailBackend') try: self.logger.error(message) self.assertTrue(mail_admins_called['called']) finally: # Revert Monkeypatches mail.mail_admins = orig_mail_admins admin_email_handler.email_backend = orig_email_backend @override_settings( ADMINS=(('whatever admin', 'admin@example.com'),), ) def test_emit_non_ascii(self): """ #23593 - AdminEmailHandler should allow Unicode characters in the request. """ handler = self.get_admin_email_handler(self.logger) record = self.logger.makeRecord('name', logging.ERROR, 'function', 'lno', 'message', None, None) rf = RequestFactory() url_path = '/º' record.request = rf.get(url_path) handler.emit(record) self.assertEqual(len(mail.outbox), 1) msg = mail.outbox[0] self.assertEqual(msg.to, ['admin@example.com']) self.assertEqual(msg.subject, "[Django] ERROR (EXTERNAL IP): message") self.assertIn("path:%s" % url_path, msg.body) @override_settings( MANAGERS=(('manager', 'manager@example.com'),), DEBUG=False, ) def test_customize_send_mail_method(self): class ManagerEmailHandler(AdminEmailHandler): def send_mail(self, subject, message, *args, **kwargs): mail.mail_managers(subject, message, *args, connection=self.connection(), **kwargs) handler = ManagerEmailHandler() record = self.logger.makeRecord('name', logging.ERROR, 'function', 'lno', 'message', None, None) self.assertEqual(len(mail.outbox), 0) handler.emit(record) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].to, ['manager@example.com']) class SettingsConfigTest(AdminScriptTestCase): """ Test that accessing settings in a custom logging handler does not trigger a circular import error. """ def setUp(self): log_config = """{ 'version': 1, 'handlers': { 'custom_handler': { 'level': 'INFO', 'class': 'logging_tests.logconfig.MyHandler', } } }""" self.write_settings('settings.py', sdict={'LOGGING': log_config}) def tearDown(self): self.remove_settings('settings.py') def test_circular_dependency(self): # validate is just an example command to trigger settings configuration out, err = self.run_manage(['validate']) self.assertNoOutput(err) self.assertOutput(out, "System check identified no issues (0 silenced).") def dictConfig(config): dictConfig.called = True dictConfig.called = False class SetupConfigureLogging(TestCase): """ Test that calling django.setup() initializes the logging configuration. """ @override_settings(LOGGING_CONFIG='logging_tests.tests.dictConfig', LOGGING=OLD_LOGGING) def test_configure_initializes_logging(self): from django import setup setup() self.assertTrue(dictConfig.called) @override_settings(DEBUG=True, ROOT_URLCONF='logging_tests.urls') class SecurityLoggerTest(TestCase): def test_suspicious_operation_creates_log_message(self): with patch_logger('django.security.SuspiciousOperation', 'error') as calls: self.client.get('/suspicious/') self.assertEqual(len(calls), 1) self.assertEqual(calls[0], 'dubious') def test_suspicious_operation_uses_sublogger(self): with patch_logger('django.security.DisallowedHost', 'error') as calls: self.client.get('/suspicious_spec/') self.assertEqual(len(calls), 1) self.assertEqual(calls[0], 'dubious') @override_settings( ADMINS=(('admin', 'admin@example.com'),), DEBUG=False, ) def test_suspicious_email_admins(self): self.client.get('/suspicious/') self.assertEqual(len(mail.outbox), 1) self.assertIn('path:/suspicious/,', mail.outbox[0].body) class SettingsCustomLoggingTest(AdminScriptTestCase): """ Test that using a logging defaults are still applied when using a custom callable in LOGGING_CONFIG (i.e., logging.config.fileConfig). """ def setUp(self): logging_conf = """ [loggers] keys=root [handlers] keys=stream [formatters] keys=simple [logger_root] handlers=stream [handler_stream] class=StreamHandler formatter=simple args=(sys.stdout,) [formatter_simple] format=%(message)s """ self.temp_file = NamedTemporaryFile() self.temp_file.write(logging_conf.encode('utf-8')) self.temp_file.flush() sdict = {'LOGGING_CONFIG': '"logging.config.fileConfig"', 'LOGGING': 'r"%s"' % self.temp_file.name} self.write_settings('settings.py', sdict=sdict) def tearDown(self): self.temp_file.close() self.remove_settings('settings.py') def test_custom_logging(self): out, err = self.run_manage(['validate']) self.assertNoOutput(err) self.assertOutput(out, "System check identified no issues (0 silenced).") Django-1.8.7/tests/logging_tests/__init__.py0000664000175000017500000000000012603513307020450 0ustar timtim00000000000000Django-1.8.7/tests/logging_tests/logconfig.py0000664000175000017500000000053312603513307020673 0ustar timtim00000000000000import logging from django.conf import settings from django.core.mail.backends.base import BaseEmailBackend class MyHandler(logging.Handler): def __init__(self): logging.Handler.__init__(self) self.config = settings.LOGGING class MyEmailBackend(BaseEmailBackend): def send_messages(self, email_messages): pass Django-1.8.7/tests/test_discovery_sample2/0000775000175000017500000000000012625116243020174 5ustar timtim00000000000000Django-1.8.7/tests/test_discovery_sample2/tests.py0000664000175000017500000000135012625116214021705 0ustar timtim00000000000000from unittest import TestCase from django.test import SimpleTestCase, TestCase as DjangoTestCase class DjangoCase1(DjangoTestCase): def test_1(self): pass def test_2(self): pass class DjangoCase2(DjangoTestCase): def test_1(self): pass def test_2(self): pass class SimpleCase1(SimpleTestCase): def test_1(self): pass def test_2(self): pass class SimpleCase2(SimpleTestCase): def test_1(self): pass def test_2(self): pass class UnittestCase1(TestCase): def test_1(self): pass def test_2(self): pass class UnittestCase2(TestCase): def test_1(self): pass def test_2(self): pass Django-1.8.7/tests/test_discovery_sample2/__init__.py0000664000175000017500000000000012603513307022271 0ustar timtim00000000000000Django-1.8.7/tests/admin_checks/0000775000175000017500000000000012625116243016113 5ustar timtim00000000000000Django-1.8.7/tests/admin_checks/tests.py0000664000175000017500000005525412625116213017637 0ustar timtim00000000000000from __future__ import unicode_literals from django import forms from django.contrib import admin from django.contrib.contenttypes.admin import GenericStackedInline from django.core import checks from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, ignore_warnings, override_settings from .models import Album, Book, City, Influence, Song, State, TwoAlbumFKAndAnE class SongForm(forms.ModelForm): pass class ValidFields(admin.ModelAdmin): form = SongForm fields = ['title'] class ValidFormFieldsets(admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): class ExtraFieldForm(SongForm): name = forms.CharField(max_length=50) return ExtraFieldForm fieldsets = ( (None, { 'fields': ('name',), }), ) class MyAdmin(admin.ModelAdmin): @classmethod def check(cls, model, **kwargs): return ['error!'] @override_settings( SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True) INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes', 'admin_checks'] ) class SystemChecksTestCase(TestCase): @override_settings(DEBUG=True) def test_checks_are_performed(self): admin.site.register(Song, MyAdmin) try: errors = checks.run_checks() expected = ['error!'] self.assertEqual(errors, expected) finally: admin.site.unregister(Song) admin.sites.system_check_errors = [] @override_settings(DEBUG=True) def test_custom_adminsite(self): class CustomAdminSite(admin.AdminSite): pass custom_site = CustomAdminSite() custom_site.register(Song, MyAdmin) try: errors = checks.run_checks() expected = ['error!'] self.assertEqual(errors, expected) finally: custom_site.unregister(Song) admin.sites.system_check_errors = [] def test_field_name_not_in_list_display(self): class SongAdmin(admin.ModelAdmin): list_editable = ["original_release"] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "The value of 'list_editable[0]' refers to 'original_release', " "which is not contained in 'list_display'.", hint=None, obj=SongAdmin, id='admin.E122', ) ] self.assertEqual(errors, expected) def test_readonly_and_editable(self): class SongAdmin(admin.ModelAdmin): readonly_fields = ["original_release"] list_display = ["pk", "original_release"] list_editable = ["original_release"] fieldsets = [ (None, { "fields": ["title", "original_release"], }), ] errors = SongAdmin.check(model=Song) expected = [ checks.Error( ("The value of 'list_editable[0]' refers to 'original_release', " "which is not editable through the admin."), hint=None, obj=SongAdmin, id='admin.E125', ) ] self.assertEqual(errors, expected) def test_editable(self): class SongAdmin(admin.ModelAdmin): list_display = ["pk", "title"] list_editable = ["title"] fieldsets = [ (None, { "fields": ["title", "original_release"], }), ] errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_custom_modelforms_with_fields_fieldsets(self): """ # Regression test for #8027: custom ModelForms with fields/fieldsets """ errors = ValidFields.check(model=Song) self.assertEqual(errors, []) def test_custom_get_form_with_fieldsets(self): """ Ensure that the fieldsets checks are skipped when the ModelAdmin.get_form() method is overridden. Refs #19445. """ errors = ValidFormFieldsets.check(model=Song) self.assertEqual(errors, []) def test_fieldsets_fields_non_tuple(self): """ Tests for a tuple/list for the first fieldset's fields. """ class NotATupleAdmin(admin.ModelAdmin): list_display = ["pk", "title"] list_editable = ["title"] fieldsets = [ (None, { "fields": "title" # not a tuple }), ] errors = NotATupleAdmin.check(model=Song) expected = [ checks.Error( "The value of 'fieldsets[0][1]['fields']' must be a list or tuple.", hint=None, obj=NotATupleAdmin, id='admin.E008', ) ] self.assertEqual(errors, expected) def test_nonfirst_fieldset(self): """ Tests for a tuple/list for the second fieldset's fields. """ class NotATupleAdmin(admin.ModelAdmin): fieldsets = [ (None, { "fields": ("title",) }), ('foo', { "fields": "author" # not a tuple }), ] errors = NotATupleAdmin.check(model=Song) expected = [ checks.Error( "The value of 'fieldsets[1][1]['fields']' must be a list or tuple.", hint=None, obj=NotATupleAdmin, id='admin.E008', ) ] self.assertEqual(errors, expected) def test_exclude_values(self): """ Tests for basic system checks of 'exclude' option values (#12689) """ class ExcludedFields1(admin.ModelAdmin): exclude = 'foo' errors = ExcludedFields1.check(model=Book) expected = [ checks.Error( "The value of 'exclude' must be a list or tuple.", hint=None, obj=ExcludedFields1, id='admin.E014', ) ] self.assertEqual(errors, expected) def test_exclude_duplicate_values(self): class ExcludedFields2(admin.ModelAdmin): exclude = ('name', 'name') errors = ExcludedFields2.check(model=Book) expected = [ checks.Error( "The value of 'exclude' contains duplicate field(s).", hint=None, obj=ExcludedFields2, id='admin.E015', ) ] self.assertEqual(errors, expected) def test_exclude_in_inline(self): class ExcludedFieldsInline(admin.TabularInline): model = Song exclude = 'foo' class ExcludedFieldsAlbumAdmin(admin.ModelAdmin): model = Album inlines = [ExcludedFieldsInline] errors = ExcludedFieldsAlbumAdmin.check(model=Album) expected = [ checks.Error( "The value of 'exclude' must be a list or tuple.", hint=None, obj=ExcludedFieldsInline, id='admin.E014', ) ] self.assertEqual(errors, expected) def test_exclude_inline_model_admin(self): """ Regression test for #9932 - exclude in InlineModelAdmin should not contain the ForeignKey field used in ModelAdmin.model """ class SongInline(admin.StackedInline): model = Song exclude = ['album'] class AlbumAdmin(admin.ModelAdmin): model = Album inlines = [SongInline] errors = AlbumAdmin.check(model=Album) expected = [ checks.Error( ("Cannot exclude the field 'album', because it is the foreign key " "to the parent model 'admin_checks.Album'."), hint=None, obj=SongInline, id='admin.E201', ) ] self.assertEqual(errors, expected) def test_valid_generic_inline_model_admin(self): """ Regression test for #22034 - check that generic inlines don't look for normal ForeignKey relations. """ class InfluenceInline(GenericStackedInline): model = Influence class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_generic_inline_model_admin_non_generic_model(self): """ Ensure that a model without a GenericForeignKey raises problems if it's included in an GenericInlineModelAdmin definition. """ class BookInline(GenericStackedInline): model = Book class SongAdmin(admin.ModelAdmin): inlines = [BookInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'admin_checks.Book' has no GenericForeignKey.", hint=None, obj=BookInline, id='admin.E301', ) ] self.assertEqual(errors, expected) def test_generic_inline_model_admin_bad_ct_field(self): "A GenericInlineModelAdmin raises problems if the ct_field points to a non-existent field." class InfluenceInline(GenericStackedInline): model = Influence ct_field = 'nonexistent' class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'ct_field' references 'nonexistent', which is not a field on 'admin_checks.Influence'.", hint=None, obj=InfluenceInline, id='admin.E302', ) ] self.assertEqual(errors, expected) def test_generic_inline_model_admin_bad_fk_field(self): "A GenericInlineModelAdmin raises problems if the ct_fk_field points to a non-existent field." class InfluenceInline(GenericStackedInline): model = Influence ct_fk_field = 'nonexistent' class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'ct_fk_field' references 'nonexistent', which is not a field on 'admin_checks.Influence'.", hint=None, obj=InfluenceInline, id='admin.E303', ) ] self.assertEqual(errors, expected) def test_generic_inline_model_admin_non_gfk_ct_field(self): "A GenericInlineModelAdmin raises problems if the ct_field points to a field that isn't part of a GenericForeignKey" class InfluenceInline(GenericStackedInline): model = Influence ct_field = 'name' class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'admin_checks.Influence' has no GenericForeignKey using content type field 'name' and object ID field 'object_id'.", hint=None, obj=InfluenceInline, id='admin.E304', ) ] self.assertEqual(errors, expected) def test_generic_inline_model_admin_non_gfk_fk_field(self): "A GenericInlineModelAdmin raises problems if the ct_fk_field points to a field that isn't part of a GenericForeignKey" class InfluenceInline(GenericStackedInline): model = Influence ct_fk_field = 'name' class SongAdmin(admin.ModelAdmin): inlines = [InfluenceInline] errors = SongAdmin.check(model=Song) expected = [ checks.Error( "'admin_checks.Influence' has no GenericForeignKey using content type field 'content_type' and object ID field 'name'.", hint=None, obj=InfluenceInline, id='admin.E304', ) ] self.assertEqual(errors, expected) def test_app_label_in_admin_checks(self): """ Regression test for #15669 - Include app label in admin system check messages """ class RawIdNonexistingAdmin(admin.ModelAdmin): raw_id_fields = ('nonexisting',) errors = RawIdNonexistingAdmin.check(model=Album) expected = [ checks.Error( ("The value of 'raw_id_fields[0]' refers to 'nonexisting', which is " "not an attribute of 'admin_checks.Album'."), hint=None, obj=RawIdNonexistingAdmin, id='admin.E002', ) ] self.assertEqual(errors, expected) def test_fk_exclusion(self): """ Regression test for #11709 - when testing for fk excluding (when exclude is given) make sure fk_name is honored or things blow up when there is more than one fk to the parent model. """ class TwoAlbumFKAndAnEInline(admin.TabularInline): model = TwoAlbumFKAndAnE exclude = ("e",) fk_name = "album1" class MyAdmin(admin.ModelAdmin): inlines = [TwoAlbumFKAndAnEInline] errors = MyAdmin.check(model=Album) self.assertEqual(errors, []) def test_inline_self_check(self): class TwoAlbumFKAndAnEInline(admin.TabularInline): model = TwoAlbumFKAndAnE class MyAdmin(admin.ModelAdmin): inlines = [TwoAlbumFKAndAnEInline] errors = MyAdmin.check(model=Album) expected = [ checks.Error( "'admin_checks.TwoAlbumFKAndAnE' has more than one ForeignKey to 'admin_checks.Album'.", hint=None, obj=TwoAlbumFKAndAnEInline, id='admin.E202', ) ] self.assertEqual(errors, expected) def test_inline_with_specified(self): class TwoAlbumFKAndAnEInline(admin.TabularInline): model = TwoAlbumFKAndAnE fk_name = "album1" class MyAdmin(admin.ModelAdmin): inlines = [TwoAlbumFKAndAnEInline] errors = MyAdmin.check(model=Album) self.assertEqual(errors, []) def test_readonly(self): class SongAdmin(admin.ModelAdmin): readonly_fields = ("title",) errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_readonly_on_method(self): def my_function(obj): pass class SongAdmin(admin.ModelAdmin): readonly_fields = (my_function,) errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_readonly_on_modeladmin(self): class SongAdmin(admin.ModelAdmin): readonly_fields = ("readonly_method_on_modeladmin",) def readonly_method_on_modeladmin(self, obj): pass errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_readonly_method_on_model(self): class SongAdmin(admin.ModelAdmin): readonly_fields = ("readonly_method_on_model",) errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_nonexistent_field(self): class SongAdmin(admin.ModelAdmin): readonly_fields = ("title", "nonexistent") errors = SongAdmin.check(model=Song) expected = [ checks.Error( ("The value of 'readonly_fields[1]' is not a callable, an attribute " "of 'SongAdmin', or an attribute of 'admin_checks.Song'."), hint=None, obj=SongAdmin, id='admin.E035', ) ] self.assertEqual(errors, expected) def test_nonexistent_field_on_inline(self): class CityInline(admin.TabularInline): model = City readonly_fields = ['i_dont_exist'] # Missing attribute errors = CityInline.check(State) expected = [ checks.Error( ("The value of 'readonly_fields[0]' is not a callable, an attribute " "of 'CityInline', or an attribute of 'admin_checks.City'."), hint=None, obj=CityInline, id='admin.E035', ) ] self.assertEqual(errors, expected) def test_extra(self): class SongAdmin(admin.ModelAdmin): def awesome_song(self, instance): if instance.title == "Born to Run": return "Best Ever!" return "Status unknown." errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_readonly_lambda(self): class SongAdmin(admin.ModelAdmin): readonly_fields = (lambda obj: "test",) errors = SongAdmin.check(model=Song) self.assertEqual(errors, []) def test_graceful_m2m_fail(self): """ Regression test for #12203/#12237 - Fail more gracefully when a M2M field that specifies the 'through' option is included in the 'fields' or the 'fieldsets' ModelAdmin options. """ class BookAdmin(admin.ModelAdmin): fields = ['authors'] errors = BookAdmin.check(model=Book) expected = [ checks.Error( ("The value of 'fields' cannot include the ManyToManyField 'authors', " "because that field manually specifies a relationship model."), hint=None, obj=BookAdmin, id='admin.E013', ) ] self.assertEqual(errors, expected) def test_cannot_include_through(self): class FieldsetBookAdmin(admin.ModelAdmin): fieldsets = ( ('Header 1', {'fields': ('name',)}), ('Header 2', {'fields': ('authors',)}), ) errors = FieldsetBookAdmin.check(model=Book) expected = [ checks.Error( ("The value of 'fieldsets[1][1][\"fields\"]' cannot include the ManyToManyField " "'authors', because that field manually specifies a relationship model."), hint=None, obj=FieldsetBookAdmin, id='admin.E013', ) ] self.assertEqual(errors, expected) def test_nested_fields(self): class NestedFieldsAdmin(admin.ModelAdmin): fields = ('price', ('name', 'subtitle')) errors = NestedFieldsAdmin.check(model=Book) self.assertEqual(errors, []) def test_nested_fieldsets(self): class NestedFieldsetAdmin(admin.ModelAdmin): fieldsets = ( ('Main', {'fields': ('price', ('name', 'subtitle'))}), ) errors = NestedFieldsetAdmin.check(model=Book) self.assertEqual(errors, []) def test_explicit_through_override(self): """ Regression test for #12209 -- If the explicitly provided through model is specified as a string, the admin should still be able use Model.m2m_field.through """ class AuthorsInline(admin.TabularInline): model = Book.authors.through class BookAdmin(admin.ModelAdmin): inlines = [AuthorsInline] errors = BookAdmin.check(model=Book) self.assertEqual(errors, []) def test_non_model_fields(self): """ Regression for ensuring ModelAdmin.fields can contain non-model fields that broke with r11737 """ class SongForm(forms.ModelForm): extra_data = forms.CharField() class FieldsOnFormOnlyAdmin(admin.ModelAdmin): form = SongForm fields = ['title', 'extra_data'] errors = FieldsOnFormOnlyAdmin.check(model=Song) self.assertEqual(errors, []) def test_non_model_first_field(self): """ Regression for ensuring ModelAdmin.field can handle first elem being a non-model field (test fix for UnboundLocalError introduced with r16225). """ class SongForm(forms.ModelForm): extra_data = forms.CharField() class Meta: model = Song fields = '__all__' class FieldsOnFormOnlyAdmin(admin.ModelAdmin): form = SongForm fields = ['extra_data', 'title'] errors = FieldsOnFormOnlyAdmin.check(model=Song) self.assertEqual(errors, []) @ignore_warnings(module='django.contrib.admin.options') def test_validator_compatibility(self): class MyValidator(object): def validate(self, cls, model): raise ImproperlyConfigured("error!") class MyModelAdmin(admin.ModelAdmin): validator_class = MyValidator errors = MyModelAdmin.check(model=Song) expected = [ checks.Error( 'error!', hint=None, obj=MyModelAdmin, ) ] self.assertEqual(errors, expected) def test_check_sublists_for_duplicates(self): class MyModelAdmin(admin.ModelAdmin): fields = ['state', ['state']] errors = MyModelAdmin.check(model=Song) expected = [ checks.Error( "The value of 'fields' contains duplicate field(s).", hint=None, obj=MyModelAdmin, id='admin.E006' ) ] self.assertEqual(errors, expected) def test_check_fieldset_sublists_for_duplicates(self): class MyModelAdmin(admin.ModelAdmin): fieldsets = [ (None, { 'fields': ['title', 'album', ('title', 'album')] }), ] errors = MyModelAdmin.check(model=Song) expected = [ checks.Error( "There are duplicate field(s) in 'fieldsets[0][1]'.", hint=None, obj=MyModelAdmin, id='admin.E012' ) ] self.assertEqual(errors, expected) def test_list_filter_works_on_through_field_even_when_apps_not_ready(self): """ Ensure list_filter can access reverse fields even when the app registry is not ready; refs #24146. """ class BookAdminWithListFilter(admin.ModelAdmin): list_filter = ['authorsbooks__featured'] # Temporarily pretending apps are not ready yet. This issue can happen # if the value of 'list_filter' refers to a 'through__field'. Book._meta.apps.ready = False try: errors = BookAdminWithListFilter.check(model=Book) self.assertEqual(errors, []) finally: Book._meta.apps.ready = True Django-1.8.7/tests/admin_checks/__init__.py0000664000175000017500000000000012603513307020210 0ustar timtim00000000000000Django-1.8.7/tests/admin_checks/models.py0000664000175000017500000000327412625116213017753 0ustar timtim00000000000000""" Tests of ModelAdmin system checks logic. """ from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible class Album(models.Model): title = models.CharField(max_length=150) @python_2_unicode_compatible class Song(models.Model): title = models.CharField(max_length=150) album = models.ForeignKey(Album) original_release = models.DateField(editable=False) class Meta: ordering = ('title',) def __str__(self): return self.title def readonly_method_on_model(self): # does nothing pass class TwoAlbumFKAndAnE(models.Model): album1 = models.ForeignKey(Album, related_name="album1_set") album2 = models.ForeignKey(Album, related_name="album2_set") e = models.CharField(max_length=1) class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): name = models.CharField(max_length=100) subtitle = models.CharField(max_length=100) price = models.FloatField() authors = models.ManyToManyField(Author, through='AuthorsBooks') class AuthorsBooks(models.Model): author = models.ForeignKey(Author) book = models.ForeignKey(Book) featured = models.BooleanField() class State(models.Model): name = models.CharField(max_length=15) class City(models.Model): state = models.ForeignKey(State) class Influence(models.Model): name = models.TextField() content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') Django-1.8.7/tests/app_loading/0000775000175000017500000000000012625116243015760 5ustar timtim00000000000000Django-1.8.7/tests/app_loading/eggs/0000775000175000017500000000000012625116243016705 5ustar timtim00000000000000Django-1.8.7/tests/app_loading/eggs/modelapp.egg0000664000175000017500000000642312603513307021175 0ustar timtim00000000000000PK7jŽn†¿bæø&6°¸/ÀŠ_lÅ~~ÎÇçÎôíëýê~-c?e÷û †‚'§ R L¨À&p¯À¤ð ðp[¥`2˜åÜa¿ÙrÅMú¹Aç)åð¢é×CäýL†ì1 7&`S¡«2(g¹TBÚdÐzhzÔš¦!aÖ…ã´”*Úf¸;|>·è É ´1’¬Á0´ ëLÝë©XÚáè°Æ®«›qÔKK:ê­ãUŒ«R”Š Ÿ0LÔMÈ·äo›¡›¦*¤èZÚBË?Œ‹ÇUOÔŸ„ÝìPK–hŽ<¾êÉê>app_with_models/tests.pyuRAjÃ0¼ü‡A'‡C} $PJ?кg³±W±Š,¹’Œ1¥¯¤8ÐKíÎîìì !DY´£òJ3ž¬ñÁQ`°Z JJvl|ØtLZ‰ˆÊšXoûÌ€—ÅbTH™CvdÏX•Ö¸Ø0b&ﱎl°Ùn1ºr=o™SÔeQo?aM6ú|>ïØé„&þ·pvï'¡¿PKËnŽ2_¤#ŠžPè×2´êkîá9<ÃÍõžÈ«ŽkÓì&SÑ*F¹ zœá>JÅ•þ`½{G§ŽzNê*¥)‚¸ eÓ)-Ö¡œÏöÊœO×r»]#¡‚P;+¤vë©ûGT bÃ* .„"Œw©\^[:8¡, Ãw²(Ê‚JuÍ|xjàV­ä4Urºw¹·d¿6øpd È ¼ þ˜ªI1D™Ìçó>2›Á4Y¶Lü„þ“ã+½ _(xŠ}8 *>q³µeäÔ~ý›9Ð)n?_(÷òm>¦äÇâ'“Áè/PK–hŽ<º-„[app_with_models/views.pySVp.JM,IU¨Ì/-R(ËL-/VÈH-JÕãåPKËnŽ<›ÛÉMe…app_with_models/views.pyc»ø‰—kÍ×£ÞÉ PÀÄ@\Ì$R‚5@"~ 94¢XH$•fæ¤Ä$¥d—è•gæŤ¦§Ç$Ä—g–dÄçæ§¤æÇ”e¦–ëT–pµØKsRí@ƒ PK–hŽ<app_with_models/__init__.pyPKËn޶Õapp_with_models/tests.pyPKËnŽ;ämÓ-XjdµvG d2a2[x{ÁB"¶ßÏãQaEi‰Kp›ÕÚš„Á&ž1C­ÖüöËBò nŒûxºEkvLè+<°³aÑ'Ëx$H¹ãCš°´~¹Å6Iªúyüê9ƒÞY¨£7PKÌA<£hNÆdªEGG-INFO/SOURCES.txtuÊA „0 @ѽw±BTAEq†i”bMboïèZ·ÿý€º‰‘˜|E€V¶èCàÈ)À%Äwüç9u4qÖÕUúiÊö‡vìób0zè£[$‹ô‹à-áuT𸣿ŽPKÌA<¯ïà£EGG-INFO/top_level.txtK,(ˆÏËÏÍOIÍ)æPKÌA<“×2EGG-INFO/zip-safeãPKhŽ<¶app_no_models/__init__.pyPKÌAù¹broken_app/models.pycÛü‰—Kõ˜w20±‹‰ bdÈf`Èadˆbd`LabÖ©ÈüŒ@V ÈÍOIÍ©òÓ` p‚4f%æ¥çë¥$ÌÔ@%е€DRifNJLRJfq‰^yfž±QLjzzLRQ~vj^|bAA ØÌb½‚Êb r ·4'Õde1ÈPKè]‹<broken_app/__init__.pyPKŽB<]ÿ‘’dƒbroken_app/__init__.pycÛü‰—‹åýAïd(`b .f) Á ? Q¬$’J3sRb’R2‹KôÊ3óŒbRÓÓc’Šò³Sóâ bâã3ó2Kâãõ *‹9€lróSJsRí@ƃŒPKŽB<“×2EGG-INFO/dependency_links.txtãPKŽB<»Ï·ðw¿EGG-INFO/PKG-INFOóM-ILI,IÔ K-*ÎÌϳR0Ô3àåòKÌMµRH*ÊÏNÍK,(àåB•.ÍÍM,ª´Rõóöó÷ãåòÈÏMÕ-HLOEs,-ÉÈ/ÂÐMÍMÌÌAöÉLNÍ+FÖé’Zœ\”YP¶.“X’–_”‹$PKŽB<V8ih¸EGG-INFO/SOURCES.txt+N-)-Ð+¨äJ*ÊÏNÍ‹O,(ÐÏÌË,‰GÎÍOIÍ)FÅôRÓÓu3óÒòõ¼Ýu=ýÜü±Éû‡9»ë•T”`“NI-HÍKIÍK®ŒÏÉÌË.Æ¥®$¿ >'µ,5¤PKŽB<„jÖ EGG-INFO/top_level.txtK*ÊÏNÍ‹O,(àPKŽB<“×2EGG-INFO/zip-safeãPK[qŽ<¾gå¢ ¶broken_app/models.pyPKŽB<Å>ù¹¶Rbroken_app/models.pycPKè]‹<¶broken_app/__init__.pyPKŽB<]ÿ‘’dƒ¶Hbroken_app/__init__.pycPKŽB<“×2¶áEGG-INFO/dependency_links.txtPKŽB<»Ï·ðw¿¶EGG-INFO/PKG-INFOPKŽB<V8ih¸¶ÅEGG-INFO/SOURCES.txtPKŽB<„jÖ ¶_EGG-INFO/top_level.txtPKŽB<“×2¶ EGG-INFO/zip-safePK ]ÒDjango-1.8.7/tests/app_loading/eggs/omelet.egg0000664000175000017500000002147412603513307020664 0ustar timtim00000000000000PKBjŽ<“×2EGG-INFO/dependency_links.txtãPKBjŽ<“×2EGG-INFO/not-zip-safeãPKBjŽ<±øRCs¼EGG-INFO/PKG-INFOóM-ILI,IÔ K-*ÎÌϳR0Ô3àåòKÌMµRÈÏMÍI-áåB• .ÍÍM,ª´Rõóöó÷ãåò*Ô-HLOEs,-ÉÈ/ÂÐMÍMÌÌAöÉLNÍ+FÖé’Zœ\”YP¶.“X’–_”‹$PKBjŽ<Øu"ÄçéEGG-INFO/SOURCES.txt•—QSÛ0 Çß÷]šÛxæÆn·»±·gŸ«‰Á±}¶ÒŸ~JÔ-ImžK?9±ä¿ì¤Þîé“íP#•B(£HˆÈÔ#C@"eš™z¯£aM³RfcË?¿~¬~þ¾¹}縻ý÷÷úû]AôÎ'Ñ¡‘hê'¡•y³Y'4nQGޜƊÎJÔan-#0(j?€L?'Â@§ü[…ÃÎß“â!gSÛf?vžÓùä¼2´7öÐÕ-úÑô8X/>u²ì 3ãºL–ײL#¨ÍL³3ƒì”9²©ÎYOÂcè5ϳOKd|KEd›ù†·ŒD6ƒ@Pž]]})^ß9Övúš}Ë®’Ðùz½ÎÎr óèst‘]æ@éŒ3”Î8CéŒ3´˜qÂÎiàmT^¬/Äh*Zêô `ÜXiš¼,ûÄË­§%ÒºAÚHyÖp à~,íbUGÆ*X²; H>t‘ð„u/h.Î3áá=Ï@×µ#¡ˆ§]‚ |¤Ôt ¶ž@Z„d‚óŒµDª<ÃSÀúüü‹9òpH—³nª$™«#{ÁYqé†OK¥@ãÉ¿G(o>ªn(oÚÿ²PGëI`ʬlסåCìËBÝ'‰ÁÿŽöÛ3gCßZÚZ×?qO×´^­ žTiMi¤zI|#D×ïQ)³us·ùà\ĤNª&ž|’…RʵíÏHK!AŠÛx¬æ*` ÆŒ@cæ÷°"³7½È3ßhiôÆÀj…Ñ z2¦¸fý4×ÙÍ—>þPKBjŽ<Þi­(ÒÂomelet/manage.pycMQÁnÓ@uœ¶Z"„zÞ ØHÅà„ª„8DHàD‘¬­wšljïZ»cÒp„ ?Æð+ˆ;Ì:‰Ú‘w4;ÏûæÍÌï?ÇÙÏ¿¿ÞÕ°³C>ç|Âwvš?W€™±™ ©®˜%ðÉžCŠ#Xeà^€`Û¡)ÌRÀÌÌèà!œþ`‚˜;B¼ØÄg¼z «Ü Ã0Ùb|˜écqÀ2Ì?¶BpD÷Ùá5Ö=aÕ*«è߇1Co½wþ¥|£lNòÒX-i‰4(ó€DÆ.BÙmriì€hã±&ç7²v–”±üƒ|äK9%©º•rãúü+ʺäZó #çÀ“}a¤iäR1LNúÞJ½Rváž*ÝË…Îd§Bˆ¤†"‘—{²uºo°ÌŠéåÈ[¥vX§F®ˆ×&ГäAÖª••Ó¶sž†¦ep-.ݺ|’™aFGì*žŽ±UUd1sw9è+kç±ÜN®EK`ÿb/€îòå="¸ 7H£÷4æpí á®Vì ª(–ch—µªål+ܸð˜ÝEo=¿ÐÜZ¹6öù³9.sn£AšoÅñšWÛi½NâÛ »‰‰‰8÷ÄCñ 9ÿPKzgŽø `Ö†ÑßãQ•í9S<¦n Ï¥ôªŒý%]2Ëdo¾‚X>™³õzíóÓ3¹ÁÆÓ)G¹¬ÒrÌp‰/8SÚOMž5ƒ`tqM)êŽ{ÖN&ŽKm­›ö¡ö{@ipªãþ´·‡Ì]·^:÷·¾¢s]-[@CÀê IÚ«ÇǤ;¶ÉïÐX¦±â…Á¤žÒõË<ÞcúBÊ]ËÀìú¯_-‘³õr~ìÿ…æV*«­n K(Á¯%€-K¤ð Ñ)Å×Ê©ØÌe‚(Ÿ†ÝI8‹þ¿ ¼£³cv–ýÄéÍõë·w›ø—ì½yûîõ+³xwòF¬OZ¦õÏÛÛäͯ׭ÖÏïÞˆò¤î(С±UÆØ?怔!×Ä5ƒCJ” Œ› ‹Â¡eE—%Öi© Óø;‹Ð`ôÜÚrX/(n©EøXÌØTpîW v`I^ûZQ]/ÔÞ³µÈ“Œl¹Ü½í¬~¯7?ègXÂGlÎy5³†$`Žsh°]ûnéßÛK#yîØ5¶²]SGÛϼ«N«…ß…ÇŸœ­ŸWGK“î²ðí;¶òyþL³¥°#cèh™mè°M`íuÇ£@·&û¥ÊÜ4¸ßŠû“+ðeiª•EÛÔ&ôW½ §LÇmw€ËæA÷Vuع?ôkÉA¶¦mkj¬M\Õbg+&p¨Fö–§gk ®/ݨ¶…¿Û£qÀPP §ëª¢ùU`%ŸƒAØ‹‚ËËG°³›š}aÇ7¤Él ¦ŸCÒÞsXïöÖy­Ï¡I D²ÛVì† O»R »q!ÝöÎóšç—ð´BÃPKBjŽ<.Ky*yomelet/settings.pycåTÛnÛFêBùž4Iчô …R ´ã‹âEFܸD(R é¦%Œ”v-¯Ã‹À]Áu¿ Ò/éôWŠ~@gÉÈfsiŸŠ>T‹Ù9ܳsv5˜ß~ß\ûù_ŸOáÕ¯‹ó Nñ iÀ4ˆZ@?„¨ ¬ QÎ58×P ¿µkש]·vzízèV`u®B„´5ˆz@×!Z©¬Vv po´^Ù  mÝ„h èGÝú1D·¾Ñ{@?èÐO!º ô>ýÏ”à»hèE’Ï ƒNŒI2}Ér* ©#NÜcÛ%R]PvиæˆT‹“€ørc3^x¾U¡ßxAX-ƞʞ:˜%‹TŠ[¸63Vòi²31‚¹hÒƒkß‘F”gÆÍ·œ]6v?~3M²çMª‰1Ë%¾»äÿ$1C9ÉŒýEâ¨ÆÄu$K™4e*šUÔ!+bäÕOýàÝo$î½å—o'-U»}U¬ý–ª¨»úÝײü¿k¢~ùîk=å?­†þ½eç Ë“J›EžžË-‘ÑØ1C×êw¦5²Ý jl#Ó5‰ÈUE2Có©: í‰#ã&FŽéŸàÖxèY¤jz‡ÚVu 6ÊØ~tä^Σ]W®©Ä²ÍØ÷¼°:´O|GÞY ‰klì“göw' CŸ„ñsò½¼Ý¼ã™–Òª¨#Û²òÂôQ‘ƒÝEo ¬©ã‡žû¬Ò}s}™êAðê¡é8ÄŠÍñ8¨ÿÎk#”,xJO'” i\ò|ï”Íf§u8LJžÏ„1¿ªnûUVÐEʾn+¶…F×ô–ÞÑ[m­­õ®ÇÖºÂpªÑÅÑÖ; é]ÐêqKï-£?PKzgŽþ…ŒflÌ'Ùú­O¿Ôr ?”?“lÉwr6JlŠ`\O!Á]Y”Å ^|KΡOÞ<^sp!°Æc„D€^59©´3~;~ê–„L3—eѨá@ª!‘6±¥O ÕzDæfz•áæ°¢. Ègû«r½Åí|¯‚x%‡ÓFÔ`|k•˜ždGÄÅz]õ§?K[ÜËFS˾ AKP^焱4"YœµQÀ\-Ïáñéô¼;öo»ãñt3–CÖ#N%C~Ñù7{Ñü_Ì­ð†ØÅïËù ¾N‰&!S’WòPKBjŽ< (¨Õ°ãomelet/urls.pyc»ø‰—«ãËQïd(`b .–)@ÄÈÍÀ’ÊÈÂÄÐÌÈÅÄÂ̬Á”ÎüŒ@V ˆÐ*à§2¢D¤=+1/=_/9?/M¯´(§X/%5-±4§¤¸„([XR’Z”W\ ä¥a| )¢XH$•fæ¤Ä$¥d—è•gæŤ¦§Çäç¦æ¤–Ä€.¨j“›ŸRš“jrO1àbPKzgŽ<omelet/__init__.pyPKBjŽ<û†X_omelet/__init__.pyc»ø‰—«ãËQïd(`b .f) Á ? Q "“J3sRb’R2‹KôÊ3óŒbRÓÓcòsSsRKbâã3ó2Kâãõ *K8€ŠmróSJsRí@FƒŒPKhŽ< omelet/app_no_models/__init__.pyPKBjŽm!omelet/app_no_models/__init__.pyc»ø‰—ká—£ÞÉ PÀÄ@\Ì$R‚5@"~ 94¢Ø H$•fæ¤Ä$¥d—è•gæŤ¦§Çäç¦æ¤–Ä$ÄçåÇçæ§¤æÇÄÇgæe–ÄÇëT–pµÚÅKsRí@ƒ PK7jŽ÷¦o_ï×÷ðk ûûpÀ`ù(xð ÊF€ \ l k€W€»"›À<Ýâ÷Í–*ŽhÂÐtý@1‡—e³"ïç2dÆ€ Ü‹…®H Ÿ§Ri›Á˜¶lК†„YžÓ\ªh—áª%¬ñå¡·$_ U#Ɇc†já¼Õ•ueK×j¬kÝ5è‘tÙ÷féèÑŒ²õxeý*Á¢fˆ&ê6 3vdEOe[w™­F1Rt#m¡åÿ„5œŒ?UJö“PK–hŽ<¾êÉê>omelet/app_with_models/tests.pyuRAjÃ0¼ü‡A'‡C} $PJ?кg³±W±Š,¹’Œ1¥¯¤8ÐKíÎîìì !DY´£òJ3ž¬ñÁQ`°Z JJvl|ØtLZ‰ˆÊšXoûÌ€—ÅbTH™CvdÏX•Ö¸Ø0b&ﱎl°Ùn1ºr=o™SÔeQo?aM6ú|>ïØé„&þ·pvï'¡¿PKBjŽEGG-INFO/not-zip-safePKBjŽ<±øRCs¼¶tEGG-INFO/PKG-INFOPKBjŽ<Øu"Äçé¶EGG-INFO/SOURCES.txtPKBjŽomelet/urls.pyPKBjŽ< (¨Õ°ã¶yomelet/urls.pycPKzgŽ<¶Vomelet/__init__.pyPKBjŽ<û†X_¶ˆomelet/__init__.pycPKhŽ< ¶omelet/app_no_models/__init__.pyPKBjŽm!¶Xomelet/app_no_models/__init__.pycPK7j޶íomelet/app_with_models/tests.pyPKBjŽ%s' % (link, i)) list_display = m.get_list_display(request) list_display_links = m.get_list_display_links(request, list_display) self.assertEqual(list_display, ('parent', 'name', 'age')) self.assertEqual(list_display_links, ['age']) def test_no_list_display_links(self): """#15185 -- Allow no links from the 'change list' view grid.""" p = Parent.objects.create(name='parent') m = NoListDisplayLinksParentAdmin(Parent, admin.site) superuser = self._create_superuser('superuser') request = self._mocked_authenticated_request('/parent/', superuser) response = m.changelist_view(request) link = reverse('admin:admin_changelist_parent_change', args=(p.pk,)) self.assertNotContains(response, '' % link) def test_tuple_list_display(self): """ Regression test for #17128 (ChangeList failing under Python 2.5 after r16319) """ swallow = Swallow.objects.create(origin='Africa', load='12.34', speed='22.2') swallow2 = Swallow.objects.create(origin='Africa', load='12.34', speed='22.2') swallow_o2o = SwallowOneToOne.objects.create(swallow=swallow2) model_admin = SwallowAdmin(Swallow, admin.site) superuser = self._create_superuser('superuser') request = self._mocked_authenticated_request('/swallow/', superuser) response = model_admin.changelist_view(request) # just want to ensure it doesn't blow up during rendering self.assertContains(response, six.text_type(swallow.origin)) self.assertContains(response, six.text_type(swallow.load)) self.assertContains(response, six.text_type(swallow.speed)) # Reverse one-to-one relations should work. self.assertContains(response, '(None)') self.assertContains(response, '%s' % swallow_o2o) def test_deterministic_order_for_unordered_model(self): """ Ensure that the primary key is systematically used in the ordering of the changelist's results to guarantee a deterministic order, even when the Model doesn't have any default ordering defined. Refs #17198. """ superuser = self._create_superuser('superuser') for counter in range(1, 51): UnorderedObject.objects.create(id=counter, bool=True) class UnorderedObjectAdmin(admin.ModelAdmin): list_per_page = 10 def check_results_order(ascending=False): admin.site.register(UnorderedObject, UnorderedObjectAdmin) model_admin = UnorderedObjectAdmin(UnorderedObject, admin.site) counter = 0 if ascending else 51 for page in range(0, 5): request = self._mocked_authenticated_request('/unorderedobject/?p=%s' % page, superuser) response = model_admin.changelist_view(request) for result in response.context_data['cl'].result_list: counter += 1 if ascending else -1 self.assertEqual(result.id, counter) admin.site.unregister(UnorderedObject) # When no order is defined at all, everything is ordered by '-pk'. check_results_order() # When an order field is defined but multiple records have the same # value for that field, make sure everything gets ordered by -pk as well. UnorderedObjectAdmin.ordering = ['bool'] check_results_order() # When order fields are defined, including the pk itself, use them. UnorderedObjectAdmin.ordering = ['bool', '-pk'] check_results_order() UnorderedObjectAdmin.ordering = ['bool', 'pk'] check_results_order(ascending=True) UnorderedObjectAdmin.ordering = ['-id', 'bool'] check_results_order() UnorderedObjectAdmin.ordering = ['id', 'bool'] check_results_order(ascending=True) def test_deterministic_order_for_model_ordered_by_its_manager(self): """ Ensure that the primary key is systematically used in the ordering of the changelist's results to guarantee a deterministic order, even when the Model has a manager that defines a default ordering. Refs #17198. """ superuser = self._create_superuser('superuser') for counter in range(1, 51): OrderedObject.objects.create(id=counter, bool=True, number=counter) class OrderedObjectAdmin(admin.ModelAdmin): list_per_page = 10 def check_results_order(ascending=False): admin.site.register(OrderedObject, OrderedObjectAdmin) model_admin = OrderedObjectAdmin(OrderedObject, admin.site) counter = 0 if ascending else 51 for page in range(0, 5): request = self._mocked_authenticated_request('/orderedobject/?p=%s' % page, superuser) response = model_admin.changelist_view(request) for result in response.context_data['cl'].result_list: counter += 1 if ascending else -1 self.assertEqual(result.id, counter) admin.site.unregister(OrderedObject) # When no order is defined at all, use the model's default ordering (i.e. 'number') check_results_order(ascending=True) # When an order field is defined but multiple records have the same # value for that field, make sure everything gets ordered by -pk as well. OrderedObjectAdmin.ordering = ['bool'] check_results_order() # When order fields are defined, including the pk itself, use them. OrderedObjectAdmin.ordering = ['bool', '-pk'] check_results_order() OrderedObjectAdmin.ordering = ['bool', 'pk'] check_results_order(ascending=True) OrderedObjectAdmin.ordering = ['-id', 'bool'] check_results_order() OrderedObjectAdmin.ordering = ['id', 'bool'] check_results_order(ascending=True) def test_dynamic_list_filter(self): """ Regression tests for ticket #17646: dynamic list_filter support. """ parent = Parent.objects.create(name='parent') for i in range(10): Child.objects.create(name='child %s' % i, parent=parent) user_noparents = self._create_superuser('noparents') user_parents = self._create_superuser('parents') # Test with user 'noparents' m = DynamicListFilterChildAdmin(Child, admin.site) request = self._mocked_authenticated_request('/child/', user_noparents) response = m.changelist_view(request) self.assertEqual(response.context_data['cl'].list_filter, ['name', 'age']) # Test with user 'parents' m = DynamicListFilterChildAdmin(Child, admin.site) request = self._mocked_authenticated_request('/child/', user_parents) response = m.changelist_view(request) self.assertEqual(response.context_data['cl'].list_filter, ('parent', 'name', 'age')) def test_dynamic_search_fields(self): child = self._create_superuser('child') m = DynamicSearchFieldsChildAdmin(Child, admin.site) request = self._mocked_authenticated_request('/child/', child) response = m.changelist_view(request) self.assertEqual(response.context_data['cl'].search_fields, ('name', 'age')) def test_pagination_page_range(self): """ Regression tests for ticket #15653: ensure the number of pages generated for changelist views are correct. """ # instantiating and setting up ChangeList object m = GroupAdmin(Group, admin.site) request = self.factory.get('/group/') cl = ChangeList(request, Group, m.list_display, m.list_display_links, m.list_filter, m.date_hierarchy, m.search_fields, m.list_select_related, m.list_per_page, m.list_max_show_all, m.list_editable, m) per_page = cl.list_per_page = 10 for page_num, objects_count, expected_page_range in [ (0, per_page, []), (0, per_page * 2, list(range(2))), (5, per_page * 11, list(range(11))), (5, per_page * 12, [0, 1, 2, 3, 4, 5, 6, 7, 8, '.', 10, 11]), (6, per_page * 12, [0, 1, '.', 3, 4, 5, 6, 7, 8, 9, 10, 11]), (6, per_page * 13, [0, 1, '.', 3, 4, 5, 6, 7, 8, 9, '.', 11, 12]), ]: # assuming we have exactly `objects_count` objects Group.objects.all().delete() for i in range(objects_count): Group.objects.create(name='test band') # setting page number and calculating page range cl.page_num = page_num cl.get_results(request) real_page_range = pagination(cl)['page_range'] self.assertListEqual( expected_page_range, list(real_page_range), ) class AdminLogNodeTestCase(TestCase): def test_get_admin_log_templatetag_custom_user(self): """ Regression test for ticket #20088: admin log depends on User model having id field as primary key. The old implementation raised an AttributeError when trying to use the id field. """ context = Context({'user': CustomIdUser()}) template_string = '{% load log %}{% get_admin_log 10 as admin_log for_user user %}' template = Template(template_string) # Rendering should be u'' since this templatetag just logs, # it doesn't render any string. self.assertEqual(template.render(context), '') @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF="admin_changelist.urls") class SeleniumFirefoxTests(AdminSeleniumWebDriverTestCase): available_apps = ['admin_changelist'] + AdminSeleniumWebDriverTestCase.available_apps fixtures = ['users.json'] webdriver_class = 'selenium.webdriver.firefox.webdriver.WebDriver' def test_add_row_selection(self): """ Ensure that the status line for selected rows gets updated correcly (#22038) """ self.admin_login(username='super', password='secret') self.selenium.get('%s%s' % (self.live_server_url, reverse('admin:auth_user_changelist'))) form_id = '#changelist-form' # Test amount of rows in the Changelist rows = self.selenium.find_elements_by_css_selector( '%s #result_list tbody tr' % form_id) self.assertEqual(len(rows), 1) # Test current selection selection_indicator = self.selenium.find_element_by_css_selector( '%s .action-counter' % form_id) self.assertEqual(selection_indicator.text, "0 of 1 selected") # Select a row and check again row_selector = self.selenium.find_element_by_css_selector( '%s #result_list tbody tr:first-child .action-select' % form_id) row_selector.click() self.assertEqual(selection_indicator.text, "1 of 1 selected") class SeleniumChromeTests(SeleniumFirefoxTests): webdriver_class = 'selenium.webdriver.chrome.webdriver.WebDriver' class SeleniumIETests(SeleniumFirefoxTests): webdriver_class = 'selenium.webdriver.ie.webdriver.WebDriver' Django-1.8.7/tests/admin_changelist/fixtures/0000775000175000017500000000000012625116243020645 5ustar timtim00000000000000Django-1.8.7/tests/admin_changelist/fixtures/users.json0000664000175000017500000000074012625113144022677 0ustar timtim00000000000000[ { "pk": 100, "model": "auth.user", "fields": { "username": "super", "first_name": "Super", "last_name": "User", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2007-05-30 13:20:10", "groups": [], "user_permissions": [], "password": "sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158", "email": "super@example.com", "date_joined": "2007-05-30 13:20:10" } } ] Django-1.8.7/tests/admin_changelist/__init__.py0000664000175000017500000000000012603513307021071 0ustar timtim00000000000000Django-1.8.7/tests/admin_changelist/models.py0000664000175000017500000000515212625116213020631 0ustar timtim00000000000000from django.db import models from django.utils.encoding import python_2_unicode_compatible class Event(models.Model): # Oracle can have problems with a column named "date" date = models.DateField(db_column="event_date") class Parent(models.Model): name = models.CharField(max_length=128) class Child(models.Model): parent = models.ForeignKey(Parent, editable=False, null=True) name = models.CharField(max_length=30, blank=True) age = models.IntegerField(null=True, blank=True) class Genre(models.Model): name = models.CharField(max_length=20) class Band(models.Model): name = models.CharField(max_length=20) nr_of_members = models.PositiveIntegerField() genres = models.ManyToManyField(Genre) @python_2_unicode_compatible class Musician(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.name @python_2_unicode_compatible class Group(models.Model): name = models.CharField(max_length=30) members = models.ManyToManyField(Musician, through='Membership') def __str__(self): return self.name class Membership(models.Model): music = models.ForeignKey(Musician) group = models.ForeignKey(Group) role = models.CharField(max_length=15) class Quartet(Group): pass class ChordsMusician(Musician): pass class ChordsBand(models.Model): name = models.CharField(max_length=30) members = models.ManyToManyField(ChordsMusician, through='Invitation') class Invitation(models.Model): player = models.ForeignKey(ChordsMusician) band = models.ForeignKey(ChordsBand) instrument = models.CharField(max_length=15) class Swallow(models.Model): origin = models.CharField(max_length=255) load = models.FloatField() speed = models.FloatField() class Meta: ordering = ('speed', 'load') class SwallowOneToOne(models.Model): swallow = models.OneToOneField(Swallow) class UnorderedObject(models.Model): """ Model without any defined `Meta.ordering`. Refs #17198. """ bool = models.BooleanField(default=True) class OrderedObjectManager(models.Manager): def get_queryset(self): return super(OrderedObjectManager, self).get_queryset().order_by('number') class OrderedObject(models.Model): """ Model with Manager that defines a default order. Refs #17198. """ name = models.CharField(max_length=255) bool = models.BooleanField(default=True) number = models.IntegerField(default=0, db_column='number_val') objects = OrderedObjectManager() class CustomIdUser(models.Model): uuid = models.AutoField(primary_key=True) Django-1.8.7/tests/admin_changelist/admin.py0000664000175000017500000000676212625116213020446 0ustar timtim00000000000000from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from django.core.paginator import Paginator from .models import Child, Event, Parent, Swallow site = admin.AdminSite(name="admin") site.register(User, UserAdmin) class CustomPaginator(Paginator): def __init__(self, queryset, page_size, orphans=0, allow_empty_first_page=True): super(CustomPaginator, self).__init__(queryset, 5, orphans=2, allow_empty_first_page=allow_empty_first_page) class EventAdmin(admin.ModelAdmin): list_display = ['event_date_func'] def event_date_func(self, event): return event.date site.register(Event, EventAdmin) class ParentAdmin(admin.ModelAdmin): list_filter = ['child__name'] search_fields = ['child__name'] class ChildAdmin(admin.ModelAdmin): list_display = ['name', 'parent'] list_per_page = 10 list_filter = ['parent', 'age'] def get_queryset(self, request): return super(ChildAdmin, self).get_queryset(request).select_related("parent__name") class CustomPaginationAdmin(ChildAdmin): paginator = CustomPaginator class FilteredChildAdmin(admin.ModelAdmin): list_display = ['name', 'parent'] list_per_page = 10 def get_queryset(self, request): return super(FilteredChildAdmin, self).get_queryset(request).filter( name__contains='filtered') class BandAdmin(admin.ModelAdmin): list_filter = ['genres'] class GroupAdmin(admin.ModelAdmin): list_filter = ['members'] class QuartetAdmin(admin.ModelAdmin): list_filter = ['members'] class ChordsBandAdmin(admin.ModelAdmin): list_filter = ['members'] class InvitationAdmin(admin.ModelAdmin): list_display = ('band', 'player') list_select_related = ('player',) class DynamicListDisplayChildAdmin(admin.ModelAdmin): list_display = ('parent', 'name', 'age') def get_list_display(self, request): my_list_display = super(DynamicListDisplayChildAdmin, self).get_list_display(request) if request.user.username == 'noparents': my_list_display = list(my_list_display) my_list_display.remove('parent') return my_list_display class DynamicListDisplayLinksChildAdmin(admin.ModelAdmin): list_display = ('parent', 'name', 'age') list_display_links = ['parent', 'name'] def get_list_display_links(self, request, list_display): return ['age'] site.register(Child, DynamicListDisplayChildAdmin) class NoListDisplayLinksParentAdmin(admin.ModelAdmin): list_display_links = None site.register(Parent, NoListDisplayLinksParentAdmin) class SwallowAdmin(admin.ModelAdmin): actions = None # prevent ['action_checkbox'] + list(list_display) list_display = ('origin', 'load', 'speed', 'swallowonetoone') site.register(Swallow, SwallowAdmin) class DynamicListFilterChildAdmin(admin.ModelAdmin): list_filter = ('parent', 'name', 'age') def get_list_filter(self, request): my_list_filter = super(DynamicListFilterChildAdmin, self).get_list_filter(request) if request.user.username == 'noparents': my_list_filter = list(my_list_filter) my_list_filter.remove('parent') return my_list_filter class DynamicSearchFieldsChildAdmin(admin.ModelAdmin): search_fields = ('name',) def get_search_fields(self, request): search_fields = super(DynamicSearchFieldsChildAdmin, self).get_search_fields(request) search_fields += ('age',) return search_fields Django-1.8.7/tests/absolute_url_overrides/0000775000175000017500000000000012625116243020265 5ustar timtim00000000000000Django-1.8.7/tests/absolute_url_overrides/tests.py0000664000175000017500000000371312625113144022002 0ustar timtim00000000000000from django.db import models from django.test import TestCase class AbsoluteUrlOverrideTests(TestCase): def test_get_absolute_url(self): """ get_absolute_url() functions as a normal method. """ get_absolute_url = lambda o: '/test-a/%s/' % o.pk TestA = self._create_model_class('TestA', get_absolute_url) self.assertTrue(hasattr(TestA, 'get_absolute_url')) obj = TestA(pk=1, name='Foo') self.assertEqual('/test-a/%s/' % obj.pk, obj.get_absolute_url()) def test_override_get_absolute_url(self): """ ABSOLUTE_URL_OVERRIDES should override get_absolute_url(). """ get_absolute_url = lambda o: '/test-b/%s/' % o.pk with self.settings( ABSOLUTE_URL_OVERRIDES={ 'absolute_url_overrides.testb': lambda o: '/overridden-test-b/%s/' % o.pk, }, ): TestB = self._create_model_class('TestB', get_absolute_url) obj = TestB(pk=1, name='Foo') self.assertEqual('/overridden-test-b/%s/' % obj.pk, obj.get_absolute_url()) def test_insert_get_absolute_url(self): """ ABSOLUTE_URL_OVERRIDES should work even if the model doesn't have a get_absolute_url() method. """ with self.settings( ABSOLUTE_URL_OVERRIDES={ 'absolute_url_overrides.testc': lambda o: '/test-c/%s/' % o.pk, }, ): TestC = self._create_model_class('TestC') obj = TestC(pk=1, name='Foo') self.assertEqual('/test-c/%s/' % obj.pk, obj.get_absolute_url()) def _create_model_class(self, class_name, get_absolute_url_method=None): attrs = { 'name': models.CharField(max_length=50), '__module__': 'absolute_url_overrides', } if get_absolute_url_method: attrs['get_absolute_url'] = get_absolute_url_method return type(class_name, (models.Model,), attrs) Django-1.8.7/tests/absolute_url_overrides/__init__.py0000664000175000017500000000000012603513307022362 0ustar timtim00000000000000Django-1.8.7/tests/admin_autodiscover/0000775000175000017500000000000012625116243017362 5ustar timtim00000000000000Django-1.8.7/tests/admin_autodiscover/tests.py0000664000175000017500000000145612603513307021102 0ustar timtim00000000000000from unittest import TestCase from django.contrib import admin class AdminAutoDiscoverTests(TestCase): """ Test for bug #8245 - don't raise an AlreadyRegistered exception when using autodiscover() and an admin.py module contains an error. """ def test_double_call_autodiscover(self): # The first time autodiscover is called, we should get our real error. with self.assertRaises(Exception) as cm: admin.autodiscover() self.assertEqual(str(cm.exception), "Bad admin module") # Calling autodiscover again should raise the very same error it did # the first time, not an AlreadyRegistered error. with self.assertRaises(Exception) as cm: admin.autodiscover() self.assertEqual(str(cm.exception), "Bad admin module") Django-1.8.7/tests/admin_autodiscover/__init__.py0000664000175000017500000000000012603513307021457 0ustar timtim00000000000000Django-1.8.7/tests/admin_autodiscover/models.py0000664000175000017500000000014612603513307021216 0ustar timtim00000000000000from django.db import models class Story(models.Model): title = models.CharField(max_length=10) Django-1.8.7/tests/admin_autodiscover/admin.py0000664000175000017500000000017412625116213021023 0ustar timtim00000000000000from django.contrib import admin from .models import Story admin.site.register(Story) raise Exception("Bad admin module") Django-1.8.7/tests/gis_tests/0000775000175000017500000000000012625116243015507 5ustar timtim00000000000000Django-1.8.7/tests/gis_tests/test_geoforms.py0000664000175000017500000003500412625116214020741 0ustar timtim00000000000000from unittest import skipUnless from django.contrib.gis.gdal import HAS_GDAL from django.forms import ValidationError from django.test import SimpleTestCase, skipUnlessDBFeature from django.utils import six from django.utils.html import escape if HAS_GDAL: from django.contrib.gis import forms from django.contrib.gis.geos import GEOSGeometry @skipUnless(HAS_GDAL, "GeometryFieldTest needs GDAL support") @skipUnlessDBFeature("gis_enabled") class GeometryFieldTest(SimpleTestCase): def test_init(self): "Testing GeometryField initialization with defaults." fld = forms.GeometryField() for bad_default in ('blah', 3, 'FoO', None, 0): self.assertRaises(ValidationError, fld.clean, bad_default) def test_srid(self): "Testing GeometryField with a SRID set." # Input that doesn't specify the SRID is assumed to be in the SRID # of the input field. fld = forms.GeometryField(srid=4326) geom = fld.clean('POINT(5 23)') self.assertEqual(4326, geom.srid) # Making the field in a different SRID from that of the geometry, and # asserting it transforms. fld = forms.GeometryField(srid=32140) tol = 0.0000001 xform_geom = GEOSGeometry('POINT (951640.547328465 4219369.26171664)', srid=32140) # The cleaned geometry should be transformed to 32140. cleaned_geom = fld.clean('SRID=4326;POINT (-95.363151 29.763374)') self.assertTrue(xform_geom.equals_exact(cleaned_geom, tol)) def test_null(self): "Testing GeometryField's handling of null (None) geometries." # Form fields, by default, are required (`required=True`) fld = forms.GeometryField() with six.assertRaisesRegex(self, forms.ValidationError, "No geometry value provided."): fld.clean(None) # This will clean None as a geometry (See #10660). fld = forms.GeometryField(required=False) self.assertIsNone(fld.clean(None)) def test_geom_type(self): "Testing GeometryField's handling of different geometry types." # By default, all geometry types are allowed. fld = forms.GeometryField() for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'): self.assertEqual(GEOSGeometry(wkt), fld.clean(wkt)) pnt_fld = forms.GeometryField(geom_type='POINT') self.assertEqual(GEOSGeometry('POINT(5 23)'), pnt_fld.clean('POINT(5 23)')) # a WKT for any other geom_type will be properly transformed by `to_python` self.assertEqual(GEOSGeometry('LINESTRING(0 0, 1 1)'), pnt_fld.to_python('LINESTRING(0 0, 1 1)')) # but rejected by `clean` self.assertRaises(forms.ValidationError, pnt_fld.clean, 'LINESTRING(0 0, 1 1)') def test_to_python(self): """ Testing to_python returns a correct GEOSGeometry object or a ValidationError """ fld = forms.GeometryField() # to_python returns the same GEOSGeometry for a WKT for wkt in ('POINT(5 23)', 'MULTIPOLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'LINESTRING(0 0, 1 1)'): self.assertEqual(GEOSGeometry(wkt), fld.to_python(wkt)) # but raises a ValidationError for any other string for wkt in ('POINT(5)', 'MULTI POLYGON(((0 0, 0 1, 1 1, 1 0, 0 0)))', 'BLAH(0 0, 1 1)'): self.assertRaises(forms.ValidationError, fld.to_python, wkt) def test_field_with_text_widget(self): class PointForm(forms.Form): pt = forms.PointField(srid=4326, widget=forms.TextInput) form = PointForm() cleaned_pt = form.fields['pt'].clean('POINT(5 23)') self.assertEqual(cleaned_pt, GEOSGeometry('POINT(5 23)')) self.assertEqual(4326, cleaned_pt.srid) point = GEOSGeometry('SRID=4326;POINT(5 23)') form = PointForm(data={'pt': 'POINT(5 23)'}, initial={'pt': point}) self.assertFalse(form.has_changed()) @skipUnless(HAS_GDAL, "SpecializedFieldTest needs GDAL support") @skipUnlessDBFeature("gis_enabled") class SpecializedFieldTest(SimpleTestCase): def setUp(self): self.geometries = { 'point': GEOSGeometry("SRID=4326;POINT(9.052734375 42.451171875)"), 'multipoint': GEOSGeometry("SRID=4326;MULTIPOINT(" "(13.18634033203125 14.504356384277344)," "(13.207969665527 14.490966796875)," "(13.177070617675 14.454917907714))"), 'linestring': GEOSGeometry("SRID=4326;LINESTRING(" "-8.26171875 -0.52734375," "-7.734375 4.21875," "6.85546875 3.779296875," "5.44921875 -3.515625)"), 'multilinestring': GEOSGeometry("SRID=4326;MULTILINESTRING(" "(-16.435546875 -2.98828125," "-17.2265625 2.98828125," "-0.703125 3.515625," "-1.494140625 -3.33984375)," "(-8.0859375 -5.9765625," "8.525390625 -8.7890625," "12.392578125 -0.87890625," "10.01953125 7.646484375))"), 'polygon': GEOSGeometry("SRID=4326;POLYGON(" "(-1.669921875 6.240234375," "-3.8671875 -0.615234375," "5.9765625 -3.955078125," "18.193359375 3.955078125," "9.84375 9.4921875," "-1.669921875 6.240234375))"), 'multipolygon': GEOSGeometry("SRID=4326;MULTIPOLYGON(" "((-17.578125 13.095703125," "-17.2265625 10.8984375," "-13.974609375 10.1953125," "-13.359375 12.744140625," "-15.732421875 13.7109375," "-17.578125 13.095703125))," "((-8.525390625 5.537109375," "-8.876953125 2.548828125," "-5.888671875 1.93359375," "-5.09765625 4.21875," "-6.064453125 6.240234375," "-8.525390625 5.537109375)))"), 'geometrycollection': GEOSGeometry("SRID=4326;GEOMETRYCOLLECTION(" "POINT(5.625 -0.263671875)," "POINT(6.767578125 -3.603515625)," "POINT(8.525390625 0.087890625)," "POINT(8.0859375 -2.13134765625)," "LINESTRING(" "6.273193359375 -1.175537109375," "5.77880859375 -1.812744140625," "7.27294921875 -2.230224609375," "7.657470703125 -1.25244140625))"), } def assertMapWidget(self, form_instance): """ Make sure the MapWidget js is passed in the form media and a MapWidget is actually created """ self.assertTrue(form_instance.is_valid()) rendered = form_instance.as_p() self.assertIn('new MapWidget(options);', rendered) self.assertIn('gis/js/OLMapWidget.js', str(form_instance.media)) def assertTextarea(self, geom, rendered): """Makes sure the wkt and a textarea are in the content""" self.assertIn('

    Attach images by dragging & dropping them or choose an image Octocat-spinner-32 Uploading your images now… Unfortunately we don't support that file type yet. Try image files less than 5MB. This browser doesn't support image attachments. Something went really wrong and we can't process that image.

    Commit_comment_tip

    Tip: You can also add notes to lines in a file. Hover to the left of a line to make a note

    Watch thread
    Thread notifications

    Not watching

    You only receive notifications for this thread if you participate or are @mentioned. Watch thread

    Watching

    Receive all notifications for this thread. Unwatch thread

    Ignoring

    You do not receive notifications for this thread. Stop ignoring thread

    You only receive notifications for this thread when you participate or are @mentioned.

    Something went wrong with that request. Please try again.
    Django-1.8.7/tests/utils_tests/test_itercompat.py0000664000175000017500000000056512603513307021651 0ustar timtim00000000000000from django.test import TestCase from .models import Category, Thing class TestIsIterator(TestCase): def test_regression(self): """This failed on Django 1.5/Py2.6 because category has a next method.""" category = Category.objects.create(name='category') Thing.objects.create(category=category) Thing.objects.filter(category=category) Django-1.8.7/tests/utils_tests/test_encoding.py0000664000175000017500000001070412625116214021264 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals import datetime import unittest from django.utils import six from django.utils.encoding import ( escape_uri_path, filepath_to_uri, force_bytes, force_text, iri_to_uri, uri_to_iri, ) from django.utils.http import urlquote_plus class TestEncodingUtils(unittest.TestCase): def test_force_text_exception(self): """ Check that broken __unicode__/__str__ actually raises an error. """ class MyString(object): def __str__(self): return b'\xc3\xb6\xc3\xa4\xc3\xbc' __unicode__ = __str__ # str(s) raises a TypeError on python 3 if the result is not a text type. # python 2 fails when it tries converting from str to unicode (via ASCII). exception = TypeError if six.PY3 else UnicodeError self.assertRaises(exception, force_text, MyString()) def test_force_bytes_exception(self): """ Test that force_bytes knows how to convert to bytes an exception containing non-ASCII characters in its args. """ error_msg = "This is an exception, voilà" exc = ValueError(error_msg) result = force_bytes(exc) self.assertEqual(result, error_msg.encode('utf-8')) def test_force_bytes_strings_only(self): today = datetime.date.today() self.assertEqual(force_bytes(today, strings_only=True), today) def test_escape_uri_path(self): self.assertEqual( escape_uri_path('/;some/=awful/?path/:with/@lots/&of/+awful/chars'), '/%3Bsome/%3Dawful/%3Fpath/:with/@lots/&of/+awful/chars' ) self.assertEqual(escape_uri_path('/foo#bar'), '/foo%23bar') self.assertEqual(escape_uri_path('/foo?bar'), '/foo%3Fbar') class TestRFC3987IEncodingUtils(unittest.TestCase): def test_filepath_to_uri(self): self.assertEqual(filepath_to_uri('upload\\чубака.mp4'), 'upload/%D1%87%D1%83%D0%B1%D0%B0%D0%BA%D0%B0.mp4') self.assertEqual(filepath_to_uri('upload\\чубака.mp4'.encode('utf-8')), 'upload/%D1%87%D1%83%D0%B1%D0%B0%D0%BA%D0%B0.mp4') def test_iri_to_uri(self): cases = [ # Valid UTF-8 sequences are encoded. ('red%09rosé#red', 'red%09ros%C3%A9#red'), ('/blog/for/Jürgen Münster/', '/blog/for/J%C3%BCrgen%20M%C3%BCnster/'), ('locations/%s' % urlquote_plus('Paris & Orléans'), 'locations/Paris+%26+Orl%C3%A9ans'), # Reserved chars remain unescaped. ('%&', '%&'), ('red&♥ros%#red', 'red&%E2%99%A5ros%#red'), ] for iri, uri in cases: self.assertEqual(iri_to_uri(iri), uri) # Test idempotency. self.assertEqual(iri_to_uri(iri_to_uri(iri)), uri) def test_uri_to_iri(self): cases = [ # Valid UTF-8 sequences are decoded. ('/%E2%99%A5%E2%99%A5/', '/♥♥/'), ('/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93', '/♥♥/?utf8=✓'), # Broken UTF-8 sequences remain escaped. ('/%AAd%AAj%AAa%AAn%AAg%AAo%AA/', '/%AAd%AAj%AAa%AAn%AAg%AAo%AA/'), ('/%E2%99%A5%E2%E2%99%A5/', '/♥%E2♥/'), ('/%E2%99%A5%E2%99%E2%99%A5/', '/♥%E2%99♥/'), ('/%E2%E2%99%A5%E2%99%A5%99/', '/%E2♥♥%99/'), ('/%E2%99%A5%E2%99%A5/?utf8=%9C%93%E2%9C%93%9C%93', '/♥♥/?utf8=%9C%93✓%9C%93'), ] for uri, iri in cases: self.assertEqual(uri_to_iri(uri), iri) # Test idempotency. self.assertEqual(uri_to_iri(uri_to_iri(uri)), iri) def test_complementarity(self): cases = [ ('/blog/for/J%C3%BCrgen%20M%C3%BCnster/', '/blog/for/J\xfcrgen M\xfcnster/'), ('%&', '%&'), ('red&%E2%99%A5ros%#red', 'red&♥ros%#red'), ('/%E2%99%A5%E2%99%A5/', '/♥♥/'), ('/%E2%99%A5%E2%99%A5/?utf8=%E2%9C%93', '/♥♥/?utf8=✓'), ('/%AAd%AAj%AAa%AAn%AAg%AAo%AA/', '/%AAd%AAj%AAa%AAn%AAg%AAo%AA/'), ('/%E2%99%A5%E2%E2%99%A5/', '/♥%E2♥/'), ('/%E2%99%A5%E2%99%E2%99%A5/', '/♥%E2%99♥/'), ('/%E2%E2%99%A5%E2%99%A5%99/', '/%E2♥♥%99/'), ('/%E2%99%A5%E2%99%A5/?utf8=%9C%93%E2%9C%93%9C%93', '/♥♥/?utf8=%9C%93✓%9C%93'), ] for uri, iri in cases: self.assertEqual(iri_to_uri(uri_to_iri(uri)), uri) self.assertEqual(uri_to_iri(iri_to_uri(iri)), iri) Django-1.8.7/tests/utils_tests/archives/0000775000175000017500000000000012625116243017671 5ustar timtim00000000000000Django-1.8.7/tests/utils_tests/archives/leadpath_foobar.tar.gz0000664000175000017500000000041112603513307024124 0ustar timtim00000000000000‹*˜Sí×M Â0à%7pò79O% …j¡Úû›¦­ÅÞ·É"BcŸ3ûKc7žAD¼WyåeÕ†òºQÚXGV[´J»Þ„FyÉCm¦Ç³ÓQâ/GŸkã­»ì¯ßã½þ‰~Í_ >#½vîËüÓ%í2™F‘à™ÞÎß>£(¦\ÿ! ÿ¶ü¯Ã 6Jú?ÓÜÿ­%‡þ_Ã>©PTÿÁåü™Qÿ5ìó—šeùsÎ?hä_Ã>ÿs+ó? ¬ÿÏù;òý¿†Ïü%f@Iý3Í÷—n€¨ÿ>ó—˜eùó’?îÿ?{»‘Jí(Django-1.8.7/tests/utils_tests/archives/foobar.tar.bz20000664000175000017500000000035612603513307022347 0ustar timtim00000000000000BZh91AY&SYê}¨®û…À€@@ÿ€3u€ž‚0 Y‚HMHh¨IQêÔ¢“G¨h{T ¯“»»¾¼Æa˜? d$•,lÓHÍ®!lБ’¶Õ¦Z›²D1B*Ò C$%  0ûÎfffã¤BʇC™r÷튎qðƒ¡,nÌœ¥ •†A Q± ²ÆÛ\EJì>P&’m(+p–äÒª{'WˆŒÄÅÀŠCË!‹l ‘ ¡á¡ ­Ä qC$;Çð:¤(B²¯ñw$S… §Ú Django-1.8.7/tests/utils_tests/archives/foobar.tar.gz0000664000175000017500000000037112603513307022267 0ustar timtim00000000000000‹êâLLí×[ ƒ0Ð,%+0™¼f=Ji?[÷ß<(ö«6… îù jÀÀufp2JœÍ8ƲGû¾¾(¢à<%òò}NÞ+å¦Ô~Ì›ÖêvÙ—OûΞÿ©Éø;JÀ)„Óü[ Ì%¦¨´?™BþƉ¿£'ÿhmÍßòa2×užßöÿZÿ1'dóúÿ-ÙÐÕÿ£«ù3ꈖ¿ì èË¿Õ?ä?BË™7ÁÐÕÿ9ï#¢RÿèÿòŽüåf@Wý§ÒÿÉù„úáÈ_nôýÿÕúwÁ#€_={ÊN(Django-1.8.7/tests/utils_tests/archives/foobar.tar0000664000175000017500000002400012603513307021643 0ustar timtim00000000000000./0000755000175000017500000000000011422706520007636 5ustar gdubgdub./10000644000175000017500000000000011422701477007715 0ustar gdubgdub./20000644000175000017500000000000011422701500007701 0ustar gdubgdub./foo/0000755000175000017500000000000011422701454010422 5ustar gdubgdub./foo/10000644000175000017500000000000011422701452010471 0ustar gdubgdub./foo/20000644000175000017500000000000011422701454010474 0ustar gdubgdub./foo/bar/0000755000175000017500000000000011422701475011171 5ustar gdubgdub./foo/bar/10000644000175000017500000000000011422701462011236 0ustar gdubgdub./foo/bar/20000644000175000017500000000000011422701475011243 0ustar gdubgdubDjango-1.8.7/tests/utils_tests/archives/foobar.zip0000664000175000017500000000215212603513307021663 0ustar timtim00000000000000PK ˆšø<1UT ?ƒKLXƒKLux èèPK ˆšø<2UT @ƒKLXƒKLux èèPK |šø<foo/UT ,ƒKL/ƒKLux èèPK {šø<foo/1UT *ƒKL*ƒKLux èèPK |šø<foo/2UT ,ƒKL,ƒKLux èèPK ‡šø<foo/bar/UT =ƒKL>„KLux èèPK šø< foo/bar/1UT 2ƒKL2ƒKLux èèPK ‡šø< foo/bar/2UT =ƒKL=ƒKLux èèPK ˆšø<¤1UT?ƒKLux èèPK ˆšø<¤;2UT@ƒKLux èèPK |šø<íAvfoo/UT,ƒKLux èèPK {šø<¤´foo/1UT*ƒKLux èèPK |šø<¤ófoo/2UT,ƒKLux èèPK ‡šø<íA2foo/bar/UT=ƒKLux èèPK šø< ¤tfoo/bar/1UT2ƒKLux èèPK ‡šø< ¤·foo/bar/2UT=ƒKLux èèPKZúDjango-1.8.7/tests/utils_tests/archives/leadpath_foobar.tar0000664000175000017500000001300012603513307023503 0ustar timtim00000000000000leaddir/000755 000765 000120 00000000000 12340313571 012527 5ustar00dudeadmin000000 000000 leaddir/1000644 000765 000120 00000000000 12340313555 012602 0ustar00dudeadmin000000 000000 leaddir/2000644 000765 000120 00000000000 12340313560 012577 0ustar00dudeadmin000000 000000 leaddir/foo/000755 000765 000120 00000000000 12340313601 013304 5ustar00dudeadmin000000 000000 leaddir/foo/1000644 000765 000120 00000000000 12340313574 013366 0ustar00dudeadmin000000 000000 leaddir/foo/2000644 000765 000120 00000000000 12340313576 013371 0ustar00dudeadmin000000 000000 leaddir/foo/bar/000755 000765 000120 00000000000 12340313606 014055 5ustar00dudeadmin000000 000000 leaddir/foo/bar/1000644 000765 000120 00000000000 12340313605 014125 0ustar00dudeadmin000000 000000 leaddir/foo/bar/2000644 000765 000120 00000000000 12340313606 014127 0ustar00dudeadmin000000 000000 Django-1.8.7/tests/utils_tests/archives/leadpath_foobar.zip0000664000175000017500000000257212603513307023533 0ustar timtim00000000000000PK Yq¹Dleaddir/UT y—S ˜Sux õPPK Sq¹D leaddir/1UT m—S˜Sux õPPK Tq¹D leaddir/2UT p—S˜Sux õPPK ]q¹D leaddir/foo/UT —S ˜Sux õPPK Zq¹D leaddir/foo/1UT |—S˜Sux õPPK [q¹D leaddir/foo/2UT ~—S˜Sux õPPK aq¹Dleaddir/foo/bar/UT †—S ˜Sux õPPK aq¹Dleaddir/foo/bar/1UT …—S˜Sux õPPK aq¹Dleaddir/foo/bar/2UT †—S˜Sux õPPK Yq¹DíAleaddir/UTy—Sux õPPK Sq¹D ¤Bleaddir/1UTm—Sux õPPK Tq¹D ¤…leaddir/2UTp—Sux õPPK ]q¹D íAÈleaddir/foo/UT—Sux õPPK Zq¹D ¤leaddir/foo/1UT|—Sux õPPK [q¹D ¤Uleaddir/foo/2UT~—Sux õPPK aq¹DíAœleaddir/foo/bar/UT†—Sux õPPK aq¹D¤æleaddir/foo/bar/1UT…—Sux õPPK aq¹D¤1leaddir/foo/bar/2UT†—Sux õPPK è|Django-1.8.7/tests/utils_tests/archives/leadpath_foobar.tar.bz20000664000175000017500000000041112603513307024201 0ustar timtim00000000000000BZh91AY&SYóÜдéÿЀÀ@ÿ€ÌÀw'ž@0#E”"hõÐh`ÓFša12` iR¦ =@'ÂÈk»ZR”¥8o$$7Ô&€‹¶Ó:›S C¢ªê£8Q ¡‚BI,9ÚÔ¶…gjsé #QÑGzPQÂl…ˆsrqVµÃ r%ÉHf…5´¤,£äø”^ŒŽÙÅSª…]ˆT®:¡gAÑË:¡:Ìšä–½®^÷FÈRT¥‹2!5óCÕÄ9¼h~§˜RaH|†û кáH[)hê%d‘w!Õ !ÕôÅÜ‘N$<÷4-Django-1.8.7/tests/generic_inline_admin/0000775000175000017500000000000012625116243017625 5ustar timtim00000000000000Django-1.8.7/tests/generic_inline_admin/urls.py0000664000175000017500000000022612625116213021161 0ustar timtim00000000000000from django.conf.urls import include, url from . import admin urlpatterns = [ url(r'^generic_inline_admin/admin/', include(admin.site.urls)), ] Django-1.8.7/tests/generic_inline_admin/tests.py0000664000175000017500000006013712625116213021345 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import warnings from django.contrib import admin from django.contrib.admin.sites import AdminSite from django.contrib.auth.models import User from django.contrib.contenttypes.admin import GenericTabularInline from django.contrib.contenttypes.forms import generic_inlineformset_factory from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse from django.forms.formsets import DEFAULT_MAX_NUM from django.forms.models import ModelForm from django.test import ( RequestFactory, TestCase, ignore_warnings, override_settings, ) from django.utils.deprecation import RemovedInDjango19Warning from .admin import MediaInline, MediaPermanentInline, site as admin_site from .models import Category, Episode, EpisodePermanent, Media, PhoneNumber # Set DEBUG to True to ensure {% include %} will raise exceptions. # That is how inlines are rendered and #9498 will bubble up if it is an issue. @override_settings( DEBUG=True, PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF="generic_inline_admin.urls", ) class GenericAdminViewTest(TestCase): fixtures = ['users.xml'] def setUp(self): self.client.login(username='super', password='secret') # Can't load content via a fixture (since the GenericForeignKey # relies on content type IDs, which will vary depending on what # other tests have been run), thus we do it here. e = Episode.objects.create(name='This Week in Django') self.episode_pk = e.pk m = Media(content_object=e, url='http://example.com/podcast.mp3') m.save() self.mp3_media_pk = m.pk m = Media(content_object=e, url='http://example.com/logo.png') m.save() self.png_media_pk = m.pk def test_basic_add_GET(self): """ A smoke test to ensure GET on the add_view works. """ response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/add/') self.assertEqual(response.status_code, 200) def test_basic_edit_GET(self): """ A smoke test to ensure GET on the change_view works. """ response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk) self.assertEqual(response.status_code, 200) def test_basic_add_POST(self): """ A smoke test to ensure POST on add_view works. """ post_data = { "name": "This Week in Django", # inline data "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": "1", "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": "0", "generic_inline_admin-media-content_type-object_id-MAX_NUM_FORMS": "0", } response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/episode/add/', post_data) self.assertEqual(response.status_code, 302) # redirect somewhere def test_basic_edit_POST(self): """ A smoke test to ensure POST on edit_view works. """ post_data = { "name": "This Week in Django", # inline data "generic_inline_admin-media-content_type-object_id-TOTAL_FORMS": "3", "generic_inline_admin-media-content_type-object_id-INITIAL_FORMS": "2", "generic_inline_admin-media-content_type-object_id-MAX_NUM_FORMS": "0", "generic_inline_admin-media-content_type-object_id-0-id": "%d" % self.mp3_media_pk, "generic_inline_admin-media-content_type-object_id-0-url": "http://example.com/podcast.mp3", "generic_inline_admin-media-content_type-object_id-1-id": "%d" % self.png_media_pk, "generic_inline_admin-media-content_type-object_id-1-url": "http://example.com/logo.png", "generic_inline_admin-media-content_type-object_id-2-id": "", "generic_inline_admin-media-content_type-object_id-2-url": "", } url = '/generic_inline_admin/admin/generic_inline_admin/episode/%d/' % self.episode_pk response = self.client.post(url, post_data) self.assertEqual(response.status_code, 302) # redirect somewhere def test_generic_inline_formset(self): EpisodeMediaFormSet = generic_inlineformset_factory(Media, can_delete=False, exclude=['description', 'keywords'], extra=3) e = Episode.objects.get(name='This Week in Django') # Works with no queryset formset = EpisodeMediaFormSet(instance=e) self.assertEqual(len(formset.forms), 5) self.assertHTMLEqual(formset.forms[0].as_p(), '

    ' % self.mp3_media_pk) self.assertHTMLEqual(formset.forms[1].as_p(), '

    ' % self.png_media_pk) self.assertHTMLEqual(formset.forms[2].as_p(), '

    ') # A queryset can be used to alter display ordering formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.order_by('url')) self.assertEqual(len(formset.forms), 5) self.assertHTMLEqual(formset.forms[0].as_p(), '

    ' % self.png_media_pk) self.assertHTMLEqual(formset.forms[1].as_p(), '

    ' % self.mp3_media_pk) self.assertHTMLEqual(formset.forms[2].as_p(), '

    ') # Works with a queryset that omits items formset = EpisodeMediaFormSet(instance=e, queryset=Media.objects.filter(url__endswith=".png")) self.assertEqual(len(formset.forms), 4) self.assertHTMLEqual(formset.forms[0].as_p(), '

    ' % self.png_media_pk) self.assertHTMLEqual(formset.forms[1].as_p(), '

    ') def test_generic_inline_formset_factory(self): # Regression test for #10522. inline_formset = generic_inlineformset_factory(Media, exclude=('url',)) # Regression test for #12340. e = Episode.objects.get(name='This Week in Django') formset = inline_formset(instance=e) self.assertTrue(formset.get_queryset().ordered) @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF="generic_inline_admin.urls") class GenericInlineAdminParametersTest(TestCase): fixtures = ['users.xml'] def setUp(self): self.client.login(username='super', password='secret') self.factory = RequestFactory() def _create_object(self, model): """ Create a model with an attached Media object via GFK. We can't load content via a fixture (since the GenericForeignKey relies on content type IDs, which will vary depending on what other tests have been run), thus we do it here. """ e = model.objects.create(name='This Week in Django') Media.objects.create(content_object=e, url='http://example.com/podcast.mp3') return e def test_no_param(self): """ With one initial form, extra (default) at 3, there should be 4 forms. """ e = self._create_object(Episode) response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) formset = response.context['inline_admin_formsets'][0].formset self.assertEqual(formset.total_form_count(), 4) self.assertEqual(formset.initial_form_count(), 1) def test_extra_param(self): """ With extra=0, there should be one form. """ class ExtraInline(GenericTabularInline): model = Media extra = 0 modeladmin = admin.ModelAdmin(Episode, admin_site) modeladmin.inlines = [ExtraInline] e = self._create_object(Episode) request = self.factory.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(e.pk)) formset = response.context_data['inline_admin_formsets'][0].formset self.assertEqual(formset.total_form_count(), 1) self.assertEqual(formset.initial_form_count(), 1) def testMaxNumParam(self): """ With extra=5 and max_num=2, there should be only 2 forms. """ class MaxNumInline(GenericTabularInline): model = Media extra = 5 max_num = 2 modeladmin = admin.ModelAdmin(Episode, admin_site) modeladmin.inlines = [MaxNumInline] e = self._create_object(Episode) request = self.factory.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(e.pk)) formset = response.context_data['inline_admin_formsets'][0].formset self.assertEqual(formset.total_form_count(), 2) self.assertEqual(formset.initial_form_count(), 1) def test_min_num_param(self): """ With extra=3 and min_num=2, there should be five forms. """ class MinNumInline(GenericTabularInline): model = Media extra = 3 min_num = 2 modeladmin = admin.ModelAdmin(Episode, admin_site) modeladmin.inlines = [MinNumInline] e = self._create_object(Episode) request = self.factory.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(e.pk)) formset = response.context_data['inline_admin_formsets'][0].formset self.assertEqual(formset.total_form_count(), 5) self.assertEqual(formset.initial_form_count(), 1) def test_get_extra(self): class GetExtraInline(GenericTabularInline): model = Media extra = 4 def get_extra(self, request, obj): return 2 modeladmin = admin.ModelAdmin(Episode, admin_site) modeladmin.inlines = [GetExtraInline] e = self._create_object(Episode) request = self.factory.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(e.pk)) formset = response.context_data['inline_admin_formsets'][0].formset self.assertEqual(formset.extra, 2) def test_get_min_num(self): class GetMinNumInline(GenericTabularInline): model = Media min_num = 5 def get_min_num(self, request, obj): return 2 modeladmin = admin.ModelAdmin(Episode, admin_site) modeladmin.inlines = [GetMinNumInline] e = self._create_object(Episode) request = self.factory.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(e.pk)) formset = response.context_data['inline_admin_formsets'][0].formset self.assertEqual(formset.min_num, 2) def test_get_max_num(self): class GetMaxNumInline(GenericTabularInline): model = Media extra = 5 def get_max_num(self, request, obj): return 2 modeladmin = admin.ModelAdmin(Episode, admin_site) modeladmin.inlines = [GetMaxNumInline] e = self._create_object(Episode) request = self.factory.get('/generic_inline_admin/admin/generic_inline_admin/episode/%s/' % e.pk) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(e.pk)) formset = response.context_data['inline_admin_formsets'][0].formset self.assertEqual(formset.max_num, 2) @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF="generic_inline_admin.urls") class GenericInlineAdminWithUniqueTogetherTest(TestCase): fixtures = ['users.xml'] def setUp(self): self.client.login(username='super', password='secret') def test_add(self): category_id = Category.objects.create(name='male').pk post_data = { "name": "John Doe", # inline data "generic_inline_admin-phonenumber-content_type-object_id-TOTAL_FORMS": "1", "generic_inline_admin-phonenumber-content_type-object_id-INITIAL_FORMS": "0", "generic_inline_admin-phonenumber-content_type-object_id-MAX_NUM_FORMS": "0", "generic_inline_admin-phonenumber-content_type-object_id-0-id": "", "generic_inline_admin-phonenumber-content_type-object_id-0-phone_number": "555-555-5555", "generic_inline_admin-phonenumber-content_type-object_id-0-category": "%s" % category_id, } response = self.client.get('/generic_inline_admin/admin/generic_inline_admin/contact/add/') self.assertEqual(response.status_code, 200) response = self.client.post('/generic_inline_admin/admin/generic_inline_admin/contact/add/', post_data) self.assertEqual(response.status_code, 302) # redirect somewhere def test_delete(self): from .models import Contact c = Contact.objects.create(name='foo') PhoneNumber.objects.create( object_id=c.id, content_type=ContentType.objects.get_for_model(Contact), phone_number="555-555-5555", ) response = self.client.post(reverse('admin:generic_inline_admin_contact_delete', args=[c.pk])) self.assertContains(response, 'Are you sure you want to delete') @override_settings(ROOT_URLCONF="generic_inline_admin.urls") class NoInlineDeletionTest(TestCase): def test_no_deletion(self): inline = MediaPermanentInline(EpisodePermanent, admin_site) fake_request = object() formset = inline.get_formset(fake_request) self.assertFalse(formset.can_delete) class MockRequest(object): pass class MockSuperUser(object): def has_perm(self, perm): return True request = MockRequest() request.user = MockSuperUser() @override_settings(ROOT_URLCONF="generic_inline_admin.urls") class GenericInlineModelAdminTest(TestCase): def setUp(self): self.site = AdminSite() def test_get_formset_kwargs(self): media_inline = MediaInline(Media, AdminSite()) # Create a formset with default arguments formset = media_inline.get_formset(request) self.assertEqual(formset.max_num, DEFAULT_MAX_NUM) self.assertEqual(formset.can_order, False) # Create a formset with custom keyword arguments formset = media_inline.get_formset(request, max_num=100, can_order=True) self.assertEqual(formset.max_num, 100) self.assertEqual(formset.can_order, True) def test_custom_form_meta_exclude_with_readonly(self): """ Ensure that the custom ModelForm's `Meta.exclude` is respected when used in conjunction with `GenericInlineModelAdmin.readonly_fields` and when no `ModelAdmin.exclude` is defined. """ class MediaForm(ModelForm): class Meta: model = Media exclude = ['url'] class MediaInline(GenericTabularInline): readonly_fields = ['description'] form = MediaForm model = Media class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline ] ma = EpisodeAdmin(Episode, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['keywords', 'id', 'DELETE']) def test_custom_form_meta_exclude(self): """ Ensure that the custom ModelForm's `Meta.exclude` is respected by `GenericInlineModelAdmin.get_formset`, and overridden if `ModelAdmin.exclude` or `GenericInlineModelAdmin.exclude` are defined. Refs #15907. """ # First with `GenericInlineModelAdmin` ----------------- class MediaForm(ModelForm): class Meta: model = Media exclude = ['url'] class MediaInline(GenericTabularInline): exclude = ['description'] form = MediaForm model = Media class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline ] ma = EpisodeAdmin(Episode, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['url', 'keywords', 'id', 'DELETE']) # Then, only with `ModelForm` ----------------- class MediaInline(GenericTabularInline): form = MediaForm model = Media class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline ] ma = EpisodeAdmin(Episode, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['description', 'keywords', 'id', 'DELETE']) def test_get_fieldsets(self): # Test that get_fieldsets is called when figuring out form fields. # Refs #18681. class MediaForm(ModelForm): class Meta: model = Media fields = '__all__' class MediaInline(GenericTabularInline): form = MediaForm model = Media can_delete = False def get_fieldsets(self, request, obj=None): return [(None, {'fields': ['url', 'description']})] ma = MediaInline(Media, self.site) form = ma.get_formset(None).form self.assertEqual(form._meta.fields, ['url', 'description']) def test_get_formsets_with_inlines(self): """ get_formsets() triggers a deprecation warning when get_formsets is overridden. """ class MediaForm(ModelForm): class Meta: model = Media exclude = ['url'] class MediaInline(GenericTabularInline): exclude = ['description'] form = MediaForm model = Media class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline ] def get_formsets(self, request, obj=None): return [] with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") ma = EpisodeAdmin(Episode, self.site) list(ma.get_formsets_with_inlines(request)) # Verify that the deprecation warning was triggered when get_formsets was called # This verifies that we called that method. self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, RemovedInDjango19Warning)) class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline ] with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") ma = EpisodeAdmin(Episode, self.site) list(ma.get_formsets_with_inlines(request)) self.assertEqual(len(w), 0) @ignore_warnings(category=RemovedInDjango19Warning) def test_get_formsets_with_inlines_returns_tuples(self): """ Ensure that get_formsets_with_inlines() returns the correct tuples. """ class MediaForm(ModelForm): class Meta: model = Media exclude = ['url'] class MediaInline(GenericTabularInline): form = MediaForm model = Media class AlternateInline(GenericTabularInline): form = MediaForm model = Media class EpisodeAdmin(admin.ModelAdmin): inlines = [ AlternateInline, MediaInline ] ma = EpisodeAdmin(Episode, self.site) inlines = ma.get_inline_instances(request) for (formset, inline), other_inline in zip(ma.get_formsets_with_inlines(request), inlines): self.assertIsInstance(formset, other_inline.get_formset(request).__class__) class EpisodeAdmin(admin.ModelAdmin): inlines = [ AlternateInline, MediaInline ] def get_formsets(self, request, obj=None): # Override get_formsets to force the usage of get_formsets in # ModelAdmin.get_formsets_with_inlines() then ignore the # warning raised by ModelAdmin.get_formsets_with_inlines() return self._get_formsets(request, obj) ma = EpisodeAdmin(Episode, self.site) inlines = ma.get_inline_instances(request) for (formset, inline), other_inline in zip(ma.get_formsets_with_inlines(request), inlines): self.assertIsInstance(formset, other_inline.get_formset(request).__class__) Django-1.8.7/tests/generic_inline_admin/fixtures/0000775000175000017500000000000012625116243021476 5ustar timtim00000000000000Django-1.8.7/tests/generic_inline_admin/fixtures/users.xml0000664000175000017500000000201112625113144023350 0ustar timtim00000000000000 super Super User super@example.com sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158 True True True 2007-05-30 13:20:10 2007-05-30 13:20:10 Django-1.8.7/tests/generic_inline_admin/__init__.py0000664000175000017500000000000012603513307021722 0ustar timtim00000000000000Django-1.8.7/tests/generic_inline_admin/models.py0000664000175000017500000000317312625116213021463 0ustar timtim00000000000000from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible class Episode(models.Model): name = models.CharField(max_length=100) length = models.CharField(max_length=100, blank=True) author = models.CharField(max_length=100, blank=True) @python_2_unicode_compatible class Media(models.Model): """ Media that can associated to any object. """ content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey() url = models.URLField() description = models.CharField(max_length=100, blank=True) keywords = models.CharField(max_length=100, blank=True) def __str__(self): return self.url # # Generic inline with unique_together # class Category(models.Model): name = models.CharField(max_length=50) class PhoneNumber(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') phone_number = models.CharField(max_length=30) category = models.ForeignKey(Category, null=True, blank=True) class Meta: unique_together = (('content_type', 'object_id', 'phone_number',),) class Contact(models.Model): name = models.CharField(max_length=50) phone_numbers = GenericRelation(PhoneNumber, related_query_name='phone_numbers') # # Generic inline with can_delete=False # class EpisodePermanent(Episode): pass Django-1.8.7/tests/generic_inline_admin/admin.py0000664000175000017500000000132212625116213021262 0ustar timtim00000000000000from django.contrib import admin from django.contrib.contenttypes.admin import GenericTabularInline from .models import ( Category, Contact, Episode, EpisodePermanent, Media, PhoneNumber, ) site = admin.AdminSite(name="admin") class MediaInline(GenericTabularInline): model = Media class EpisodeAdmin(admin.ModelAdmin): inlines = [ MediaInline, ] class PhoneNumberInline(GenericTabularInline): model = PhoneNumber class MediaPermanentInline(GenericTabularInline): model = Media can_delete = False site.register(Episode, EpisodeAdmin) site.register(Contact, inlines=[PhoneNumberInline]) site.register(Category) site.register(EpisodePermanent, inlines=[MediaPermanentInline]) Django-1.8.7/tests/multiple_database/0000775000175000017500000000000012625116243017162 5ustar timtim00000000000000Django-1.8.7/tests/multiple_database/routers.py0000664000175000017500000000360712625116214021243 0ustar timtim00000000000000from __future__ import unicode_literals from django.db import DEFAULT_DB_ALIAS class TestRouter(object): """ Vaguely behave like primary/replica, but the databases aren't assumed to propagate changes. """ def db_for_read(self, model, instance=None, **hints): if instance: return instance._state.db or 'other' return 'other' def db_for_write(self, model, **hints): return DEFAULT_DB_ALIAS def allow_relation(self, obj1, obj2, **hints): return obj1._state.db in ('default', 'other') and obj2._state.db in ('default', 'other') def allow_migrate(self, db, app_label, **hints): return True class AuthRouter(object): """ Control all database operations on models in the contrib.auth application. """ def db_for_read(self, model, **hints): "Point all read operations on auth models to 'default'" if model._meta.app_label == 'auth': # We use default here to ensure we can tell the difference # between a read request and a write request for Auth objects return 'default' return None def db_for_write(self, model, **hints): "Point all operations on auth models to 'other'" if model._meta.app_label == 'auth': return 'other' return None def allow_relation(self, obj1, obj2, **hints): "Allow any relation if a model in Auth is involved" if obj1._meta.app_label == 'auth' or obj2._meta.app_label == 'auth': return True return None def allow_migrate(self, db, app_label, **hints): "Make sure the auth app only appears on the 'other' db" if app_label == 'auth': return db == 'other' return None class WriteRouter(object): # A router that only expresses an opinion on writes def db_for_write(self, model, **hints): return 'writer' Django-1.8.7/tests/multiple_database/tests.py0000664000175000017500000027000212625116214020675 0ustar timtim00000000000000from __future__ import unicode_literals import datetime import pickle import warnings from operator import attrgetter from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.core import management from django.db import DEFAULT_DB_ALIAS, connections, router, transaction from django.db.models import signals from django.db.utils import ConnectionRouter from django.test import TestCase, override_settings from django.utils.encoding import force_text from django.utils.six import StringIO from .models import Book, Person, Pet, Review, UserProfile from .routers import AuthRouter, TestRouter, WriteRouter class QueryTestCase(TestCase): multi_db = True def test_db_selection(self): "Check that querysets will use the default database by default" self.assertEqual(Book.objects.db, DEFAULT_DB_ALIAS) self.assertEqual(Book.objects.all().db, DEFAULT_DB_ALIAS) self.assertEqual(Book.objects.using('other').db, 'other') self.assertEqual(Book.objects.db_manager('other').db, 'other') self.assertEqual(Book.objects.db_manager('other').all().db, 'other') def test_default_creation(self): "Objects created on the default database don't leak onto other databases" # Create a book on the default database using create() Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) # Create a book on the default database using a save dive = Book() dive.title = "Dive into Python" dive.published = datetime.date(2009, 5, 4) dive.save() # Check that book exists on the default database, but not on other database try: Book.objects.get(title="Pro Django") Book.objects.using('default').get(title="Pro Django") except Book.DoesNotExist: self.fail('"Pro Django" should exist on default database') self.assertRaises( Book.DoesNotExist, Book.objects.using('other').get, title="Pro Django" ) try: Book.objects.get(title="Dive into Python") Book.objects.using('default').get(title="Dive into Python") except Book.DoesNotExist: self.fail('"Dive into Python" should exist on default database') self.assertRaises( Book.DoesNotExist, Book.objects.using('other').get, title="Dive into Python" ) def test_other_creation(self): "Objects created on another database don't leak onto the default database" # Create a book on the second database Book.objects.using('other').create(title="Pro Django", published=datetime.date(2008, 12, 16)) # Create a book on the default database using a save dive = Book() dive.title = "Dive into Python" dive.published = datetime.date(2009, 5, 4) dive.save(using='other') # Check that book exists on the default database, but not on other database try: Book.objects.using('other').get(title="Pro Django") except Book.DoesNotExist: self.fail('"Pro Django" should exist on other database') self.assertRaises( Book.DoesNotExist, Book.objects.get, title="Pro Django" ) self.assertRaises( Book.DoesNotExist, Book.objects.using('default').get, title="Pro Django" ) try: Book.objects.using('other').get(title="Dive into Python") except Book.DoesNotExist: self.fail('"Dive into Python" should exist on other database') self.assertRaises( Book.DoesNotExist, Book.objects.get, title="Dive into Python" ) self.assertRaises( Book.DoesNotExist, Book.objects.using('default').get, title="Dive into Python" ) def test_refresh(self): dive = Book() dive.title = "Dive into Python" dive = Book() dive.title = "Dive into Python" dive.published = datetime.date(2009, 5, 4) dive.save(using='other') dive.published = datetime.date(2009, 5, 4) dive.save(using='other') dive2 = Book.objects.using('other').get() dive2.title = "Dive into Python (on default)" dive2.save(using='default') dive.refresh_from_db() self.assertEqual(dive.title, "Dive into Python") dive.refresh_from_db(using='default') self.assertEqual(dive.title, "Dive into Python (on default)") self.assertEqual(dive._state.db, "default") def test_basic_queries(self): "Queries are constrained to a single database" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) dive = Book.objects.using('other').get(published=datetime.date(2009, 5, 4)) self.assertEqual(dive.title, "Dive into Python") self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, published=datetime.date(2009, 5, 4)) dive = Book.objects.using('other').get(title__icontains="dive") self.assertEqual(dive.title, "Dive into Python") self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, title__icontains="dive") dive = Book.objects.using('other').get(title__iexact="dive INTO python") self.assertEqual(dive.title, "Dive into Python") self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, title__iexact="dive INTO python") dive = Book.objects.using('other').get(published__year=2009) self.assertEqual(dive.title, "Dive into Python") self.assertEqual(dive.published, datetime.date(2009, 5, 4)) self.assertRaises(Book.DoesNotExist, Book.objects.using('default').get, published__year=2009) years = Book.objects.using('other').dates('published', 'year') self.assertEqual([o.year for o in years], [2009]) years = Book.objects.using('default').dates('published', 'year') self.assertEqual([o.year for o in years], []) months = Book.objects.using('other').dates('published', 'month') self.assertEqual([o.month for o in months], [5]) months = Book.objects.using('default').dates('published', 'month') self.assertEqual([o.month for o in months], []) def test_m2m_separation(self): "M2M fields are constrained to a single database" # Create a book and author on the default database pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) marty = Person.objects.create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('other').create(name="Mark Pilgrim") # Save the author relations pro.authors = [marty] dive.authors = [mark] # Inspect the m2m tables directly. # There should be 1 entry in each database self.assertEqual(Book.authors.through.objects.using('default').count(), 1) self.assertEqual(Book.authors.through.objects.using('other').count(), 1) # 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']) 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']) # Reget the objects to clear caches dive = Book.objects.using('other').get(title="Dive into Python") mark = Person.objects.using('other').get(name="Mark Pilgrim") # Retrieve related object by descriptor. Related objects should be database-bound self.assertEqual(list(dive.authors.all().values_list('name', flat=True)), ['Mark Pilgrim']) self.assertEqual(list(mark.book_set.all().values_list('title', flat=True)), ['Dive into Python']) def test_m2m_forward_operations(self): "M2M forward manipulations are all constrained to a single DB" # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('other').create(name="Mark Pilgrim") # Save the author relations dive.authors = [mark] # Add a second author john = Person.objects.using('other').create(name="John Smith") self.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']) self.assertEqual(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)), ['Dive into Python']) # Remove the second author dive.authors.remove(john) self.assertEqual(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)), ['Dive into Python']) self.assertEqual(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)), []) # Clear all authors dive.authors.clear() self.assertEqual(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)), []) self.assertEqual(list(Book.objects.using('other').filter(authors__name='John Smith').values_list('title', flat=True)), []) # Create an author through the m2m interface dive.authors.create(name='Jane Brown') self.assertEqual(list(Book.objects.using('other').filter(authors__name='Mark Pilgrim').values_list('title', flat=True)), []) self.assertEqual(list(Book.objects.using('other').filter(authors__name='Jane Brown').values_list('title', flat=True)), ['Dive into Python']) def test_m2m_reverse_operations(self): "M2M reverse manipulations are all constrained to a single DB" # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('other').create(name="Mark Pilgrim") # Save the author relations dive.authors = [mark] # Create a second book on the other database grease = Book.objects.using('other').create(title="Greasemonkey Hacks", published=datetime.date(2005, 11, 1)) # Add a books to the m2m mark.book_set.add(grease) self.assertEqual(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), ['Mark Pilgrim']) self.assertEqual(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), ['Mark Pilgrim']) # Remove a book from the m2m mark.book_set.remove(grease) self.assertEqual(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), ['Mark Pilgrim']) self.assertEqual(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), []) # Clear the books associated with mark mark.book_set.clear() self.assertEqual(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('other').filter(book__title='Greasemonkey Hacks').values_list('name', flat=True)), []) # Create a book through the m2m interface mark.book_set.create(title="Dive into HTML5", published=datetime.date(2020, 1, 1)) self.assertEqual(list(Person.objects.using('other').filter(book__title='Dive into Python').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('other').filter(book__title='Dive into HTML5').values_list('name', flat=True)), ['Mark Pilgrim']) def test_m2m_cross_database_protection(self): "Operations that involve sharing M2M objects across databases raise an error" # Create a book and author on the default database pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) marty = Person.objects.create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('other').create(name="Mark Pilgrim") # Set a foreign key set with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='default'): marty.edited = [pro, dive] # Add to an m2m with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='default'): marty.book_set.add(dive) # Set a m2m with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='default'): marty.book_set = [pro, dive] # Add to a reverse m2m with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='other'): dive.authors.add(marty) # Set a reverse m2m with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='other'): dive.authors = [mark, marty] def test_m2m_deletion(self): "Cascaded deletions of m2m relations issue queries on the right database" # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('other').create(name="Mark Pilgrim") dive.authors = [mark] # Check the initial state self.assertEqual(Person.objects.using('default').count(), 0) self.assertEqual(Book.objects.using('default').count(), 0) self.assertEqual(Book.authors.through.objects.using('default').count(), 0) self.assertEqual(Person.objects.using('other').count(), 1) self.assertEqual(Book.objects.using('other').count(), 1) self.assertEqual(Book.authors.through.objects.using('other').count(), 1) # Delete the object on the other database dive.delete(using='other') self.assertEqual(Person.objects.using('default').count(), 0) self.assertEqual(Book.objects.using('default').count(), 0) self.assertEqual(Book.authors.through.objects.using('default').count(), 0) # The person still exists ... self.assertEqual(Person.objects.using('other').count(), 1) # ... but the book has been deleted self.assertEqual(Book.objects.using('other').count(), 0) # ... and the relationship object has also been deleted. self.assertEqual(Book.authors.through.objects.using('other').count(), 0) # Now try deletion in the reverse direction. Set up the relation again dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) dive.authors = [mark] # Check the initial state self.assertEqual(Person.objects.using('default').count(), 0) self.assertEqual(Book.objects.using('default').count(), 0) self.assertEqual(Book.authors.through.objects.using('default').count(), 0) self.assertEqual(Person.objects.using('other').count(), 1) self.assertEqual(Book.objects.using('other').count(), 1) self.assertEqual(Book.authors.through.objects.using('other').count(), 1) # Delete the object on the other database mark.delete(using='other') self.assertEqual(Person.objects.using('default').count(), 0) self.assertEqual(Book.objects.using('default').count(), 0) self.assertEqual(Book.authors.through.objects.using('default').count(), 0) # The person has been deleted ... self.assertEqual(Person.objects.using('other').count(), 0) # ... but the book still exists self.assertEqual(Book.objects.using('other').count(), 1) # ... and the relationship object has been deleted. self.assertEqual(Book.authors.through.objects.using('other').count(), 0) def test_foreign_key_separation(self): "FK fields are constrained to a single database" # Create a book and author on the default database pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) george = Person.objects.create(name="George Vilches") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) chris = Person.objects.using('other').create(name="Chris Mills") # Save the author's favorite books pro.editor = george pro.save() dive.editor = chris dive.save() pro = Book.objects.using('default').get(title="Pro Django") self.assertEqual(pro.editor.name, "George Vilches") dive = Book.objects.using('other').get(title="Dive into Python") self.assertEqual(dive.editor.name, "Chris Mills") # Check that queries work across foreign key joins self.assertEqual(list(Person.objects.using('default').filter(edited__title='Pro Django').values_list('name', flat=True)), ['George Vilches']) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Pro Django').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('default').filter(edited__title='Dive into Python').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), ['Chris Mills']) # Reget the objects to clear caches chris = Person.objects.using('other').get(name="Chris Mills") dive = Book.objects.using('other').get(title="Dive into Python") # Retrieve related object by descriptor. Related objects should be database-bound self.assertEqual(list(chris.edited.values_list('title', flat=True)), ['Dive into Python']) def test_foreign_key_reverse_operations(self): "FK reverse manipulations are all constrained to a single DB" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) chris = Person.objects.using('other').create(name="Chris Mills") # Save the author relations dive.editor = chris dive.save() # Add a second book edited by chris html5 = Book.objects.using('other').create(title="Dive into HTML5", published=datetime.date(2010, 3, 15)) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) chris.edited.add(html5) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), ['Chris Mills']) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), ['Chris Mills']) # Remove the second editor chris.edited.remove(html5) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), ['Chris Mills']) # Clear all edited books chris.edited.clear() self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), []) # Create an author through the m2m interface chris.edited.create(title='Dive into Water', published=datetime.date(2010, 3, 15)) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into HTML5').values_list('name', flat=True)), []) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into Water').values_list('name', flat=True)), ['Chris Mills']) self.assertEqual(list(Person.objects.using('other').filter(edited__title='Dive into Python').values_list('name', flat=True)), []) def test_foreign_key_cross_database_protection(self): "Operations that involve sharing FK objects across databases raise an error" # Create a book and author on the default database pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) marty = Person.objects.create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) # Set a foreign key with an object from a different database with self.assertRaises(ValueError): dive.editor = marty # Set a foreign key set with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='default'): marty.edited = [pro, dive] # Add to a foreign key set with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='default'): marty.edited.add(dive) def test_foreign_key_deletion(self): "Cascaded deletions of Foreign Key relations issue queries on the right database" mark = Person.objects.using('other').create(name="Mark Pilgrim") Pet.objects.using('other').create(name="Fido", owner=mark) # Check the initial state self.assertEqual(Person.objects.using('default').count(), 0) self.assertEqual(Pet.objects.using('default').count(), 0) self.assertEqual(Person.objects.using('other').count(), 1) self.assertEqual(Pet.objects.using('other').count(), 1) # Delete the person object, which will cascade onto the pet mark.delete(using='other') self.assertEqual(Person.objects.using('default').count(), 0) self.assertEqual(Pet.objects.using('default').count(), 0) # Both the pet and the person have been deleted from the right database self.assertEqual(Person.objects.using('other').count(), 0) self.assertEqual(Pet.objects.using('other').count(), 0) def test_foreign_key_validation(self): "ForeignKey.validate() uses the correct database" mickey = Person.objects.using('other').create(name="Mickey") pluto = Pet.objects.using('other').create(name="Pluto", owner=mickey) self.assertIsNone(pluto.full_clean()) def test_o2o_separation(self): "OneToOne fields are constrained to a single database" # Create a user and profile on the default database alice = User.objects.db_manager('default').create_user('alice', 'alice@example.com') alice_profile = UserProfile.objects.using('default').create(user=alice, flavor='chocolate') # Create a user and profile on the other database bob = User.objects.db_manager('other').create_user('bob', 'bob@example.com') bob_profile = UserProfile.objects.using('other').create(user=bob, flavor='crunchy frog') # Retrieve related objects; queries should be database constrained alice = User.objects.using('default').get(username="alice") self.assertEqual(alice.userprofile.flavor, "chocolate") bob = User.objects.using('other').get(username="bob") self.assertEqual(bob.userprofile.flavor, "crunchy frog") # Check that queries work across joins self.assertEqual(list(User.objects.using('default').filter(userprofile__flavor='chocolate').values_list('username', flat=True)), ['alice']) self.assertEqual(list(User.objects.using('other').filter(userprofile__flavor='chocolate').values_list('username', flat=True)), []) self.assertEqual(list(User.objects.using('default').filter(userprofile__flavor='crunchy frog').values_list('username', flat=True)), []) self.assertEqual(list(User.objects.using('other').filter(userprofile__flavor='crunchy frog').values_list('username', flat=True)), ['bob']) # Reget the objects to clear caches alice_profile = UserProfile.objects.using('default').get(flavor='chocolate') bob_profile = UserProfile.objects.using('other').get(flavor='crunchy frog') # Retrieve related object by descriptor. Related objects should be database-bound self.assertEqual(alice_profile.user.username, 'alice') self.assertEqual(bob_profile.user.username, 'bob') def test_o2o_cross_database_protection(self): "Operations that involve sharing FK objects across databases raise an error" # Create a user and profile on the default database alice = User.objects.db_manager('default').create_user('alice', 'alice@example.com') # Create a user and profile on the other database bob = User.objects.db_manager('other').create_user('bob', 'bob@example.com') # Set a one-to-one relation with an object from a different database alice_profile = UserProfile.objects.using('default').create(user=alice, flavor='chocolate') with self.assertRaises(ValueError): bob.userprofile = alice_profile # BUT! if you assign a FK object when the base object hasn't # been saved yet, you implicitly assign the database for the # base object. bob_profile = UserProfile.objects.using('other').create(user=bob, flavor='crunchy frog') new_bob_profile = UserProfile(flavor="spring surprise") # assigning a profile requires an explicit pk as the object isn't saved charlie = User(pk=51, username='charlie', email='charlie@example.com') charlie.set_unusable_password() # initially, no db assigned self.assertEqual(new_bob_profile._state.db, None) self.assertEqual(charlie._state.db, None) # old object comes from 'other', so the new object is set to use 'other'... new_bob_profile.user = bob charlie.userprofile = bob_profile self.assertEqual(new_bob_profile._state.db, 'other') self.assertEqual(charlie._state.db, 'other') # ... but it isn't saved yet self.assertEqual(list(User.objects.using('other').values_list('username', flat=True)), ['bob']) self.assertEqual(list(UserProfile.objects.using('other').values_list('flavor', flat=True)), ['crunchy frog']) # When saved (no using required), new objects goes to 'other' charlie.save() bob_profile.save() new_bob_profile.save() self.assertEqual(list(User.objects.using('default').values_list('username', flat=True)), ['alice']) self.assertEqual(list(User.objects.using('other').values_list('username', flat=True)), ['bob', 'charlie']) self.assertEqual(list(UserProfile.objects.using('default').values_list('flavor', flat=True)), ['chocolate']) self.assertEqual(list(UserProfile.objects.using('other').values_list('flavor', flat=True)), ['crunchy frog', 'spring surprise']) # This also works if you assign the O2O relation in the constructor denise = User.objects.db_manager('other').create_user('denise', 'denise@example.com') denise_profile = UserProfile(flavor="tofu", user=denise) self.assertEqual(denise_profile._state.db, 'other') # ... but it isn't saved yet self.assertEqual(list(UserProfile.objects.using('default').values_list('flavor', flat=True)), ['chocolate']) self.assertEqual(list(UserProfile.objects.using('other').values_list('flavor', flat=True)), ['crunchy frog', 'spring surprise']) # When saved, the new profile goes to 'other' denise_profile.save() self.assertEqual(list(UserProfile.objects.using('default').values_list('flavor', flat=True)), ['chocolate']) self.assertEqual(list(UserProfile.objects.using('other').values_list('flavor', flat=True)), ['crunchy frog', 'spring surprise', 'tofu']) def test_generic_key_separation(self): "Generic fields are constrained to a single database" # Create a book and author on the default database pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) review1 = Review.objects.create(source="Python Monthly", content_object=pro) # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) review2 = Review.objects.using('other').create(source="Python Weekly", content_object=dive) review1 = Review.objects.using('default').get(source="Python Monthly") self.assertEqual(review1.content_object.title, "Pro Django") review2 = Review.objects.using('other').get(source="Python Weekly") self.assertEqual(review2.content_object.title, "Dive into Python") # Reget the objects to clear caches dive = Book.objects.using('other').get(title="Dive into Python") # Retrieve related object by descriptor. Related objects should be database-bound self.assertEqual(list(dive.reviews.all().values_list('source', flat=True)), ['Python Weekly']) def test_generic_key_reverse_operations(self): "Generic reverse manipulations are all constrained to a single DB" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) temp = Book.objects.using('other').create(title="Temp", published=datetime.date(2009, 5, 4)) review1 = Review.objects.using('other').create(source="Python Weekly", content_object=dive) review2 = Review.objects.using('other').create(source="Python Monthly", content_object=temp) self.assertEqual(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)), []) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), ['Python Weekly']) # Add a second review dive.reviews.add(review2) self.assertEqual(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)), []) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), ['Python Monthly', 'Python Weekly']) # Remove the second author dive.reviews.remove(review1) self.assertEqual(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)), []) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), ['Python Monthly']) # Clear all reviews dive.reviews.clear() self.assertEqual(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)), []) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), []) # Create an author through the generic interface dive.reviews.create(source='Python Daily') self.assertEqual(list(Review.objects.using('default').filter(object_id=dive.pk).values_list('source', flat=True)), []) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), ['Python Daily']) def test_generic_key_cross_database_protection(self): "Operations that involve sharing generic key objects across databases raise an error" # Create a book and author on the default database pro = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) review1 = Review.objects.create(source="Python Monthly", content_object=pro) # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) Review.objects.using('other').create(source="Python Weekly", content_object=dive) # Set a foreign key with an object from a different database with self.assertRaises(ValueError): review1.content_object = dive # Add to a foreign key set with an object from a different database with self.assertRaises(ValueError): with transaction.atomic(using='other'): dive.reviews.add(review1) # BUT! if you assign a FK object when the base object hasn't # been saved yet, you implicitly assign the database for the # base object. review3 = Review(source="Python Daily") # initially, no db assigned self.assertEqual(review3._state.db, None) # Dive comes from 'other', so review3 is set to use 'other'... review3.content_object = dive self.assertEqual(review3._state.db, 'other') # ... but it isn't saved yet self.assertEqual(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)), ['Python Monthly']) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), ['Python Weekly']) # When saved, John goes to 'other' review3.save() self.assertEqual(list(Review.objects.using('default').filter(object_id=pro.pk).values_list('source', flat=True)), ['Python Monthly']) self.assertEqual(list(Review.objects.using('other').filter(object_id=dive.pk).values_list('source', flat=True)), ['Python Daily', 'Python Weekly']) def test_generic_key_deletion(self): "Cascaded deletions of Generic Key relations issue queries on the right database" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) Review.objects.using('other').create(source="Python Weekly", content_object=dive) # Check the initial state self.assertEqual(Book.objects.using('default').count(), 0) self.assertEqual(Review.objects.using('default').count(), 0) self.assertEqual(Book.objects.using('other').count(), 1) self.assertEqual(Review.objects.using('other').count(), 1) # Delete the Book object, which will cascade onto the pet dive.delete(using='other') self.assertEqual(Book.objects.using('default').count(), 0) self.assertEqual(Review.objects.using('default').count(), 0) # Both the pet and the person have been deleted from the right database self.assertEqual(Book.objects.using('other').count(), 0) self.assertEqual(Review.objects.using('other').count(), 0) def test_ordering(self): "get_next_by_XXX commands stick to a single database" Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) learn = Book.objects.using('other').create(title="Learning Python", published=datetime.date(2008, 7, 16)) self.assertEqual(learn.get_next_by_published().title, "Dive into Python") self.assertEqual(dive.get_previous_by_published().title, "Learning Python") def test_raw(self): "test the raw() method across databases" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) val = Book.objects.db_manager("other").raw('SELECT id FROM multiple_database_book') self.assertQuerysetEqual(val, [dive.pk], attrgetter("pk")) val = Book.objects.raw('SELECT id FROM multiple_database_book').using('other') self.assertQuerysetEqual(val, [dive.pk], attrgetter("pk")) def test_select_related(self): "Database assignment is retained if an object is retrieved with select_related()" # Create a book and author on the other database mark = Person.objects.using('other').create(name="Mark Pilgrim") Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4), editor=mark) # Retrieve the Person using select_related() book = Book.objects.using('other').select_related('editor').get(title="Dive into Python") # The editor instance should have a db state self.assertEqual(book.editor._state.db, 'other') def test_subquery(self): """Make sure as_sql works with subqueries and primary/replica.""" sub = Person.objects.using('other').filter(name='fff') qs = Book.objects.filter(editor__in=sub) # When you call __str__ on the query object, it doesn't know about using # so it falls back to the default. If the subquery explicitly uses a # different database, an error should be raised. self.assertRaises(ValueError, str, qs.query) # Evaluating the query shouldn't work, either with self.assertRaises(ValueError): for obj in qs: pass def test_related_manager(self): "Related managers return managers, not querysets" mark = Person.objects.using('other').create(name="Mark Pilgrim") # extra_arg is removed by the BookManager's implementation of # create(); but the BookManager's implementation won't get called # unless edited returns a Manager, not a queryset mark.book_set.create(title="Dive into Python", published=datetime.date(2009, 5, 4), extra_arg=True) mark.book_set.get_or_create(title="Dive into Python", published=datetime.date(2009, 5, 4), extra_arg=True) mark.edited.create(title="Dive into Water", published=datetime.date(2009, 5, 4), extra_arg=True) mark.edited.get_or_create(title="Dive into Water", published=datetime.date(2009, 5, 4), extra_arg=True) class ConnectionRouterTestCase(TestCase): @override_settings(DATABASE_ROUTERS=[ 'multiple_database.tests.TestRouter', 'multiple_database.tests.WriteRouter']) def test_router_init_default(self): connection_router = ConnectionRouter() self.assertListEqual([r.__class__.__name__ for r in connection_router.routers], ['TestRouter', 'WriteRouter']) def test_router_init_arg(self): connection_router = ConnectionRouter([ 'multiple_database.tests.TestRouter', 'multiple_database.tests.WriteRouter' ]) self.assertListEqual([r.__class__.__name__ for r in connection_router.routers], ['TestRouter', 'WriteRouter']) # Init with instances instead of strings connection_router = ConnectionRouter([TestRouter(), WriteRouter()]) self.assertListEqual([r.__class__.__name__ for r in connection_router.routers], ['TestRouter', 'WriteRouter']) # Make the 'other' database appear to be a replica of the 'default' @override_settings(DATABASE_ROUTERS=[TestRouter()]) class RouterTestCase(TestCase): multi_db = True def test_db_selection(self): "Check that querysets obey the router for db suggestions" self.assertEqual(Book.objects.db, 'other') self.assertEqual(Book.objects.all().db, 'other') self.assertEqual(Book.objects.using('default').db, 'default') self.assertEqual(Book.objects.db_manager('default').db, 'default') self.assertEqual(Book.objects.db_manager('default').all().db, 'default') def test_migrate_selection(self): "Synchronization behavior is predictable" self.assertTrue(router.allow_migrate_model('default', User)) self.assertTrue(router.allow_migrate_model('default', Book)) self.assertTrue(router.allow_migrate_model('other', User)) self.assertTrue(router.allow_migrate_model('other', Book)) with override_settings(DATABASE_ROUTERS=[TestRouter(), AuthRouter()]): # Add the auth router to the chain. TestRouter is a universal # synchronizer, so it should have no effect. self.assertTrue(router.allow_migrate_model('default', User)) self.assertTrue(router.allow_migrate_model('default', Book)) self.assertTrue(router.allow_migrate_model('other', User)) self.assertTrue(router.allow_migrate_model('other', Book)) with override_settings(DATABASE_ROUTERS=[AuthRouter(), TestRouter()]): # Now check what happens if the router order is reversed. self.assertFalse(router.allow_migrate_model('default', User)) self.assertTrue(router.allow_migrate_model('default', Book)) self.assertTrue(router.allow_migrate_model('other', User)) self.assertTrue(router.allow_migrate_model('other', Book)) def test_migrate_legacy_router(self): class LegacyRouter(object): def allow_migrate(self, db, model): """ Deprecated allow_migrate signature should trigger RemovedInDjango110Warning. """ assert db == 'default' assert model is User return True with override_settings(DATABASE_ROUTERS=[LegacyRouter()]): with warnings.catch_warnings(record=True) as recorded: warnings.filterwarnings('always') msg = ( "The signature of allow_migrate has changed from " "allow_migrate(self, db, model) to " "allow_migrate(self, db, app_label, model_name=None, **hints). " "Support for the old signature will be removed in Django 1.10." ) self.assertTrue(router.allow_migrate_model('default', User)) self.assertEqual(force_text(recorded.pop().message), msg) self.assertEqual(recorded, []) self.assertTrue(router.allow_migrate('default', 'app_label')) self.assertEqual(force_text(recorded.pop().message), msg) def test_allow_syncdb_deprecation(self): class LegacyRouter(object): def allow_syncdb(self, db, model): assert db == 'default' assert model is User return True with override_settings(DATABASE_ROUTERS=[LegacyRouter()]): with warnings.catch_warnings(record=True) as recorded: warnings.filterwarnings('always') msg = ( "Router.allow_syncdb has been deprecated and will stop " "working in Django 1.9. Rename the method to allow_migrate." ) self.assertTrue(router.allow_migrate_model('default', User)) self.assertEqual(force_text(recorded.pop().message), msg) self.assertEqual(recorded, []) self.assertTrue(router.allow_migrate('default', 'app_label')) self.assertEqual(force_text(recorded.pop().message), msg) def test_partial_router(self): "A router can choose to implement a subset of methods" dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) # First check the baseline behavior. self.assertEqual(router.db_for_read(User), 'other') self.assertEqual(router.db_for_read(Book), 'other') self.assertEqual(router.db_for_write(User), 'default') self.assertEqual(router.db_for_write(Book), 'default') self.assertTrue(router.allow_relation(dive, dive)) self.assertTrue(router.allow_migrate_model('default', User)) self.assertTrue(router.allow_migrate_model('default', Book)) with override_settings(DATABASE_ROUTERS=[WriteRouter(), AuthRouter(), TestRouter()]): self.assertEqual(router.db_for_read(User), 'default') self.assertEqual(router.db_for_read(Book), 'other') self.assertEqual(router.db_for_write(User), 'writer') self.assertEqual(router.db_for_write(Book), 'writer') self.assertTrue(router.allow_relation(dive, dive)) self.assertFalse(router.allow_migrate_model('default', User)) self.assertTrue(router.allow_migrate_model('default', Book)) def test_database_routing(self): marty = Person.objects.using('default').create(name="Marty Alchin") pro = Book.objects.using('default').create(title="Pro Django", published=datetime.date(2008, 12, 16), editor=marty) pro.authors = [marty] # Create a book and author on the other database Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) # An update query will be routed to the default database Book.objects.filter(title='Pro Django').update(pages=200) with self.assertRaises(Book.DoesNotExist): # By default, the get query will be directed to 'other' Book.objects.get(title='Pro Django') # But the same query issued explicitly at a database will work. pro = Book.objects.using('default').get(title='Pro Django') # Check that the update worked. self.assertEqual(pro.pages, 200) # An update query with an explicit using clause will be routed # to the requested database. Book.objects.using('other').filter(title='Dive into Python').update(pages=300) self.assertEqual(Book.objects.get(title='Dive into Python').pages, 300) # Related object queries stick to the same database # as the original object, regardless of the router self.assertEqual(list(pro.authors.values_list('name', flat=True)), ['Marty Alchin']) self.assertEqual(pro.editor.name, 'Marty Alchin') # get_or_create is a special case. The get needs to be targeted at # the write database in order to avoid potential transaction # consistency problems book, created = Book.objects.get_or_create(title="Pro Django") self.assertFalse(created) book, created = Book.objects.get_or_create(title="Dive Into Python", defaults={'published': datetime.date(2009, 5, 4)}) self.assertTrue(created) # Check the head count of objects self.assertEqual(Book.objects.using('default').count(), 2) self.assertEqual(Book.objects.using('other').count(), 1) # If a database isn't specified, the read database is used self.assertEqual(Book.objects.count(), 1) # A delete query will also be routed to the default database Book.objects.filter(pages__gt=150).delete() # The default database has lost the book. self.assertEqual(Book.objects.using('default').count(), 1) self.assertEqual(Book.objects.using('other').count(), 1) def test_foreign_key_cross_database_protection(self): "Foreign keys can cross databases if they two databases have a common source" # Create a book and author on the default database pro = Book.objects.using('default').create(title="Pro Django", published=datetime.date(2008, 12, 16)) marty = Person.objects.using('default').create(name="Marty Alchin") # Create a book and author on the other database dive = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('other').create(name="Mark Pilgrim") # Set a foreign key with an object from a different database try: dive.editor = marty except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(mark._state.db, 'other') # ... but they will when the affected object is saved. dive.save() self.assertEqual(dive._state.db, 'default') # ...and the source database now has a copy of any object saved try: Book.objects.using('default').get(title='Dive into Python').delete() except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') # This isn't a real primary/replica database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') self.assertEqual(dive._state.db, 'other') # Set a foreign key set with an object from a different database try: marty.edited = [pro, dive] except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Assignment implies a save, so database assignments of original objects have changed... self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'default') self.assertEqual(mark._state.db, 'other') # ...and the source database now has a copy of any object saved try: Book.objects.using('default').get(title='Dive into Python').delete() except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') # This isn't a real primary/replica database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') self.assertEqual(dive._state.db, 'other') # Add to a foreign key set with an object from a different database try: marty.edited.add(dive) except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Add implies a save, so database assignments of original objects have changed... self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'default') self.assertEqual(mark._state.db, 'other') # ...and the source database now has a copy of any object saved try: Book.objects.using('default').get(title='Dive into Python').delete() except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') # This isn't a real primary/replica database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') # If you assign a FK object when the base object hasn't # been saved yet, you implicitly assign the database for the # base object. chris = Person(name="Chris Mills") html5 = Book(title="Dive into HTML5", published=datetime.date(2010, 3, 15)) # initially, no db assigned self.assertEqual(chris._state.db, None) self.assertEqual(html5._state.db, None) # old object comes from 'other', so the new object is set to use the # source of 'other'... self.assertEqual(dive._state.db, 'other') chris.save() dive.editor = chris html5.editor = mark self.assertEqual(dive._state.db, 'other') self.assertEqual(mark._state.db, 'other') self.assertEqual(chris._state.db, 'default') self.assertEqual(html5._state.db, 'default') # This also works if you assign the FK in the constructor water = Book(title="Dive into Water", published=datetime.date(2001, 1, 1), editor=mark) self.assertEqual(water._state.db, 'default') # For the remainder of this test, create a copy of 'mark' in the # 'default' database to prevent integrity errors on backends that # don't defer constraints checks until the end of the transaction mark.save(using='default') # This moved 'mark' in the 'default' database, move it back in 'other' mark.save(using='other') self.assertEqual(mark._state.db, 'other') # If you create an object through a FK relation, it will be # written to the write database, even if the original object # was on the read database cheesecake = mark.edited.create(title='Dive into Cheesecake', published=datetime.date(2010, 3, 15)) self.assertEqual(cheesecake._state.db, 'default') # Same goes for get_or_create, regardless of whether getting or creating cheesecake, created = mark.edited.get_or_create(title='Dive into Cheesecake', published=datetime.date(2010, 3, 15)) self.assertEqual(cheesecake._state.db, 'default') puddles, created = mark.edited.get_or_create(title='Dive into Puddles', published=datetime.date(2010, 3, 15)) self.assertEqual(puddles._state.db, 'default') def test_m2m_cross_database_protection(self): "M2M relations can cross databases if the database share a source" # Create books and authors on the inverse to the usual database pro = Book.objects.using('other').create(pk=1, title="Pro Django", published=datetime.date(2008, 12, 16)) marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") dive = Book.objects.using('default').create(pk=2, title="Dive into Python", published=datetime.date(2009, 5, 4)) mark = Person.objects.using('default').create(pk=2, name="Mark Pilgrim") # Now save back onto the usual database. # This simulates primary/replica - the objects exist on both database, # but the _state.db is as it is for all other tests. pro.save(using='default') marty.save(using='default') dive.save(using='other') mark.save(using='other') # Check that we have 2 of both types of object on both databases self.assertEqual(Book.objects.using('default').count(), 2) self.assertEqual(Book.objects.using('other').count(), 2) self.assertEqual(Person.objects.using('default').count(), 2) self.assertEqual(Person.objects.using('other').count(), 2) # Set a m2m set with an object from a different database try: marty.book_set = [pro, dive] except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(mark._state.db, 'other') # All m2m relations should be saved on the default database self.assertEqual(Book.authors.through.objects.using('default').count(), 2) self.assertEqual(Book.authors.through.objects.using('other').count(), 0) # Reset relations Book.authors.through.objects.using('default').delete() # Add to an m2m with an object from a different database try: marty.book_set.add(dive) except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(mark._state.db, 'other') # All m2m relations should be saved on the default database self.assertEqual(Book.authors.through.objects.using('default').count(), 1) self.assertEqual(Book.authors.through.objects.using('other').count(), 0) # Reset relations Book.authors.through.objects.using('default').delete() # Set a reverse m2m with an object from a different database try: dive.authors = [mark, marty] except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(mark._state.db, 'other') # All m2m relations should be saved on the default database self.assertEqual(Book.authors.through.objects.using('default').count(), 2) self.assertEqual(Book.authors.through.objects.using('other').count(), 0) # Reset relations Book.authors.through.objects.using('default').delete() self.assertEqual(Book.authors.through.objects.using('default').count(), 0) self.assertEqual(Book.authors.through.objects.using('other').count(), 0) # Add to a reverse m2m with an object from a different database try: dive.authors.add(marty) except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments don't change self.assertEqual(marty._state.db, 'default') self.assertEqual(pro._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(mark._state.db, 'other') # All m2m relations should be saved on the default database self.assertEqual(Book.authors.through.objects.using('default').count(), 1) self.assertEqual(Book.authors.through.objects.using('other').count(), 0) # If you create an object through a M2M relation, it will be # written to the write database, even if the original object # was on the read database alice = dive.authors.create(name='Alice') self.assertEqual(alice._state.db, 'default') # Same goes for get_or_create, regardless of whether getting or creating alice, created = dive.authors.get_or_create(name='Alice') self.assertEqual(alice._state.db, 'default') bob, created = dive.authors.get_or_create(name='Bob') self.assertEqual(bob._state.db, 'default') def test_o2o_cross_database_protection(self): "Operations that involve sharing FK objects across databases raise an error" # Create a user and profile on the default database alice = User.objects.db_manager('default').create_user('alice', 'alice@example.com') # Create a user and profile on the other database bob = User.objects.db_manager('other').create_user('bob', 'bob@example.com') # Set a one-to-one relation with an object from a different database alice_profile = UserProfile.objects.create(user=alice, flavor='chocolate') try: bob.userprofile = alice_profile except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(alice._state.db, 'default') self.assertEqual(alice_profile._state.db, 'default') self.assertEqual(bob._state.db, 'other') # ... but they will when the affected object is saved. bob.save() self.assertEqual(bob._state.db, 'default') def test_generic_key_cross_database_protection(self): "Generic Key operations can span databases if they share a source" # Create a book and author on the default database pro = Book.objects.using( 'default').create(title="Pro Django", published=datetime.date(2008, 12, 16)) review1 = Review.objects.using( 'default').create(source="Python Monthly", content_object=pro) # Create a book and author on the other database dive = Book.objects.using( 'other').create(title="Dive into Python", published=datetime.date(2009, 5, 4)) review2 = Review.objects.using( 'other').create(source="Python Weekly", content_object=dive) # Set a generic foreign key with an object from a different database try: review1.content_object = dive except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(pro._state.db, 'default') self.assertEqual(review1._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(review2._state.db, 'other') # ... but they will when the affected object is saved. dive.save() self.assertEqual(review1._state.db, 'default') self.assertEqual(dive._state.db, 'default') # ...and the source database now has a copy of any object saved try: Book.objects.using('default').get(title='Dive into Python').delete() except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') # This isn't a real primary/replica database, so restore the original from other dive = Book.objects.using('other').get(title='Dive into Python') self.assertEqual(dive._state.db, 'other') # Add to a generic foreign key set with an object from a different database try: dive.reviews.add(review1) except ValueError: self.fail("Assignment across primary/replica databases with a common source should be ok") # Database assignments of original objects haven't changed... self.assertEqual(pro._state.db, 'default') self.assertEqual(review1._state.db, 'default') self.assertEqual(dive._state.db, 'other') self.assertEqual(review2._state.db, 'other') # ... but they will when the affected object is saved. dive.save() self.assertEqual(dive._state.db, 'default') # ...and the source database now has a copy of any object saved try: Book.objects.using('default').get(title='Dive into Python').delete() except Book.DoesNotExist: self.fail('Source database should have a copy of saved object') # BUT! if you assign a FK object when the base object hasn't # been saved yet, you implicitly assign the database for the # base object. review3 = Review(source="Python Daily") # initially, no db assigned self.assertEqual(review3._state.db, None) # Dive comes from 'other', so review3 is set to use the source of 'other'... review3.content_object = dive self.assertEqual(review3._state.db, 'default') # If you create an object through a M2M relation, it will be # written to the write database, even if the original object # was on the read database dive = Book.objects.using('other').get(title='Dive into Python') nyt = dive.reviews.create(source="New York Times", content_object=dive) self.assertEqual(nyt._state.db, 'default') def test_m2m_managers(self): "M2M relations are represented by managers, and can be controlled like managers" pro = Book.objects.using('other').create(pk=1, title="Pro Django", published=datetime.date(2008, 12, 16)) marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") self.assertEqual(pro.authors.db, 'other') self.assertEqual(pro.authors.db_manager('default').db, 'default') self.assertEqual(pro.authors.db_manager('default').all().db, 'default') self.assertEqual(marty.book_set.db, 'other') self.assertEqual(marty.book_set.db_manager('default').db, 'default') self.assertEqual(marty.book_set.db_manager('default').all().db, 'default') def test_foreign_key_managers(self): "FK reverse relations are represented by managers, and can be controlled like managers" marty = Person.objects.using('other').create(pk=1, name="Marty Alchin") Book.objects.using('other').create(pk=1, title="Pro Django", published=datetime.date(2008, 12, 16), editor=marty) self.assertEqual(marty.edited.db, 'other') self.assertEqual(marty.edited.db_manager('default').db, 'default') self.assertEqual(marty.edited.db_manager('default').all().db, 'default') def test_generic_key_managers(self): "Generic key relations are represented by managers, and can be controlled like managers" pro = Book.objects.using('other').create(title="Pro Django", published=datetime.date(2008, 12, 16)) Review.objects.using('other').create(source="Python Monthly", content_object=pro) self.assertEqual(pro.reviews.db, 'other') self.assertEqual(pro.reviews.db_manager('default').db, 'default') self.assertEqual(pro.reviews.db_manager('default').all().db, 'default') def test_subquery(self): """Make sure as_sql works with subqueries and primary/replica.""" # Create a book and author on the other database mark = Person.objects.using('other').create(name="Mark Pilgrim") Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4), editor=mark) sub = Person.objects.filter(name='Mark Pilgrim') qs = Book.objects.filter(editor__in=sub) # When you call __str__ on the query object, it doesn't know about using # so it falls back to the default. Don't let routing instructions # force the subquery to an incompatible database. str(qs.query) # If you evaluate the query, it should work, running on 'other' self.assertEqual(list(qs.values_list('title', flat=True)), ['Dive into Python']) def test_deferred_models(self): mark_def = Person.objects.using('default').create(name="Mark Pilgrim") mark_other = Person.objects.using('other').create(name="Mark Pilgrim") orig_b = Book.objects.using('other').create(title="Dive into Python", published=datetime.date(2009, 5, 4), editor=mark_other) b = Book.objects.using('other').only('title').get(pk=orig_b.pk) self.assertEqual(b.published, datetime.date(2009, 5, 4)) b = Book.objects.using('other').only('title').get(pk=orig_b.pk) b.editor = mark_def b.save(using='default') self.assertEqual(Book.objects.using('default').get(pk=b.pk).published, datetime.date(2009, 5, 4)) @override_settings(DATABASE_ROUTERS=[AuthRouter()]) class AuthTestCase(TestCase): multi_db = True def test_auth_manager(self): "The methods on the auth manager obey database hints" # Create one user using default allocation policy User.objects.create_user('alice', 'alice@example.com') # Create another user, explicitly specifying the database User.objects.db_manager('default').create_user('bob', 'bob@example.com') # The second user only exists on the other database alice = User.objects.using('other').get(username='alice') self.assertEqual(alice.username, 'alice') self.assertEqual(alice._state.db, 'other') self.assertRaises(User.DoesNotExist, User.objects.using('default').get, username='alice') # The second user only exists on the default database bob = User.objects.using('default').get(username='bob') self.assertEqual(bob.username, 'bob') self.assertEqual(bob._state.db, 'default') self.assertRaises(User.DoesNotExist, User.objects.using('other').get, username='bob') # That is... there is one user on each database self.assertEqual(User.objects.using('default').count(), 1) self.assertEqual(User.objects.using('other').count(), 1) def test_dumpdata(self): "Check that dumpdata honors allow_migrate restrictions on the router" User.objects.create_user('alice', 'alice@example.com') User.objects.db_manager('default').create_user('bob', 'bob@example.com') # Check that dumping the default database doesn't try to include auth # because allow_migrate prohibits auth on default new_io = StringIO() management.call_command('dumpdata', 'auth', format='json', database='default', stdout=new_io) command_output = new_io.getvalue().strip() self.assertEqual(command_output, '[]') # Check that dumping the other database does include auth new_io = StringIO() management.call_command('dumpdata', 'auth', format='json', database='other', stdout=new_io) command_output = new_io.getvalue().strip() self.assertIn('"email": "alice@example.com"', command_output) class AntiPetRouter(object): # A router that only expresses an opinion on migrate, # passing pets to the 'other' database def allow_migrate(self, db, app_label, model_name=None, **hints): if db == 'other': return model_name == 'pet' else: return model_name != 'pet' class FixtureTestCase(TestCase): multi_db = True fixtures = ['multidb-common', 'multidb'] @override_settings(DATABASE_ROUTERS=[AntiPetRouter()]) def test_fixture_loading(self): "Multi-db fixtures are loaded correctly" # Check that "Pro Django" exists on the default database, but not on other database try: Book.objects.get(title="Pro Django") Book.objects.using('default').get(title="Pro Django") except Book.DoesNotExist: self.fail('"Pro Django" should exist on default database') self.assertRaises( Book.DoesNotExist, Book.objects.using('other').get, title="Pro Django" ) # Check that "Dive into Python" exists on the default database, but not on other database try: Book.objects.using('other').get(title="Dive into Python") except Book.DoesNotExist: self.fail('"Dive into Python" should exist on other database') self.assertRaises( Book.DoesNotExist, Book.objects.get, title="Dive into Python" ) self.assertRaises( Book.DoesNotExist, Book.objects.using('default').get, title="Dive into Python" ) # Check that "Definitive Guide" exists on the both databases try: Book.objects.get(title="The Definitive Guide to Django") Book.objects.using('default').get(title="The Definitive Guide to Django") Book.objects.using('other').get(title="The Definitive Guide to Django") except Book.DoesNotExist: self.fail('"The Definitive Guide to Django" should exist on both databases') @override_settings(DATABASE_ROUTERS=[AntiPetRouter()]) def test_pseudo_empty_fixtures(self): "A fixture can contain entries, but lead to nothing in the database; this shouldn't raise an error (ref #14068)" new_io = StringIO() management.call_command('loaddata', 'pets', stdout=new_io, stderr=new_io) command_output = new_io.getvalue().strip() # No objects will actually be loaded self.assertEqual(command_output, "Installed 0 object(s) (of 2) from 1 fixture(s)") class PickleQuerySetTestCase(TestCase): multi_db = True def test_pickling(self): for db in connections: Book.objects.using(db).create(title='Dive into Python', published=datetime.date(2009, 5, 4)) qs = Book.objects.all() self.assertEqual(qs.db, pickle.loads(pickle.dumps(qs)).db) class DatabaseReceiver(object): """ Used in the tests for the database argument in signals (#13552) """ def __call__(self, signal, sender, **kwargs): self._database = kwargs['using'] class WriteToOtherRouter(object): """ A router that sends all writes to the other database. """ def db_for_write(self, model, **hints): return "other" class SignalTests(TestCase): multi_db = True def override_router(self): return override_settings(DATABASE_ROUTERS=[WriteToOtherRouter()]) def test_database_arg_save_and_delete(self): """ Tests that the pre/post_save signal contains the correct database. (#13552) """ # Make some signal receivers pre_save_receiver = DatabaseReceiver() post_save_receiver = DatabaseReceiver() pre_delete_receiver = DatabaseReceiver() post_delete_receiver = DatabaseReceiver() # Make model and connect receivers signals.pre_save.connect(sender=Person, receiver=pre_save_receiver) signals.post_save.connect(sender=Person, receiver=post_save_receiver) signals.pre_delete.connect(sender=Person, receiver=pre_delete_receiver) signals.post_delete.connect(sender=Person, receiver=post_delete_receiver) p = Person.objects.create(name='Darth Vader') # Save and test receivers got calls p.save() self.assertEqual(pre_save_receiver._database, DEFAULT_DB_ALIAS) self.assertEqual(post_save_receiver._database, DEFAULT_DB_ALIAS) # Delete, and test p.delete() self.assertEqual(pre_delete_receiver._database, DEFAULT_DB_ALIAS) self.assertEqual(post_delete_receiver._database, DEFAULT_DB_ALIAS) # Save again to a different database p.save(using="other") self.assertEqual(pre_save_receiver._database, "other") self.assertEqual(post_save_receiver._database, "other") # Delete, and test p.delete(using="other") self.assertEqual(pre_delete_receiver._database, "other") self.assertEqual(post_delete_receiver._database, "other") signals.pre_save.disconnect(sender=Person, receiver=pre_save_receiver) signals.post_save.disconnect(sender=Person, receiver=post_save_receiver) signals.pre_delete.disconnect(sender=Person, receiver=pre_delete_receiver) signals.post_delete.disconnect(sender=Person, receiver=post_delete_receiver) def test_database_arg_m2m(self): """ Test that the m2m_changed signal has a correct database arg (#13552) """ # Make a receiver receiver = DatabaseReceiver() # Connect it signals.m2m_changed.connect(receiver=receiver) # Create the models that will be used for the tests b = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) p = Person.objects.create(name="Marty Alchin") # Create a copy of the models on the 'other' database to prevent # integrity errors on backends that don't defer constraints checks Book.objects.using('other').create(pk=b.pk, title=b.title, published=b.published) Person.objects.using('other').create(pk=p.pk, name=p.name) # Test addition b.authors.add(p) self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) with self.override_router(): b.authors.add(p) self.assertEqual(receiver._database, "other") # Test removal b.authors.remove(p) self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) with self.override_router(): b.authors.remove(p) self.assertEqual(receiver._database, "other") # Test addition in reverse p.book_set.add(b) self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) with self.override_router(): p.book_set.add(b) self.assertEqual(receiver._database, "other") # Test clearing b.authors.clear() self.assertEqual(receiver._database, DEFAULT_DB_ALIAS) with self.override_router(): b.authors.clear() self.assertEqual(receiver._database, "other") class AttributeErrorRouter(object): "A router to test the exception handling of ConnectionRouter" def db_for_read(self, model, **hints): raise AttributeError def db_for_write(self, model, **hints): raise AttributeError class RouterAttributeErrorTestCase(TestCase): multi_db = True def override_router(self): return override_settings(DATABASE_ROUTERS=[AttributeErrorRouter()]) def test_attribute_error_read(self): "Check that the AttributeError from AttributeErrorRouter bubbles up" b = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) with self.override_router(): self.assertRaises(AttributeError, Book.objects.get, pk=b.pk) def test_attribute_error_save(self): "Check that the AttributeError from AttributeErrorRouter bubbles up" dive = Book() dive.title = "Dive into Python" dive.published = datetime.date(2009, 5, 4) with self.override_router(): self.assertRaises(AttributeError, dive.save) def test_attribute_error_delete(self): "Check that the AttributeError from AttributeErrorRouter bubbles up" b = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) p = Person.objects.create(name="Marty Alchin") b.authors = [p] b.editor = p with self.override_router(): self.assertRaises(AttributeError, b.delete) def test_attribute_error_m2m(self): "Check that the AttributeError from AttributeErrorRouter bubbles up" b = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) p = Person.objects.create(name="Marty Alchin") with self.override_router(): self.assertRaises(AttributeError, setattr, b, 'authors', [p]) class ModelMetaRouter(object): "A router to ensure model arguments are real model classes" def db_for_write(self, model, **hints): if not hasattr(model, '_meta'): raise ValueError @override_settings(DATABASE_ROUTERS=[ModelMetaRouter()]) class RouterModelArgumentTestCase(TestCase): multi_db = True def test_m2m_collection(self): b = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) p = Person.objects.create(name="Marty Alchin") # test add b.authors.add(p) # test remove b.authors.remove(p) # test clear b.authors.clear() # test setattr b.authors = [p] # test M2M collection b.delete() def test_foreignkey_collection(self): person = Person.objects.create(name='Bob') Pet.objects.create(owner=person, name='Wart') # test related FK collection person.delete() class SyncOnlyDefaultDatabaseRouter(object): def allow_migrate(self, db, app_label, **hints): return db == DEFAULT_DB_ALIAS class MigrateTestCase(TestCase): available_apps = [ 'multiple_database', 'django.contrib.auth', 'django.contrib.contenttypes' ] multi_db = True def test_migrate_to_other_database(self): """Regression test for #16039: migrate with --database option.""" cts = ContentType.objects.using('other').filter(app_label='multiple_database') count = cts.count() self.assertGreater(count, 0) cts.delete() management.call_command('migrate', verbosity=0, interactive=False, load_initial_data=False, database='other') self.assertEqual(cts.count(), count) def test_migrate_to_other_database_with_router(self): """Regression test for #16039: migrate with --database option.""" cts = ContentType.objects.using('other').filter(app_label='multiple_database') cts.delete() with override_settings(DATABASE_ROUTERS=[SyncOnlyDefaultDatabaseRouter()]): management.call_command('migrate', verbosity=0, interactive=False, load_initial_data=False, database='other') self.assertEqual(cts.count(), 0) class RouterUsed(Exception): WRITE = 'write' def __init__(self, mode, model, hints): self.mode = mode self.model = model self.hints = hints class RouteForWriteTestCase(TestCase): multi_db = True class WriteCheckRouter(object): def db_for_write(self, model, **hints): raise RouterUsed(mode=RouterUsed.WRITE, model=model, hints=hints) def override_router(self): return override_settings(DATABASE_ROUTERS=[RouteForWriteTestCase.WriteCheckRouter()]) def test_fk_delete(self): owner = Person.objects.create(name='Someone') pet = Pet.objects.create(name='fido', owner=owner) try: with self.override_router(): pet.owner.delete() self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Person) self.assertEqual(e.hints, {'instance': owner}) def test_reverse_fk_delete(self): owner = Person.objects.create(name='Someone') to_del_qs = owner.pet_set.all() try: with self.override_router(): to_del_qs.delete() self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Pet) self.assertEqual(e.hints, {'instance': owner}) def test_reverse_fk_get_or_create(self): owner = Person.objects.create(name='Someone') try: with self.override_router(): owner.pet_set.get_or_create(name='fido') self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Pet) self.assertEqual(e.hints, {'instance': owner}) def test_reverse_fk_update(self): owner = Person.objects.create(name='Someone') Pet.objects.create(name='fido', owner=owner) try: with self.override_router(): owner.pet_set.update(name='max') self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Pet) self.assertEqual(e.hints, {'instance': owner}) def test_m2m_add(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) try: with self.override_router(): book.authors.add(auth) self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book.authors.through) self.assertEqual(e.hints, {'instance': book}) def test_m2m_clear(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): book.authors.clear() self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book.authors.through) self.assertEqual(e.hints, {'instance': book}) def test_m2m_delete(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): book.authors.all().delete() self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Person) self.assertEqual(e.hints, {'instance': book}) def test_m2m_get_or_create(self): Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) try: with self.override_router(): book.authors.get_or_create(name='Someone else') self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book) self.assertEqual(e.hints, {'instance': book}) def test_m2m_remove(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): book.authors.remove(auth) self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book.authors.through) self.assertEqual(e.hints, {'instance': book}) def test_m2m_update(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): book.authors.all().update(name='Different') self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Person) self.assertEqual(e.hints, {'instance': book}) def test_reverse_m2m_add(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) try: with self.override_router(): auth.book_set.add(book) self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book.authors.through) self.assertEqual(e.hints, {'instance': auth}) def test_reverse_m2m_clear(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): auth.book_set.clear() self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book.authors.through) self.assertEqual(e.hints, {'instance': auth}) def test_reverse_m2m_delete(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): auth.book_set.all().delete() self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book) self.assertEqual(e.hints, {'instance': auth}) def test_reverse_m2m_get_or_create(self): auth = Person.objects.create(name='Someone') Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) try: with self.override_router(): auth.book_set.get_or_create(title="New Book", published=datetime.datetime.now()) self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Person) self.assertEqual(e.hints, {'instance': auth}) def test_reverse_m2m_remove(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): auth.book_set.remove(book) self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book.authors.through) self.assertEqual(e.hints, {'instance': auth}) def test_reverse_m2m_update(self): auth = Person.objects.create(name='Someone') book = Book.objects.create(title="Pro Django", published=datetime.date(2008, 12, 16)) book.authors.add(auth) try: with self.override_router(): auth.book_set.all().update(title='Different') self.fail('db_for_write() not invoked on router') except RouterUsed as e: self.assertEqual(e.mode, RouterUsed.WRITE) self.assertEqual(e.model, Book) self.assertEqual(e.hints, {'instance': auth}) Django-1.8.7/tests/multiple_database/fixtures/0000775000175000017500000000000012625116243021033 5ustar timtim00000000000000Django-1.8.7/tests/multiple_database/fixtures/multidb.other.json0000664000175000017500000000103712603513307024505 0ustar timtim00000000000000[ { "pk": 1, "model": "multiple_database.person", "fields": { "name": "Mark Pilgrim" } }, { "pk": 2, "model": "multiple_database.person", "fields": { "name": "Chris Mills" } }, { "pk": 2, "model": "multiple_database.book", "fields": { "title": "Dive into Python", "published": "2009-5-4", "authors": [["Mark Pilgrim"]], "editor": ["Chris Mills"] } } ]Django-1.8.7/tests/multiple_database/fixtures/multidb.default.json0000664000175000017500000000104212603513307025004 0ustar timtim00000000000000[ { "pk": 1, "model": "multiple_database.person", "fields": { "name": "Marty Alchin" } }, { "pk": 2, "model": "multiple_database.person", "fields": { "name": "George Vilches" } }, { "pk": 2, "model": "multiple_database.book", "fields": { "title": "Pro Django", "published": "2008-12-16", "authors": [["Marty Alchin"]], "editor": ["George Vilches"] } } ] Django-1.8.7/tests/multiple_database/fixtures/multidb-common.json0000664000175000017500000000030412603513307024647 0ustar timtim00000000000000[ { "pk": 1, "model": "multiple_database.book", "fields": { "title": "The Definitive Guide to Django", "published": "2009-7-8" } } ]Django-1.8.7/tests/multiple_database/fixtures/pets.json0000664000175000017500000000047712603513307022707 0ustar timtim00000000000000[ { "pk": 1, "model": "multiple_database.pet", "fields": { "name": "Mr Bigglesworth", "owner": 1 } }, { "pk": 2, "model": "multiple_database.pet", "fields": { "name": "Spot", "owner": 2 } } ]Django-1.8.7/tests/multiple_database/__init__.py0000664000175000017500000000000012603513307021257 0ustar timtim00000000000000Django-1.8.7/tests/multiple_database/models.py0000664000175000017500000000454612625116214021026 0ustar timtim00000000000000from django.contrib.auth.models import User from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Review(models.Model): source = models.CharField(max_length=100) content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey() def __str__(self): return self.source class Meta: ordering = ('source',) class PersonManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) @python_2_unicode_compatible class Person(models.Model): objects = PersonManager() name = models.CharField(max_length=100) def __str__(self): return self.name class Meta: ordering = ('name',) # This book manager doesn't do anything interesting; it just # exists to strip out the 'extra_arg' argument to certain # calls. This argument is used to establish that the BookManager # is actually getting used when it should be. class BookManager(models.Manager): def create(self, *args, **kwargs): kwargs.pop('extra_arg', None) return super(BookManager, self).create(*args, **kwargs) def get_or_create(self, *args, **kwargs): kwargs.pop('extra_arg', None) return super(BookManager, self).get_or_create(*args, **kwargs) @python_2_unicode_compatible class Book(models.Model): objects = BookManager() title = models.CharField(max_length=100) published = models.DateField() authors = models.ManyToManyField(Person) editor = models.ForeignKey(Person, null=True, related_name='edited') reviews = GenericRelation(Review) pages = models.IntegerField(default=100) def __str__(self): return self.title class Meta: ordering = ('title',) @python_2_unicode_compatible class Pet(models.Model): name = models.CharField(max_length=100) owner = models.ForeignKey(Person) def __str__(self): return self.name class Meta: ordering = ('name',) class UserProfile(models.Model): user = models.OneToOneField(User, null=True) flavor = models.CharField(max_length=100) class Meta: ordering = ('flavor',) Django-1.8.7/tests/inspectdb/0000775000175000017500000000000012625116243015456 5ustar timtim00000000000000Django-1.8.7/tests/inspectdb/tests.py0000664000175000017500000003125512625116214017176 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals import re from unittest import skipUnless from django.core.management import call_command from django.db import connection from django.test import TestCase, skipUnlessDBFeature from django.utils.six import PY3, StringIO from .models import ColumnTypes class InspectDBTestCase(TestCase): def test_stealth_table_name_filter_option(self): out = StringIO() # Lets limit the introspection to tables created for models of this # application call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_'), stdout=out) error_message = "inspectdb has examined a table that should have been filtered out." # contrib.contenttypes is one of the apps always installed when running # the Django test suite, check that one of its tables hasn't been # inspected self.assertNotIn("class DjangoContentType(models.Model):", out.getvalue(), msg=error_message) def make_field_type_asserter(self): """Call inspectdb and return a function to validate a field type in its output""" out = StringIO() call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_columntypes'), stdout=out) output = out.getvalue() def assertFieldType(name, definition): out_def = re.search(r'^\s*%s = (models.*)$' % name, output, re.MULTILINE).groups()[0] self.assertEqual(definition, out_def) return assertFieldType def test_field_types(self): """Test introspection of various Django field types""" assertFieldType = self.make_field_type_asserter() # Inspecting Oracle DB doesn't produce correct results (#19884): # - it gets max_length wrong: it returns a number of bytes. # - it reports fields as blank=True when they aren't. if (connection.features.can_introspect_max_length and not connection.features.interprets_empty_strings_as_nulls): assertFieldType('char_field', "models.CharField(max_length=10)") assertFieldType('null_char_field', "models.CharField(max_length=10, blank=True, null=True)") assertFieldType('comma_separated_int_field', "models.CharField(max_length=99)") assertFieldType('date_field', "models.DateField()") assertFieldType('date_time_field', "models.DateTimeField()") if (connection.features.can_introspect_max_length and not connection.features.interprets_empty_strings_as_nulls): assertFieldType('email_field', "models.CharField(max_length=254)") assertFieldType('file_field', "models.CharField(max_length=100)") assertFieldType('file_path_field', "models.CharField(max_length=100)") if connection.features.can_introspect_ip_address_field: assertFieldType('ip_address_field', "models.GenericIPAddressField()") assertFieldType('gen_ip_adress_field', "models.GenericIPAddressField()") elif (connection.features.can_introspect_max_length and not connection.features.interprets_empty_strings_as_nulls): assertFieldType('ip_address_field', "models.CharField(max_length=15)") assertFieldType('gen_ip_adress_field', "models.CharField(max_length=39)") if (connection.features.can_introspect_max_length and not connection.features.interprets_empty_strings_as_nulls): assertFieldType('slug_field', "models.CharField(max_length=50)") if not connection.features.interprets_empty_strings_as_nulls: assertFieldType('text_field', "models.TextField()") if connection.features.can_introspect_time_field: assertFieldType('time_field', "models.TimeField()") if (connection.features.can_introspect_max_length and not connection.features.interprets_empty_strings_as_nulls): assertFieldType('url_field', "models.CharField(max_length=200)") def test_number_field_types(self): """Test introspection of various Django field types""" assertFieldType = self.make_field_type_asserter() if not connection.features.can_introspect_autofield: assertFieldType('id', "models.IntegerField(primary_key=True) # AutoField?") if connection.features.can_introspect_big_integer_field: assertFieldType('big_int_field', "models.BigIntegerField()") else: assertFieldType('big_int_field', "models.IntegerField()") bool_field = ColumnTypes._meta.get_field('bool_field') bool_field_type = connection.features.introspected_boolean_field_type(bool_field) assertFieldType('bool_field', "models.{}()".format(bool_field_type)) null_bool_field = ColumnTypes._meta.get_field('null_bool_field') null_bool_field_type = connection.features.introspected_boolean_field_type(null_bool_field) if 'BooleanField' in null_bool_field_type: assertFieldType('null_bool_field', "models.{}()".format(null_bool_field_type)) else: if connection.features.can_introspect_null: assertFieldType('null_bool_field', "models.{}(blank=True, null=True)".format(null_bool_field_type)) else: assertFieldType('null_bool_field', "models.{}()".format(null_bool_field_type)) if connection.features.can_introspect_decimal_field: assertFieldType('decimal_field', "models.DecimalField(max_digits=6, decimal_places=1)") else: # Guessed arguments on SQLite, see #5014 assertFieldType('decimal_field', "models.DecimalField(max_digits=10, decimal_places=5) " "# max_digits and decimal_places have been guessed, " "as this database handles decimal fields as float") assertFieldType('float_field', "models.FloatField()") assertFieldType('int_field', "models.IntegerField()") if connection.features.can_introspect_positive_integer_field: assertFieldType('pos_int_field', "models.PositiveIntegerField()") else: assertFieldType('pos_int_field', "models.IntegerField()") if connection.features.can_introspect_positive_integer_field: if connection.features.can_introspect_small_integer_field: assertFieldType('pos_small_int_field', "models.PositiveSmallIntegerField()") else: assertFieldType('pos_small_int_field', "models.PositiveIntegerField()") else: if connection.features.can_introspect_small_integer_field: assertFieldType('pos_small_int_field', "models.SmallIntegerField()") else: assertFieldType('pos_small_int_field', "models.IntegerField()") if connection.features.can_introspect_small_integer_field: assertFieldType('small_int_field', "models.SmallIntegerField()") else: assertFieldType('small_int_field', "models.IntegerField()") @skipUnlessDBFeature('can_introspect_foreign_keys') def test_attribute_name_not_python_keyword(self): out = StringIO() # Lets limit the introspection to tables created for models of this # application call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_'), stdout=out) output = out.getvalue() error_message = "inspectdb generated an attribute name which is a python keyword" # Recursive foreign keys should be set to 'self' self.assertIn("parent = models.ForeignKey('self')", output) self.assertNotIn("from = models.ForeignKey(InspectdbPeople)", output, msg=error_message) # As InspectdbPeople model is defined after InspectdbMessage, it should be quoted self.assertIn("from_field = models.ForeignKey('InspectdbPeople', db_column='from_id')", output) self.assertIn("people_pk = models.ForeignKey(InspectdbPeople, primary_key=True)", output) self.assertIn("people_unique = models.ForeignKey(InspectdbPeople, unique=True)", output) def test_digits_column_name_introspection(self): """Introspection of column names consist/start with digits (#16536/#17676)""" out = StringIO() # Lets limit the introspection to tables created for models of this # application call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_'), stdout=out) output = out.getvalue() error_message = "inspectdb generated a model field name which is a number" self.assertNotIn(" 123 = models.CharField", output, msg=error_message) self.assertIn("number_123 = models.CharField", output) error_message = "inspectdb generated a model field name which starts with a digit" self.assertNotIn(" 4extra = models.CharField", output, msg=error_message) self.assertIn("number_4extra = models.CharField", output) self.assertNotIn(" 45extra = models.CharField", output, msg=error_message) self.assertIn("number_45extra = models.CharField", output) def test_special_column_name_introspection(self): """ Introspection of column names containing special characters, unsuitable for Python identifiers """ out = StringIO() call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_'), stdout=out) output = out.getvalue() base_name = 'Field' if not connection.features.uppercases_column_names else 'field' self.assertIn("field = models.IntegerField()", output) self.assertIn("field_field = models.IntegerField(db_column='%s_')" % base_name, output) self.assertIn("field_field_0 = models.IntegerField(db_column='%s__')" % base_name, output) self.assertIn("field_field_1 = models.IntegerField(db_column='__field')", output) self.assertIn("prc_x = models.IntegerField(db_column='prc(%) x')", output) if PY3: # Python 3 allows non-ASCII identifiers self.assertIn("tamaño = models.IntegerField()", output) else: self.assertIn("tama_o = models.IntegerField(db_column='tama\\xf1o')", output) def test_table_name_introspection(self): """ Introspection of table names containing special characters, unsuitable for Python identifiers """ out = StringIO() call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_'), stdout=out) output = out.getvalue() self.assertIn("class InspectdbSpecialTableName(models.Model):", output) def test_managed_models(self): """Test that by default the command generates models with `Meta.managed = False` (#14305)""" out = StringIO() call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_columntypes'), stdout=out) output = out.getvalue() self.longMessage = False self.assertIn(" managed = False", output, msg='inspectdb should generate unmanaged models.') def test_unique_together_meta(self): out = StringIO() call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_uniquetogether'), stdout=out) output = out.getvalue() self.assertIn(" unique_together = (('field1', 'field2'),)", output, msg='inspectdb should generate unique_together.') @skipUnless(connection.vendor == 'sqlite', "Only patched sqlite's DatabaseIntrospection.data_types_reverse for this test") def test_custom_fields(self): """ Introspection of columns with a custom field (#21090) """ out = StringIO() orig_data_types_reverse = connection.introspection.data_types_reverse try: connection.introspection.data_types_reverse = { 'text': 'myfields.TextField', 'bigint': 'BigIntegerField', } call_command('inspectdb', table_name_filter=lambda tn: tn.startswith('inspectdb_columntypes'), stdout=out) output = out.getvalue() self.assertIn("text_field = myfields.TextField()", output) self.assertIn("big_int_field = models.BigIntegerField()", output) finally: connection.introspection.data_types_reverse = orig_data_types_reverse Django-1.8.7/tests/inspectdb/__init__.py0000664000175000017500000000000012603513307017553 0ustar timtim00000000000000Django-1.8.7/tests/inspectdb/models.py0000664000175000017500000000524012625116214017312 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals from django.db import models class People(models.Model): name = models.CharField(max_length=255) parent = models.ForeignKey('self') class Message(models.Model): from_field = models.ForeignKey(People, db_column='from_id') class PeopleData(models.Model): people_pk = models.ForeignKey(People, primary_key=True) ssn = models.CharField(max_length=11) class PeopleMoreData(models.Model): people_unique = models.ForeignKey(People, unique=True) license = models.CharField(max_length=255) class DigitsInColumnName(models.Model): all_digits = models.CharField(max_length=11, db_column='123') leading_digit = models.CharField(max_length=11, db_column='4extra') leading_digits = models.CharField(max_length=11, db_column='45extra') class SpecialName(models.Model): field = models.IntegerField(db_column='field') # Underscores field_field_0 = models.IntegerField(db_column='Field_') field_field_1 = models.IntegerField(db_column='Field__') field_field_2 = models.IntegerField(db_column='__field') # Other chars prc_x = models.IntegerField(db_column='prc(%) x') non_ascii = models.IntegerField(db_column='tamaño') class Meta: db_table = "inspectdb_special.table name" class ColumnTypes(models.Model): id = models.AutoField(primary_key=True) big_int_field = models.BigIntegerField() bool_field = models.BooleanField(default=False) null_bool_field = models.NullBooleanField() char_field = models.CharField(max_length=10) null_char_field = models.CharField(max_length=10, blank=True, null=True) comma_separated_int_field = models.CommaSeparatedIntegerField(max_length=99) date_field = models.DateField() date_time_field = models.DateTimeField() decimal_field = models.DecimalField(max_digits=6, decimal_places=1) email_field = models.EmailField() file_field = models.FileField(upload_to="unused") file_path_field = models.FilePathField() float_field = models.FloatField() int_field = models.IntegerField() ip_address_field = models.IPAddressField() gen_ip_adress_field = models.GenericIPAddressField(protocol="ipv4") pos_int_field = models.PositiveIntegerField() pos_small_int_field = models.PositiveSmallIntegerField() slug_field = models.SlugField() small_int_field = models.SmallIntegerField() text_field = models.TextField() time_field = models.TimeField() url_field = models.URLField() class UniqueTogether(models.Model): field1 = models.IntegerField() field2 = models.CharField(max_length=10) class Meta: unique_together = ('field1', 'field2') Django-1.8.7/tests/queries/0000775000175000017500000000000012625116243015160 5ustar timtim00000000000000Django-1.8.7/tests/queries/tests.py0000664000175000017500000046132612625116214016706 0ustar timtim00000000000000from __future__ import unicode_literals import datetime import pickle import unittest import warnings from collections import OrderedDict from operator import attrgetter from django.core.exceptions import FieldError from django.db import DEFAULT_DB_ALIAS, connection from django.db.models import F, Q, Count from django.db.models.sql.constants import LOUTER from django.db.models.sql.datastructures import EmptyResultSet from django.db.models.sql.where import EverythingNode, NothingNode, WhereNode from django.test import TestCase, skipUnlessDBFeature from django.test.utils import CaptureQueriesContext from django.utils import six from django.utils.deprecation import RemovedInDjango19Warning from django.utils.six.moves import range from .models import ( FK1, X, Annotation, Article, Author, BaseA, Book, CategoryItem, CategoryRelationship, Celebrity, Channel, Chapter, Child, ChildObjectA, Classroom, Company, Cover, CustomPk, CustomPkTag, Detail, DumbCategory, Eaten, Employment, ExtraInfo, Fan, Food, Identifier, Individual, Item, Job, JobResponsibilities, Join, LeafA, LeafB, LoopX, LoopZ, ManagedModel, Member, ModelA, ModelB, ModelC, ModelD, MyObject, NamedCategory, Node, Note, NullableName, Number, ObjectA, ObjectB, ObjectC, OneToOneCategory, Order, OrderItem, Page, Paragraph, Person, Plaything, PointerA, Program, ProxyCategory, ProxyObjectA, ProxyObjectB, Ranking, Related, RelatedIndividual, RelatedObject, Report, ReservedName, Responsibility, School, SharedConnection, SimpleCategory, SingleObject, SpecialCategory, Staff, StaffUser, Student, Tag, Task, Ticket21203Child, Ticket21203Parent, Ticket23605A, Ticket23605B, Ticket23605C, TvChef, Valid, ) class BaseQuerysetTest(TestCase): def assertValueQuerysetEqual(self, qs, values): return self.assertQuerysetEqual(qs, values, transform=lambda x: x) class Queries1Tests(BaseQuerysetTest): @classmethod def setUpTestData(cls): generic = NamedCategory.objects.create(name="Generic") cls.t1 = Tag.objects.create(name='t1', category=generic) cls.t2 = Tag.objects.create(name='t2', parent=cls.t1, category=generic) cls.t3 = Tag.objects.create(name='t3', parent=cls.t1) t4 = Tag.objects.create(name='t4', parent=cls.t3) cls.t5 = Tag.objects.create(name='t5', parent=cls.t3) cls.n1 = Note.objects.create(note='n1', misc='foo', id=1) n2 = Note.objects.create(note='n2', misc='bar', id=2) cls.n3 = Note.objects.create(note='n3', misc='foo', id=3) ann1 = Annotation.objects.create(name='a1', tag=cls.t1) ann1.notes.add(cls.n1) ann2 = Annotation.objects.create(name='a2', tag=t4) ann2.notes.add(n2, cls.n3) # Create these out of order so that sorting by 'id' will be different to sorting # by 'info'. Helps detect some problems later. cls.e2 = ExtraInfo.objects.create(info='e2', note=n2, value=41) e1 = ExtraInfo.objects.create(info='e1', note=cls.n1, value=42) cls.a1 = Author.objects.create(name='a1', num=1001, extra=e1) cls.a2 = Author.objects.create(name='a2', num=2002, extra=e1) a3 = Author.objects.create(name='a3', num=3003, extra=cls.e2) cls.a4 = Author.objects.create(name='a4', num=4004, extra=cls.e2) cls.time1 = datetime.datetime(2007, 12, 19, 22, 25, 0) cls.time2 = datetime.datetime(2007, 12, 19, 21, 0, 0) time3 = datetime.datetime(2007, 12, 20, 22, 25, 0) time4 = datetime.datetime(2007, 12, 20, 21, 0, 0) cls.i1 = Item.objects.create(name='one', created=cls.time1, modified=cls.time1, creator=cls.a1, note=cls.n3) cls.i1.tags = [cls.t1, cls.t2] cls.i2 = Item.objects.create(name='two', created=cls.time2, creator=cls.a2, note=n2) cls.i2.tags = [cls.t1, cls.t3] cls.i3 = Item.objects.create(name='three', created=time3, creator=cls.a2, note=cls.n3) i4 = Item.objects.create(name='four', created=time4, creator=cls.a4, note=cls.n3) i4.tags = [t4] cls.r1 = Report.objects.create(name='r1', creator=cls.a1) Report.objects.create(name='r2', creator=a3) Report.objects.create(name='r3') # Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the Meta.ordering # will be rank3, rank2, rank1. cls.rank1 = Ranking.objects.create(rank=2, author=cls.a2) Cover.objects.create(title="first", item=i4) Cover.objects.create(title="second", item=cls.i2) def test_subquery_condition(self): qs1 = Tag.objects.filter(pk__lte=0) qs2 = Tag.objects.filter(parent__in=qs1) qs3 = Tag.objects.filter(parent__in=qs2) self.assertEqual(qs3.query.subq_aliases, {'T', 'U', 'V'}) self.assertIn('v0', str(qs3.query).lower()) qs4 = qs3.filter(parent__in=qs1) self.assertEqual(qs4.query.subq_aliases, {'T', 'U', 'V'}) # It is possible to reuse U for the second subquery, no need to use W. self.assertNotIn('w0', str(qs4.query).lower()) # So, 'U0."id"' is referenced twice. self.assertTrue(str(qs4.query).lower().count('u0'), 2) def test_ticket1050(self): self.assertQuerysetEqual( Item.objects.filter(tags__isnull=True), [''] ) self.assertQuerysetEqual( Item.objects.filter(tags__id__isnull=True), [''] ) def test_ticket1801(self): self.assertQuerysetEqual( Author.objects.filter(item=self.i2), [''] ) self.assertQuerysetEqual( Author.objects.filter(item=self.i3), [''] ) self.assertQuerysetEqual( Author.objects.filter(item=self.i2) & Author.objects.filter(item=self.i3), [''] ) def test_ticket2306(self): # Checking that no join types are "left outer" joins. query = Item.objects.filter(tags=self.t2).query self.assertNotIn(LOUTER, [x.join_type for x in query.alias_map.values()]) self.assertQuerysetEqual( Item.objects.filter(Q(tags=self.t1)).order_by('name'), ['', ''] ) self.assertQuerysetEqual( Item.objects.filter(Q(tags=self.t1)).filter(Q(tags=self.t2)), [''] ) self.assertQuerysetEqual( Item.objects.filter(Q(tags=self.t1)).filter(Q(creator__name='fred') | Q(tags=self.t2)), [''] ) # Each filter call is processed "at once" against a single table, so this is # different from the previous example as it tries to find tags that are two # things at once (rather than two tags). self.assertQuerysetEqual( Item.objects.filter(Q(tags=self.t1) & Q(tags=self.t2)), [] ) self.assertQuerysetEqual( Item.objects.filter(Q(tags=self.t1), Q(creator__name='fred') | Q(tags=self.t2)), [] ) qs = Author.objects.filter(ranking__rank=2, ranking__id=self.rank1.id) self.assertQuerysetEqual(list(qs), ['']) self.assertEqual(2, qs.query.count_active_tables(), 2) qs = Author.objects.filter(ranking__rank=2).filter(ranking__id=self.rank1.id) self.assertEqual(qs.query.count_active_tables(), 3) def test_ticket4464(self): self.assertQuerysetEqual( Item.objects.filter(tags=self.t1).filter(tags=self.t2), [''] ) self.assertQuerysetEqual( Item.objects.filter(tags__in=[self.t1, self.t2]).distinct().order_by('name'), ['', ''] ) self.assertQuerysetEqual( Item.objects.filter(tags__in=[self.t1, self.t2]).filter(tags=self.t3), [''] ) # Make sure .distinct() works with slicing (this was broken in Oracle). self.assertQuerysetEqual( Item.objects.filter(tags__in=[self.t1, self.t2]).order_by('name')[:3], ['', '', ''] ) self.assertQuerysetEqual( Item.objects.filter(tags__in=[self.t1, self.t2]).distinct().order_by('name')[:3], ['', ''] ) def test_tickets_2080_3592(self): self.assertQuerysetEqual( Author.objects.filter(item__name='one') | Author.objects.filter(name='a3'), ['', ''] ) self.assertQuerysetEqual( Author.objects.filter(Q(item__name='one') | Q(name='a3')), ['', ''] ) self.assertQuerysetEqual( Author.objects.filter(Q(name='a3') | Q(item__name='one')), ['', ''] ) self.assertQuerysetEqual( Author.objects.filter(Q(item__name='three') | Q(report__name='r3')), [''] ) def test_ticket6074(self): # Merging two empty result sets shouldn't leave a queryset with no constraints # (which would match everything). self.assertQuerysetEqual(Author.objects.filter(Q(id__in=[])), []) self.assertQuerysetEqual( Author.objects.filter(Q(id__in=[]) | Q(id__in=[])), [] ) def test_tickets_1878_2939(self): self.assertEqual(Item.objects.values('creator').distinct().count(), 3) # Create something with a duplicate 'name' so that we can test multi-column # cases (which require some tricky SQL transformations under the covers). xx = Item(name='four', created=self.time1, creator=self.a2, note=self.n1) xx.save() self.assertEqual( Item.objects.exclude(name='two').values('creator', 'name').distinct().count(), 4 ) self.assertEqual( Item.objects.exclude(name='two').extra(select={'foo': '%s'}, select_params=(1,)).values('creator', 'name', 'foo').distinct().count(), 4 ) self.assertEqual( Item.objects.exclude(name='two').extra(select={'foo': '%s'}, select_params=(1,)).values('creator', 'name').distinct().count(), 4 ) xx.delete() def test_ticket7323(self): self.assertEqual(Item.objects.values('creator', 'name').count(), 4) def test_ticket2253(self): q1 = Item.objects.order_by('name') q2 = Item.objects.filter(id=self.i1.id) self.assertQuerysetEqual( q1, ['', '', '', ''] ) self.assertQuerysetEqual(q2, ['']) self.assertQuerysetEqual( (q1 | q2).order_by('name'), ['', '', '', ''] ) self.assertQuerysetEqual((q1 & q2).order_by('name'), ['']) q1 = Item.objects.filter(tags=self.t1) q2 = Item.objects.filter(note=self.n3, tags=self.t2) q3 = Item.objects.filter(creator=self.a4) self.assertQuerysetEqual( ((q1 & q2) | q3).order_by('name'), ['', ''] ) def test_order_by_tables(self): q1 = Item.objects.order_by('name') q2 = Item.objects.filter(id=self.i1.id) list(q2) combined_query = (q1 & q2).order_by('name').query self.assertEqual(len([ t for t in combined_query.tables if combined_query.alias_refcount[t] ]), 1) def test_order_by_join_unref(self): """ This test is related to the above one, testing that there aren't old JOINs in the query. """ qs = Celebrity.objects.order_by('greatest_fan__fan_of') self.assertIn('OUTER JOIN', str(qs.query)) qs = qs.order_by('id') self.assertNotIn('OUTER JOIN', str(qs.query)) def test_tickets_4088_4306(self): self.assertQuerysetEqual( Report.objects.filter(creator=1001), [''] ) self.assertQuerysetEqual( Report.objects.filter(creator__num=1001), [''] ) self.assertQuerysetEqual(Report.objects.filter(creator__id=1001), []) self.assertQuerysetEqual( Report.objects.filter(creator__id=self.a1.id), [''] ) self.assertQuerysetEqual( Report.objects.filter(creator__name='a1'), [''] ) def test_ticket4510(self): self.assertQuerysetEqual( Author.objects.filter(report__name='r1'), [''] ) def test_ticket7378(self): self.assertQuerysetEqual(self.a1.report_set.all(), ['']) def test_tickets_5324_6704(self): self.assertQuerysetEqual( Item.objects.filter(tags__name='t4'), [''] ) self.assertQuerysetEqual( Item.objects.exclude(tags__name='t4').order_by('name').distinct(), ['', '', ''] ) self.assertQuerysetEqual( Item.objects.exclude(tags__name='t4').order_by('name').distinct().reverse(), ['', '', ''] ) self.assertQuerysetEqual( Author.objects.exclude(item__name='one').distinct().order_by('name'), ['', '', ''] ) # Excluding across a m2m relation when there is more than one related # object associated was problematic. self.assertQuerysetEqual( Item.objects.exclude(tags__name='t1').order_by('name'), ['', ''] ) self.assertQuerysetEqual( Item.objects.exclude(tags__name='t1').exclude(tags__name='t4'), [''] ) # Excluding from a relation that cannot be NULL should not use outer joins. query = Item.objects.exclude(creator__in=[self.a1, self.a2]).query self.assertNotIn(LOUTER, [x.join_type for x in query.alias_map.values()]) # Similarly, when one of the joins cannot possibly, ever, involve NULL # values (Author -> ExtraInfo, in the following), it should never be # promoted to a left outer join. So the following query should only # involve one "left outer" join (Author -> Item is 0-to-many). qs = Author.objects.filter(id=self.a1.id).filter(Q(extra__note=self.n1) | Q(item__note=self.n3)) self.assertEqual( len([x for x in qs.query.alias_map.values() if x.join_type == LOUTER and qs.query.alias_refcount[x.table_alias]]), 1 ) # The previous changes shouldn't affect nullable foreign key joins. self.assertQuerysetEqual( Tag.objects.filter(parent__isnull=True).order_by('name'), [''] ) self.assertQuerysetEqual( Tag.objects.exclude(parent__isnull=True).order_by('name'), ['', '', '', ''] ) self.assertQuerysetEqual( Tag.objects.exclude(Q(parent__name='t1') | Q(parent__isnull=True)).order_by('name'), ['', ''] ) self.assertQuerysetEqual( Tag.objects.exclude(Q(parent__isnull=True) | Q(parent__name='t1')).order_by('name'), ['', ''] ) self.assertQuerysetEqual( Tag.objects.exclude(Q(parent__parent__isnull=True)).order_by('name'), ['', ''] ) self.assertQuerysetEqual( Tag.objects.filter(~Q(parent__parent__isnull=True)).order_by('name'), ['', ''] ) def test_ticket2091(self): t = Tag.objects.get(name='t4') self.assertQuerysetEqual( Item.objects.filter(tags__in=[t]), [''] ) def test_avoid_infinite_loop_on_too_many_subqueries(self): x = Tag.objects.filter(pk=1) local_recursion_limit = 127 msg = 'Maximum recursion depth exceeded: too many subqueries.' with self.assertRaisesMessage(RuntimeError, msg): for i in six.moves.range(local_recursion_limit * 2): x = Tag.objects.filter(pk__in=x) def test_reasonable_number_of_subq_aliases(self): x = Tag.objects.filter(pk=1) for _ in range(20): x = Tag.objects.filter(pk__in=x) self.assertEqual( x.query.subq_aliases, { 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'AA', 'AB', 'AC', 'AD', 'AE', 'AF', 'AG', 'AH', 'AI', 'AJ', 'AK', 'AL', 'AM', 'AN', } ) def test_heterogeneous_qs_combination(self): # Combining querysets built on different models should behave in a well-defined # fashion. We raise an error. self.assertRaisesMessage( AssertionError, 'Cannot combine queries on two different base models.', lambda: Author.objects.all() & Tag.objects.all() ) self.assertRaisesMessage( AssertionError, 'Cannot combine queries on two different base models.', lambda: Author.objects.all() | Tag.objects.all() ) def test_ticket3141(self): self.assertEqual(Author.objects.extra(select={'foo': '1'}).count(), 4) self.assertEqual( Author.objects.extra(select={'foo': '%s'}, select_params=(1,)).count(), 4 ) def test_ticket2400(self): self.assertQuerysetEqual( Author.objects.filter(item__isnull=True), [''] ) self.assertQuerysetEqual( Tag.objects.filter(item__isnull=True), [''] ) def test_ticket2496(self): self.assertQuerysetEqual( Item.objects.extra(tables=['queries_author']).select_related().order_by('name')[:1], [''] ) def test_tickets_2076_7256(self): # Ordering on related tables should be possible, even if the table is # not otherwise involved. self.assertQuerysetEqual( Item.objects.order_by('note__note', 'name'), ['', '', '', ''] ) # Ordering on a related field should use the remote model's default # ordering as a final step. self.assertQuerysetEqual( Author.objects.order_by('extra', '-name'), ['', '', '', ''] ) # Using remote model default ordering can span multiple models (in this # case, Cover is ordered by Item's default, which uses Note's default). self.assertQuerysetEqual( Cover.objects.all(), ['', ''] ) # If the remote model does not have a default ordering, we order by its 'id' # field. self.assertQuerysetEqual( Item.objects.order_by('creator', 'name'), ['', '', '', ''] ) # Ordering by a many-valued attribute (e.g. a many-to-many or reverse # ForeignKey) is legal, but the results might not make sense. That # isn't Django's problem. Garbage in, garbage out. self.assertQuerysetEqual( Item.objects.filter(tags__isnull=False).order_by('tags', 'id'), ['', '', '', '', ''] ) # If we replace the default ordering, Django adjusts the required # tables automatically. Item normally requires a join with Note to do # the default ordering, but that isn't needed here. qs = Item.objects.order_by('name') self.assertQuerysetEqual( qs, ['', '', '', ''] ) self.assertEqual(len(qs.query.tables), 1) def test_tickets_2874_3002(self): qs = Item.objects.select_related().order_by('note__note', 'name') self.assertQuerysetEqual( qs, ['', '', '', ''] ) # This is also a good select_related() test because there are multiple # Note entries in the SQL. The two Note items should be different. self.assertTrue(repr(qs[0].note), '') self.assertEqual(repr(qs[0].creator.extra.note), '') def test_ticket3037(self): self.assertQuerysetEqual( Item.objects.filter(Q(creator__name='a3', name='two') | Q(creator__name='a4', name='four')), [''] ) def test_tickets_5321_7070(self): # Ordering columns must be included in the output columns. Note that # this means results that might otherwise be distinct are not (if there # are multiple values in the ordering cols), as in this example. This # isn't a bug; it's a warning to be careful with the selection of # ordering columns. self.assertValueQuerysetEqual( Note.objects.values('misc').distinct().order_by('note', '-misc'), [{'misc': 'foo'}, {'misc': 'bar'}, {'misc': 'foo'}] ) def test_ticket4358(self): # If you don't pass any fields to values(), relation fields are # returned as "foo_id" keys, not "foo". For consistency, you should be # able to pass "foo_id" in the fields list and have it work, too. We # actually allow both "foo" and "foo_id". # The *_id version is returned by default. self.assertIn('note_id', ExtraInfo.objects.values()[0]) # You can also pass it in explicitly. self.assertValueQuerysetEqual( ExtraInfo.objects.values('note_id'), [{'note_id': 1}, {'note_id': 2}] ) # ...or use the field name. self.assertValueQuerysetEqual( ExtraInfo.objects.values('note'), [{'note': 1}, {'note': 2}] ) def test_ticket2902(self): # Parameters can be given to extra_select, *if* you use an OrderedDict. # (First we need to know which order the keys fall in "naturally" on # your system, so we can put things in the wrong way around from # normal. A normal dict would thus fail.) s = [('a', '%s'), ('b', '%s')] params = ['one', 'two'] if {'a': 1, 'b': 2}.keys() == ['a', 'b']: s.reverse() params.reverse() # This slightly odd comparison works around the fact that PostgreSQL will # return 'one' and 'two' as strings, not Unicode objects. It's a side-effect of # using constants here and not a real concern. d = Item.objects.extra(select=OrderedDict(s), select_params=params).values('a', 'b')[0] self.assertEqual(d, {'a': 'one', 'b': 'two'}) # Order by the number of tags attached to an item. l = Item.objects.extra(select={'count': 'select count(*) from queries_item_tags where queries_item_tags.item_id = queries_item.id'}).order_by('-count') self.assertEqual([o.count for o in l], [2, 2, 1, 0]) def test_ticket6154(self): # Multiple filter statements are joined using "AND" all the time. self.assertQuerysetEqual( Author.objects.filter(id=self.a1.id).filter(Q(extra__note=self.n1) | Q(item__note=self.n3)), [''] ) self.assertQuerysetEqual( Author.objects.filter(Q(extra__note=self.n1) | Q(item__note=self.n3)).filter(id=self.a1.id), [''] ) def test_ticket6981(self): self.assertQuerysetEqual( Tag.objects.select_related('parent').order_by('name'), ['', '', '', '', ''] ) def test_ticket9926(self): self.assertQuerysetEqual( Tag.objects.select_related("parent", "category").order_by('name'), ['', '', '', '', ''] ) self.assertQuerysetEqual( Tag.objects.select_related('parent', "parent__category").order_by('name'), ['', '', '', '', ''] ) def test_tickets_6180_6203(self): # Dates with limits and/or counts self.assertEqual(Item.objects.count(), 4) self.assertEqual(Item.objects.datetimes('created', 'month').count(), 1) self.assertEqual(Item.objects.datetimes('created', 'day').count(), 2) self.assertEqual(len(Item.objects.datetimes('created', 'day')), 2) self.assertEqual(Item.objects.datetimes('created', 'day')[0], datetime.datetime(2007, 12, 19, 0, 0)) def test_tickets_7087_12242(self): # Dates with extra select columns self.assertQuerysetEqual( Item.objects.datetimes('created', 'day').extra(select={'a': 1}), ['datetime.datetime(2007, 12, 19, 0, 0)', 'datetime.datetime(2007, 12, 20, 0, 0)'] ) self.assertQuerysetEqual( Item.objects.extra(select={'a': 1}).datetimes('created', 'day'), ['datetime.datetime(2007, 12, 19, 0, 0)', 'datetime.datetime(2007, 12, 20, 0, 0)'] ) name = "one" self.assertQuerysetEqual( Item.objects.datetimes('created', 'day').extra(where=['name=%s'], params=[name]), ['datetime.datetime(2007, 12, 19, 0, 0)'] ) self.assertQuerysetEqual( Item.objects.extra(where=['name=%s'], params=[name]).datetimes('created', 'day'), ['datetime.datetime(2007, 12, 19, 0, 0)'] ) def test_ticket7155(self): # Nullable dates self.assertQuerysetEqual( Item.objects.datetimes('modified', 'day'), ['datetime.datetime(2007, 12, 19, 0, 0)'] ) def test_ticket7098(self): # Make sure semi-deprecated ordering by related models syntax still # works. self.assertValueQuerysetEqual( Item.objects.values('note__note').order_by('queries_note.note', 'id'), [{'note__note': 'n2'}, {'note__note': 'n3'}, {'note__note': 'n3'}, {'note__note': 'n3'}] ) def test_ticket7096(self): # Make sure exclude() with multiple conditions continues to work. self.assertQuerysetEqual( Tag.objects.filter(parent=self.t1, name='t3').order_by('name'), [''] ) self.assertQuerysetEqual( Tag.objects.exclude(parent=self.t1, name='t3').order_by('name'), ['', '', '', ''] ) self.assertQuerysetEqual( Item.objects.exclude(tags__name='t1', name='one').order_by('name').distinct(), ['', '', ''] ) self.assertQuerysetEqual( Item.objects.filter(name__in=['three', 'four']).exclude(tags__name='t1').order_by('name'), ['', ''] ) # More twisted cases, involving nested negations. self.assertQuerysetEqual( Item.objects.exclude(~Q(tags__name='t1', name='one')), [''] ) self.assertQuerysetEqual( Item.objects.filter(~Q(tags__name='t1', name='one'), name='two'), [''] ) self.assertQuerysetEqual( Item.objects.exclude(~Q(tags__name='t1', name='one'), name='two'), ['', '', ''] ) def test_tickets_7204_7506(self): # Make sure querysets with related fields can be pickled. If this # doesn't crash, it's a Good Thing. pickle.dumps(Item.objects.all()) def test_ticket7813(self): # We should also be able to pickle things that use select_related(). # The only tricky thing here is to ensure that we do the related # selections properly after unpickling. qs = Item.objects.select_related() query = qs.query.get_compiler(qs.db).as_sql()[0] query2 = pickle.loads(pickle.dumps(qs.query)) self.assertEqual( query2.get_compiler(qs.db).as_sql()[0], query ) def test_deferred_load_qs_pickling(self): # Check pickling of deferred-loading querysets qs = Item.objects.defer('name', 'creator') q2 = pickle.loads(pickle.dumps(qs)) self.assertEqual(list(qs), list(q2)) q3 = pickle.loads(pickle.dumps(qs, pickle.HIGHEST_PROTOCOL)) self.assertEqual(list(qs), list(q3)) def test_ticket7277(self): self.assertQuerysetEqual( self.n1.annotation_set.filter(Q(tag=self.t5) | Q(tag__children=self.t5) | Q(tag__children__children=self.t5)), [''] ) def test_tickets_7448_7707(self): # Complex objects should be converted to strings before being used in # lookups. self.assertQuerysetEqual( Item.objects.filter(created__in=[self.time1, self.time2]), ['', ''] ) def test_ticket7235(self): # An EmptyQuerySet should not raise exceptions if it is filtered. Eaten.objects.create(meal='m') q = Eaten.objects.none() with self.assertNumQueries(0): self.assertQuerysetEqual(q.all(), []) self.assertQuerysetEqual(q.filter(meal='m'), []) self.assertQuerysetEqual(q.exclude(meal='m'), []) self.assertQuerysetEqual(q.complex_filter({'pk': 1}), []) self.assertQuerysetEqual(q.select_related('food'), []) self.assertQuerysetEqual(q.annotate(Count('food')), []) self.assertQuerysetEqual(q.order_by('meal', 'food'), []) self.assertQuerysetEqual(q.distinct(), []) self.assertQuerysetEqual( q.extra(select={'foo': "1"}), [] ) q.query.low_mark = 1 self.assertRaisesMessage( AssertionError, 'Cannot change a query once a slice has been taken', q.extra, select={'foo': "1"} ) self.assertQuerysetEqual(q.reverse(), []) self.assertQuerysetEqual(q.defer('meal'), []) self.assertQuerysetEqual(q.only('meal'), []) def test_ticket7791(self): # There were "issues" when ordering and distinct-ing on fields related # via ForeignKeys. self.assertEqual( len(Note.objects.order_by('extrainfo__info').distinct()), 3 ) # Pickling of QuerySets using datetimes() should work. qs = Item.objects.datetimes('created', 'month') pickle.loads(pickle.dumps(qs)) def test_ticket9997(self): # If a ValuesList or Values queryset is passed as an inner query, we # make sure it's only requesting a single value and use that as the # thing to select. self.assertQuerysetEqual( Tag.objects.filter(name__in=Tag.objects.filter(parent=self.t1).values('name')), ['', ''] ) # Multi-valued values() and values_list() querysets should raise errors. self.assertRaisesMessage( TypeError, 'Cannot use a multi-field ValuesQuerySet as a filter value.', lambda: Tag.objects.filter(name__in=Tag.objects.filter(parent=self.t1).values('name', 'id')) ) self.assertRaisesMessage( TypeError, 'Cannot use a multi-field ValuesListQuerySet as a filter value.', lambda: Tag.objects.filter(name__in=Tag.objects.filter(parent=self.t1).values_list('name', 'id')) ) def test_ticket9985(self): # qs.values_list(...).values(...) combinations should work. self.assertValueQuerysetEqual( Note.objects.values_list("note", flat=True).values("id").order_by("id"), [{'id': 1}, {'id': 2}, {'id': 3}] ) self.assertQuerysetEqual( Annotation.objects.filter(notes__in=Note.objects.filter(note="n1").values_list('note').values('id')), [''] ) def test_ticket10205(self): # When bailing out early because of an empty "__in" filter, we need # to set things up correctly internally so that subqueries can continue properly. self.assertEqual(Tag.objects.filter(name__in=()).update(name="foo"), 0) def test_ticket10432(self): # Testing an empty "__in" filter with a generator as the value. def f(): return iter([]) n_obj = Note.objects.all()[0] def g(): for i in [n_obj.pk]: yield i self.assertQuerysetEqual(Note.objects.filter(pk__in=f()), []) self.assertEqual(list(Note.objects.filter(pk__in=g())), [n_obj]) def test_ticket10742(self): # Queries used in an __in clause don't execute subqueries subq = Author.objects.filter(num__lt=3000) qs = Author.objects.filter(pk__in=subq) self.assertQuerysetEqual(qs, ['', '']) # The subquery result cache should not be populated self.assertIsNone(subq._result_cache) subq = Author.objects.filter(num__lt=3000) qs = Author.objects.exclude(pk__in=subq) self.assertQuerysetEqual(qs, ['', '']) # The subquery result cache should not be populated self.assertIsNone(subq._result_cache) subq = Author.objects.filter(num__lt=3000) self.assertQuerysetEqual( Author.objects.filter(Q(pk__in=subq) & Q(name='a1')), [''] ) # The subquery result cache should not be populated self.assertIsNone(subq._result_cache) def test_ticket7076(self): # Excluding shouldn't eliminate NULL entries. self.assertQuerysetEqual( Item.objects.exclude(modified=self.time1).order_by('name'), ['', '', ''] ) self.assertQuerysetEqual( Tag.objects.exclude(parent__name=self.t1.name), ['', '', ''] ) def test_ticket7181(self): # Ordering by related tables should accommodate nullable fields (this # test is a little tricky, since NULL ordering is database dependent. # Instead, we just count the number of results). self.assertEqual(len(Tag.objects.order_by('parent__name')), 5) # Empty querysets can be merged with others. self.assertQuerysetEqual( Note.objects.none() | Note.objects.all(), ['', '', ''] ) self.assertQuerysetEqual( Note.objects.all() | Note.objects.none(), ['', '', ''] ) self.assertQuerysetEqual(Note.objects.none() & Note.objects.all(), []) self.assertQuerysetEqual(Note.objects.all() & Note.objects.none(), []) def test_ticket9411(self): # Make sure bump_prefix() (an internal Query method) doesn't (re-)break. It's # sufficient that this query runs without error. 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))) def test_ticket8439(self): # Complex combinations of conjunctions, disjunctions and nullable # relations. self.assertQuerysetEqual( Author.objects.filter(Q(item__note__extrainfo=self.e2) | Q(report=self.r1, name='xyz')), [''] ) self.assertQuerysetEqual( Author.objects.filter(Q(report=self.r1, name='xyz') | Q(item__note__extrainfo=self.e2)), [''] ) self.assertQuerysetEqual( Annotation.objects.filter(Q(tag__parent=self.t1) | Q(notes__note='n1', name='a1')), [''] ) xx = ExtraInfo.objects.create(info='xx', note=self.n3) self.assertQuerysetEqual( Note.objects.filter(Q(extrainfo__author=self.a1) | Q(extrainfo=xx)), ['', ''] ) q = Note.objects.filter(Q(extrainfo__author=self.a1) | Q(extrainfo=xx)).query self.assertEqual( len([x for x in q.alias_map.values() if x.join_type == LOUTER and q.alias_refcount[x.table_alias]]), 1 ) def test_ticket17429(self): """ Ensure that Meta.ordering=None works the same as Meta.ordering=[] """ original_ordering = Tag._meta.ordering Tag._meta.ordering = None try: self.assertQuerysetEqual( Tag.objects.all(), ['', '', '', '', ''], ordered=False ) finally: Tag._meta.ordering = original_ordering def test_exclude(self): self.assertQuerysetEqual( Item.objects.exclude(tags__name='t4'), [repr(i) for i in Item.objects.filter(~Q(tags__name='t4'))]) self.assertQuerysetEqual( Item.objects.exclude(Q(tags__name='t4') | Q(tags__name='t3')), [repr(i) for i in Item.objects.filter(~(Q(tags__name='t4') | Q(tags__name='t3')))]) self.assertQuerysetEqual( Item.objects.exclude(Q(tags__name='t4') | ~Q(tags__name='t3')), [repr(i) for i in Item.objects.filter(~(Q(tags__name='t4') | ~Q(tags__name='t3')))]) def test_nested_exclude(self): self.assertQuerysetEqual( Item.objects.exclude(~Q(tags__name='t4')), [repr(i) for i in Item.objects.filter(~~Q(tags__name='t4'))]) def test_double_exclude(self): self.assertQuerysetEqual( Item.objects.filter(Q(tags__name='t4')), [repr(i) for i in Item.objects.filter(~~Q(tags__name='t4'))]) self.assertQuerysetEqual( Item.objects.filter(Q(tags__name='t4')), [repr(i) for i in Item.objects.filter(~Q(~Q(tags__name='t4')))]) def test_exclude_in(self): self.assertQuerysetEqual( Item.objects.exclude(Q(tags__name__in=['t4', 't3'])), [repr(i) for i in Item.objects.filter(~Q(tags__name__in=['t4', 't3']))]) self.assertQuerysetEqual( Item.objects.filter(Q(tags__name__in=['t4', 't3'])), [repr(i) for i in Item.objects.filter(~~Q(tags__name__in=['t4', 't3']))]) def test_ticket_10790_1(self): # Querying direct fields with isnull should trim the left outer join. # It also should not create INNER JOIN. q = Tag.objects.filter(parent__isnull=True) self.assertQuerysetEqual(q, ['']) self.assertNotIn('JOIN', str(q.query)) q = Tag.objects.filter(parent__isnull=False) self.assertQuerysetEqual( q, ['', '', '', ''], ) self.assertNotIn('JOIN', str(q.query)) q = Tag.objects.exclude(parent__isnull=True) self.assertQuerysetEqual( q, ['', '', '', ''], ) self.assertNotIn('JOIN', str(q.query)) q = Tag.objects.exclude(parent__isnull=False) self.assertQuerysetEqual(q, ['']) self.assertNotIn('JOIN', str(q.query)) q = Tag.objects.exclude(parent__parent__isnull=False) self.assertQuerysetEqual( q, ['', '', ''], ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 1) self.assertNotIn('INNER JOIN', str(q.query)) def test_ticket_10790_2(self): # Querying across several tables should strip only the last outer join, # while preserving the preceding inner joins. q = Tag.objects.filter(parent__parent__isnull=False) self.assertQuerysetEqual( q, ['', ''], ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 1) # Querying without isnull should not convert anything to left outer join. q = Tag.objects.filter(parent__parent=self.t1) self.assertQuerysetEqual( q, ['', ''], ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 1) def test_ticket_10790_3(self): # Querying via indirect fields should populate the left outer join q = NamedCategory.objects.filter(tag__isnull=True) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 1) # join to dumbcategory ptr_id self.assertEqual(str(q.query).count('INNER JOIN'), 1) self.assertQuerysetEqual(q, []) # Querying across several tables should strip only the last join, while # preserving the preceding left outer joins. q = NamedCategory.objects.filter(tag__parent__isnull=True) self.assertEqual(str(q.query).count('INNER JOIN'), 1) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 1) self.assertQuerysetEqual(q, ['']) def test_ticket_10790_4(self): # Querying across m2m field should not strip the m2m table from join. q = Author.objects.filter(item__tags__isnull=True) self.assertQuerysetEqual( q, ['', ''], ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 2) self.assertNotIn('INNER JOIN', str(q.query)) q = Author.objects.filter(item__tags__parent__isnull=True) self.assertQuerysetEqual( q, ['', '', '', ''], ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 3) self.assertNotIn('INNER JOIN', str(q.query)) def test_ticket_10790_5(self): # Querying with isnull=False across m2m field should not create outer joins q = Author.objects.filter(item__tags__isnull=False) self.assertQuerysetEqual( q, ['', '', '', '', ''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 2) q = Author.objects.filter(item__tags__parent__isnull=False) self.assertQuerysetEqual( q, ['', '', ''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 3) q = Author.objects.filter(item__tags__parent__parent__isnull=False) self.assertQuerysetEqual( q, [''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 4) def test_ticket_10790_6(self): # Querying with isnull=True across m2m field should not create inner joins # and strip last outer join q = Author.objects.filter(item__tags__parent__parent__isnull=True) self.assertQuerysetEqual( q, ['', '', '', '', '', ''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 4) self.assertEqual(str(q.query).count('INNER JOIN'), 0) q = Author.objects.filter(item__tags__parent__isnull=True) self.assertQuerysetEqual( q, ['', '', '', ''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 3) self.assertEqual(str(q.query).count('INNER JOIN'), 0) def test_ticket_10790_7(self): # Reverse querying with isnull should not strip the join q = Author.objects.filter(item__isnull=True) self.assertQuerysetEqual( q, [''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 1) self.assertEqual(str(q.query).count('INNER JOIN'), 0) q = Author.objects.filter(item__isnull=False) self.assertQuerysetEqual( q, ['', '', '', ''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 1) def test_ticket_10790_8(self): # Querying with combined q-objects should also strip the left outer join q = Tag.objects.filter(Q(parent__isnull=True) | Q(parent=self.t1)) self.assertQuerysetEqual( q, ['', '', ''] ) self.assertEqual(str(q.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q.query).count('INNER JOIN'), 0) def test_ticket_10790_combine(self): # Combining queries should not re-populate the left outer join q1 = Tag.objects.filter(parent__isnull=True) q2 = Tag.objects.filter(parent__isnull=False) q3 = q1 | q2 self.assertQuerysetEqual( q3, ['', '', '', '', ''], ) self.assertEqual(str(q3.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q3.query).count('INNER JOIN'), 0) q3 = q1 & q2 self.assertQuerysetEqual(q3, []) self.assertEqual(str(q3.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q3.query).count('INNER JOIN'), 0) q2 = Tag.objects.filter(parent=self.t1) q3 = q1 | q2 self.assertQuerysetEqual( q3, ['', '', ''] ) self.assertEqual(str(q3.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q3.query).count('INNER JOIN'), 0) q3 = q2 | q1 self.assertQuerysetEqual( q3, ['', '', ''] ) self.assertEqual(str(q3.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(q3.query).count('INNER JOIN'), 0) q1 = Tag.objects.filter(parent__isnull=True) q2 = Tag.objects.filter(parent__parent__isnull=True) q3 = q1 | q2 self.assertQuerysetEqual( q3, ['', '', ''] ) self.assertEqual(str(q3.query).count('LEFT OUTER JOIN'), 1) self.assertEqual(str(q3.query).count('INNER JOIN'), 0) q3 = q2 | q1 self.assertQuerysetEqual( q3, ['', '', ''] ) self.assertEqual(str(q3.query).count('LEFT OUTER JOIN'), 1) self.assertEqual(str(q3.query).count('INNER JOIN'), 0) def test_ticket19672(self): self.assertQuerysetEqual( Report.objects.filter(Q(creator__isnull=False) & ~Q(creator__extra__value=41)), [''] ) def test_ticket_20250(self): # A negated Q along with an annotated queryset failed in Django 1.4 qs = Author.objects.annotate(Count('item')) qs = qs.filter(~Q(extra__value=0)) self.assertIn('SELECT', str(qs.query)) self.assertQuerysetEqual( qs, ['', '', '', ''] ) def test_callable_args(self): with warnings.catch_warnings(record=True) as w: warnings.simplefilter('always') qs = Tag.objects.filter(name__startswith=lambda: 't') self.assertQuerysetEqual( qs, ['', '', '', '', ''] ) self.assertEqual(len(w), 1) self.assertTrue(issubclass(w[0].category, RemovedInDjango19Warning)) def test_lookup_constraint_fielderror(self): msg = ( "Cannot resolve keyword 'unknown_field' into field. Choices are: " "annotation, category, category_id, children, id, item, " "managedmodel, name, note, parent, parent_id" ) with self.assertRaisesMessage(FieldError, msg): Tag.objects.filter(unknown_field__name='generic') class Queries2Tests(TestCase): @classmethod def setUpTestData(cls): Number.objects.create(num=4) Number.objects.create(num=8) Number.objects.create(num=12) def test_ticket4289(self): # A slight variation on the restricting the filtering choices by the # lookup constraints. self.assertQuerysetEqual(Number.objects.filter(num__lt=4), []) self.assertQuerysetEqual(Number.objects.filter(num__gt=8, num__lt=12), []) self.assertQuerysetEqual( Number.objects.filter(num__gt=8, num__lt=13), [''] ) self.assertQuerysetEqual( Number.objects.filter(Q(num__lt=4) | Q(num__gt=8, num__lt=12)), [] ) self.assertQuerysetEqual( Number.objects.filter(Q(num__gt=8, num__lt=12) | Q(num__lt=4)), [] ) self.assertQuerysetEqual( Number.objects.filter(Q(num__gt=8) & Q(num__lt=12) | Q(num__lt=4)), [] ) self.assertQuerysetEqual( Number.objects.filter(Q(num__gt=7) & Q(num__lt=12) | Q(num__lt=4)), [''] ) def test_ticket12239(self): # Float was being rounded to integer on gte queries on integer field. Tests # show that gt, lt, gte, and lte work as desired. Note that the fix changes # get_prep_lookup for gte and lt queries only. self.assertQuerysetEqual( Number.objects.filter(num__gt=11.9), [''] ) self.assertQuerysetEqual(Number.objects.filter(num__gt=12), []) self.assertQuerysetEqual(Number.objects.filter(num__gt=12.0), []) self.assertQuerysetEqual(Number.objects.filter(num__gt=12.1), []) self.assertQuerysetEqual( Number.objects.filter(num__lt=12), ['', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__lt=12.0), ['', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__lt=12.1), ['', '', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__gte=11.9), [''] ) self.assertQuerysetEqual( Number.objects.filter(num__gte=12), [''] ) self.assertQuerysetEqual( Number.objects.filter(num__gte=12.0), [''] ) self.assertQuerysetEqual(Number.objects.filter(num__gte=12.1), []) self.assertQuerysetEqual(Number.objects.filter(num__gte=12.9), []) self.assertQuerysetEqual( Number.objects.filter(num__lte=11.9), ['', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__lte=12), ['', '', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__lte=12.0), ['', '', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__lte=12.1), ['', '', ''], ordered=False ) self.assertQuerysetEqual( Number.objects.filter(num__lte=12.9), ['', '', ''], ordered=False ) def test_ticket7759(self): # Count should work with a partially read result set. count = Number.objects.count() qs = Number.objects.all() def run(): for obj in qs: return qs.count() == count self.assertTrue(run()) class Queries3Tests(BaseQuerysetTest): def test_ticket7107(self): # This shouldn't create an infinite loop. self.assertQuerysetEqual(Valid.objects.all(), []) def test_ticket8683(self): # An error should be raised when QuerySet.datetimes() is passed the # wrong type of field. self.assertRaisesMessage( AssertionError, "'name' isn't a DateTimeField.", Item.objects.datetimes, 'name', 'month' ) def test_ticket22023(self): # only() and defer() are not applicable for ValuesQuerySet with self.assertRaisesMessage(NotImplementedError, "ValuesQuerySet does not implement only()"): Valid.objects.values().only() with self.assertRaisesMessage(NotImplementedError, "ValuesQuerySet does not implement defer()"): Valid.objects.values().defer() class Queries4Tests(BaseQuerysetTest): @classmethod def setUpTestData(cls): generic = NamedCategory.objects.create(name="Generic") cls.t1 = Tag.objects.create(name='t1', category=generic) n1 = Note.objects.create(note='n1', misc='foo') n2 = Note.objects.create(note='n2', misc='bar') e1 = ExtraInfo.objects.create(info='e1', note=n1) e2 = ExtraInfo.objects.create(info='e2', note=n2) cls.a1 = Author.objects.create(name='a1', num=1001, extra=e1) cls.a3 = Author.objects.create(name='a3', num=3003, extra=e2) cls.r1 = Report.objects.create(name='r1', creator=cls.a1) cls.r2 = Report.objects.create(name='r2', creator=cls.a3) cls.r3 = Report.objects.create(name='r3') Item.objects.create(name='i1', created=datetime.datetime.now(), note=n1, creator=cls.a1) Item.objects.create(name='i2', created=datetime.datetime.now(), note=n1, creator=cls.a3) def test_ticket24525(self): tag = Tag.objects.create() anth100 = tag.note_set.create(note='ANTH', misc='100') math101 = tag.note_set.create(note='MATH', misc='101') s1 = tag.annotation_set.create(name='1') s2 = tag.annotation_set.create(name='2') s1.notes = [math101, anth100] s2.notes = [math101] result = math101.annotation_set.all() & tag.annotation_set.exclude(notes__in=[anth100]) self.assertEqual(list(result), [s2]) def test_ticket11811(self): unsaved_category = NamedCategory(name="Other") with six.assertRaisesRegex(self, ValueError, 'Unsaved model instance ' 'cannot be used in an ORM query.'): Tag.objects.filter(pk=self.t1.pk).update(category=unsaved_category) def test_ticket14876(self): # Note: when combining the query we need to have information available # about the join type of the trimmed "creator__isnull" join. If we # don't have that information, then the join is created as INNER JOIN # and results will be incorrect. q1 = Report.objects.filter(Q(creator__isnull=True) | Q(creator__extra__info='e1')) q2 = Report.objects.filter(Q(creator__isnull=True)) | Report.objects.filter(Q(creator__extra__info='e1')) self.assertQuerysetEqual(q1, ["", ""], ordered=False) self.assertEqual(str(q1.query), str(q2.query)) q1 = Report.objects.filter(Q(creator__extra__info='e1') | Q(creator__isnull=True)) q2 = Report.objects.filter(Q(creator__extra__info='e1')) | Report.objects.filter(Q(creator__isnull=True)) self.assertQuerysetEqual(q1, ["", ""], ordered=False) self.assertEqual(str(q1.query), str(q2.query)) q1 = Item.objects.filter(Q(creator=self.a1) | Q(creator__report__name='r1')).order_by() q2 = Item.objects.filter(Q(creator=self.a1)).order_by() | Item.objects.filter(Q(creator__report__name='r1')).order_by() self.assertQuerysetEqual(q1, [""]) self.assertEqual(str(q1.query), str(q2.query)) q1 = Item.objects.filter(Q(creator__report__name='e1') | Q(creator=self.a1)).order_by() q2 = Item.objects.filter(Q(creator__report__name='e1')).order_by() | Item.objects.filter(Q(creator=self.a1)).order_by() self.assertQuerysetEqual(q1, [""]) self.assertEqual(str(q1.query), str(q2.query)) def test_combine_join_reuse(self): # Test that we correctly recreate joins having identical connections # in the rhs query, in case the query is ORed together. Related to # ticket #18748 Report.objects.create(name='r4', creator=self.a1) q1 = Author.objects.filter(report__name='r5') q2 = Author.objects.filter(report__name='r4').filter(report__name='r1') combined = q1 | q2 self.assertEqual(str(combined.query).count('JOIN'), 2) self.assertEqual(len(combined), 1) self.assertEqual(combined[0].name, 'a1') def test_ticket7095(self): # Updates that are filtered on the model being updated are somewhat # tricky in MySQL. This exercises that case. ManagedModel.objects.create(data='mm1', tag=self.t1, public=True) self.assertEqual(ManagedModel.objects.update(data='mm'), 1) # A values() or values_list() query across joined models must use outer # joins appropriately. # Note: In Oracle, we expect a null CharField to return '' instead of # None. if connection.features.interprets_empty_strings_as_nulls: expected_null_charfield_repr = '' else: expected_null_charfield_repr = None self.assertValueQuerysetEqual( Report.objects.values_list("creator__extra__info", flat=True).order_by("name"), ['e1', 'e2', expected_null_charfield_repr], ) # Similarly for select_related(), joins beyond an initial nullable join # must use outer joins so that all results are included. self.assertQuerysetEqual( Report.objects.select_related("creator", "creator__extra").order_by("name"), ['', '', ''] ) # When there are multiple paths to a table from another table, we have # to be careful not to accidentally reuse an inappropriate join when # using select_related(). We used to return the parent's Detail record # here by mistake. d1 = Detail.objects.create(data="d1") d2 = Detail.objects.create(data="d2") m1 = Member.objects.create(name="m1", details=d1) m2 = Member.objects.create(name="m2", details=d2) Child.objects.create(person=m2, parent=m1) obj = m1.children.select_related("person__details")[0] self.assertEqual(obj.person.details.data, 'd2') def test_order_by_resetting(self): # Calling order_by() with no parameters removes any existing ordering on the # model. But it should still be possible to add new ordering after that. qs = Author.objects.order_by().order_by('name') self.assertIn('ORDER BY', qs.query.get_compiler(qs.db).as_sql()[0]) def test_order_by_reverse_fk(self): # It is possible to order by reverse of foreign key, although that can lead # to duplicate results. c1 = SimpleCategory.objects.create(name="category1") c2 = SimpleCategory.objects.create(name="category2") CategoryItem.objects.create(category=c1) CategoryItem.objects.create(category=c2) CategoryItem.objects.create(category=c1) self.assertQuerysetEqual( SimpleCategory.objects.order_by('categoryitem', 'pk'), [c1, c2, c1], lambda x: x) def test_ticket10181(self): # Avoid raising an EmptyResultSet if an inner query is probably # empty (and hence, not executed). self.assertQuerysetEqual( Tag.objects.filter(id__in=Tag.objects.filter(id__in=[])), [] ) def test_ticket15316_filter_false(self): c1 = SimpleCategory.objects.create(name="category1") c2 = SpecialCategory.objects.create(name="named category1", special_name="special1") c3 = SpecialCategory.objects.create(name="named category2", special_name="special2") CategoryItem.objects.create(category=c1) ci2 = CategoryItem.objects.create(category=c2) ci3 = CategoryItem.objects.create(category=c3) qs = CategoryItem.objects.filter(category__specialcategory__isnull=False) self.assertEqual(qs.count(), 2) self.assertQuerysetEqual(qs, [ci2.pk, ci3.pk], lambda x: x.pk, False) def test_ticket15316_exclude_false(self): c1 = SimpleCategory.objects.create(name="category1") c2 = SpecialCategory.objects.create(name="named category1", special_name="special1") c3 = SpecialCategory.objects.create(name="named category2", special_name="special2") ci1 = CategoryItem.objects.create(category=c1) CategoryItem.objects.create(category=c2) CategoryItem.objects.create(category=c3) qs = CategoryItem.objects.exclude(category__specialcategory__isnull=False) self.assertEqual(qs.count(), 1) self.assertQuerysetEqual(qs, [ci1.pk], lambda x: x.pk) def test_ticket15316_filter_true(self): c1 = SimpleCategory.objects.create(name="category1") c2 = SpecialCategory.objects.create(name="named category1", special_name="special1") c3 = SpecialCategory.objects.create(name="named category2", special_name="special2") ci1 = CategoryItem.objects.create(category=c1) CategoryItem.objects.create(category=c2) CategoryItem.objects.create(category=c3) qs = CategoryItem.objects.filter(category__specialcategory__isnull=True) self.assertEqual(qs.count(), 1) self.assertQuerysetEqual(qs, [ci1.pk], lambda x: x.pk) def test_ticket15316_exclude_true(self): c1 = SimpleCategory.objects.create(name="category1") c2 = SpecialCategory.objects.create(name="named category1", special_name="special1") c3 = SpecialCategory.objects.create(name="named category2", special_name="special2") CategoryItem.objects.create(category=c1) ci2 = CategoryItem.objects.create(category=c2) ci3 = CategoryItem.objects.create(category=c3) qs = CategoryItem.objects.exclude(category__specialcategory__isnull=True) self.assertEqual(qs.count(), 2) self.assertQuerysetEqual(qs, [ci2.pk, ci3.pk], lambda x: x.pk, False) def test_ticket15316_one2one_filter_false(self): c = SimpleCategory.objects.create(name="cat") 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") CategoryItem.objects.create(category=c) ci2 = CategoryItem.objects.create(category=c0) ci3 = CategoryItem.objects.create(category=c1) qs = CategoryItem.objects.filter(category__onetoonecategory__isnull=False) self.assertEqual(qs.count(), 2) self.assertQuerysetEqual(qs, [ci2.pk, ci3.pk], lambda x: x.pk, False) def test_ticket15316_one2one_exclude_false(self): c = SimpleCategory.objects.create(name="cat") 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") ci1 = CategoryItem.objects.create(category=c) CategoryItem.objects.create(category=c0) CategoryItem.objects.create(category=c1) qs = CategoryItem.objects.exclude(category__onetoonecategory__isnull=False) self.assertEqual(qs.count(), 1) self.assertQuerysetEqual(qs, [ci1.pk], lambda x: x.pk) def test_ticket15316_one2one_filter_true(self): c = SimpleCategory.objects.create(name="cat") 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") ci1 = CategoryItem.objects.create(category=c) CategoryItem.objects.create(category=c0) CategoryItem.objects.create(category=c1) qs = CategoryItem.objects.filter(category__onetoonecategory__isnull=True) self.assertEqual(qs.count(), 1) self.assertQuerysetEqual(qs, [ci1.pk], lambda x: x.pk) def test_ticket15316_one2one_exclude_true(self): c = SimpleCategory.objects.create(name="cat") 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") CategoryItem.objects.create(category=c) ci2 = CategoryItem.objects.create(category=c0) ci3 = CategoryItem.objects.create(category=c1) qs = CategoryItem.objects.exclude(category__onetoonecategory__isnull=True) self.assertEqual(qs.count(), 2) self.assertQuerysetEqual(qs, [ci2.pk, ci3.pk], lambda x: x.pk, False) class Queries5Tests(TestCase): @classmethod def setUpTestData(cls): # Ordering by 'rank' gives us rank2, rank1, rank3. Ordering by the # Meta.ordering will be rank3, rank2, rank1. n1 = Note.objects.create(note='n1', misc='foo', id=1) n2 = Note.objects.create(note='n2', misc='bar', id=2) e1 = ExtraInfo.objects.create(info='e1', note=n1) e2 = ExtraInfo.objects.create(info='e2', note=n2) a1 = Author.objects.create(name='a1', num=1001, extra=e1) a2 = Author.objects.create(name='a2', num=2002, extra=e1) a3 = Author.objects.create(name='a3', num=3003, extra=e2) cls.rank1 = Ranking.objects.create(rank=2, author=a2) Ranking.objects.create(rank=1, author=a3) Ranking.objects.create(rank=3, author=a1) def test_ordering(self): # Cross model ordering is possible in Meta, too. self.assertQuerysetEqual( Ranking.objects.all(), ['', '', ''] ) self.assertQuerysetEqual( Ranking.objects.all().order_by('rank'), ['', '', ''] ) # Ordering of extra() pieces is possible, too and you can mix extra # fields and model fields in the ordering. self.assertQuerysetEqual( Ranking.objects.extra(tables=['django_site'], order_by=['-django_site.id', 'rank']), ['', '', ''] ) qs = Ranking.objects.extra(select={'good': 'case when rank > 2 then 1 else 0 end'}) self.assertEqual( [o.good for o in qs.extra(order_by=('-good',))], [True, False, False] ) self.assertQuerysetEqual( qs.extra(order_by=('-good', 'id')), ['', '', ''] ) # Despite having some extra aliases in the query, we can still omit # them in a values() query. dicts = qs.values('id', 'rank').order_by('id') self.assertEqual( [d['rank'] for d in dicts], [2, 1, 3] ) def test_ticket7256(self): # An empty values() call includes all aliases, including those from an # extra() qs = Ranking.objects.extra(select={'good': 'case when rank > 2 then 1 else 0 end'}) dicts = qs.values().order_by('id') for d in dicts: del d['id'] del d['author_id'] self.assertEqual( [sorted(d.items()) for d in dicts], [[('good', 0), ('rank', 2)], [('good', 0), ('rank', 1)], [('good', 1), ('rank', 3)]] ) def test_ticket7045(self): # Extra tables used to crash SQL construction on the second use. qs = Ranking.objects.extra(tables=['django_site']) qs.query.get_compiler(qs.db).as_sql() # test passes if this doesn't raise an exception. qs.query.get_compiler(qs.db).as_sql() def test_ticket9848(self): # Make sure that updates which only filter on sub-tables don't # inadvertently update the wrong records (bug #9848). # Make sure that the IDs from different tables don't happen to match. self.assertQuerysetEqual( Ranking.objects.filter(author__name='a1'), [''] ) self.assertEqual( Ranking.objects.filter(author__name='a1').update(rank='4'), 1 ) r = Ranking.objects.filter(author__name='a1')[0] self.assertNotEqual(r.id, r.author.id) self.assertEqual(r.rank, 4) r.rank = 3 r.save() self.assertQuerysetEqual( Ranking.objects.all(), ['', '', ''] ) def test_ticket5261(self): # Test different empty excludes. self.assertQuerysetEqual( Note.objects.exclude(Q()), ['', ''] ) self.assertQuerysetEqual( Note.objects.filter(~Q()), ['', ''] ) self.assertQuerysetEqual( Note.objects.filter(~Q() | ~Q()), ['', ''] ) self.assertQuerysetEqual( Note.objects.exclude(~Q() & ~Q()), ['', ''] ) def test_extra_select_literal_percent_s(self): # Allow %%s to escape select clauses self.assertEqual( Note.objects.extra(select={'foo': "'%%s'"})[0].foo, '%s' ) self.assertEqual( Note.objects.extra(select={'foo': "'%%s bar %%s'"})[0].foo, '%s bar %s' ) self.assertEqual( Note.objects.extra(select={'foo': "'bar %%s'"})[0].foo, 'bar %s' ) class SelectRelatedTests(TestCase): def test_tickets_3045_3288(self): # Once upon a time, select_related() with circular relations would loop # infinitely if you forgot to specify "depth". Now we set an arbitrary # default upper bound. self.assertQuerysetEqual(X.objects.all(), []) self.assertQuerysetEqual(X.objects.select_related(), []) class SubclassFKTests(TestCase): def test_ticket7778(self): # Model subclasses could not be deleted if a nullable foreign key # relates to a model that relates back. num_celebs = Celebrity.objects.count() tvc = TvChef.objects.create(name="Huey") self.assertEqual(Celebrity.objects.count(), num_celebs + 1) Fan.objects.create(fan_of=tvc) Fan.objects.create(fan_of=tvc) tvc.delete() # The parent object should have been deleted as well. self.assertEqual(Celebrity.objects.count(), num_celebs) class CustomPkTests(TestCase): def test_ticket7371(self): self.assertQuerysetEqual(Related.objects.order_by('custom'), []) class NullableRelOrderingTests(TestCase): def test_ticket10028(self): # Ordering by model related to nullable relations(!) should use outer # joins, so that all results are included. Plaything.objects.create(name="p1") self.assertQuerysetEqual( Plaything.objects.all(), [''] ) def test_join_already_in_query(self): # Ordering by model related to nullable relations should not change # the join type of already existing joins. Plaything.objects.create(name="p1") s = SingleObject.objects.create(name='s') r = RelatedObject.objects.create(single=s, f=1) Plaything.objects.create(name="p2", others=r) qs = Plaything.objects.all().filter(others__isnull=False).order_by('pk') self.assertNotIn('JOIN', str(qs.query)) qs = Plaything.objects.all().filter(others__f__isnull=False).order_by('pk') self.assertIn('INNER', str(qs.query)) qs = qs.order_by('others__single__name') # The ordering by others__single__pk will add one new join (to single) # and that join must be LEFT join. The already existing join to related # objects must be kept INNER. So, we have both an INNER and a LEFT join # in the query. self.assertEqual(str(qs.query).count('LEFT'), 1) self.assertEqual(str(qs.query).count('INNER'), 1) self.assertQuerysetEqual( qs, [''] ) class DisjunctiveFilterTests(TestCase): @classmethod def setUpTestData(cls): cls.n1 = Note.objects.create(note='n1', misc='foo', id=1) ExtraInfo.objects.create(info='e1', note=cls.n1) def test_ticket7872(self): # Another variation on the disjunctive filtering theme. # For the purposes of this regression test, it's important that there is no # Join object related to the LeafA we create. LeafA.objects.create(data='first') self.assertQuerysetEqual(LeafA.objects.all(), ['']) self.assertQuerysetEqual( LeafA.objects.filter(Q(data='first') | Q(join__b__data='second')), [''] ) def test_ticket8283(self): # Checking that applying filters after a disjunction works correctly. self.assertQuerysetEqual( (ExtraInfo.objects.filter(note=self.n1) | ExtraInfo.objects.filter(info='e2')).filter(note=self.n1), [''] ) self.assertQuerysetEqual( (ExtraInfo.objects.filter(info='e2') | ExtraInfo.objects.filter(note=self.n1)).filter(note=self.n1), [''] ) class Queries6Tests(TestCase): @classmethod def setUpTestData(cls): generic = NamedCategory.objects.create(name="Generic") t1 = Tag.objects.create(name='t1', category=generic) Tag.objects.create(name='t2', parent=t1, category=generic) t3 = Tag.objects.create(name='t3', parent=t1) t4 = Tag.objects.create(name='t4', parent=t3) Tag.objects.create(name='t5', parent=t3) n1 = Note.objects.create(note='n1', misc='foo', id=1) ann1 = Annotation.objects.create(name='a1', tag=t1) ann1.notes.add(n1) Annotation.objects.create(name='a2', tag=t4) def test_parallel_iterators(self): # Test that parallel iterators work. qs = Tag.objects.all() i1, i2 = iter(qs), iter(qs) self.assertEqual(repr(next(i1)), '') self.assertEqual(repr(next(i1)), '') self.assertEqual(repr(next(i2)), '') self.assertEqual(repr(next(i2)), '') self.assertEqual(repr(next(i2)), '') self.assertEqual(repr(next(i1)), '') qs = X.objects.all() self.assertEqual(bool(qs), False) self.assertEqual(bool(qs), False) def test_nested_queries_sql(self): # Nested queries should not evaluate the inner query as part of constructing the # SQL (so we should see a nested query here, indicated by two "SELECT" calls). qs = Annotation.objects.filter(notes__in=Note.objects.filter(note="xyzzy")) self.assertEqual( qs.query.get_compiler(qs.db).as_sql()[0].count('SELECT'), 2 ) def test_tickets_8921_9188(self): # Incorrect SQL was being generated for certain types of exclude() # queries that crossed multi-valued relations (#8921, #9188 and some # pre-emptively discovered cases). self.assertQuerysetEqual( PointerA.objects.filter(connection__pointerb__id=1), [] ) self.assertQuerysetEqual( PointerA.objects.exclude(connection__pointerb__id=1), [] ) self.assertQuerysetEqual( Tag.objects.exclude(children=None), ['', ''] ) # This example is tricky because the parent could be NULL, so only checking # parents with annotations omits some results (tag t1, in this case). self.assertQuerysetEqual( Tag.objects.exclude(parent__annotation__name="a1"), ['', '', ''] ) # The annotation->tag link is single values and tag->children links is # multi-valued. So we have to split the exclude filter in the middle # and then optimize the inner query without losing results. self.assertQuerysetEqual( Annotation.objects.exclude(tag__children__name="t2"), [''] ) # Nested queries are possible (although should be used with care, since # they have performance problems on backends like MySQL. self.assertQuerysetEqual( Annotation.objects.filter(notes__in=Note.objects.filter(note="n1")), [''] ) def test_ticket3739(self): # The all() method on querysets returns a copy of the queryset. q1 = Tag.objects.order_by('name') self.assertIsNot(q1, q1.all()) def test_ticket_11320(self): qs = Tag.objects.exclude(category=None).exclude(category__name='foo') self.assertEqual(str(qs.query).count(' INNER JOIN '), 1) class RawQueriesTests(TestCase): def setUp(self): Note.objects.create(note='n1', misc='foo', id=1) def test_ticket14729(self): # Test representation of raw query with one or few parameters passed as list query = "SELECT * FROM queries_note WHERE note = %s" params = ['n1'] qs = Note.objects.raw(query, params=params) self.assertEqual(repr(qs), "") query = "SELECT * FROM queries_note WHERE note = %s and misc = %s" params = ['n1', 'foo'] qs = Note.objects.raw(query, params=params) self.assertEqual(repr(qs), "") class GeneratorExpressionTests(TestCase): def test_ticket10432(self): # Using an empty generator expression as the rvalue for an "__in" # lookup is legal. self.assertQuerysetEqual( Note.objects.filter(pk__in=(x for x in ())), [] ) class ComparisonTests(TestCase): def setUp(self): self.n1 = Note.objects.create(note='n1', misc='foo', id=1) e1 = ExtraInfo.objects.create(info='e1', note=self.n1) self.a2 = Author.objects.create(name='a2', num=2002, extra=e1) def test_ticket8597(self): # Regression tests for case-insensitive comparisons Item.objects.create(name="a_b", created=datetime.datetime.now(), creator=self.a2, note=self.n1) Item.objects.create(name="x%y", created=datetime.datetime.now(), creator=self.a2, note=self.n1) self.assertQuerysetEqual( Item.objects.filter(name__iexact="A_b"), [''] ) self.assertQuerysetEqual( Item.objects.filter(name__iexact="x%Y"), [''] ) self.assertQuerysetEqual( Item.objects.filter(name__istartswith="A_b"), [''] ) self.assertQuerysetEqual( Item.objects.filter(name__iendswith="A_b"), [''] ) class ExistsSql(TestCase): def test_exists(self): with CaptureQueriesContext(connection) as captured_queries: self.assertFalse(Tag.objects.exists()) # Ok - so the exist query worked - but did it include too many columns? self.assertEqual(len(captured_queries), 1) qstr = captured_queries[0] id, name = connection.ops.quote_name('id'), connection.ops.quote_name('name') self.assertNotIn(id, qstr) self.assertNotIn(name, qstr) def test_ticket_18414(self): Article.objects.create(name='one', created=datetime.datetime.now()) Article.objects.create(name='one', created=datetime.datetime.now()) Article.objects.create(name='two', created=datetime.datetime.now()) self.assertTrue(Article.objects.exists()) self.assertTrue(Article.objects.distinct().exists()) self.assertTrue(Article.objects.distinct()[1:3].exists()) self.assertFalse(Article.objects.distinct()[1:1].exists()) @unittest.skipUnless(connection.features.can_distinct_on_fields, 'Uses distinct(fields)') def test_ticket_18414_distinct_on(self): Article.objects.create(name='one', created=datetime.datetime.now()) Article.objects.create(name='one', created=datetime.datetime.now()) Article.objects.create(name='two', created=datetime.datetime.now()) self.assertTrue(Article.objects.distinct('name').exists()) self.assertTrue(Article.objects.distinct('name')[1:2].exists()) self.assertFalse(Article.objects.distinct('name')[2:3].exists()) class QuerysetOrderedTests(unittest.TestCase): """ Tests for the Queryset.ordered attribute. """ def test_no_default_or_explicit_ordering(self): self.assertEqual(Annotation.objects.all().ordered, False) def test_cleared_default_ordering(self): self.assertEqual(Tag.objects.all().ordered, True) self.assertEqual(Tag.objects.all().order_by().ordered, False) def test_explicit_ordering(self): self.assertEqual(Annotation.objects.all().order_by('id').ordered, True) def test_order_by_extra(self): self.assertEqual(Annotation.objects.all().extra(order_by=['id']).ordered, True) def test_annotated_ordering(self): qs = Annotation.objects.annotate(num_notes=Count('notes')) self.assertEqual(qs.ordered, False) self.assertEqual(qs.order_by('num_notes').ordered, True) @skipUnlessDBFeature('allow_sliced_subqueries') class SubqueryTests(TestCase): @classmethod def setUpTestData(cls): DumbCategory.objects.create(id=1) DumbCategory.objects.create(id=2) DumbCategory.objects.create(id=3) DumbCategory.objects.create(id=4) def test_ordered_subselect(self): "Subselects honor any manual ordering" query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:2]) self.assertEqual(set(query.values_list('id', flat=True)), {3, 4}) query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[:2]) self.assertEqual(set(query.values_list('id', flat=True)), {3, 4}) query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[1:2]) self.assertEqual(set(query.values_list('id', flat=True)), {3}) query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[2:]) self.assertEqual(set(query.values_list('id', flat=True)), {1, 2}) def test_slice_subquery_and_query(self): """ Slice a query that has a sliced subquery """ query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:2])[0:2] self.assertEqual({x.id for x in query}, {3, 4}) query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[1:3])[1:3] self.assertEqual({x.id for x in query}, {3}) query = DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[2:])[1:] self.assertEqual({x.id for x in query}, {2}) def test_related_sliced_subquery(self): """ Related objects constraints can safely contain sliced subqueries. refs #22434 """ generic = NamedCategory.objects.create(id=5, name="Generic") t1 = Tag.objects.create(name='t1', category=generic) t2 = Tag.objects.create(name='t2', category=generic) ManagedModel.objects.create(data='mm1', tag=t1, public=True) mm2 = ManagedModel.objects.create(data='mm2', tag=t2, public=True) query = ManagedModel.normal_manager.filter( tag__in=Tag.objects.order_by('-id')[:1] ) self.assertEqual({x.id for x in query}, {mm2.id}) def test_sliced_delete(self): "Delete queries can safely contain sliced subqueries" DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[0:1]).delete() self.assertEqual(set(DumbCategory.objects.values_list('id', flat=True)), {1, 2, 3}) DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[1:2]).delete() self.assertEqual(set(DumbCategory.objects.values_list('id', flat=True)), {1, 3}) DumbCategory.objects.filter(id__in=DumbCategory.objects.order_by('-id')[1:]).delete() self.assertEqual(set(DumbCategory.objects.values_list('id', flat=True)), {3}) class CloneTests(TestCase): def test_evaluated_queryset_as_argument(self): "#13227 -- If a queryset is already evaluated, it can still be used as a query arg" n = Note(note='Test1', misc='misc') n.save() e = ExtraInfo(info='good', note=n) e.save() n_list = Note.objects.all() # Evaluate the Note queryset, populating the query cache list(n_list) # Use the note queryset in a query, and evaluate # that query in a way that involves cloning. self.assertEqual(ExtraInfo.objects.filter(note__in=n_list)[0].info, 'good') def test_no_model_options_cloning(self): """ Test that cloning a queryset does not get out of hand. While complete testing is impossible, this is a sanity check against invalid use of deepcopy. refs #16759. """ opts_class = type(Note._meta) note_deepcopy = getattr(opts_class, "__deepcopy__", None) opts_class.__deepcopy__ = lambda obj, memo: self.fail("Model options shouldn't be cloned.") try: Note.objects.filter(pk__lte=F('pk') + 1).all() finally: if note_deepcopy is None: delattr(opts_class, "__deepcopy__") else: opts_class.__deepcopy__ = note_deepcopy def test_no_fields_cloning(self): """ Test that cloning a queryset does not get out of hand. While complete testing is impossible, this is a sanity check against invalid use of deepcopy. refs #16759. """ opts_class = type(Note._meta.get_field("misc")) note_deepcopy = getattr(opts_class, "__deepcopy__", None) opts_class.__deepcopy__ = lambda obj, memo: self.fail("Model fields shouldn't be cloned") try: Note.objects.filter(note=F('misc')).all() finally: if note_deepcopy is None: delattr(opts_class, "__deepcopy__") else: opts_class.__deepcopy__ = note_deepcopy class EmptyQuerySetTests(TestCase): def test_emptyqueryset_values(self): # #14366 -- Calling .values() on an empty QuerySet and then cloning # that should not cause an error self.assertQuerysetEqual( Number.objects.none().values('num').order_by('num'), [] ) def test_values_subquery(self): self.assertQuerysetEqual( Number.objects.filter(pk__in=Number.objects.none().values("pk")), [] ) self.assertQuerysetEqual( Number.objects.filter(pk__in=Number.objects.none().values_list("pk")), [] ) def test_ticket_19151(self): # #19151 -- Calling .values() or .values_list() on an empty QuerySet # should return an empty QuerySet and not cause an error. q = Author.objects.none() self.assertQuerysetEqual(q.values(), []) self.assertQuerysetEqual(q.values_list(), []) class ValuesQuerysetTests(BaseQuerysetTest): @classmethod def setUpTestData(cls): Number.objects.create(num=72) cls.identity = staticmethod(lambda x: x) def test_flat_values_list(self): qs = Number.objects.values_list("num") qs = qs.values_list("num", flat=True) self.assertValueQuerysetEqual(qs, [72]) def test_extra_values(self): # testing for ticket 14930 issues qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'), ('value_minus_x', 'num-%s')]), select_params=(1, 2)) qs = qs.order_by('value_minus_x') qs = qs.values('num') self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) def test_extra_values_order_twice(self): # testing for ticket 14930 issues qs = Number.objects.extra(select={'value_plus_one': 'num+1', 'value_minus_one': 'num-1'}) qs = qs.order_by('value_minus_one').order_by('value_plus_one') qs = qs.values('num') self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) def test_extra_values_order_multiple(self): # Postgres doesn't allow constants in order by, so check for that. qs = Number.objects.extra(select={ 'value_plus_one': 'num+1', 'value_minus_one': 'num-1', 'constant_value': '1' }) qs = qs.order_by('value_plus_one', 'value_minus_one', 'constant_value') qs = qs.values('num') self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) def test_extra_values_order_in_extra(self): # testing for ticket 14930 issues qs = Number.objects.extra( select={'value_plus_one': 'num+1', 'value_minus_one': 'num-1'}, order_by=['value_minus_one']) qs = qs.values('num') def test_extra_select_params_values_order_in_extra(self): # testing for 23259 issue qs = Number.objects.extra( select={'value_plus_x': 'num+%s'}, select_params=[1], order_by=['value_plus_x']) qs = qs.filter(num=72) qs = qs.values('num') self.assertQuerysetEqual(qs, [{'num': 72}], self.identity) def test_extra_multiple_select_params_values_order_by(self): # testing for 23259 issue qs = Number.objects.extra(select=OrderedDict([('value_plus_x', 'num+%s'), ('value_minus_x', 'num-%s')]), select_params=(72, 72)) qs = qs.order_by('value_minus_x') qs = qs.filter(num=1) qs = qs.values('num') self.assertQuerysetEqual(qs, [], self.identity) def test_extra_values_list(self): # testing for ticket 14930 issues qs = Number.objects.extra(select={'value_plus_one': 'num+1'}) qs = qs.order_by('value_plus_one') qs = qs.values_list('num') self.assertQuerysetEqual(qs, [(72,)], self.identity) def test_flat_extra_values_list(self): # testing for ticket 14930 issues qs = Number.objects.extra(select={'value_plus_one': 'num+1'}) qs = qs.order_by('value_plus_one') qs = qs.values_list('num', flat=True) self.assertQuerysetEqual(qs, [72], self.identity) def test_field_error_values_list(self): # see #23443 with self.assertRaisesMessage(FieldError, "Cannot resolve keyword %r into field." " Join on 'name' not permitted." % 'foo'): Tag.objects.values_list('name__foo') class QuerySetSupportsPythonIdioms(TestCase): @classmethod def setUpTestData(cls): some_date = datetime.datetime(2014, 5, 16, 12, 1) for i in range(1, 8): Article.objects.create( name="Article {}".format(i), created=some_date) def get_ordered_articles(self): return Article.objects.all().order_by('name') def test_can_get_items_using_index_and_slice_notation(self): self.assertEqual(self.get_ordered_articles()[0].name, 'Article 1') self.assertQuerysetEqual(self.get_ordered_articles()[1:3], ["", ""]) def test_slicing_with_steps_can_be_used(self): self.assertQuerysetEqual(self.get_ordered_articles()[::2], ["", "", "", ""]) @unittest.skipUnless(six.PY2, "Python 2 only -- Python 3 doesn't have longs.") def test_slicing_works_with_longs(self): # NOQA: long undefined on PY3 self.assertEqual(self.get_ordered_articles()[long(0)].name, 'Article 1') # NOQA self.assertQuerysetEqual(self.get_ordered_articles()[long(1):long(3)], # NOQA ["", ""]) self.assertQuerysetEqual(self.get_ordered_articles()[::long(2)], # NOQA ["", "", "", ""]) # And can be mixed with ints. self.assertQuerysetEqual(self.get_ordered_articles()[1:long(3)], # NOQA ["", ""]) def test_slicing_without_step_is_lazy(self): with self.assertNumQueries(0): self.get_ordered_articles()[0:5] def test_slicing_with_tests_is_not_lazy(self): with self.assertNumQueries(1): self.get_ordered_articles()[0:5:3] def test_slicing_can_slice_again_after_slicing(self): self.assertQuerysetEqual(self.get_ordered_articles()[0:5][0:2], ["", ""]) self.assertQuerysetEqual(self.get_ordered_articles()[0:5][4:], [""]) self.assertQuerysetEqual(self.get_ordered_articles()[0:5][5:], []) # Some more tests! self.assertQuerysetEqual(self.get_ordered_articles()[2:][0:2], ["", ""]) self.assertQuerysetEqual(self.get_ordered_articles()[2:][:2], ["", ""]) self.assertQuerysetEqual(self.get_ordered_articles()[2:][2:3], [""]) # Using an offset without a limit is also possible. self.assertQuerysetEqual(self.get_ordered_articles()[5:], ["", ""]) def test_slicing_cannot_filter_queryset_once_sliced(self): six.assertRaisesRegex( self, AssertionError, "Cannot filter a query once a slice has been taken.", Article.objects.all()[0:5].filter, id=1, ) def test_slicing_cannot_reorder_queryset_once_sliced(self): six.assertRaisesRegex( self, AssertionError, "Cannot reorder a query once a slice has been taken.", Article.objects.all()[0:5].order_by, 'id', ) def test_slicing_cannot_combine_queries_once_sliced(self): six.assertRaisesRegex( self, AssertionError, "Cannot combine queries once a slice has been taken.", lambda: Article.objects.all()[0:1] & Article.objects.all()[4:5] ) def test_slicing_negative_indexing_not_supported_for_single_element(self): """hint: inverting your ordering might do what you need""" six.assertRaisesRegex( self, AssertionError, "Negative indexing is not supported.", lambda: Article.objects.all()[-1] ) def test_slicing_negative_indexing_not_supported_for_range(self): """hint: inverting your ordering might do what you need""" six.assertRaisesRegex( self, AssertionError, "Negative indexing is not supported.", lambda: Article.objects.all()[0:-5] ) def test_can_get_number_of_items_in_queryset_using_standard_len(self): self.assertEqual(len(Article.objects.filter(name__exact='Article 1')), 1) def test_can_combine_queries_using_and_and_or_operators(self): s1 = Article.objects.filter(name__exact='Article 1') s2 = Article.objects.filter(name__exact='Article 2') self.assertQuerysetEqual((s1 | s2).order_by('name'), ["", ""]) self.assertQuerysetEqual(s1 & s2, []) class WeirdQuerysetSlicingTests(BaseQuerysetTest): @classmethod def setUpTestData(cls): Number.objects.create(num=1) Number.objects.create(num=2) Article.objects.create(name='one', created=datetime.datetime.now()) Article.objects.create(name='two', created=datetime.datetime.now()) Article.objects.create(name='three', created=datetime.datetime.now()) Article.objects.create(name='four', created=datetime.datetime.now()) def test_tickets_7698_10202(self): # People like to slice with '0' as the high-water mark. self.assertQuerysetEqual(Article.objects.all()[0:0], []) self.assertQuerysetEqual(Article.objects.all()[0:0][:10], []) self.assertEqual(Article.objects.all()[:0].count(), 0) self.assertRaisesMessage( AssertionError, 'Cannot change a query once a slice has been taken.', Article.objects.all()[:0].latest, 'created' ) def test_empty_resultset_sql(self): # ticket #12192 self.assertNumQueries(0, lambda: list(Number.objects.all()[1:1])) class EscapingTests(TestCase): def test_ticket_7302(self): # Reserved names are appropriately escaped ReservedName.objects.create(name='a', order=42) ReservedName.objects.create(name='b', order=37) self.assertQuerysetEqual( ReservedName.objects.all().order_by('order'), ['', ''] ) self.assertQuerysetEqual( ReservedName.objects.extra(select={'stuff': 'name'}, order_by=('order', 'stuff')), ['', ''] ) class ToFieldTests(TestCase): def test_in_query(self): apple = Food.objects.create(name="apple") pear = Food.objects.create(name="pear") lunch = Eaten.objects.create(food=apple, meal="lunch") dinner = Eaten.objects.create(food=pear, meal="dinner") self.assertEqual( set(Eaten.objects.filter(food__in=[apple, pear])), {lunch, dinner}, ) def test_reverse_in(self): apple = Food.objects.create(name="apple") pear = Food.objects.create(name="pear") lunch_apple = Eaten.objects.create(food=apple, meal="lunch") lunch_pear = Eaten.objects.create(food=pear, meal="dinner") self.assertEqual( set(Food.objects.filter(eaten__in=[lunch_apple, lunch_pear])), {apple, pear} ) def test_single_object(self): apple = Food.objects.create(name="apple") lunch = Eaten.objects.create(food=apple, meal="lunch") dinner = Eaten.objects.create(food=apple, meal="dinner") self.assertEqual( set(Eaten.objects.filter(food=apple)), {lunch, dinner} ) def test_single_object_reverse(self): apple = Food.objects.create(name="apple") lunch = Eaten.objects.create(food=apple, meal="lunch") self.assertEqual( set(Food.objects.filter(eaten=lunch)), {apple} ) def test_recursive_fk(self): node1 = Node.objects.create(num=42) node2 = Node.objects.create(num=1, parent=node1) self.assertEqual( list(Node.objects.filter(parent=node1)), [node2] ) def test_recursive_fk_reverse(self): node1 = Node.objects.create(num=42) node2 = Node.objects.create(num=1, parent=node1) self.assertEqual( list(Node.objects.filter(node=node2)), [node1] ) class ConditionalTests(BaseQuerysetTest): """Tests whose execution depend on different environment conditions like Python version or DB backend features""" @classmethod def setUpTestData(cls): generic = NamedCategory.objects.create(name="Generic") t1 = Tag.objects.create(name='t1', category=generic) Tag.objects.create(name='t2', parent=t1, category=generic) t3 = Tag.objects.create(name='t3', parent=t1) Tag.objects.create(name='t4', parent=t3) Tag.objects.create(name='t5', parent=t3) def test_infinite_loop(self): # If you're not careful, it's possible to introduce infinite loops via # default ordering on foreign keys in a cycle. We detect that. self.assertRaisesMessage( FieldError, 'Infinite loop caused by ordering.', 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() ) # Note that this doesn't cause an infinite loop, since the default # ordering on the Tag model is empty (and thus defaults to using "id" # for the related field). self.assertEqual(len(Tag.objects.order_by('parent')), 5) # ... but you can still order in a non-recursive fashion among linked # fields (the previous test failed because the default ordering was # recursive). self.assertQuerysetEqual( LoopX.objects.all().order_by('y__x__y__x__id'), [] ) # When grouping without specifying ordering, we add an explicit "ORDER BY NULL" # portion in MySQL to prevent unnecessary sorting. @skipUnlessDBFeature('requires_explicit_null_ordering_when_grouping') def test_null_ordering_added(self): query = Tag.objects.values_list('parent_id', flat=True).order_by().query query.group_by = ['parent_id'] sql = query.get_compiler(DEFAULT_DB_ALIAS).as_sql()[0] fragment = "ORDER BY " pos = sql.find(fragment) self.assertEqual(sql.find(fragment, pos + 1), -1) self.assertEqual(sql.find("NULL", pos + len(fragment)), pos + len(fragment)) # Sqlite 3 does not support passing in more than 1000 parameters except by # changing a parameter at compilation time. @skipUnlessDBFeature('supports_1000_query_parameters') def test_ticket14244(self): # Test that the "in" lookup works with lists of 1000 items or more. # The numbers amount is picked to force three different IN batches # for Oracle, yet to be less than 2100 parameter limit for MSSQL. numbers = list(range(2050)) Number.objects.all().delete() Number.objects.bulk_create(Number(num=num) for num in numbers) self.assertEqual( Number.objects.filter(num__in=numbers[:1000]).count(), 1000 ) self.assertEqual( Number.objects.filter(num__in=numbers[:1001]).count(), 1001 ) self.assertEqual( Number.objects.filter(num__in=numbers[:2000]).count(), 2000 ) self.assertEqual( Number.objects.filter(num__in=numbers).count(), len(numbers) ) class UnionTests(unittest.TestCase): """ Tests for the union of two querysets. Bug #12252. """ @classmethod def setUpTestData(cls): objectas = [] objectbs = [] objectcs = [] a_info = ['one', 'two', 'three'] for name in a_info: o = ObjectA(name=name) o.save() objectas.append(o) b_info = [('un', 1, objectas[0]), ('deux', 2, objectas[0]), ('trois', 3, objectas[2])] for name, number, objecta in b_info: o = ObjectB(name=name, num=number, objecta=objecta) o.save() objectbs.append(o) c_info = [('ein', objectas[2], objectbs[2]), ('zwei', objectas[1], objectbs[1])] for name, objecta, objectb in c_info: o = ObjectC(name=name, objecta=objecta, objectb=objectb) o.save() objectcs.append(o) def check_union(self, model, Q1, Q2): filter = model.objects.filter self.assertEqual(set(filter(Q1) | filter(Q2)), set(filter(Q1 | Q2))) self.assertEqual(set(filter(Q2) | filter(Q1)), set(filter(Q1 | Q2))) def test_A_AB(self): Q1 = Q(name='two') Q2 = Q(objectb__name='deux') self.check_union(ObjectA, Q1, Q2) def test_A_AB2(self): Q1 = Q(name='two') Q2 = Q(objectb__name='deux', objectb__num=2) self.check_union(ObjectA, Q1, Q2) def test_AB_ACB(self): Q1 = Q(objectb__name='deux') Q2 = Q(objectc__objectb__name='deux') self.check_union(ObjectA, Q1, Q2) def test_BAB_BAC(self): Q1 = Q(objecta__objectb__name='deux') Q2 = Q(objecta__objectc__name='ein') self.check_union(ObjectB, Q1, Q2) def test_BAB_BACB(self): Q1 = Q(objecta__objectb__name='deux') Q2 = Q(objecta__objectc__objectb__name='trois') self.check_union(ObjectB, Q1, Q2) def test_BA_BCA__BAB_BAC_BCA(self): Q1 = Q(objecta__name='one', objectc__objecta__name='two') Q2 = Q(objecta__objectc__name='ein', objectc__objecta__name='three', objecta__objectb__name='trois') self.check_union(ObjectB, Q1, Q2) class DefaultValuesInsertTest(TestCase): def test_no_extra_params(self): # Ticket #17056 -- affects Oracle try: DumbCategory.objects.create() 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): @classmethod def setUpTestData(cls): f1 = Food.objects.create(name='apples') Food.objects.create(name='oranges') Eaten.objects.create(food=f1, meal='dinner') j1 = Job.objects.create(name='Manager') r1 = Responsibility.objects.create(description='Playing golf') j2 = Job.objects.create(name='Programmer') r2 = Responsibility.objects.create(description='Programming') JobResponsibilities.objects.create(job=j1, responsibility=r1) JobResponsibilities.objects.create(job=j2, responsibility=r2) def test_to_field(self): self.assertQuerysetEqual( Food.objects.exclude(eaten__meal='dinner'), ['']) self.assertQuerysetEqual( Job.objects.exclude(responsibilities__description='Playing golf'), ['']) self.assertQuerysetEqual( Responsibility.objects.exclude(jobs__name='Manager'), ['']) def test_ticket14511(self): alex = Person.objects.get_or_create(name='Alex')[0] jane = Person.objects.get_or_create(name='Jane')[0] oracle = Company.objects.get_or_create(name='Oracle')[0] google = Company.objects.get_or_create(name='Google')[0] microsoft = Company.objects.get_or_create(name='Microsoft')[0] intel = Company.objects.get_or_create(name='Intel')[0] def employ(employer, employee, title): Employment.objects.get_or_create(employee=employee, employer=employer, title=title) employ(oracle, alex, 'Engineer') employ(oracle, alex, 'Developer') employ(google, alex, 'Engineer') employ(google, alex, 'Manager') employ(microsoft, alex, 'Manager') employ(intel, alex, 'Manager') employ(microsoft, jane, 'Developer') employ(intel, jane, 'Manager') alex_tech_employers = alex.employers.filter( employment__title__in=('Engineer', 'Developer')).distinct().order_by('name') self.assertQuerysetEqual(alex_tech_employers, [google, oracle], lambda x: x) alex_nontech_employers = alex.employers.exclude( employment__title__in=('Engineer', 'Developer')).distinct().order_by('name') self.assertQuerysetEqual(alex_nontech_employers, [google, intel, microsoft], lambda x: x) class ExcludeTest17600(TestCase): """ Some regressiontests for ticket #17600. Some of these likely duplicate other existing tests. """ @classmethod def setUpTestData(cls): # Create a few Orders. cls.o1 = Order.objects.create(pk=1) cls.o2 = Order.objects.create(pk=2) cls.o3 = Order.objects.create(pk=3) # Create some OrderItems for the first order with homogeneous # status_id values cls.oi1 = OrderItem.objects.create(order=cls.o1, status=1) cls.oi2 = OrderItem.objects.create(order=cls.o1, status=1) cls.oi3 = OrderItem.objects.create(order=cls.o1, status=1) # Create some OrderItems for the second order with heterogeneous # status_id values cls.oi4 = OrderItem.objects.create(order=cls.o2, status=1) cls.oi5 = OrderItem.objects.create(order=cls.o2, status=2) cls.oi6 = OrderItem.objects.create(order=cls.o2, status=3) # Create some OrderItems for the second order with heterogeneous # status_id values cls.oi7 = OrderItem.objects.create(order=cls.o3, status=2) cls.oi8 = OrderItem.objects.create(order=cls.o3, status=3) cls.oi9 = OrderItem.objects.create(order=cls.o3, status=4) def test_exclude_plain(self): """ This should exclude Orders which have some items with status 1 """ self.assertQuerysetEqual( Order.objects.exclude(items__status=1), ['']) def test_exclude_plain_distinct(self): """ This should exclude Orders which have some items with status 1 """ self.assertQuerysetEqual( Order.objects.exclude(items__status=1).distinct(), ['']) def test_exclude_with_q_object_distinct(self): """ This should exclude Orders which have some items with status 1 """ self.assertQuerysetEqual( Order.objects.exclude(Q(items__status=1)).distinct(), ['']) def test_exclude_with_q_object_no_distinct(self): """ This should exclude Orders which have some items with status 1 """ self.assertQuerysetEqual( Order.objects.exclude(Q(items__status=1)), ['']) def test_exclude_with_q_is_equal_to_plain_exclude(self): """ Using exclude(condition) and exclude(Q(condition)) should yield the same QuerySet """ self.assertEqual( list(Order.objects.exclude(items__status=1).distinct()), list(Order.objects.exclude(Q(items__status=1)).distinct())) def test_exclude_with_q_is_equal_to_plain_exclude_variation(self): """ Using exclude(condition) and exclude(Q(condition)) should yield the same QuerySet """ self.assertEqual( list(Order.objects.exclude(items__status=1)), list(Order.objects.exclude(Q(items__status=1)).distinct())) @unittest.expectedFailure def test_only_orders_with_all_items_having_status_1(self): """ This should only return orders having ALL items set to status 1, or those items not having any orders at all. The correct way to write this query in SQL seems to be using two nested subqueries. """ self.assertQuerysetEqual( Order.objects.exclude(~Q(items__status=1)).distinct(), ['']) class Exclude15786(TestCase): """Regression test for #15786""" def test_ticket15786(self): c1 = SimpleCategory.objects.create(name='c1') c2 = SimpleCategory.objects.create(name='c2') OneToOneCategory.objects.create(category=c1) OneToOneCategory.objects.create(category=c2) rel = CategoryRelationship.objects.create(first=c1, second=c2) self.assertEqual( CategoryRelationship.objects.exclude( first__onetoonecategory=F('second__onetoonecategory') ).get(), rel ) class NullInExcludeTest(TestCase): @classmethod def setUpTestData(cls): NullableName.objects.create(name='i1') NullableName.objects.create() def test_null_in_exclude_qs(self): none_val = '' if connection.features.interprets_empty_strings_as_nulls else None self.assertQuerysetEqual( NullableName.objects.exclude(name__in=[]), ['i1', none_val], attrgetter('name')) self.assertQuerysetEqual( NullableName.objects.exclude(name__in=['i1']), [none_val], attrgetter('name')) self.assertQuerysetEqual( NullableName.objects.exclude(name__in=['i3']), ['i1', none_val], attrgetter('name')) inner_qs = NullableName.objects.filter(name='i1').values_list('name') self.assertQuerysetEqual( NullableName.objects.exclude(name__in=inner_qs), [none_val], attrgetter('name')) # Check that the inner queryset wasn't executed - it should be turned # into subquery above self.assertIs(inner_qs._result_cache, None) @unittest.expectedFailure def test_col_not_in_list_containing_null(self): """ The following case is not handled properly because SQL's COL NOT IN (list containing null) handling is too weird to abstract away. """ self.assertQuerysetEqual( NullableName.objects.exclude(name__in=[None]), ['i1'], attrgetter('name')) def test_double_exclude(self): self.assertEqual( list(NullableName.objects.filter(~~Q(name='i1'))), list(NullableName.objects.filter(Q(name='i1')))) self.assertNotIn( '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. The reason for these tests is that Oracle treats '' as NULL, and this can cause problems in query construction. Refs #17957. """ @classmethod def setUpTestData(cls): cls.nc = NamedCategory.objects.create(name='') def test_direct_exclude(self): self.assertQuerysetEqual( NamedCategory.objects.exclude(name__in=['nonexisting']), [self.nc.pk], attrgetter('pk') ) def test_joined_exclude(self): self.assertQuerysetEqual( DumbCategory.objects.exclude(namedcategory__name__in=['nonexisting']), [self.nc.pk], attrgetter('pk') ) def test_21001(self): foo = NamedCategory.objects.create(name='foo') self.assertQuerysetEqual( NamedCategory.objects.exclude(name=''), [foo.pk], attrgetter('pk') ) class ProxyQueryCleanupTest(TestCase): def test_evaluated_proxy_count(self): """ Test that generating the query string doesn't alter the query's state in irreversible ways. Refs #18248. """ ProxyCategory.objects.create() qs = ProxyCategory.objects.all() self.assertEqual(qs.count(), 1) str(qs.query) self.assertEqual(qs.count(), 1) class WhereNodeTest(TestCase): class DummyNode(object): def as_sql(self, compiler, connection): return 'dummy', [] class MockCompiler(object): def compile(self, node): return node.as_sql(self, connection) def __call__(self, name): return connection.ops.quote_name(name) def test_empty_full_handling_conjunction(self): compiler = WhereNodeTest.MockCompiler() w = WhereNode(children=[EverythingNode()]) self.assertEqual(w.as_sql(compiler, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w = WhereNode(children=[NothingNode()]) self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w.negate() self.assertEqual(w.as_sql(compiler, connection), ('', [])) w = WhereNode(children=[EverythingNode(), EverythingNode()]) self.assertEqual(w.as_sql(compiler, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w = WhereNode(children=[EverythingNode(), self.DummyNode()]) self.assertEqual(w.as_sql(compiler, connection), ('dummy', [])) w = WhereNode(children=[self.DummyNode(), self.DummyNode()]) self.assertEqual(w.as_sql(compiler, connection), ('(dummy AND dummy)', [])) w.negate() self.assertEqual(w.as_sql(compiler, connection), ('NOT (dummy AND dummy)', [])) w = WhereNode(children=[NothingNode(), self.DummyNode()]) self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w.negate() self.assertEqual(w.as_sql(compiler, connection), ('', [])) def test_empty_full_handling_disjunction(self): compiler = WhereNodeTest.MockCompiler() w = WhereNode(children=[EverythingNode()], connector='OR') self.assertEqual(w.as_sql(compiler, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w = WhereNode(children=[NothingNode()], connector='OR') self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w.negate() self.assertEqual(w.as_sql(compiler, connection), ('', [])) w = WhereNode(children=[EverythingNode(), EverythingNode()], connector='OR') self.assertEqual(w.as_sql(compiler, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w = WhereNode(children=[EverythingNode(), self.DummyNode()], connector='OR') self.assertEqual(w.as_sql(compiler, connection), ('', [])) w.negate() self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) w = WhereNode(children=[self.DummyNode(), self.DummyNode()], connector='OR') self.assertEqual(w.as_sql(compiler, connection), ('(dummy OR dummy)', [])) w.negate() self.assertEqual(w.as_sql(compiler, connection), ('NOT (dummy OR dummy)', [])) w = WhereNode(children=[NothingNode(), self.DummyNode()], connector='OR') self.assertEqual(w.as_sql(compiler, connection), ('dummy', [])) w.negate() self.assertEqual(w.as_sql(compiler, connection), ('NOT (dummy)', [])) def test_empty_nodes(self): compiler = WhereNodeTest.MockCompiler() empty_w = WhereNode() w = WhereNode(children=[empty_w, empty_w]) self.assertEqual(w.as_sql(compiler, connection), (None, [])) w.negate() self.assertEqual(w.as_sql(compiler, connection), (None, [])) w.connector = 'OR' self.assertEqual(w.as_sql(compiler, connection), (None, [])) w.negate() self.assertEqual(w.as_sql(compiler, connection), (None, [])) w = WhereNode(children=[empty_w, NothingNode()], connector='OR') self.assertRaises(EmptyResultSet, w.as_sql, compiler, connection) class IteratorExceptionsTest(TestCase): def test_iter_exceptions(self): qs = ExtraInfo.objects.only('author') with self.assertRaises(AttributeError): list(qs) def test_invalid_qs_list(self): # Test for #19895 - second iteration over invalid queryset # raises errors. qs = Article.objects.order_by('invalid_column') self.assertRaises(FieldError, list, qs) self.assertRaises(FieldError, list, qs) class NullJoinPromotionOrTest(TestCase): @classmethod def setUpTestData(cls): cls.d1 = ModelD.objects.create(name='foo') d2 = ModelD.objects.create(name='bar') cls.a1 = ModelA.objects.create(name='a1', d=cls.d1) c = ModelC.objects.create(name='c') b = ModelB.objects.create(name='b', c=c) cls.a2 = ModelA.objects.create(name='a2', b=b, d=d2) def test_ticket_17886(self): # The first Q-object is generating the match, the rest of the filters # should not remove the match even if they do not match anything. The # problem here was that b__name generates a LOUTER JOIN, then # b__c__name generates join to c, which the ORM tried to promote but # failed as that join isn't nullable. q_obj = ( Q(d__name='foo') | Q(b__name='foo') | Q(b__c__name='foo') ) qset = ModelA.objects.filter(q_obj) self.assertEqual(list(qset), [self.a1]) # We generate one INNER JOIN to D. The join is direct and not nullable # so we can use INNER JOIN for it. However, we can NOT use INNER JOIN # for the b->c join, as a->b is nullable. self.assertEqual(str(qset.query).count('INNER JOIN'), 1) def test_isnull_filter_promotion(self): qs = ModelA.objects.filter(Q(b__name__isnull=True)) self.assertEqual(str(qs.query).count('LEFT OUTER'), 1) self.assertEqual(list(qs), [self.a1]) qs = ModelA.objects.filter(~Q(b__name__isnull=True)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(list(qs), [self.a2]) qs = ModelA.objects.filter(~~Q(b__name__isnull=True)) self.assertEqual(str(qs.query).count('LEFT OUTER'), 1) self.assertEqual(list(qs), [self.a1]) qs = ModelA.objects.filter(Q(b__name__isnull=False)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(list(qs), [self.a2]) qs = ModelA.objects.filter(~Q(b__name__isnull=False)) self.assertEqual(str(qs.query).count('LEFT OUTER'), 1) self.assertEqual(list(qs), [self.a1]) qs = ModelA.objects.filter(~~Q(b__name__isnull=False)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(list(qs), [self.a2]) def test_null_join_demotion(self): qs = ModelA.objects.filter(Q(b__name__isnull=False) & Q(b__name__isnull=True)) self.assertIn(' INNER JOIN ', str(qs.query)) qs = ModelA.objects.filter(Q(b__name__isnull=True) & Q(b__name__isnull=False)) self.assertIn(' INNER JOIN ', str(qs.query)) qs = ModelA.objects.filter(Q(b__name__isnull=False) | Q(b__name__isnull=True)) self.assertIn(' LEFT OUTER JOIN ', str(qs.query)) qs = ModelA.objects.filter(Q(b__name__isnull=True) | Q(b__name__isnull=False)) self.assertIn(' LEFT OUTER JOIN ', str(qs.query)) def test_ticket_21366(self): n = Note.objects.create(note='n', misc='m') e = ExtraInfo.objects.create(info='info', note=n) a = Author.objects.create(name='Author1', num=1, extra=e) Ranking.objects.create(rank=1, author=a) r1 = Report.objects.create(name='Foo', creator=a) r2 = Report.objects.create(name='Bar') Report.objects.create(name='Bar', creator=a) qs = Report.objects.filter( Q(creator__ranking__isnull=True) | Q(creator__ranking__rank=1, name='Foo') ) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) self.assertEqual(str(qs.query).count(' JOIN '), 2) self.assertQuerysetEqual( qs.order_by('name'), [r2, r1], lambda x: x) def test_ticket_21748(self): i1 = Identifier.objects.create(name='i1') i2 = Identifier.objects.create(name='i2') i3 = Identifier.objects.create(name='i3') Program.objects.create(identifier=i1) Channel.objects.create(identifier=i1) Program.objects.create(identifier=i2) self.assertQuerysetEqual( Identifier.objects.filter(program=None, channel=None), [i3], lambda x: x) self.assertQuerysetEqual( Identifier.objects.exclude(program=None, channel=None).order_by('name'), [i1, i2], lambda x: x) def test_ticket_21748_double_negated_and(self): i1 = Identifier.objects.create(name='i1') i2 = Identifier.objects.create(name='i2') Identifier.objects.create(name='i3') p1 = Program.objects.create(identifier=i1) c1 = Channel.objects.create(identifier=i1) Program.objects.create(identifier=i2) # Check the ~~Q() (or equivalently .exclude(~Q)) works like Q() for # join promotion. qs1_doubleneg = Identifier.objects.exclude(~Q(program__id=p1.id, channel__id=c1.id)).order_by('pk') qs1_filter = Identifier.objects.filter(program__id=p1.id, channel__id=c1.id).order_by('pk') self.assertQuerysetEqual(qs1_doubleneg, qs1_filter, lambda x: x) self.assertEqual(str(qs1_filter.query).count('JOIN'), str(qs1_doubleneg.query).count('JOIN')) self.assertEqual(2, str(qs1_doubleneg.query).count('INNER JOIN')) self.assertEqual(str(qs1_filter.query).count('INNER JOIN'), str(qs1_doubleneg.query).count('INNER JOIN')) def test_ticket_21748_double_negated_or(self): i1 = Identifier.objects.create(name='i1') i2 = Identifier.objects.create(name='i2') Identifier.objects.create(name='i3') p1 = Program.objects.create(identifier=i1) c1 = Channel.objects.create(identifier=i1) p2 = Program.objects.create(identifier=i2) # Test OR + doubleneq. The expected result is that channel is LOUTER # joined, program INNER joined qs1_filter = Identifier.objects.filter( Q(program__id=p2.id, channel__id=c1.id) | Q(program__id=p1.id) ).order_by('pk') qs1_doubleneg = Identifier.objects.exclude( ~Q(Q(program__id=p2.id, channel__id=c1.id) | Q(program__id=p1.id)) ).order_by('pk') self.assertQuerysetEqual(qs1_doubleneg, qs1_filter, lambda x: x) self.assertEqual(str(qs1_filter.query).count('JOIN'), str(qs1_doubleneg.query).count('JOIN')) self.assertEqual(1, str(qs1_doubleneg.query).count('INNER JOIN')) self.assertEqual(str(qs1_filter.query).count('INNER JOIN'), str(qs1_doubleneg.query).count('INNER JOIN')) def test_ticket_21748_complex_filter(self): i1 = Identifier.objects.create(name='i1') i2 = Identifier.objects.create(name='i2') Identifier.objects.create(name='i3') p1 = Program.objects.create(identifier=i1) c1 = Channel.objects.create(identifier=i1) p2 = Program.objects.create(identifier=i2) # Finally, a more complex case, one time in a way where each # NOT is pushed to lowest level in the boolean tree, and # another query where this isn't done. qs1 = Identifier.objects.filter( ~Q(~Q(program__id=p2.id, channel__id=c1.id) & Q(program__id=p1.id))).order_by('pk') qs2 = Identifier.objects.filter( Q(Q(program__id=p2.id, channel__id=c1.id) | ~Q(program__id=p1.id))).order_by('pk') self.assertQuerysetEqual(qs1, qs2, lambda x: x) self.assertEqual(str(qs1.query).count('JOIN'), str(qs2.query).count('JOIN')) self.assertEqual(0, str(qs1.query).count('INNER JOIN')) self.assertEqual(str(qs1.query).count('INNER JOIN'), str(qs2.query).count('INNER JOIN')) class ReverseJoinTrimmingTest(TestCase): def test_reverse_trimming(self): # Check that we don't accidentally trim reverse joins - we can't know # if there is anything on the other side of the join, so trimming # reverse joins can't be done, ever. t = Tag.objects.create() qs = Tag.objects.filter(annotation__tag=t.pk) 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 are always reused). """ def test_fk_reuse(self): qs = Annotation.objects.filter(tag__name='foo').filter(tag__name='bar') self.assertEqual(str(qs.query).count('JOIN'), 1) def test_fk_reuse_select_related(self): qs = Annotation.objects.filter(tag__name='foo').select_related('tag') self.assertEqual(str(qs.query).count('JOIN'), 1) def test_fk_reuse_annotation(self): qs = Annotation.objects.filter(tag__name='foo').annotate(cnt=Count('tag__name')) self.assertEqual(str(qs.query).count('JOIN'), 1) def test_fk_reuse_disjunction(self): qs = Annotation.objects.filter(Q(tag__name='foo') | Q(tag__name='bar')) self.assertEqual(str(qs.query).count('JOIN'), 1) def test_fk_reuse_order_by(self): qs = Annotation.objects.filter(tag__name='foo').order_by('tag__name') self.assertEqual(str(qs.query).count('JOIN'), 1) def test_revo2o_reuse(self): qs = Detail.objects.filter(member__name='foo').filter(member__name='foo') self.assertEqual(str(qs.query).count('JOIN'), 1) def test_revfk_noreuse(self): 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') basea = BaseA.objects.create(a=fk1) qs = BaseA.objects.filter(Q(a=fk1) | Q(b=2)) self.assertEqual(str(qs.query).count(' JOIN '), 0) qs = qs.select_related('a', 'b') self.assertEqual(str(qs.query).count(' INNER JOIN '), 0) self.assertEqual(str(qs.query).count(' LEFT OUTER JOIN '), 2) with self.assertNumQueries(1): self.assertQuerysetEqual(qs, [basea], lambda x: x) self.assertEqual(qs[0].a, fk1) self.assertIs(qs[0].b, None) def test_disjunction_promotion1(self): # Pre-existing join, add two ORed filters to the same join, # all joins can be INNER JOINS. qs = BaseA.objects.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) qs = qs.filter(Q(b__f1='foo') | Q(b__f2='foo')) self.assertEqual(str(qs.query).count('INNER JOIN'), 2) # Reverse the order of AND and OR filters. qs = BaseA.objects.filter(Q(b__f1='foo') | Q(b__f2='foo')) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) qs = qs.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 2) def test_disjunction_promotion2(self): qs = BaseA.objects.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) # Now we have two different joins in an ORed condition, these # must be OUTER joins. The pre-existing join should remain INNER. qs = qs.filter(Q(b__f1='foo') | Q(c__f2='foo')) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) # Reverse case. qs = BaseA.objects.filter(Q(b__f1='foo') | Q(c__f2='foo')) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) qs = qs.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) def test_disjunction_promotion3(self): qs = BaseA.objects.filter(a__f2='bar') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) # The ANDed a__f2 filter allows us to use keep using INNER JOIN # even inside the ORed case. If the join to a__ returns nothing, # the ANDed filter for a__f2 can't be true. qs = qs.filter(Q(a__f1='foo') | Q(b__f2='foo')) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) def test_disjunction_promotion3_demote(self): # This one needs demotion logic: the first filter causes a to be # outer joined, the second filter makes it inner join again. qs = BaseA.objects.filter( Q(a__f1='foo') | Q(b__f2='foo')).filter(a__f2='bar') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) def test_disjunction_promotion4_demote(self): qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('JOIN'), 0) # Demote needed for the "a" join. It is marked as outer join by # above filter (even if it is trimmed away). qs = qs.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) def test_disjunction_promotion4(self): qs = BaseA.objects.filter(a__f1='foo') self.assertEqual(str(qs.query).count('INNER JOIN'), 1) qs = qs.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) def test_disjunction_promotion5_demote(self): qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) # Note that the above filters on a force the join to an # inner join even if it is trimmed. self.assertEqual(str(qs.query).count('JOIN'), 0) qs = qs.filter(Q(a__f1='foo') | Q(b__f1='foo')) # So, now the a__f1 join doesn't need promotion. self.assertEqual(str(qs.query).count('INNER JOIN'), 1) # But b__f1 does. self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) qs = BaseA.objects.filter(Q(a__f1='foo') | Q(b__f1='foo')) # Now the join to a is created as LOUTER self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) qs = qs.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) def test_disjunction_promotion6(self): qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('JOIN'), 0) qs = BaseA.objects.filter(Q(a__f1='foo') & Q(b__f1='foo')) self.assertEqual(str(qs.query).count('INNER JOIN'), 2) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 0) qs = BaseA.objects.filter(Q(a__f1='foo') & Q(b__f1='foo')) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 0) self.assertEqual(str(qs.query).count('INNER JOIN'), 2) qs = qs.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('INNER JOIN'), 2) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 0) def test_disjunction_promotion7(self): qs = BaseA.objects.filter(Q(a=1) | Q(a=2)) self.assertEqual(str(qs.query).count('JOIN'), 0) qs = BaseA.objects.filter(Q(a__f1='foo') | (Q(b__f1='foo') & Q(a__f1='bar'))) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) qs = BaseA.objects.filter( (Q(a__f1='foo') | Q(b__f1='foo')) & (Q(a__f1='bar') | Q(c__f1='foo')) ) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 3) self.assertEqual(str(qs.query).count('INNER JOIN'), 0) qs = BaseA.objects.filter( (Q(a__f1='foo') | (Q(a__f1='bar')) & (Q(b__f1='bar') | Q(c__f1='foo'))) ) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) def test_disjunction_promotion_fexpression(self): qs = BaseA.objects.filter(Q(a__f1=F('b__f1')) | Q(b__f1='foo')) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 1) self.assertEqual(str(qs.query).count('INNER JOIN'), 1) qs = BaseA.objects.filter(Q(a__f1=F('c__f1')) | Q(b__f1='foo')) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 3) qs = BaseA.objects.filter(Q(a__f1=F('b__f1')) | Q(a__f2=F('b__f2')) | Q(c__f1='foo')) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 3) qs = BaseA.objects.filter(Q(a__f1=F('c__f1')) | (Q(pk=1) & Q(pk=2))) self.assertEqual(str(qs.query).count('LEFT OUTER JOIN'), 2) self.assertEqual(str(qs.query).count('INNER JOIN'), 0) class ManyToManyExcludeTest(TestCase): def test_exclude_many_to_many(self): Identifier.objects.create(name='extra') program = Program.objects.create(identifier=Identifier.objects.create(name='program')) channel = Channel.objects.create(identifier=Identifier.objects.create(name='channel')) channel.programs.add(program) # channel contains 'program1', so all Identifiers except that one # should be returned self.assertQuerysetEqual( Identifier.objects.exclude(program__channel=channel).order_by('name'), ['', ''] ) self.assertQuerysetEqual( Identifier.objects.exclude(program__channel=None).order_by('name'), [''] ) def test_ticket_12823(self): pg3 = Page.objects.create(text='pg3') pg2 = Page.objects.create(text='pg2') pg1 = Page.objects.create(text='pg1') pa1 = Paragraph.objects.create(text='pa1') pa1.page = [pg1, pg2] pa2 = Paragraph.objects.create(text='pa2') pa2.page = [pg2, pg3] pa3 = Paragraph.objects.create(text='pa3') ch1 = Chapter.objects.create(title='ch1', paragraph=pa1) ch2 = Chapter.objects.create(title='ch2', paragraph=pa2) ch3 = Chapter.objects.create(title='ch3', paragraph=pa3) b1 = Book.objects.create(title='b1', chapter=ch1) b2 = Book.objects.create(title='b2', chapter=ch2) b3 = Book.objects.create(title='b3', chapter=ch3) q = Book.objects.exclude(chapter__paragraph__page__text='pg1') self.assertNotIn('IS NOT NULL', str(q.query)) self.assertEqual(len(q), 2) self.assertNotIn(b1, q) self.assertIn(b2, q) self.assertIn(b3, q) class RelabelCloneTest(TestCase): def test_ticket_19964(self): my1 = MyObject.objects.create(data='foo') my1.parent = my1 my1.save() my2 = MyObject.objects.create(data='bar', parent=my1) parents = MyObject.objects.filter(parent=F('id')) children = MyObject.objects.filter(parent__in=parents).exclude(parent=F('id')) self.assertEqual(list(parents), [my1]) # Evaluating the children query (which has parents as part of it) does # not change results for the parents query. self.assertEqual(list(children), [my2]) self.assertEqual(list(parents), [my1]) class Ticket20101Tests(TestCase): def test_ticket_20101(self): """ Tests QuerySet ORed combining in exclude subquery case. """ t = Tag.objects.create(name='foo') a1 = Annotation.objects.create(tag=t, name='a1') a2 = Annotation.objects.create(tag=t, name='a2') a3 = Annotation.objects.create(tag=t, name='a3') n = Note.objects.create(note='foo', misc='bar') qs1 = Note.objects.exclude(annotation__in=[a1, a2]) qs2 = Note.objects.filter(annotation__in=[a3]) self.assertIn(n, qs1) self.assertNotIn(n, qs2) self.assertIn(n, (qs1 | qs2)) class EmptyStringPromotionTests(TestCase): def test_empty_string_promotion(self): qs = RelatedObject.objects.filter(single__name='') if connection.features.interprets_empty_strings_as_nulls: self.assertIn('LEFT OUTER JOIN', str(qs.query)) 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 # will be used instead of forcing use of the relation's field. o1 = Order.objects.create(id=-2) o2 = Order.objects.create(id=-1) oi1 = OrderItem.objects.create(order=o1, status=0) oi1.status = oi1.pk oi1.save() OrderItem.objects.create(order=o2, status=0) # The query below should match o1 as it has related order_item # with id == status. self.assertQuerysetEqual( 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') lfa2 = LeafA.objects.create(data='bar') lfb1 = LeafB.objects.create(data='lfb1') lfb2 = LeafB.objects.create(data='lfb2') Join.objects.create(a=lfa1, b=lfb1) Join.objects.create(a=lfa2, b=lfb2) leaf_as = LeafA.objects.filter(data='foo').values_list('pk', flat=True) joins = Join.objects.filter(a__in=leaf_as).values_list('b__id', flat=True) qs = LeafB.objects.filter(pk__in=joins) self.assertQuerysetEqual( qs, [lfb1], lambda x: x) class Ticket18785Tests(TestCase): def test_ticket_18785(self): # Test join trimming from ticket18785 qs = Item.objects.exclude( note__isnull=False ).filter( name='something', creator__extra__isnull=True ).order_by() self.assertEqual(1, str(qs.query).count('INNER JOIN')) self.assertEqual(0, str(qs.query).count('OUTER JOIN')) class Ticket20788Tests(TestCase): def test_ticket_20788(self): Paragraph.objects.create() paragraph = Paragraph.objects.create() page = paragraph.page.create() chapter = Chapter.objects.create(paragraph=paragraph) Book.objects.create(chapter=chapter) paragraph2 = Paragraph.objects.create() Page.objects.create() chapter2 = Chapter.objects.create(paragraph=paragraph2) book2 = Book.objects.create(chapter=chapter2) sentences_not_in_pub = Book.objects.exclude( chapter__paragraph__page=page) self.assertQuerysetEqual( sentences_not_in_pub, [book2], lambda x: x) class Ticket12807Tests(TestCase): def test_ticket_12807(self): p1 = Paragraph.objects.create() p2 = Paragraph.objects.create() # The ORed condition below should have no effect on the query - the # ~Q(pk__in=[]) will always be True. qs = Paragraph.objects.filter((Q(pk=p2.pk) | ~Q(pk__in=[])) & Q(pk=p1.pk)) self.assertQuerysetEqual(qs, [p1], lambda x: x) class RelatedLookupTypeTests(TestCase): error = 'Cannot query "%s": Must be "%s" instance.' @classmethod def setUpTestData(cls): cls.oa = ObjectA.objects.create(name="oa") cls.poa = ProxyObjectA.objects.get(name="oa") cls.coa = ChildObjectA.objects.create(name="coa") cls.wrong_type = Order.objects.create(id=cls.oa.pk) cls.ob = ObjectB.objects.create(name="ob", objecta=cls.oa, num=1) ProxyObjectB.objects.create(name="pob", objecta=cls.oa, num=2) cls.pob = ProxyObjectB.objects.all() ObjectC.objects.create(childobjecta=cls.coa) def test_wrong_type_lookup(self): """ A ValueError is raised when the incorrect object type is passed to a query lookup. """ # Passing incorrect object type with self.assertRaisesMessage(ValueError, self.error % (self.wrong_type, ObjectA._meta.object_name)): ObjectB.objects.get(objecta=self.wrong_type) with self.assertRaisesMessage(ValueError, self.error % (self.wrong_type, ObjectA._meta.object_name)): ObjectB.objects.filter(objecta__in=[self.wrong_type]) with self.assertRaisesMessage(ValueError, self.error % (self.wrong_type, ObjectA._meta.object_name)): ObjectB.objects.filter(objecta=self.wrong_type) with self.assertRaisesMessage(ValueError, self.error % (self.wrong_type, ObjectB._meta.object_name)): ObjectA.objects.filter(objectb__in=[self.wrong_type, self.ob]) # Passing an object of the class on which query is done. with self.assertRaisesMessage(ValueError, self.error % (self.ob, ObjectA._meta.object_name)): ObjectB.objects.filter(objecta__in=[self.poa, self.ob]) with self.assertRaisesMessage(ValueError, self.error % (self.ob, ChildObjectA._meta.object_name)): ObjectC.objects.exclude(childobjecta__in=[self.coa, self.ob]) def test_wrong_backward_lookup(self): """ A ValueError is raised when the incorrect object type is passed to a query lookup for backward relations. """ with self.assertRaisesMessage(ValueError, self.error % (self.oa, ObjectB._meta.object_name)): ObjectA.objects.filter(objectb__in=[self.oa, self.ob]) with self.assertRaisesMessage(ValueError, self.error % (self.oa, ObjectB._meta.object_name)): ObjectA.objects.exclude(objectb=self.oa) with self.assertRaisesMessage(ValueError, self.error % (self.wrong_type, ObjectB._meta.object_name)): ObjectA.objects.get(objectb=self.wrong_type) def test_correct_lookup(self): """ When passing proxy model objects, child objects, or parent objects, lookups work fine. """ out_a = ['', ] out_b = ['', ''] out_c = [''] # proxy model objects self.assertQuerysetEqual(ObjectB.objects.filter(objecta=self.poa).order_by('name'), out_b) self.assertQuerysetEqual(ObjectA.objects.filter(objectb__in=self.pob).order_by('pk'), out_a * 2) # child objects self.assertQuerysetEqual(ObjectB.objects.filter(objecta__in=[self.coa]), []) self.assertQuerysetEqual(ObjectB.objects.filter(objecta__in=[self.poa, self.coa]).order_by('name'), out_b) self.assertQuerysetEqual(ObjectB.objects.filter(objecta__in=iter([self.poa, self.coa])).order_by('name'), out_b) # parent objects self.assertQuerysetEqual(ObjectC.objects.exclude(childobjecta=self.oa), out_c) # Test for #23226 with self.assertNumQueries(0): ObjectB.objects.filter(objecta__in=ObjectA.objects.all()) def test_values_queryset_lookup(self): """ #23396 - Ensure ValueQuerySets are not checked for compatibility with the lookup field """ self.assertQuerysetEqual(ObjectB.objects.filter( objecta__in=ObjectB.objects.all().values_list('pk') ).order_by('pk'), ['', '']) class Ticket14056Tests(TestCase): def test_ticket_14056(self): s1 = SharedConnection.objects.create(data='s1') s2 = SharedConnection.objects.create(data='s2') s3 = SharedConnection.objects.create(data='s3') PointerA.objects.create(connection=s2) expected_ordering = ( [s1, s3, s2] if connection.features.nulls_order_largest else [s2, s1, s3] ) self.assertQuerysetEqual( SharedConnection.objects.order_by('-pointera__connection', 'pk'), expected_ordering, lambda x: x ) class Ticket20955Tests(TestCase): def test_ticket_20955(self): jack = Staff.objects.create(name='jackstaff') jackstaff = StaffUser.objects.create(staff=jack) jill = Staff.objects.create(name='jillstaff') jillstaff = StaffUser.objects.create(staff=jill) task = Task.objects.create(creator=jackstaff, owner=jillstaff, title="task") task_get = Task.objects.get(pk=task.pk) # Load data so that assertNumQueries doesn't complain about the get # version's queries. task_get.creator.staffuser.staff task_get.owner.staffuser.staff qs = Task.objects.select_related( 'creator__staffuser__staff', 'owner__staffuser__staff') self.assertEqual(str(qs.query).count(' JOIN '), 6) task_select_related = qs.get(pk=task.pk) with self.assertNumQueries(0): self.assertEqual(task_select_related.creator.staffuser.staff, task_get.creator.staffuser.staff) 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) c = Ticket21203Child.objects.create(parent=p) qs = Ticket21203Child.objects.select_related('parent').defer('parent__created') 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) self.assertIn(' INNER JOIN ', str(qs.query)) qs = qs.values('parent__parent__id') self.assertIn(' INNER JOIN ', str(qs.query)) # Make sure there is a left outer join without the filter. qs = Node.objects.values('parent__parent__id') self.assertIn(' LEFT OUTER JOIN ', str(qs.query)) def test_non_nullable_fk_not_promoted(self): qs = ObjectB.objects.values('objecta__name') self.assertIn(' INNER JOIN ', str(qs.query)) def test_ticket_21376(self): a = ObjectA.objects.create() ObjectC.objects.create(objecta=a) qs = ObjectC.objects.filter( Q(objecta=a) | Q(objectb__objecta=a), ) qs = qs.filter( Q(objectb=1) | Q(objecta=a), ) self.assertEqual(qs.count(), 1) tblname = connection.ops.quote_name(ObjectB._meta.db_table) self.assertIn(' LEFT OUTER JOIN %s' % tblname, str(qs.query)) class ForeignKeyToBaseExcludeTests(TestCase): def test_ticket_21787(self): sc1 = SpecialCategory.objects.create(special_name='sc1', name='sc1') sc2 = SpecialCategory.objects.create(special_name='sc2', name='sc2') sc3 = SpecialCategory.objects.create(special_name='sc3', name='sc3') c1 = CategoryItem.objects.create(category=sc1) CategoryItem.objects.create(category=sc2) self.assertQuerysetEqual( SpecialCategory.objects.exclude( categoryitem__id=c1.pk).order_by('name'), [sc2, sc3], lambda x: x ) self.assertQuerysetEqual( SpecialCategory.objects.filter(categoryitem__id=c1.pk), [sc1], lambda x: x ) class ReverseM2MCustomPkTests(TestCase): def test_ticket_21879(self): cpt1 = CustomPkTag.objects.create(id='cpt1', tag='cpt1') cp1 = CustomPk.objects.create(name='cp1', extra='extra') cp1.custompktag_set.add(cpt1) self.assertQuerysetEqual( CustomPk.objects.filter(custompktag=cpt1), [cp1], lambda x: x) self.assertQuerysetEqual( CustomPkTag.objects.filter(custom_pk=cp1), [cpt1], lambda x: x) class Ticket22429Tests(TestCase): def test_ticket_22429(self): sc1 = School.objects.create() st1 = Student.objects.create(school=sc1) sc2 = School.objects.create() st2 = Student.objects.create(school=sc2) cr = Classroom.objects.create(school=sc1) cr.students.add(st1) queryset = Student.objects.filter(~Q(classroom__school=F('school'))) self.assertQuerysetEqual(queryset, [st2], lambda x: x) class Ticket23605Tests(TestCase): def test_ticket_23605(self): # Test filtering on a complicated q-object from ticket's report. # The query structure is such that we have multiple nested subqueries. # The original problem was that the inner queries weren't relabeled # correctly. a1 = Ticket23605A.objects.create() a2 = Ticket23605A.objects.create() c1 = Ticket23605C.objects.create(field_c0=10000.0) Ticket23605B.objects.create( field_b0=10000.0, field_b1=True, modelc_fk=c1, modela_fk=a1) complex_q = Q(pk__in=Ticket23605A.objects.filter( Q( # True for a1 as field_b0 = 10000, field_c0=10000 # False for a2 as no ticket23605b found ticket23605b__field_b0__gte=1000000 / F("ticket23605b__modelc_fk__field_c0") ) & # True for a1 (field_b1=True) Q(ticket23605b__field_b1=True) & ~Q(ticket23605b__pk__in=Ticket23605B.objects.filter( ~( # Same filters as above commented filters, but # double-negated (one for Q() above, one for # parentheses). So, again a1 match, a2 not. Q(field_b1=True) & Q(field_b0__gte=1000000 / F("modelc_fk__field_c0")) ) ))).filter(ticket23605b__field_b1=True)) qs1 = Ticket23605A.objects.filter(complex_q) self.assertQuerysetEqual(qs1, [a1], lambda x: x) qs2 = Ticket23605A.objects.exclude(complex_q) self.assertQuerysetEqual(qs2, [a2], lambda x: x) class TestTicket24605(TestCase): def test_ticket_24605(self): """ Subquery table names should be quoted. """ i1 = Individual.objects.create(alive=True) RelatedIndividual.objects.create(related=i1) i2 = Individual.objects.create(alive=False) RelatedIndividual.objects.create(related=i2) i3 = Individual.objects.create(alive=True) i4 = Individual.objects.create(alive=False) self.assertQuerysetEqual( Individual.objects.filter(Q(alive=False), Q(related_individual__isnull=True)), [i4], lambda x: x ) self.assertQuerysetEqual( Individual.objects.exclude( Q(alive=False), Q(related_individual__isnull=True) ).order_by('pk'), [i1, i2, i3], lambda x: x ) Django-1.8.7/tests/queries/__init__.py0000664000175000017500000000000012603513307017255 0ustar timtim00000000000000Django-1.8.7/tests/queries/models.py0000664000175000017500000004035412625116214017021 0ustar timtim00000000000000""" Various complex queries that have been problematic in the past. """ from __future__ import unicode_literals import threading from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible class DumbCategory(models.Model): pass class ProxyCategory(DumbCategory): class Meta: proxy = True @python_2_unicode_compatible class NamedCategory(DumbCategory): name = models.CharField(max_length=10) def __str__(self): return self.name @python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, related_name='children') category = models.ForeignKey(NamedCategory, null=True, default=None) class Meta: ordering = ['name'] def __str__(self): return self.name @python_2_unicode_compatible class Note(models.Model): note = models.CharField(max_length=100) misc = models.CharField(max_length=10) tag = models.ForeignKey(Tag, blank=True, null=True) class Meta: ordering = ['note'] def __str__(self): return self.note def __init__(self, *args, **kwargs): super(Note, self).__init__(*args, **kwargs) # Regression for #13227 -- having an attribute that # is unpickleable doesn't stop you from cloning queries # that use objects of that type as an argument. self.lock = threading.Lock() @python_2_unicode_compatible class Annotation(models.Model): name = models.CharField(max_length=10) tag = models.ForeignKey(Tag) notes = models.ManyToManyField(Note) def __str__(self): return self.name @python_2_unicode_compatible class ExtraInfo(models.Model): info = models.CharField(max_length=100) note = models.ForeignKey(Note) value = models.IntegerField(null=True) class Meta: ordering = ['info'] def __str__(self): return self.info @python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=10) num = models.IntegerField(unique=True) extra = models.ForeignKey(ExtraInfo) class Meta: ordering = ['name'] def __str__(self): return self.name @python_2_unicode_compatible class Item(models.Model): name = models.CharField(max_length=10) created = models.DateTimeField() modified = models.DateTimeField(blank=True, null=True) tags = models.ManyToManyField(Tag, blank=True) creator = models.ForeignKey(Author) note = models.ForeignKey(Note) class Meta: ordering = ['-note', 'name'] def __str__(self): return self.name @python_2_unicode_compatible class Report(models.Model): name = models.CharField(max_length=10) creator = models.ForeignKey(Author, to_field='num', null=True) def __str__(self): return self.name @python_2_unicode_compatible class Ranking(models.Model): rank = models.IntegerField() author = models.ForeignKey(Author) class Meta: # A complex ordering specification. Should stress the system a bit. ordering = ('author__extra__note', 'author__name', 'rank') def __str__(self): return '%d: %s' % (self.rank, self.author.name) @python_2_unicode_compatible class Cover(models.Model): title = models.CharField(max_length=50) item = models.ForeignKey(Item) class Meta: ordering = ['item'] def __str__(self): return self.title @python_2_unicode_compatible class Number(models.Model): num = models.IntegerField() def __str__(self): return six.text_type(self.num) # Symmetrical m2m field with a normal field using the reverse accessor name # ("valid"). class Valid(models.Model): valid = models.CharField(max_length=10) parent = models.ManyToManyField('self') class Meta: ordering = ['valid'] # 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) tag = models.ForeignKey(Tag) public = models.BooleanField(default=True) objects = CustomManager() normal_manager = models.Manager() def __str__(self): 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) class Meta: ordering = ['name', 'extra'] class Related(models.Model): custom = models.ForeignKey(CustomPk) class CustomPkTag(models.Model): id = models.CharField(max_length=20, primary_key=True) custom_pk = models.ManyToManyField(CustomPk) tag = models.CharField(max_length=20) # An inter-related setup with a model subclass that has a nullable # path to another model, and a return path from that model. @python_2_unicode_compatible class Celebrity(models.Model): name = models.CharField("Name", max_length=20) greatest_fan = models.ForeignKey("Fan", null=True, unique=True) def __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) 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) order = models.IntegerField() def __str__(self): 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) 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) class Meta: ordering = ['name'] def __str__(self): return self.name class RelatedObject(models.Model): single = models.ForeignKey(SingleObject, null=True) f = models.IntegerField(null=True) class Meta: ordering = ['single'] @python_2_unicode_compatible class Plaything(models.Model): name = models.CharField(max_length=10) others = models.ForeignKey(RelatedObject, null=True) class Meta: ordering = ['others'] def __str__(self): return self.name @python_2_unicode_compatible class Article(models.Model): name = models.CharField(max_length=20) created = models.DateTimeField() def __str__(self): return self.name @python_2_unicode_compatible class Food(models.Model): name = models.CharField(max_length=20, unique=True) def __str__(self): return self.name @python_2_unicode_compatible class Eaten(models.Model): food = models.ForeignKey(Food, to_field="name", null=True) meal = models.CharField(max_length=20) def __str__(self): return "%s at %s" % (self.food, self.meal) @python_2_unicode_compatible class Node(models.Model): num = models.IntegerField(unique=True) parent = models.ForeignKey("self", to_field="num", null=True) def __str__(self): return "%s" % self.num # Bug #12252 @python_2_unicode_compatible class ObjectA(models.Model): name = models.CharField(max_length=50) def __str__(self): return self.name def __iter__(self): # Ticket #23721 assert False, 'type checking should happen without calling model __iter__' class ProxyObjectA(ObjectA): class Meta: proxy = True class ChildObjectA(ObjectA): pass @python_2_unicode_compatible class ObjectB(models.Model): name = models.CharField(max_length=50) objecta = models.ForeignKey(ObjectA) num = models.PositiveSmallIntegerField() def __str__(self): return self.name class ProxyObjectB(ObjectB): class Meta: proxy = True @python_2_unicode_compatible class ObjectC(models.Model): name = models.CharField(max_length=50) objecta = models.ForeignKey(ObjectA, null=True) objectb = models.ForeignKey(ObjectB, null=True) childobjecta = models.ForeignKey(ChildObjectA, null=True, related_name='ca_pk') def __str__(self): return self.name @python_2_unicode_compatible class SimpleCategory(models.Model): name = models.CharField(max_length=15) def __str__(self): return self.name @python_2_unicode_compatible class SpecialCategory(SimpleCategory): special_name = models.CharField(max_length=15) def __str__(self): return self.name + " " + self.special_name @python_2_unicode_compatible class CategoryItem(models.Model): category = models.ForeignKey(SimpleCategory) def __str__(self): return "category item: " + str(self.category) @python_2_unicode_compatible class OneToOneCategory(models.Model): new_name = models.CharField(max_length=15) category = models.OneToOneField(SimpleCategory) def __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) 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) jobs = models.ManyToManyField(Job, through=JobResponsibilities, related_name='responsibilities') def __str__(self): 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) 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') 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) class Meta: ordering = ('pk', ) def __str__(self): return '%s' % self.pk @python_2_unicode_compatible class OrderItem(models.Model): order = models.ForeignKey(Order, related_name='items') status = models.IntegerField() class Meta: ordering = ('pk', ) 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) owner = models.ForeignKey(BaseUser, related_name='owner') creator = models.ForeignKey(BaseUser, related_name='creator') def __str__(self): return self.title @python_2_unicode_compatible class Staff(models.Model): name = models.CharField(max_length=10) def __str__(self): return self.name @python_2_unicode_compatible class StaffUser(BaseUser): staff = models.OneToOneField(Staff, related_name='user') 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) class Person(models.Model): name = models.CharField(max_length=128) @python_2_unicode_compatible class Company(models.Model): name = models.CharField(max_length=128) employees = models.ManyToManyField(Person, related_name='employers', through='Employment') def __str__(self): return self.name class Employment(models.Model): employer = models.ForeignKey(Company) employee = models.ForeignKey(Person) title = models.CharField(max_length=128) # Bug #22429 class School(models.Model): pass class Student(models.Model): school = models.ForeignKey(School) class Classroom(models.Model): school = models.ForeignKey(School) students = models.ManyToManyField(Student, related_name='classroom') class Ticket23605A(models.Model): pass class Ticket23605B(models.Model): modela_fk = models.ForeignKey(Ticket23605A) modelc_fk = models.ForeignKey("Ticket23605C") field_b0 = models.IntegerField(null=True) field_b1 = models.BooleanField(default=False) class Ticket23605C(models.Model): field_c0 = models.FloatField() # db_table names have capital letters to ensure they are quoted in queries. class Individual(models.Model): alive = models.BooleanField() class Meta: db_table = 'Individual' class RelatedIndividual(models.Model): related = models.ForeignKey(Individual, related_name='related_individual') class Meta: db_table = 'RelatedIndividual' Django-1.8.7/tests/distinct_on_fields/0000775000175000017500000000000012625116243017346 5ustar timtim00000000000000Django-1.8.7/tests/distinct_on_fields/tests.py0000664000175000017500000001305012625116213021056 0ustar timtim00000000000000from __future__ import unicode_literals from django.db.models import Max from django.test import TestCase, skipUnlessDBFeature from django.test.utils import str_prefix from .models import Celebrity, Fan, Staff, StaffTag, Tag @skipUnlessDBFeature('can_distinct_on_fields') @skipUnlessDBFeature('supports_nullable_unique_constraints') class DistinctOnTests(TestCase): def setUp(self): t1 = Tag.objects.create(name='t1') Tag.objects.create(name='t2', parent=t1) t3 = Tag.objects.create(name='t3', parent=t1) Tag.objects.create(name='t4', parent=t3) Tag.objects.create(name='t5', parent=t3) self.p1_o1 = Staff.objects.create(id=1, name="p1", organisation="o1") self.p2_o1 = Staff.objects.create(id=2, name="p2", organisation="o1") self.p3_o1 = Staff.objects.create(id=3, name="p3", organisation="o1") self.p1_o2 = Staff.objects.create(id=4, name="p1", organisation="o2") self.p1_o1.coworkers.add(self.p2_o1, self.p3_o1) StaffTag.objects.create(staff=self.p1_o1, tag=t1) StaffTag.objects.create(staff=self.p1_o1, tag=t1) celeb1 = Celebrity.objects.create(name="c1") celeb2 = Celebrity.objects.create(name="c2") self.fan1 = Fan.objects.create(fan_of=celeb1) self.fan2 = Fan.objects.create(fan_of=celeb1) self.fan3 = Fan.objects.create(fan_of=celeb2) def test_basic_distinct_on(self): """QuerySet.distinct('field', ...) works""" # (qset, expected) tuples qsets = ( ( Staff.objects.distinct().order_by('name'), ['', '', '', ''], ), ( Staff.objects.distinct('name').order_by('name'), ['', '', ''], ), ( Staff.objects.distinct('organisation').order_by('organisation', 'name'), ['', ''], ), ( Staff.objects.distinct('name', 'organisation').order_by('name', 'organisation'), ['', '', '', ''], ), ( Celebrity.objects.filter(fan__in=[self.fan1, self.fan2, self.fan3]).distinct('name').order_by('name'), ['', ''], ), # Does combining querysets work? ( (Celebrity.objects.filter(fan__in=[self.fan1, self.fan2]). distinct('name').order_by('name') | Celebrity.objects.filter(fan__in=[self.fan3]). distinct('name').order_by('name')), ['', ''], ), ( StaffTag.objects.distinct('staff', 'tag'), [' p1>'], ), ( Tag.objects.order_by('parent__pk', 'pk').distinct('parent'), ['', '', ''], ), ( StaffTag.objects.select_related('staff').distinct('staff__name').order_by('staff__name'), [' p1>'], ), # Fetch the alphabetically first coworker for each worker ( (Staff.objects.distinct('id').order_by('id', 'coworkers__name'). values_list('id', 'coworkers__name')), [str_prefix("(1, %(_)s'p2')"), str_prefix("(2, %(_)s'p1')"), str_prefix("(3, %(_)s'p1')"), "(4, None)"] ), ) for qset, expected in qsets: self.assertQuerysetEqual(qset, expected) self.assertEqual(qset.count(), len(expected)) # Combining queries with different distinct_fields is not allowed. base_qs = Celebrity.objects.all() self.assertRaisesMessage( AssertionError, "Cannot combine queries with different distinct fields.", lambda: (base_qs.distinct('id') & base_qs.distinct('name')) ) # Test join unreffing c1 = Celebrity.objects.distinct('greatest_fan__id', 'greatest_fan__fan_of') self.assertIn('OUTER JOIN', str(c1.query)) c2 = c1.distinct('pk') self.assertNotIn('OUTER JOIN', str(c2.query)) def test_distinct_not_implemented_checks(self): # distinct + annotate not allowed with self.assertRaises(NotImplementedError): Celebrity.objects.annotate(Max('id')).distinct('id')[0] with self.assertRaises(NotImplementedError): Celebrity.objects.distinct('id').annotate(Max('id'))[0] # However this check is done only when the query executes, so you # can use distinct() to remove the fields before execution. Celebrity.objects.distinct('id').annotate(Max('id')).distinct()[0] # distinct + aggregate not allowed with self.assertRaises(NotImplementedError): Celebrity.objects.distinct('id').aggregate(Max('id')) def test_distinct_on_in_ordered_subquery(self): qs = Staff.objects.distinct('name').order_by('name', 'id') qs = Staff.objects.filter(pk__in=qs).order_by('name') self.assertQuerysetEqual( qs, [self.p1_o1, self.p2_o1, self.p3_o1], lambda x: x ) qs = Staff.objects.distinct('name').order_by('name', '-id') qs = Staff.objects.filter(pk__in=qs).order_by('name') self.assertQuerysetEqual( qs, [self.p1_o2, self.p2_o1, self.p3_o1], lambda x: x ) Django-1.8.7/tests/distinct_on_fields/__init__.py0000664000175000017500000000000012603513307021443 0ustar timtim00000000000000Django-1.8.7/tests/distinct_on_fields/models.py0000664000175000017500000000240412625113144021200 0ustar timtim00000000000000from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Tag(models.Model): name = models.CharField(max_length=10) parent = models.ForeignKey('self', blank=True, null=True, related_name='children') class Meta: ordering = ['name'] def __str__(self): return self.name @python_2_unicode_compatible class Celebrity(models.Model): name = models.CharField("Name", max_length=20) greatest_fan = models.ForeignKey("Fan", null=True, unique=True) def __str__(self): return self.name class Fan(models.Model): fan_of = models.ForeignKey(Celebrity) @python_2_unicode_compatible class Staff(models.Model): id = models.IntegerField(primary_key=True) name = models.CharField(max_length=50) organisation = models.CharField(max_length=100) tags = models.ManyToManyField(Tag, through='StaffTag') coworkers = models.ManyToManyField('self') def __str__(self): return self.name @python_2_unicode_compatible class StaffTag(models.Model): staff = models.ForeignKey(Staff) tag = models.ForeignKey(Tag) def __str__(self): return "%s -> %s" % (self.tag, self.staff) Django-1.8.7/tests/custom_migration_operations/0000775000175000017500000000000012625116243021331 5ustar timtim00000000000000Django-1.8.7/tests/custom_migration_operations/operations.py0000664000175000017500000000417312625116213024070 0ustar timtim00000000000000from django.db.migrations.operations.base import Operation class TestOperation(Operation): def __init__(self): pass def deconstruct(self): return ( self.__class__.__name__, [], {} ) @property def reversible(self): return True def state_forwards(self, app_label, state): pass def database_forwards(self, app_label, schema_editor, from_state, to_state): pass def state_backwards(self, app_label, state): pass def database_backwards(self, app_label, schema_editor, from_state, to_state): pass class CreateModel(TestOperation): pass class ArgsOperation(TestOperation): def __init__(self, arg1, arg2): self.arg1, self.arg2 = arg1, arg2 def deconstruct(self): return ( self.__class__.__name__, [self.arg1, self.arg2], {} ) class KwargsOperation(TestOperation): def __init__(self, kwarg1=None, kwarg2=None): self.kwarg1, self.kwarg2 = kwarg1, kwarg2 def deconstruct(self): kwargs = {} if self.kwarg1 is not None: kwargs['kwarg1'] = self.kwarg1 if self.kwarg2 is not None: kwargs['kwarg2'] = self.kwarg2 return ( self.__class__.__name__, [], kwargs ) class ArgsKwargsOperation(TestOperation): def __init__(self, arg1, arg2, kwarg1=None, kwarg2=None): self.arg1, self.arg2 = arg1, arg2 self.kwarg1, self.kwarg2 = kwarg1, kwarg2 def deconstruct(self): kwargs = {} if self.kwarg1 is not None: kwargs['kwarg1'] = self.kwarg1 if self.kwarg2 is not None: kwargs['kwarg2'] = self.kwarg2 return ( self.__class__.__name__, [self.arg1, self.arg2], kwargs, ) class ExpandArgsOperation(TestOperation): serialization_expand_args = ['arg'] def __init__(self, arg): self.arg = arg def deconstruct(self): return ( self.__class__.__name__, [self.arg], {} ) Django-1.8.7/tests/custom_migration_operations/__init__.py0000664000175000017500000000000012603513307023426 0ustar timtim00000000000000Django-1.8.7/tests/custom_migration_operations/more_operations.py0000664000175000017500000000116612625116213025111 0ustar timtim00000000000000from django.db.migrations.operations.base import Operation class TestOperation(Operation): def __init__(self): pass def deconstruct(self): return ( self.__class__.__name__, [], {} ) @property def reversible(self): return True def state_forwards(self, app_label, state): pass def database_forwards(self, app_label, schema_editor, from_state, to_state): pass def state_backwards(self, app_label, state): pass def database_backwards(self, app_label, schema_editor, from_state, to_state): pass Django-1.8.7/tests/syndication_tests/0000775000175000017500000000000012625116243017251 5ustar timtim00000000000000Django-1.8.7/tests/syndication_tests/urls.py0000664000175000017500000000200312625116214020601 0ustar timtim00000000000000from django.conf.urls import url from . import feeds urlpatterns = [ url(r'^syndication/rss2/$', feeds.TestRss2Feed()), url(r'^syndication/rss2/guid_ispermalink_true/$', feeds.TestRss2FeedWithGuidIsPermaLinkTrue()), url(r'^syndication/rss2/guid_ispermalink_false/$', feeds.TestRss2FeedWithGuidIsPermaLinkFalse()), url(r'^syndication/rss091/$', feeds.TestRss091Feed()), url(r'^syndication/no_pubdate/$', feeds.TestNoPubdateFeed()), url(r'^syndication/atom/$', feeds.TestAtomFeed()), url(r'^syndication/latest/$', feeds.TestLatestFeed()), url(r'^syndication/custom/$', feeds.TestCustomFeed()), url(r'^syndication/naive-dates/$', feeds.NaiveDatesFeed()), url(r'^syndication/aware-dates/$', feeds.TZAwareDatesFeed()), url(r'^syndication/feedurl/$', feeds.TestFeedUrlFeed()), url(r'^syndication/articles/$', feeds.ArticlesFeed()), url(r'^syndication/template/$', feeds.TemplateFeed()), url(r'^syndication/template_context/$', feeds.TemplateContextFeed()), ] Django-1.8.7/tests/syndication_tests/templates/0000775000175000017500000000000012625116242021246 5ustar timtim00000000000000Django-1.8.7/tests/syndication_tests/templates/syndication/0000775000175000017500000000000012625116243023573 5ustar timtim00000000000000Django-1.8.7/tests/syndication_tests/templates/syndication/title_context.html0000664000175000017500000000003512625116214027342 0ustar timtim00000000000000{{ obj }} (foo is {{ foo }}) Django-1.8.7/tests/syndication_tests/templates/syndication/title.html0000664000175000017500000000004312625116214025575 0ustar timtim00000000000000Title in your templates: {{ obj }} Django-1.8.7/tests/syndication_tests/templates/syndication/description.html0000664000175000017500000000005112625116214026776 0ustar timtim00000000000000Description in your templates: {{ obj }} Django-1.8.7/tests/syndication_tests/templates/syndication/description_context.html0000664000175000017500000000003512625116214030544 0ustar timtim00000000000000{{ obj }} (foo is {{ foo }}) Django-1.8.7/tests/syndication_tests/tests.py0000664000175000017500000004370512625116214020774 0ustar timtim00000000000000from __future__ import unicode_literals import datetime from xml.dom import minidom from django.contrib.sites.models import Site from django.contrib.syndication import views from django.core.exceptions import ImproperlyConfigured from django.test import TestCase, override_settings from django.test.utils import requires_tz_support from django.utils import timezone from django.utils.feedgenerator import rfc2822_date, rfc3339_date from .models import Entry try: import pytz except ImportError: pytz = None TZ = timezone.get_default_timezone() class FeedTestCase(TestCase): fixtures = ['feeddata.json'] def setUp(self): # Django cannot deal with very old dates when pytz isn't installed. if pytz is None: old_entry = Entry.objects.get(pk=1) old_entry.updated = datetime.datetime(1980, 1, 1, 12, 30) old_entry.published = datetime.datetime(1986, 9, 25, 20, 15, 00) old_entry.save() def assertChildNodes(self, elem, expected): actual = set(n.nodeName for n in elem.childNodes) expected = set(expected) self.assertEqual(actual, expected) def assertChildNodeContent(self, elem, expected): for k, v in expected.items(): self.assertEqual( elem.getElementsByTagName(k)[0].firstChild.wholeText, v) def assertCategories(self, elem, expected): self.assertEqual(set(i.firstChild.wholeText for i in elem.childNodes if i.nodeName == 'category'), set(expected)) ###################################### # Feed view ###################################### @override_settings(ROOT_URLCONF='syndication_tests.urls') class SyndicationFeedTest(FeedTestCase): """ Tests for the high-level syndication feed framework. """ @classmethod def setUpClass(cls): super(SyndicationFeedTest, cls).setUpClass() # This cleanup is necessary because contrib.sites cache # makes tests interfere with each other, see #11505 Site.objects.clear_cache() def test_rss2_feed(self): """ Test the structure and content of feeds generated by Rss201rev2Feed. """ response = self.client.get('/syndication/rss2/') doc = minidom.parseString(response.content) # Making sure there's only 1 `rss` element and that the correct # RSS version was specified. feed_elem = doc.getElementsByTagName('rss') self.assertEqual(len(feed_elem), 1) feed = feed_elem[0] self.assertEqual(feed.getAttribute('version'), '2.0') # Making sure there's only one `channel` element w/in the # `rss` element. chan_elem = feed.getElementsByTagName('channel') self.assertEqual(len(chan_elem), 1) chan = chan_elem[0] # Find the last build date d = Entry.objects.latest('published').published last_build_date = rfc2822_date(timezone.make_aware(d, TZ)) self.assertChildNodes(chan, ['title', 'link', 'description', 'language', 'lastBuildDate', 'item', 'atom:link', 'ttl', 'copyright', 'category']) self.assertChildNodeContent(chan, { 'title': 'My blog', 'description': 'A more thorough description of my blog.', 'link': 'http://example.com/blog/', 'language': 'en', 'lastBuildDate': last_build_date, 'ttl': '600', 'copyright': 'Copyright (c) 2007, Sally Smith', }) self.assertCategories(chan, ['python', 'django']) # Ensure the content of the channel is correct self.assertChildNodeContent(chan, { 'title': 'My blog', 'link': 'http://example.com/blog/', }) # Check feed_url is passed self.assertEqual( chan.getElementsByTagName('atom:link')[0].getAttribute('href'), 'http://example.com/syndication/rss2/' ) # Find the pubdate of the first feed item d = Entry.objects.get(pk=1).published pub_date = rfc2822_date(timezone.make_aware(d, TZ)) items = chan.getElementsByTagName('item') self.assertEqual(len(items), Entry.objects.count()) self.assertChildNodeContent(items[0], { 'title': 'My first entry', 'description': 'Overridden description: My first entry', 'link': 'http://example.com/blog/1/', 'guid': 'http://example.com/blog/1/', 'pubDate': pub_date, 'author': 'test@example.com (Sally Smith)', }) self.assertCategories(items[0], ['python', 'testing']) for item in items: self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'category', 'pubDate', 'author']) # Assert that does not have any 'isPermaLink' attribute self.assertIsNone(item.getElementsByTagName( 'guid')[0].attributes.get('isPermaLink')) def test_rss2_feed_guid_permalink_false(self): """ Test if the 'isPermaLink' attribute of element of an item in the RSS feed is 'false'. """ response = self.client.get( '/syndication/rss2/guid_ispermalink_false/') doc = minidom.parseString(response.content) chan = doc.getElementsByTagName( 'rss')[0].getElementsByTagName('channel')[0] items = chan.getElementsByTagName('item') for item in items: self.assertEqual( item.getElementsByTagName('guid')[0].attributes.get( 'isPermaLink').value, "false") def test_rss2_feed_guid_permalink_true(self): """ Test if the 'isPermaLink' attribute of element of an item in the RSS feed is 'true'. """ response = self.client.get( '/syndication/rss2/guid_ispermalink_true/') doc = minidom.parseString(response.content) chan = doc.getElementsByTagName( 'rss')[0].getElementsByTagName('channel')[0] items = chan.getElementsByTagName('item') for item in items: self.assertEqual( item.getElementsByTagName('guid')[0].attributes.get( 'isPermaLink').value, "true") def test_rss091_feed(self): """ Test the structure and content of feeds generated by RssUserland091Feed. """ response = self.client.get('/syndication/rss091/') doc = minidom.parseString(response.content) # Making sure there's only 1 `rss` element and that the correct # RSS version was specified. feed_elem = doc.getElementsByTagName('rss') self.assertEqual(len(feed_elem), 1) feed = feed_elem[0] self.assertEqual(feed.getAttribute('version'), '0.91') # Making sure there's only one `channel` element w/in the # `rss` element. chan_elem = feed.getElementsByTagName('channel') self.assertEqual(len(chan_elem), 1) chan = chan_elem[0] self.assertChildNodes(chan, ['title', 'link', 'description', 'language', 'lastBuildDate', 'item', 'atom:link', 'ttl', 'copyright', 'category']) # Ensure the content of the channel is correct self.assertChildNodeContent(chan, { 'title': 'My blog', 'link': 'http://example.com/blog/', }) self.assertCategories(chan, ['python', 'django']) # Check feed_url is passed self.assertEqual( chan.getElementsByTagName('atom:link')[0].getAttribute('href'), 'http://example.com/syndication/rss091/' ) items = chan.getElementsByTagName('item') self.assertEqual(len(items), Entry.objects.count()) self.assertChildNodeContent(items[0], { 'title': 'My first entry', 'description': 'Overridden description: My first entry', 'link': 'http://example.com/blog/1/', }) for item in items: self.assertChildNodes(item, ['title', 'link', 'description']) self.assertCategories(item, []) def test_atom_feed(self): """ Test the structure and content of feeds generated by Atom1Feed. """ response = self.client.get('/syndication/atom/') feed = minidom.parseString(response.content).firstChild self.assertEqual(feed.nodeName, 'feed') self.assertEqual(feed.getAttribute('xmlns'), 'http://www.w3.org/2005/Atom') self.assertChildNodes(feed, ['title', 'subtitle', 'link', 'id', 'updated', 'entry', 'rights', 'category', 'author']) for link in feed.getElementsByTagName('link'): if link.getAttribute('rel') == 'self': self.assertEqual(link.getAttribute('href'), 'http://example.com/syndication/atom/') entries = feed.getElementsByTagName('entry') self.assertEqual(len(entries), Entry.objects.count()) for entry in entries: self.assertChildNodes(entry, [ 'title', 'link', 'id', 'summary', 'category', 'updated', 'published', 'rights', 'author', ]) summary = entry.getElementsByTagName('summary')[0] self.assertEqual(summary.getAttribute('type'), 'html') def test_atom_feed_published_and_updated_elements(self): """ Test that the published and updated elements are not the same and now adhere to RFC 4287. """ response = self.client.get('/syndication/atom/') feed = minidom.parseString(response.content).firstChild entries = feed.getElementsByTagName('entry') published = entries[0].getElementsByTagName('published')[0].firstChild.wholeText updated = entries[0].getElementsByTagName('updated')[0].firstChild.wholeText self.assertNotEqual(published, updated) def test_latest_post_date(self): """ Test that both the published and updated dates are considered when determining the latest post date. """ # this feed has a `published` element with the latest date response = self.client.get('/syndication/atom/') feed = minidom.parseString(response.content).firstChild updated = feed.getElementsByTagName('updated')[0].firstChild.wholeText d = Entry.objects.latest('published').published latest_published = rfc3339_date(timezone.make_aware(d, TZ)) self.assertEqual(updated, latest_published) # this feed has an `updated` element with the latest date response = self.client.get('/syndication/latest/') feed = minidom.parseString(response.content).firstChild updated = feed.getElementsByTagName('updated')[0].firstChild.wholeText d = Entry.objects.exclude(pk=5).latest('updated').updated latest_updated = rfc3339_date(timezone.make_aware(d, TZ)) self.assertEqual(updated, latest_updated) def test_custom_feed_generator(self): response = self.client.get('/syndication/custom/') feed = minidom.parseString(response.content).firstChild self.assertEqual(feed.nodeName, 'feed') self.assertEqual(feed.getAttribute('django'), 'rocks') self.assertChildNodes(feed, ['title', 'subtitle', 'link', 'id', 'updated', 'entry', 'spam', 'rights', 'category', 'author']) entries = feed.getElementsByTagName('entry') self.assertEqual(len(entries), Entry.objects.count()) for entry in entries: self.assertEqual(entry.getAttribute('bacon'), 'yum') self.assertChildNodes(entry, [ 'title', 'link', 'id', 'summary', 'ministry', 'rights', 'author', 'updated', 'published', 'category', ]) summary = entry.getElementsByTagName('summary')[0] self.assertEqual(summary.getAttribute('type'), 'html') def test_title_escaping(self): """ Tests that titles are escaped correctly in RSS feeds. """ response = self.client.get('/syndication/rss2/') doc = minidom.parseString(response.content) for item in doc.getElementsByTagName('item'): link = item.getElementsByTagName('link')[0] if link.firstChild.wholeText == 'http://example.com/blog/4/': title = item.getElementsByTagName('title')[0] self.assertEqual(title.firstChild.wholeText, 'A & B < C > D') def test_naive_datetime_conversion(self): """ Test that datetimes are correctly converted to the local time zone. """ # Naive date times passed in get converted to the local time zone, so # check the received zone offset against the local offset. response = self.client.get('/syndication/naive-dates/') doc = minidom.parseString(response.content) updated = doc.getElementsByTagName('updated')[0].firstChild.wholeText d = Entry.objects.latest('published').published latest = rfc3339_date(timezone.make_aware(d, TZ)) self.assertEqual(updated, latest) def test_aware_datetime_conversion(self): """ Test that datetimes with timezones don't get trodden on. """ response = self.client.get('/syndication/aware-dates/') doc = minidom.parseString(response.content) published = doc.getElementsByTagName('published')[0].firstChild.wholeText self.assertEqual(published[-6:], '+00:42') @requires_tz_support def test_feed_last_modified_time_naive_date(self): """ Tests the Last-Modified header with naive publication dates. """ response = self.client.get('/syndication/naive-dates/') self.assertEqual(response['Last-Modified'], 'Tue, 26 Mar 2013 01:00:00 GMT') def test_feed_last_modified_time(self): """ Tests the Last-Modified header with aware publication dates. """ response = self.client.get('/syndication/aware-dates/') self.assertEqual(response['Last-Modified'], 'Mon, 25 Mar 2013 19:18:00 GMT') # No last-modified when feed has no item_pubdate response = self.client.get('/syndication/no_pubdate/') self.assertFalse(response.has_header('Last-Modified')) def test_feed_url(self): """ Test that the feed_url can be overridden. """ response = self.client.get('/syndication/feedurl/') doc = minidom.parseString(response.content) for link in doc.getElementsByTagName('link'): if link.getAttribute('rel') == 'self': self.assertEqual(link.getAttribute('href'), 'http://example.com/customfeedurl/') def test_secure_urls(self): """ Test URLs are prefixed with https:// when feed is requested over HTTPS. """ response = self.client.get('/syndication/rss2/', **{ 'wsgi.url_scheme': 'https', }) doc = minidom.parseString(response.content) chan = doc.getElementsByTagName('channel')[0] self.assertEqual( chan.getElementsByTagName('link')[0].firstChild.wholeText[0:5], 'https' ) atom_link = chan.getElementsByTagName('atom:link')[0] self.assertEqual(atom_link.getAttribute('href')[0:5], 'https') for link in doc.getElementsByTagName('link'): if link.getAttribute('rel') == 'self': self.assertEqual(link.getAttribute('href')[0:5], 'https') def test_item_link_error(self): """ Test that an ImproperlyConfigured is raised if no link could be found for the item(s). """ self.assertRaises(ImproperlyConfigured, self.client.get, '/syndication/articles/') def test_template_feed(self): """ Test that the item title and description can be overridden with templates. """ response = self.client.get('/syndication/template/') doc = minidom.parseString(response.content) feed = doc.getElementsByTagName('rss')[0] chan = feed.getElementsByTagName('channel')[0] items = chan.getElementsByTagName('item') self.assertChildNodeContent(items[0], { 'title': 'Title in your templates: My first entry\n', 'description': 'Description in your templates: My first entry\n', 'link': 'http://example.com/blog/1/', }) def test_template_context_feed(self): """ Test that custom context data can be passed to templates for title and description. """ response = self.client.get('/syndication/template_context/') doc = minidom.parseString(response.content) feed = doc.getElementsByTagName('rss')[0] chan = feed.getElementsByTagName('channel')[0] items = chan.getElementsByTagName('item') self.assertChildNodeContent(items[0], { 'title': 'My first entry (foo is bar)\n', 'description': 'My first entry (foo is bar)\n', }) def test_add_domain(self): """ Test add_domain() prefixes domains onto the correct URLs. """ self.assertEqual( views.add_domain('example.com', '/foo/?arg=value'), 'http://example.com/foo/?arg=value' ) self.assertEqual( views.add_domain('example.com', '/foo/?arg=value', True), 'https://example.com/foo/?arg=value' ) self.assertEqual( views.add_domain('example.com', 'http://djangoproject.com/doc/'), 'http://djangoproject.com/doc/' ) self.assertEqual( views.add_domain('example.com', 'https://djangoproject.com/doc/'), 'https://djangoproject.com/doc/' ) self.assertEqual( views.add_domain('example.com', 'mailto:uhoh@djangoproject.com'), 'mailto:uhoh@djangoproject.com' ) self.assertEqual( views.add_domain('example.com', '//example.com/foo/?arg=value'), 'http://example.com/foo/?arg=value' ) Django-1.8.7/tests/syndication_tests/fixtures/0000775000175000017500000000000012625116243021122 5ustar timtim00000000000000Django-1.8.7/tests/syndication_tests/fixtures/feeddata.json0000664000175000017500000000215412625113144023551 0ustar timtim00000000000000[ { "model": "syndication_tests.entry", "pk": 1, "fields": { "title": "My first entry", "updated": "1850-01-01 12:30:00", "published": "1066-09-25 20:15:00" } }, { "model": "syndication_tests.entry", "pk": 2, "fields": { "title": "My second entry", "updated": "2008-01-02 12:30:00", "published": "2006-03-17 18:00:00" } }, { "model": "syndication_tests.entry", "pk": 3, "fields": { "title": "My third entry", "updated": "2008-01-02 13:30:00", "published": "2005-06-14 10:45:00" } }, { "model": "syndication_tests.entry", "pk": 4, "fields": { "title": "A & B < C > D", "updated": "2008-01-03 13:30:00", "published": "2005-11-25 12:11:23" } }, { "model": "syndication_tests.entry", "pk": 5, "fields": { "title": "My last entry", "updated": "2013-01-20 00:00:00", "published": "2013-03-25 20:00:00" } }, { "model": "syndication_tests.article", "pk": 1, "fields": { "title": "My first article", "entry": "1" } } ] Django-1.8.7/tests/syndication_tests/__init__.py0000664000175000017500000000000012603513307021346 0ustar timtim00000000000000Django-1.8.7/tests/syndication_tests/feeds.py0000664000175000017500000001070312625116214020710 0ustar timtim00000000000000from __future__ import unicode_literals from django.contrib.syndication import views from django.utils import feedgenerator from django.utils.timezone import get_fixed_timezone from .models import Article, Entry class TestRss2Feed(views.Feed): title = 'My blog' description = 'A more thorough description of my blog.' link = '/blog/' feed_guid = '/foo/bar/1234' author_name = 'Sally Smith' author_email = 'test@example.com' author_link = 'http://www.example.com/' categories = ('python', 'django') feed_copyright = 'Copyright (c) 2007, Sally Smith' ttl = 600 def items(self): return Entry.objects.all() def item_description(self, item): return "Overridden description: %s" % item def item_pubdate(self, item): return item.published def item_updateddate(self, item): return item.updated item_author_name = 'Sally Smith' item_author_email = 'test@example.com' item_author_link = 'http://www.example.com/' item_categories = ('python', 'testing') item_copyright = 'Copyright (c) 2007, Sally Smith' class TestRss2FeedWithGuidIsPermaLinkTrue(TestRss2Feed): def item_guid_is_permalink(self, item): return True class TestRss2FeedWithGuidIsPermaLinkFalse(TestRss2Feed): def item_guid(self, item): return str(item.pk) def item_guid_is_permalink(self, item): return False class TestRss091Feed(TestRss2Feed): feed_type = feedgenerator.RssUserland091Feed class TestNoPubdateFeed(views.Feed): title = 'Test feed' link = '/feed/' def items(self): return Entry.objects.all() class TestAtomFeed(TestRss2Feed): feed_type = feedgenerator.Atom1Feed subtitle = TestRss2Feed.description class TestLatestFeed(TestRss2Feed): """ A feed where the latest entry date is an `updated` element. """ feed_type = feedgenerator.Atom1Feed subtitle = TestRss2Feed.description def items(self): return Entry.objects.exclude(pk=5) class ArticlesFeed(TestRss2Feed): """ A feed to test no link being defined. Articles have no get_absolute_url() method, and item_link() is not defined. """ def items(self): return Article.objects.all() class TestEnclosureFeed(TestRss2Feed): pass class TemplateFeed(TestRss2Feed): """ A feed to test defining item titles and descriptions with templates. """ title_template = 'syndication/title.html' description_template = 'syndication/description.html' # Defining a template overrides any item_title definition def item_title(self): return "Not in a template" class TemplateContextFeed(TestRss2Feed): """ A feed to test custom context data in templates for title or description. """ title_template = 'syndication/title_context.html' description_template = 'syndication/description_context.html' def get_context_data(self, **kwargs): context = super(TemplateContextFeed, self).get_context_data(**kwargs) context['foo'] = 'bar' return context class NaiveDatesFeed(TestAtomFeed): """ A feed with naive (non-timezone-aware) dates. """ def item_pubdate(self, item): return item.published class TZAwareDatesFeed(TestAtomFeed): """ A feed with timezone-aware dates. """ def item_pubdate(self, item): # Provide a weird offset so that the test can know it's getting this # specific offset and not accidentally getting on from # settings.TIME_ZONE. return item.published.replace(tzinfo=get_fixed_timezone(42)) class TestFeedUrlFeed(TestAtomFeed): feed_url = 'http://example.com/customfeedurl/' class MyCustomAtom1Feed(feedgenerator.Atom1Feed): """ Test of a custom feed generator class. """ def root_attributes(self): attrs = super(MyCustomAtom1Feed, self).root_attributes() attrs['django'] = 'rocks' return attrs def add_root_elements(self, handler): super(MyCustomAtom1Feed, self).add_root_elements(handler) handler.addQuickElement('spam', 'eggs') def item_attributes(self, item): attrs = super(MyCustomAtom1Feed, self).item_attributes(item) attrs['bacon'] = 'yum' return attrs def add_item_elements(self, handler, item): super(MyCustomAtom1Feed, self).add_item_elements(handler, item) handler.addQuickElement('ministry', 'silly walks') class TestCustomFeed(TestAtomFeed): feed_type = MyCustomAtom1Feed Django-1.8.7/tests/syndication_tests/models.py0000664000175000017500000000116712625113144021110 0ustar timtim00000000000000from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Entry(models.Model): title = models.CharField(max_length=200) updated = models.DateTimeField() published = models.DateTimeField() class Meta: ordering = ('updated',) def __str__(self): return self.title def get_absolute_url(self): return "/blog/%s/" % self.pk @python_2_unicode_compatible class Article(models.Model): title = models.CharField(max_length=200) entry = models.ForeignKey(Entry) def __str__(self): return self.title Django-1.8.7/tests/custom_pk/0000775000175000017500000000000012625116243015507 5ustar timtim00000000000000Django-1.8.7/tests/custom_pk/tests.py0000664000175000017500000001647112625116213017231 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import IntegrityError, transaction from django.test import TestCase, skipIfDBFeature from django.utils import six from .models import Bar, Business, Employee, Foo class BasicCustomPKTests(TestCase): @classmethod def setUpTestData(cls): cls.dan = Employee.objects.create( employee_code=123, first_name="Dan", last_name="Jones", ) cls.fran = Employee.objects.create( employee_code=456, first_name="Fran", last_name="Bones", ) cls.business = Business.objects.create(name="Sears") cls.business.employees.add(cls.dan, cls.fran) def test_querysets(self): """ Both pk and custom attribute_name can be used in filter and friends """ self.assertQuerysetEqual( Employee.objects.filter(pk=123), [ "Dan Jones", ], six.text_type ) self.assertQuerysetEqual( Employee.objects.filter(employee_code=123), [ "Dan Jones", ], six.text_type ) self.assertQuerysetEqual( Employee.objects.filter(pk__in=[123, 456]), [ "Fran Bones", "Dan Jones", ], six.text_type ) self.assertQuerysetEqual( Employee.objects.all(), [ "Fran Bones", "Dan Jones", ], six.text_type ) self.assertQuerysetEqual( Business.objects.filter(name="Sears"), [ "Sears" ], lambda b: b.name ) self.assertQuerysetEqual( Business.objects.filter(pk="Sears"), [ "Sears", ], lambda b: b.name ) def test_querysets_related_name(self): """ Custom pk doesn't affect related_name based lookups """ self.assertQuerysetEqual( self.business.employees.all(), [ "Fran Bones", "Dan Jones", ], six.text_type ) self.assertQuerysetEqual( self.fran.business_set.all(), [ "Sears", ], lambda b: b.name ) def test_querysets_relational(self): """ Queries across tables, involving primary key """ self.assertQuerysetEqual( Employee.objects.filter(business__name="Sears"), [ "Fran Bones", "Dan Jones", ], six.text_type, ) self.assertQuerysetEqual( Employee.objects.filter(business__pk="Sears"), [ "Fran Bones", "Dan Jones", ], six.text_type, ) self.assertQuerysetEqual( Business.objects.filter(employees__employee_code=123), [ "Sears", ], lambda b: b.name ) self.assertQuerysetEqual( Business.objects.filter(employees__pk=123), [ "Sears", ], lambda b: b.name, ) self.assertQuerysetEqual( Business.objects.filter(employees__first_name__startswith="Fran"), [ "Sears", ], lambda b: b.name ) def test_get(self): """ Get can accept pk or the real attribute name """ self.assertEqual(Employee.objects.get(pk=123), self.dan) self.assertEqual(Employee.objects.get(pk=456), self.fran) self.assertRaises( Employee.DoesNotExist, lambda: Employee.objects.get(pk=42) ) # Use the name of the primary key, rather than pk. self.assertEqual(Employee.objects.get(employee_code=123), self.dan) def test_pk_attributes(self): """ pk and attribute name are available on the model No default id attribute is added """ # pk can be used as a substitute for the primary key. # The primary key can be accessed via the pk property on the model. e = Employee.objects.get(pk=123) self.assertEqual(e.pk, 123) # Or we can use the real attribute name for the primary key: self.assertEqual(e.employee_code, 123) self.assertRaises(AttributeError, lambda: e.id) def test_in_bulk(self): """ Custom pks work with in_bulk, both for integer and non-integer types """ emps = Employee.objects.in_bulk([123, 456]) self.assertEqual(emps[123], self.dan) self.assertEqual(Business.objects.in_bulk(["Sears"]), { "Sears": self.business, }) def test_save(self): """ custom pks do not affect save """ fran = Employee.objects.get(pk=456) fran.last_name = "Jones" fran.save() self.assertQuerysetEqual( Employee.objects.filter(last_name="Jones"), [ "Dan Jones", "Fran Jones", ], six.text_type ) class CustomPKTests(TestCase): def test_custom_pk_create(self): """ New objects can be created both with pk and the custom name """ Employee.objects.create(employee_code=1234, first_name="Foo", last_name="Bar") Employee.objects.create(pk=1235, first_name="Foo", last_name="Baz") Business.objects.create(name="Bears") Business.objects.create(pk="Tears") def test_unicode_pk(self): # Primary key may be unicode string Business.objects.create(name='jaźń') def test_unique_pk(self): # The primary key must also obviously be unique, so trying to create a # new object with the same primary key will fail. Employee.objects.create( employee_code=123, first_name="Frank", last_name="Jones" ) with self.assertRaises(IntegrityError): with transaction.atomic(): Employee.objects.create(employee_code=123, first_name="Fred", last_name="Jones") def test_zero_non_autoincrement_pk(self): Employee.objects.create( employee_code=0, first_name="Frank", last_name="Jones" ) employee = Employee.objects.get(pk=0) self.assertEqual(employee.employee_code, 0) def test_custom_field_pk(self): # Regression for #10785 -- Custom fields can be used for primary keys. new_bar = Bar.objects.create() new_foo = Foo.objects.create(bar=new_bar) f = Foo.objects.get(bar=new_bar.pk) self.assertEqual(f, new_foo) self.assertEqual(f.bar, new_bar) f = Foo.objects.get(bar=new_bar) self.assertEqual(f, new_foo), self.assertEqual(f.bar, new_bar) # SQLite lets objects be saved with an empty primary key, even though an # integer is expected. So we can't check for an error being raised in that # case for SQLite. Remove it from the suite for this next bit. @skipIfDBFeature('supports_unspecified_pk') def test_required_pk(self): # The primary key must be specified, so an error is raised if you # try to create an object without it. with self.assertRaises(IntegrityError): with transaction.atomic(): Employee.objects.create(first_name="Tom", last_name="Smith") Django-1.8.7/tests/custom_pk/__init__.py0000664000175000017500000000000012603513307017604 0ustar timtim00000000000000Django-1.8.7/tests/custom_pk/models.py0000664000175000017500000000235012625116213017341 0ustar timtim00000000000000# -*- coding: utf-8 -*- """ Using a custom primary key By default, Django adds an ``"id"`` field to each model. But you can override this behavior by explicitly adding ``primary_key=True`` to a field. """ from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible from .fields import MyAutoField @python_2_unicode_compatible class Employee(models.Model): employee_code = models.IntegerField(primary_key=True, db_column='code') first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) class Meta: ordering = ('last_name', 'first_name') def __str__(self): return "%s %s" % (self.first_name, self.last_name) @python_2_unicode_compatible class Business(models.Model): name = models.CharField(max_length=20, primary_key=True) employees = models.ManyToManyField(Employee) class Meta: verbose_name_plural = 'businesses' def __str__(self): return self.name @python_2_unicode_compatible class Bar(models.Model): id = MyAutoField(primary_key=True, db_index=True) def __str__(self): return repr(self.pk) class Foo(models.Model): bar = models.ForeignKey(Bar) Django-1.8.7/tests/custom_pk/fields.py0000664000175000017500000000330312625116213017323 0ustar timtim00000000000000import random import string from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class MyWrapper(object): def __init__(self, value): self.value = value def __repr__(self): return "<%s: %s>" % (self.__class__.__name__, self.value) def __str__(self): return self.value def __eq__(self, other): if isinstance(other, self.__class__): return self.value == other.value return self.value == other class MyAutoField(models.CharField): def __init__(self, *args, **kwargs): kwargs['max_length'] = 10 super(MyAutoField, self).__init__(*args, **kwargs) def pre_save(self, instance, add): value = getattr(instance, self.attname, None) if not value: value = MyWrapper(''.join(random.sample(string.ascii_lowercase, 10))) setattr(instance, self.attname, value) return value def to_python(self, value): if not value: return if not isinstance(value, MyWrapper): value = MyWrapper(value) return value def from_db_value(self, value, expression, connection, context): if not value: return return MyWrapper(value) def get_db_prep_save(self, value, connection): if not value: return if isinstance(value, MyWrapper): return six.text_type(value) return value def get_db_prep_value(self, value, connection, prepared=False): if not value: return if isinstance(value, MyWrapper): return six.text_type(value) return value Django-1.8.7/tests/null_fk/0000775000175000017500000000000012625116243015135 5ustar timtim00000000000000Django-1.8.7/tests/null_fk/tests.py0000664000175000017500000000564612625116214016662 0ustar timtim00000000000000from __future__ import unicode_literals from django.db.models import Q from django.test import TestCase from .models import ( Comment, Forum, Item, Post, PropertyValue, SystemDetails, SystemInfo, ) class NullFkTests(TestCase): def test_null_fk(self): d = SystemDetails.objects.create(details='First details') s = SystemInfo.objects.create(system_name='First forum', system_details=d) f = Forum.objects.create(system_info=s, forum_name='First forum') p = Post.objects.create(forum=f, title='First Post') c1 = Comment.objects.create(post=p, comment_text='My first comment') c2 = Comment.objects.create(comment_text='My second comment') # Starting from comment, make sure that a .select_related(...) with a specified # set of fields will properly LEFT JOIN multiple levels of NULLs (and the things # that come after the NULLs, or else data that should exist won't). Regression # test for #7369. c = Comment.objects.select_related().get(id=c1.id) self.assertEqual(c.post, p) self.assertEqual(Comment.objects.select_related().get(id=c2.id).post, None) self.assertQuerysetEqual( Comment.objects.select_related('post__forum__system_info').all(), [ (c1.id, 'My first comment', ''), (c2.id, 'My second comment', 'None') ], transform=lambda c: (c.id, c.comment_text, repr(c.post)) ) # Regression test for #7530, #7716. self.assertIsNone(Comment.objects.select_related('post').filter(post__isnull=True)[0].post) self.assertQuerysetEqual( Comment.objects.select_related('post__forum__system_info__system_details'), [ (c1.id, 'My first comment', ''), (c2.id, 'My second comment', 'None') ], transform=lambda c: (c.id, c.comment_text, repr(c.post)) ) def test_combine_isnull(self): item = Item.objects.create(title='Some Item') pv = PropertyValue.objects.create(label='Some Value') item.props.create(key='a', value=pv) item.props.create(key='b') # value=NULL q1 = Q(props__key='a', props__value=pv) q2 = Q(props__key='b', props__value__isnull=True) # Each of these individually should return the item. self.assertEqual(Item.objects.get(q1), item) self.assertEqual(Item.objects.get(q2), item) # Logically, qs1 and qs2, and qs3 and qs4 should be the same. qs1 = Item.objects.filter(q1) & Item.objects.filter(q2) qs2 = Item.objects.filter(q2) & Item.objects.filter(q1) qs3 = Item.objects.filter(q1) | Item.objects.filter(q2) qs4 = Item.objects.filter(q2) | Item.objects.filter(q1) # Regression test for #15823. self.assertEqual(list(qs1), list(qs2)) self.assertEqual(list(qs3), list(qs4)) Django-1.8.7/tests/null_fk/__init__.py0000664000175000017500000000000012603513307017232 0ustar timtim00000000000000Django-1.8.7/tests/null_fk/models.py0000664000175000017500000000245312625113144016773 0ustar timtim00000000000000""" Regression tests for proper working of ForeignKey(null=True). """ from django.db import models from django.utils.encoding import python_2_unicode_compatible class SystemDetails(models.Model): 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) title = models.CharField(max_length=32) def __str__(self): return self.title @python_2_unicode_compatible class Comment(models.Model): post = models.ForeignKey(Post, null=True) comment_text = models.CharField(max_length=250) class Meta: ordering = ('comment_text',) def __str__(self): return self.comment_text # 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) value = models.ForeignKey(PropertyValue, null=True) Django-1.8.7/tests/model_meta/0000775000175000017500000000000012625116243015611 5ustar timtim00000000000000Django-1.8.7/tests/model_meta/tests.py0000664000175000017500000002605112625116214017327 0ustar timtim00000000000000from django.apps import apps from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.core.exceptions import FieldDoesNotExist from django.db.models.fields import CharField, Field, related from django.db.models.options import EMPTY_RELATION_TREE, IMMUTABLE_WARNING from django.test import TestCase from .models import ( AbstractPerson, BasePerson, Child, CommonAncestor, FirstParent, Person, ProxyPerson, Relating, Relation, SecondParent, ) from .results import TEST_RESULTS class OptionsBaseTests(TestCase): def _map_related_query_names(self, res): return tuple((o.name, m) for o, m in res) def _map_names(self, res): return tuple((f.name, m) for f, m in res) def _model(self, current_model, field): model = field.model._meta.concrete_model return None if model == current_model else model def _details(self, current_model, relation): direct = isinstance(relation, Field) or isinstance(relation, GenericForeignKey) model = relation.model._meta.concrete_model if model == current_model: model = None field = relation if direct else relation.field m2m = isinstance(field, related.ManyToManyField) return relation, model, direct, m2m class GetFieldsTests(OptionsBaseTests): def test_get_fields_is_immutable(self): msg = IMMUTABLE_WARNING % "get_fields()" for _ in range(2): # Running unit test twice to ensure both non-cached and cached result # are immutable. fields = Person._meta.get_fields() with self.assertRaisesMessage(AttributeError, msg): fields += ["errors"] class DataTests(OptionsBaseTests): def test_fields(self): for model, expected_result in TEST_RESULTS['fields'].items(): fields = model._meta.fields self.assertEqual([f.attname for f in fields], expected_result) def test_local_fields(self): is_data_field = lambda f: isinstance(f, Field) and not isinstance(f, related.ManyToManyField) for model, expected_result in TEST_RESULTS['local_fields'].items(): fields = model._meta.local_fields self.assertEqual([f.attname for f in fields], expected_result) for f in fields: self.assertEqual(f.model, model) self.assertTrue(is_data_field(f)) def test_local_concrete_fields(self): for model, expected_result in TEST_RESULTS['local_concrete_fields'].items(): fields = model._meta.local_concrete_fields self.assertEqual([f.attname for f in fields], expected_result) for f in fields: self.assertIsNotNone(f.column) class M2MTests(OptionsBaseTests): def test_many_to_many(self): for model, expected_result in TEST_RESULTS['many_to_many'].items(): fields = model._meta.many_to_many self.assertEqual([f.attname for f in fields], expected_result) for f in fields: self.assertTrue(f.many_to_many and f.is_relation) def test_many_to_many_with_model(self): for model, expected_result in TEST_RESULTS['many_to_many_with_model'].items(): models = [self._model(model, field) for field in model._meta.many_to_many] self.assertEqual(models, expected_result) class RelatedObjectsTests(OptionsBaseTests): key_name = lambda self, r: r[0] def test_related_objects(self): result_key = 'get_all_related_objects_with_model' for model, expected in TEST_RESULTS[result_key].items(): objects = [ (field, self._model(model, field)) for field in model._meta.get_fields() if field.auto_created and not field.concrete ] self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_local(self): result_key = 'get_all_related_objects_with_model_local' for model, expected in TEST_RESULTS[result_key].items(): objects = [ (field, self._model(model, field)) for field in model._meta.get_fields(include_parents=False) if field.auto_created and not field.concrete ] self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_include_hidden(self): result_key = 'get_all_related_objects_with_model_hidden' for model, expected in TEST_RESULTS[result_key].items(): objects = [ (field, self._model(model, field)) for field in model._meta.get_fields(include_hidden=True) if field.auto_created and not field.concrete ] self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_include_hidden_local_only(self): result_key = 'get_all_related_objects_with_model_hidden_local' for model, expected in TEST_RESULTS[result_key].items(): objects = [ (field, self._model(model, field)) for field in model._meta.get_fields(include_hidden=True, include_parents=False) if field.auto_created and not field.concrete ] self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) class VirtualFieldsTests(OptionsBaseTests): def test_virtual_fields(self): for model, expected_names in TEST_RESULTS['virtual_fields'].items(): objects = model._meta.virtual_fields self.assertEqual(sorted([f.name for f in objects]), sorted(expected_names)) class GetFieldByNameTests(OptionsBaseTests): def test_get_data_field(self): field_info = self._details(Person, Person._meta.get_field('data_abstract')) self.assertEqual(field_info[1:], (BasePerson, True, False)) self.assertIsInstance(field_info[0], CharField) def test_get_m2m_field(self): field_info = self._details(Person, Person._meta.get_field('m2m_base')) self.assertEqual(field_info[1:], (BasePerson, True, True)) self.assertIsInstance(field_info[0], related.ManyToManyField) def test_get_related_object(self): field_info = self._details(Person, Person._meta.get_field('relating_baseperson')) self.assertEqual(field_info[1:], (BasePerson, False, False)) self.assertIsInstance(field_info[0], related.ForeignObjectRel) def test_get_related_m2m(self): field_info = self._details(Person, Person._meta.get_field('relating_people')) self.assertEqual(field_info[1:], (None, False, True)) self.assertIsInstance(field_info[0], related.ForeignObjectRel) def test_get_generic_relation(self): field_info = self._details(Person, Person._meta.get_field('generic_relation_base')) self.assertEqual(field_info[1:], (None, True, False)) self.assertIsInstance(field_info[0], GenericRelation) def test_get_fields_only_searches_forward_on_apps_not_ready(self): opts = Person._meta # If apps registry is not ready, get_field() searches over only # forward fields. opts.apps.models_ready = False try: # 'data_abstract' is a forward field, and therefore will be found self.assertTrue(opts.get_field('data_abstract')) msg = ( "Person has no field named 'relating_baseperson'. The app " "cache isn't ready yet, so if this is an auto-created related " "field, it won't be available yet." ) # 'data_abstract' is a reverse field, and will raise an exception with self.assertRaisesMessage(FieldDoesNotExist, msg): opts.get_field('relating_baseperson') finally: opts.apps.models_ready = True class RelationTreeTests(TestCase): all_models = (Relation, AbstractPerson, BasePerson, Person, ProxyPerson, Relating) def setUp(self): apps.clear_cache() def test_clear_cache_clears_relation_tree(self): # The apps.clear_cache is setUp() should have deleted all trees. # Exclude abstract models that are not included in the Apps registry # and have no cache. all_models_with_cache = (m for m in self.all_models if not m._meta.abstract) for m in all_models_with_cache: self.assertNotIn('_relation_tree', m._meta.__dict__) def test_first_relation_tree_access_populates_all(self): # On first access, relation tree should have populated cache. self.assertTrue(self.all_models[0]._meta._relation_tree) # AbstractPerson does not have any relations, so relation_tree # should just return an EMPTY_RELATION_TREE. self.assertEqual(AbstractPerson._meta._relation_tree, EMPTY_RELATION_TREE) # All the other models should already have their relation tree # in the internal __dict__ . all_models_but_abstractperson = (m for m in self.all_models if m is not AbstractPerson) for m in all_models_but_abstractperson: self.assertIn('_relation_tree', m._meta.__dict__) def test_relations_related_objects(self): # Testing non hidden related objects self.assertEqual( sorted([field.related_query_name() for field in Relation._meta._relation_tree if not field.rel.field.rel.is_hidden()]), sorted([ 'fk_abstract_rel', 'fk_base_rel', 'fk_concrete_rel', 'fo_abstract_rel', 'fo_base_rel', 'fo_concrete_rel', 'm2m_abstract_rel', 'm2m_base_rel', 'm2m_concrete_rel' ]) ) # Testing hidden related objects self.assertEqual( sorted([field.related_query_name() for field in BasePerson._meta._relation_tree]), sorted([ '+', '_relating_basepeople_hidden_+', 'BasePerson_following_abstract+', 'BasePerson_following_abstract+', 'BasePerson_following_base+', 'BasePerson_following_base+', 'BasePerson_friends_abstract+', 'BasePerson_friends_abstract+', 'BasePerson_friends_base+', 'BasePerson_friends_base+', 'BasePerson_m2m_abstract+', 'BasePerson_m2m_base+', 'Relating_basepeople+', 'Relating_basepeople_hidden+', 'followers_abstract', 'followers_base', 'friends_abstract_rel_+', 'friends_base_rel_+', 'person', 'relating_basepeople', 'relating_baseperson', ]) ) self.assertEqual([field.related_query_name() for field in AbstractPerson._meta._relation_tree], []) class ParentListTests(TestCase): def test_get_parent_list(self): self.assertEqual(CommonAncestor._meta.get_parent_list(), []) self.assertEqual(FirstParent._meta.get_parent_list(), [CommonAncestor]) self.assertEqual(SecondParent._meta.get_parent_list(), [CommonAncestor]) self.assertEqual(Child._meta.get_parent_list(), [FirstParent, SecondParent, CommonAncestor]) Django-1.8.7/tests/model_meta/__init__.py0000664000175000017500000000000012625116214017706 0ustar timtim00000000000000Django-1.8.7/tests/model_meta/models.py0000664000175000017500000001114712625116214017450 0ustar timtim00000000000000from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.contrib.contenttypes.models import ContentType from django.db import models class Relation(models.Model): pass class AbstractPerson(models.Model): # DATA fields data_abstract = models.CharField(max_length=10) fk_abstract = models.ForeignKey(Relation, related_name='fk_abstract_rel') # M2M fields m2m_abstract = models.ManyToManyField(Relation, related_name='m2m_abstract_rel') friends_abstract = models.ManyToManyField('self', related_name='friends_abstract', symmetrical=True) following_abstract = models.ManyToManyField('self', related_name='followers_abstract', symmetrical=False) # VIRTUAL fields data_not_concrete_abstract = models.ForeignObject( Relation, from_fields=['abstract_non_concrete_id'], to_fields=['id'], related_name='fo_abstract_rel', ) # GFK fields content_type_abstract = models.ForeignKey(ContentType, related_name='+') object_id_abstract = models.PositiveIntegerField() content_object_abstract = GenericForeignKey('content_type_abstract', 'object_id_abstract') # GR fields generic_relation_abstract = GenericRelation(Relation) class Meta: abstract = True class BasePerson(AbstractPerson): # DATA fields data_base = models.CharField(max_length=10) fk_base = models.ForeignKey(Relation, related_name='fk_base_rel') # M2M fields m2m_base = models.ManyToManyField(Relation, related_name='m2m_base_rel') friends_base = models.ManyToManyField('self', related_name='friends_base', symmetrical=True) following_base = models.ManyToManyField('self', related_name='followers_base', symmetrical=False) # VIRTUAL fields data_not_concrete_base = models.ForeignObject( Relation, from_fields=['base_non_concrete_id'], to_fields=['id'], related_name='fo_base_rel', ) # GFK fields content_type_base = models.ForeignKey(ContentType, related_name='+') object_id_base = models.PositiveIntegerField() content_object_base = GenericForeignKey('content_type_base', 'object_id_base') # GR fields generic_relation_base = GenericRelation(Relation) class Person(BasePerson): # DATA fields data_inherited = models.CharField(max_length=10) fk_inherited = models.ForeignKey(Relation, related_name='fk_concrete_rel') # M2M Fields m2m_inherited = models.ManyToManyField(Relation, related_name='m2m_concrete_rel') friends_inherited = models.ManyToManyField('self', related_name='friends_concrete', symmetrical=True) following_inherited = models.ManyToManyField('self', related_name='followers_concrete', symmetrical=False) # VIRTUAL fields data_not_concrete_inherited = models.ForeignObject( Relation, from_fields=['model_non_concrete_id'], to_fields=['id'], related_name='fo_concrete_rel', ) # GFK fields content_type_concrete = models.ForeignKey(ContentType, related_name='+') object_id_concrete = models.PositiveIntegerField() content_object_concrete = GenericForeignKey('content_type_concrete', 'object_id_concrete') # GR fields generic_relation_concrete = GenericRelation(Relation) class ProxyPerson(Person): class Meta: proxy = True class Relating(models.Model): # ForeignKey to BasePerson baseperson = models.ForeignKey(BasePerson, related_name='relating_baseperson') baseperson_hidden = models.ForeignKey(BasePerson, related_name='+') # ForeignKey to Person person = models.ForeignKey(Person, related_name='relating_person') person_hidden = models.ForeignKey(Person, related_name='+') # ForeignKey to ProxyPerson proxyperson = models.ForeignKey(ProxyPerson, related_name='relating_proxyperson') proxyperson_hidden = models.ForeignKey(ProxyPerson, related_name='+') # ManyToManyField to BasePerson basepeople = models.ManyToManyField(BasePerson, related_name='relating_basepeople') basepeople_hidden = models.ManyToManyField(BasePerson, related_name='+') # ManyToManyField to Person people = models.ManyToManyField(Person, related_name='relating_people') people_hidden = models.ManyToManyField(Person, related_name='+') # ParentListTests models class CommonAncestor(models.Model): pass class FirstParent(CommonAncestor): first_ancestor = models.OneToOneField(CommonAncestor, primary_key=True, parent_link=True) class SecondParent(CommonAncestor): second_ancestor = models.OneToOneField(CommonAncestor, primary_key=True, parent_link=True) class Child(FirstParent, SecondParent): pass Django-1.8.7/tests/model_meta/results.py0000664000175000017500000006404112625116214017667 0ustar timtim00000000000000from .models import AbstractPerson, BasePerson, Person, Relating, Relation TEST_RESULTS = { 'get_all_field_names': { Person: [ 'baseperson_ptr', 'baseperson_ptr_id', 'content_type_abstract', 'content_type_abstract_id', 'content_type_base', 'content_type_base_id', 'content_type_concrete', 'content_type_concrete_id', 'data_abstract', 'data_base', 'data_inherited', 'data_not_concrete_abstract', 'data_not_concrete_base', 'data_not_concrete_inherited', 'fk_abstract', 'fk_abstract_id', 'fk_base', 'fk_base_id', 'fk_inherited', 'fk_inherited_id', 'followers_abstract', 'followers_base', 'followers_concrete', 'following_abstract', 'following_base', 'following_inherited', 'friends_abstract', 'friends_base', 'friends_inherited', 'generic_relation_abstract', 'generic_relation_base', 'generic_relation_concrete', 'id', 'm2m_abstract', 'm2m_base', 'm2m_inherited', 'object_id_abstract', 'object_id_base', 'object_id_concrete', 'relating_basepeople', 'relating_baseperson', 'relating_people', 'relating_person', ], BasePerson: [ 'content_type_abstract', 'content_type_abstract_id', 'content_type_base', 'content_type_base_id', 'data_abstract', 'data_base', 'data_not_concrete_abstract', 'data_not_concrete_base', 'fk_abstract', 'fk_abstract_id', 'fk_base', 'fk_base_id', 'followers_abstract', 'followers_base', 'following_abstract', 'following_base', 'friends_abstract', 'friends_base', 'generic_relation_abstract', 'generic_relation_base', 'id', 'm2m_abstract', 'm2m_base', 'object_id_abstract', 'object_id_base', 'person', 'relating_basepeople', 'relating_baseperson' ], AbstractPerson: [ 'content_type_abstract', 'content_type_abstract_id', 'data_abstract', 'data_not_concrete_abstract', 'fk_abstract', 'fk_abstract_id', 'following_abstract', 'friends_abstract', 'generic_relation_abstract', 'm2m_abstract', 'object_id_abstract', ], Relating: [ 'basepeople', 'basepeople_hidden', 'baseperson', 'baseperson_hidden', 'baseperson_hidden_id', 'baseperson_id', 'id', 'people', 'people_hidden', 'person', 'person_hidden', 'person_hidden_id', 'person_id', 'proxyperson', 'proxyperson_hidden', 'proxyperson_hidden_id', 'proxyperson_id', ], }, 'fields': { Person: [ 'id', 'data_abstract', 'fk_abstract_id', 'data_not_concrete_abstract', 'content_type_abstract_id', 'object_id_abstract', 'data_base', 'fk_base_id', 'data_not_concrete_base', 'content_type_base_id', 'object_id_base', 'baseperson_ptr_id', 'data_inherited', 'fk_inherited_id', 'data_not_concrete_inherited', 'content_type_concrete_id', 'object_id_concrete', ], BasePerson: [ 'id', 'data_abstract', 'fk_abstract_id', 'data_not_concrete_abstract', 'content_type_abstract_id', 'object_id_abstract', 'data_base', 'fk_base_id', 'data_not_concrete_base', 'content_type_base_id', 'object_id_base', ], AbstractPerson: [ 'data_abstract', 'fk_abstract_id', 'data_not_concrete_abstract', 'content_type_abstract_id', 'object_id_abstract', ], Relating: [ 'id', 'baseperson_id', 'baseperson_hidden_id', 'person_id', 'person_hidden_id', 'proxyperson_id', 'proxyperson_hidden_id', ], }, 'local_fields': { Person: [ 'baseperson_ptr_id', 'data_inherited', 'fk_inherited_id', 'data_not_concrete_inherited', 'content_type_concrete_id', 'object_id_concrete', ], BasePerson: [ 'id', 'data_abstract', 'fk_abstract_id', 'data_not_concrete_abstract', 'content_type_abstract_id', 'object_id_abstract', 'data_base', 'fk_base_id', 'data_not_concrete_base', 'content_type_base_id', 'object_id_base', ], AbstractPerson: [ 'data_abstract', 'fk_abstract_id', 'data_not_concrete_abstract', 'content_type_abstract_id', 'object_id_abstract', ], Relating: [ 'id', 'baseperson_id', 'baseperson_hidden_id', 'person_id', 'person_hidden_id', 'proxyperson_id', 'proxyperson_hidden_id', ], }, 'local_concrete_fields': { Person: [ 'baseperson_ptr_id', 'data_inherited', 'fk_inherited_id', 'content_type_concrete_id', 'object_id_concrete', ], BasePerson: [ 'id', 'data_abstract', 'fk_abstract_id', 'content_type_abstract_id', 'object_id_abstract', 'data_base', 'fk_base_id', 'content_type_base_id', 'object_id_base', ], AbstractPerson: [ 'data_abstract', 'fk_abstract_id', 'content_type_abstract_id', 'object_id_abstract', ], Relating: [ 'id', 'baseperson_id', 'baseperson_hidden_id', 'person_id', 'person_hidden_id', 'proxyperson_id', 'proxyperson_hidden_id', ], }, 'many_to_many': { Person: [ 'm2m_abstract', 'friends_abstract', 'following_abstract', 'm2m_base', 'friends_base', 'following_base', 'm2m_inherited', 'friends_inherited', 'following_inherited', ], BasePerson: [ 'm2m_abstract', 'friends_abstract', 'following_abstract', 'm2m_base', 'friends_base', 'following_base', ], AbstractPerson: [ 'm2m_abstract', 'friends_abstract', 'following_abstract', ], Relating: [ 'basepeople', 'basepeople_hidden', 'people', 'people_hidden', ], }, 'many_to_many_with_model': { Person: [ BasePerson, BasePerson, BasePerson, BasePerson, BasePerson, BasePerson, None, None, None, ], BasePerson: [ None, None, None, None, None, None, ], AbstractPerson: [ None, None, None, ], Relating: [ None, None, None, None, ], }, 'get_all_related_objects_with_model_legacy': { Person: ( ('relating_baseperson', BasePerson), ('relating_person', None), ), BasePerson: ( ('person', None), ('relating_baseperson', None), ), Relation: ( ('fk_abstract_rel', None), ('fo_abstract_rel', None), ('fk_base_rel', None), ('fo_base_rel', None), ('fk_concrete_rel', None), ('fo_concrete_rel', None), ), }, 'get_all_related_objects_with_model_hidden_local': { Person: ( ('+', None), ('_relating_people_hidden_+', None), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), ('Person_friends_inherited+', None), ('Person_m2m_inherited+', None), ('Relating_people+', None), ('Relating_people_hidden+', None), ('followers_concrete', None), ('friends_inherited_rel_+', None), ('relating_people', None), ('relating_person', None), ), BasePerson: ( ('+', None), ('_relating_basepeople_hidden_+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), ('BasePerson_following_base+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_base+', None), ('BasePerson_friends_base+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Relating_basepeople+', None), ('Relating_basepeople_hidden+', None), ('followers_abstract', None), ('followers_base', None), ('friends_abstract_rel_+', None), ('friends_base_rel_+', None), ('person', None), ('relating_basepeople', None), ('relating_baseperson', None), ), Relation: ( ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Person_m2m_inherited+', None), ('fk_abstract_rel', None), ('fk_base_rel', None), ('fk_concrete_rel', None), ('fo_abstract_rel', None), ('fo_base_rel', None), ('fo_concrete_rel', None), ('m2m_abstract_rel', None), ('m2m_base_rel', None), ('m2m_concrete_rel', None), ), }, 'get_all_related_objects_with_model_hidden': { Person: ( ('+', BasePerson), ('+', None), ('_relating_basepeople_hidden_+', BasePerson), ('_relating_people_hidden_+', None), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_base+', BasePerson), ('BasePerson_following_base+', BasePerson), ('BasePerson_friends_abstract+', BasePerson), ('BasePerson_friends_abstract+', BasePerson), ('BasePerson_friends_base+', BasePerson), ('BasePerson_friends_base+', BasePerson), ('BasePerson_m2m_abstract+', BasePerson), ('BasePerson_m2m_base+', BasePerson), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), ('Person_friends_inherited+', None), ('Person_m2m_inherited+', None), ('Relating_basepeople+', BasePerson), ('Relating_basepeople_hidden+', BasePerson), ('Relating_people+', None), ('Relating_people_hidden+', None), ('followers_abstract', BasePerson), ('followers_base', BasePerson), ('followers_concrete', None), ('friends_abstract_rel_+', BasePerson), ('friends_base_rel_+', BasePerson), ('friends_inherited_rel_+', None), ('relating_basepeople', BasePerson), ('relating_baseperson', BasePerson), ('relating_people', None), ('relating_person', None), ), BasePerson: ( ('+', None), ('_relating_basepeople_hidden_+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), ('BasePerson_following_base+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_base+', None), ('BasePerson_friends_base+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Relating_basepeople+', None), ('Relating_basepeople_hidden+', None), ('followers_abstract', None), ('followers_base', None), ('friends_abstract_rel_+', None), ('friends_base_rel_+', None), ('person', None), ('relating_basepeople', None), ('relating_baseperson', None), ), Relation: ( ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Person_m2m_inherited+', None), ('fk_abstract_rel', None), ('fk_base_rel', None), ('fk_concrete_rel', None), ('fo_abstract_rel', None), ('fo_base_rel', None), ('fo_concrete_rel', None), ('m2m_abstract_rel', None), ('m2m_base_rel', None), ('m2m_concrete_rel', None), ), }, 'get_all_related_objects_with_model_local': { Person: ( ('followers_concrete', None), ('relating_person', None), ('relating_people', None), ), BasePerson: ( ('followers_abstract', None), ('followers_base', None), ('person', None), ('relating_baseperson', None), ('relating_basepeople', None), ), Relation: ( ('fk_abstract_rel', None), ('fo_abstract_rel', None), ('fk_base_rel', None), ('fo_base_rel', None), ('m2m_abstract_rel', None), ('m2m_base_rel', None), ('fk_concrete_rel', None), ('fo_concrete_rel', None), ('m2m_concrete_rel', None), ), }, 'get_all_related_objects_with_model': { Person: ( ('followers_abstract', BasePerson), ('followers_base', BasePerson), ('relating_baseperson', BasePerson), ('relating_basepeople', BasePerson), ('followers_concrete', None), ('relating_person', None), ('relating_people', None), ), BasePerson: ( ('followers_abstract', None), ('followers_base', None), ('person', None), ('relating_baseperson', None), ('relating_basepeople', None), ), Relation: ( ('fk_abstract_rel', None), ('fo_abstract_rel', None), ('fk_base_rel', None), ('fo_base_rel', None), ('m2m_abstract_rel', None), ('m2m_base_rel', None), ('fk_concrete_rel', None), ('fo_concrete_rel', None), ('m2m_concrete_rel', None), ), }, 'get_all_related_objects_with_model_local_legacy': { Person: ( ('relating_person', None), ), BasePerson: ( ('person', None), ('relating_baseperson', None) ), Relation: ( ('fk_abstract_rel', None), ('fo_abstract_rel', None), ('fk_base_rel', None), ('fo_base_rel', None), ('fk_concrete_rel', None), ('fo_concrete_rel', None), ), }, 'get_all_related_objects_with_model_hidden_legacy': { BasePerson: ( ('+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), ('BasePerson_following_base+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_base+', None), ('BasePerson_friends_base+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Relating_basepeople+', None), ('Relating_basepeople_hidden+', None), ('person', None), ('relating_baseperson', None), ), Person: ( ('+', BasePerson), ('+', None), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_base+', BasePerson), ('BasePerson_following_base+', BasePerson), ('BasePerson_friends_abstract+', BasePerson), ('BasePerson_friends_abstract+', BasePerson), ('BasePerson_friends_base+', BasePerson), ('BasePerson_friends_base+', BasePerson), ('BasePerson_m2m_abstract+', BasePerson), ('BasePerson_m2m_base+', BasePerson), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), ('Person_friends_inherited+', None), ('Person_m2m_inherited+', None), ('Relating_basepeople+', BasePerson), ('Relating_basepeople_hidden+', BasePerson), ('Relating_people+', None), ('Relating_people_hidden+', None), ('relating_baseperson', BasePerson), ('relating_person', None), ), Relation: ( ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Person_m2m_inherited+', None), ('fk_abstract_rel', None), ('fk_base_rel', None), ('fk_concrete_rel', None), ('fo_abstract_rel', None), ('fo_base_rel', None), ('fo_concrete_rel', None), ), }, 'get_all_related_objects_with_model_hidden_local_legacy': { BasePerson: ( ('+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), ('BasePerson_following_base+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_base+', None), ('BasePerson_friends_base+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Relating_basepeople+', None), ('Relating_basepeople_hidden+', None), ('person', None), ('relating_baseperson', None), ), Person: ( ('+', None), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), ('Person_friends_inherited+', None), ('Person_m2m_inherited+', None), ('Relating_people+', None), ('Relating_people_hidden+', None), ('relating_person', None), ), Relation: ( ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Person_m2m_inherited+', None), ('fk_abstract_rel', None), ('fk_base_rel', None), ('fk_concrete_rel', None), ('fo_abstract_rel', None), ('fo_base_rel', None), ('fo_concrete_rel', None), ), }, 'get_all_related_objects_with_model_proxy_legacy': { BasePerson: ( ('person', None), ('relating_baseperson', None), ), Person: ( ('relating_baseperson', BasePerson), ('relating_person', None), ('relating_proxyperson', None), ), Relation: ( ('fk_abstract_rel', None), ('fo_abstract_rel', None), ('fk_base_rel', None), ('fo_base_rel', None), ('fk_concrete_rel', None), ('fo_concrete_rel', None), ), }, 'get_all_related_objects_with_model_proxy_hidden_legacy': { BasePerson: ( ('+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_abstract+', None), ('BasePerson_following_base+', None), ('BasePerson_following_base+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_abstract+', None), ('BasePerson_friends_base+', None), ('BasePerson_friends_base+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Relating_basepeople+', None), ('Relating_basepeople_hidden+', None), ('person', None), ('relating_baseperson', None), ), Person: ( ('+', BasePerson), ('+', None), ('+', None), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_abstract+', BasePerson), ('BasePerson_following_base+', BasePerson), ('BasePerson_following_base+', BasePerson), ('BasePerson_friends_abstract+', BasePerson), ('BasePerson_friends_abstract+', BasePerson), ('BasePerson_friends_base+', BasePerson), ('BasePerson_friends_base+', BasePerson), ('BasePerson_m2m_abstract+', BasePerson), ('BasePerson_m2m_base+', BasePerson), ('Person_following_inherited+', None), ('Person_following_inherited+', None), ('Person_friends_inherited+', None), ('Person_friends_inherited+', None), ('Person_m2m_inherited+', None), ('Relating_basepeople+', BasePerson), ('Relating_basepeople_hidden+', BasePerson), ('Relating_people+', None), ('Relating_people_hidden+', None), ('relating_baseperson', BasePerson), ('relating_person', None), ('relating_proxyperson', None), ), Relation: ( ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('+', None), ('BasePerson_m2m_abstract+', None), ('BasePerson_m2m_base+', None), ('Person_m2m_inherited+', None), ('fk_abstract_rel', None), ('fk_base_rel', None), ('fk_concrete_rel', None), ('fo_abstract_rel', None), ('fo_base_rel', None), ('fo_concrete_rel', None), ), }, 'get_all_related_many_to_many_with_model_legacy': { BasePerson: ( ('friends_abstract_rel_+', None), ('followers_abstract', None), ('friends_base_rel_+', None), ('followers_base', None), ('relating_basepeople', None), ('_relating_basepeople_hidden_+', None), ), Person: ( ('friends_abstract_rel_+', BasePerson), ('followers_abstract', BasePerson), ('friends_base_rel_+', BasePerson), ('followers_base', BasePerson), ('relating_basepeople', BasePerson), ('_relating_basepeople_hidden_+', BasePerson), ('friends_inherited_rel_+', None), ('followers_concrete', None), ('relating_people', None), ('_relating_people_hidden_+', None), ), Relation: ( ('m2m_abstract_rel', None), ('m2m_base_rel', None), ('m2m_concrete_rel', None), ), }, 'get_all_related_many_to_many_local_legacy': { BasePerson: [ 'friends_abstract_rel_+', 'followers_abstract', 'friends_base_rel_+', 'followers_base', 'relating_basepeople', '_relating_basepeople_hidden_+', ], Person: [ 'friends_inherited_rel_+', 'followers_concrete', 'relating_people', '_relating_people_hidden_+', ], Relation: [ 'm2m_abstract_rel', 'm2m_base_rel', 'm2m_concrete_rel', ], }, 'virtual_fields': { AbstractPerson: [ 'generic_relation_abstract', 'content_object_abstract', ], BasePerson: [ 'generic_relation_base', 'content_object_base', 'generic_relation_abstract', 'content_object_abstract', ], Person: [ 'content_object_concrete', 'generic_relation_concrete', 'generic_relation_base', 'content_object_base', 'generic_relation_abstract', 'content_object_abstract', ], }, } Django-1.8.7/tests/model_meta/test_legacy.py0000664000175000017500000001657612625116214020503 0ustar timtim00000000000000import warnings from django import test from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import FieldDoesNotExist from django.db.models.fields import CharField, related from django.utils.deprecation import RemovedInDjango110Warning from .models import BasePerson, Person from .results import TEST_RESULTS class OptionsBaseTests(test.TestCase): def _map_related_query_names(self, res): return tuple((o.field.related_query_name(), m) for o, m in res) def _map_names(self, res): return tuple((f.name, m) for f, m in res) class M2MTests(OptionsBaseTests): def test_many_to_many_with_model(self): for model, expected_result in TEST_RESULTS['many_to_many_with_model'].items(): with warnings.catch_warnings(record=True) as warning: warnings.simplefilter("always") models = [model for field, model in model._meta.get_m2m_with_model()] self.assertEqual([RemovedInDjango110Warning], [w.message.__class__ for w in warning]) self.assertEqual(models, expected_result) @test.ignore_warnings(category=RemovedInDjango110Warning) class RelatedObjectsTests(OptionsBaseTests): key_name = lambda self, r: r[0] def test_related_objects(self): result_key = 'get_all_related_objects_with_model_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model() self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_local(self): result_key = 'get_all_related_objects_with_model_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model(local_only=True) self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_include_hidden(self): result_key = 'get_all_related_objects_with_model_hidden_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model(include_hidden=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_include_hidden_local_only(self): result_key = 'get_all_related_objects_with_model_hidden_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_hidden=True, local_only=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) def test_related_objects_proxy(self): result_key = 'get_all_related_objects_with_model_proxy_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_proxy_eq=True) self.assertEqual(self._map_related_query_names(objects), expected) def test_related_objects_proxy_hidden(self): result_key = 'get_all_related_objects_with_model_proxy_hidden_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_objects_with_model( include_proxy_eq=True, include_hidden=True) self.assertEqual( sorted(self._map_names(objects), key=self.key_name), sorted(expected, key=self.key_name) ) @test.ignore_warnings(category=RemovedInDjango110Warning) class RelatedM2MTests(OptionsBaseTests): def test_related_m2m_with_model(self): result_key = 'get_all_related_many_to_many_with_model_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_m2m_objects_with_model() self.assertEqual(self._map_related_query_names(objects), expected) def test_related_m2m_local_only(self): result_key = 'get_all_related_many_to_many_local_legacy' for model, expected in TEST_RESULTS[result_key].items(): objects = model._meta.get_all_related_many_to_many_objects(local_only=True) self.assertEqual([o.field.related_query_name() for o in objects], expected) def test_related_m2m_asymmetrical(self): m2m = Person._meta.many_to_many self.assertTrue('following_base' in [f.attname for f in m2m]) related_m2m = Person._meta.get_all_related_many_to_many_objects() self.assertTrue('followers_base' in [o.field.related_query_name() for o in related_m2m]) def test_related_m2m_symmetrical(self): m2m = Person._meta.many_to_many self.assertTrue('friends_base' in [f.attname for f in m2m]) related_m2m = Person._meta.get_all_related_many_to_many_objects() self.assertIn('friends_inherited_rel_+', [o.field.related_query_name() for o in related_m2m]) @test.ignore_warnings(category=RemovedInDjango110Warning) class GetFieldByNameTests(OptionsBaseTests): def test_get_data_field(self): field_info = Person._meta.get_field_by_name('data_abstract') self.assertEqual(field_info[1:], (BasePerson, True, False)) self.assertIsInstance(field_info[0], CharField) def test_get_m2m_field(self): field_info = Person._meta.get_field_by_name('m2m_base') self.assertEqual(field_info[1:], (BasePerson, True, True)) self.assertIsInstance(field_info[0], related.ManyToManyField) def test_get_related_object(self): field_info = Person._meta.get_field_by_name('relating_baseperson') self.assertEqual(field_info[1:], (BasePerson, False, False)) self.assertTrue(field_info[0].auto_created) def test_get_related_m2m(self): field_info = Person._meta.get_field_by_name('relating_people') self.assertEqual(field_info[1:], (None, False, True)) self.assertTrue(field_info[0].auto_created) def test_get_generic_relation(self): field_info = Person._meta.get_field_by_name('generic_relation_base') self.assertEqual(field_info[1:], (None, True, False)) self.assertIsInstance(field_info[0], GenericRelation) def test_get_m2m_field_invalid(self): with warnings.catch_warnings(record=True) as warning: warnings.simplefilter("always") self.assertRaises( FieldDoesNotExist, Person._meta.get_field, **{'field_name': 'm2m_base', 'many_to_many': False} ) self.assertEqual(Person._meta.get_field('m2m_base', many_to_many=True).name, 'm2m_base') # 2 RemovedInDjango110Warning messages should be raised, one for each call of get_field() # with the 'many_to_many' argument. self.assertEqual( [RemovedInDjango110Warning, RemovedInDjango110Warning], [w.message.__class__ for w in warning] ) @test.ignore_warnings(category=RemovedInDjango110Warning) class GetAllFieldNamesTestCase(OptionsBaseTests): def test_get_all_field_names(self): for model, expected_names in TEST_RESULTS['get_all_field_names'].items(): objects = model._meta.get_all_field_names() self.assertEqual(sorted(map(str, objects)), sorted(expected_names)) Django-1.8.7/tests/sessions_tests/0000775000175000017500000000000012625116243016573 5ustar timtim00000000000000Django-1.8.7/tests/sessions_tests/tests.py0000664000175000017500000006462112625116214020316 0ustar timtim00000000000000import base64 import os import shutil import string import sys import tempfile import unittest from datetime import timedelta from django.conf import settings from django.contrib.sessions.backends.cache import SessionStore as CacheSession from django.contrib.sessions.backends.cached_db import \ SessionStore as CacheDBSession from django.contrib.sessions.backends.db import SessionStore as DatabaseSession from django.contrib.sessions.backends.file import SessionStore as FileSession from django.contrib.sessions.backends.signed_cookies import \ SessionStore as CookieSession from django.contrib.sessions.exceptions import InvalidSessionKey from django.contrib.sessions.middleware import SessionMiddleware from django.contrib.sessions.models import Session from django.core import management from django.core.cache import caches from django.core.cache.backends.base import InvalidCacheBackendError from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponse from django.test import ( RequestFactory, TestCase, ignore_warnings, override_settings, ) from django.test.utils import patch_logger from django.utils import six, timezone from django.utils.encoding import force_text from django.utils.six.moves import http_cookies class SessionTestsMixin(object): # This does not inherit from TestCase to avoid any tests being run with this # class, which wouldn't work, and to allow different TestCase subclasses to # be used. backend = None # subclasses must specify def setUp(self): self.session = self.backend() def tearDown(self): # NB: be careful to delete any sessions created; stale sessions fill up # the /tmp (with some backends) and eventually overwhelm it after lots # of runs (think buildbots) self.session.delete() def test_new_session(self): self.assertFalse(self.session.modified) self.assertFalse(self.session.accessed) def test_get_empty(self): self.assertEqual(self.session.get('cat'), None) def test_store(self): self.session['cat'] = "dog" self.assertTrue(self.session.modified) self.assertEqual(self.session.pop('cat'), 'dog') def test_pop(self): self.session['some key'] = 'exists' # Need to reset these to pretend we haven't accessed it: self.accessed = False self.modified = False self.assertEqual(self.session.pop('some key'), 'exists') self.assertTrue(self.session.accessed) self.assertTrue(self.session.modified) self.assertEqual(self.session.get('some key'), None) def test_pop_default(self): self.assertEqual(self.session.pop('some key', 'does not exist'), 'does not exist') self.assertTrue(self.session.accessed) self.assertFalse(self.session.modified) def test_setdefault(self): self.assertEqual(self.session.setdefault('foo', 'bar'), 'bar') self.assertEqual(self.session.setdefault('foo', 'baz'), 'bar') self.assertTrue(self.session.accessed) self.assertTrue(self.session.modified) def test_update(self): self.session.update({'update key': 1}) self.assertTrue(self.session.accessed) self.assertTrue(self.session.modified) self.assertEqual(self.session.get('update key', None), 1) def test_has_key(self): self.session['some key'] = 1 self.session.modified = False self.session.accessed = False self.assertIn('some key', self.session) self.assertTrue(self.session.accessed) self.assertFalse(self.session.modified) def test_values(self): self.assertEqual(list(self.session.values()), []) self.assertTrue(self.session.accessed) self.session['some key'] = 1 self.assertEqual(list(self.session.values()), [1]) def test_iterkeys(self): self.session['x'] = 1 self.session.modified = False self.session.accessed = False i = six.iterkeys(self.session) self.assertTrue(hasattr(i, '__iter__')) self.assertTrue(self.session.accessed) self.assertFalse(self.session.modified) self.assertEqual(list(i), ['x']) def test_itervalues(self): self.session['x'] = 1 self.session.modified = False self.session.accessed = False i = six.itervalues(self.session) self.assertTrue(hasattr(i, '__iter__')) self.assertTrue(self.session.accessed) self.assertFalse(self.session.modified) self.assertEqual(list(i), [1]) def test_iteritems(self): self.session['x'] = 1 self.session.modified = False self.session.accessed = False i = six.iteritems(self.session) self.assertTrue(hasattr(i, '__iter__')) self.assertTrue(self.session.accessed) self.assertFalse(self.session.modified) self.assertEqual(list(i), [('x', 1)]) def test_clear(self): self.session['x'] = 1 self.session.modified = False self.session.accessed = False self.assertEqual(list(self.session.items()), [('x', 1)]) self.session.clear() self.assertEqual(list(self.session.items()), []) self.assertTrue(self.session.accessed) self.assertTrue(self.session.modified) def test_save(self): if (hasattr(self.session, '_cache') and 'DummyCache' in settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND']): raise unittest.SkipTest("Session saving tests require a real cache backend") self.session.save() self.assertTrue(self.session.exists(self.session.session_key)) def test_delete(self): self.session.save() self.session.delete(self.session.session_key) self.assertFalse(self.session.exists(self.session.session_key)) def test_flush(self): self.session['foo'] = 'bar' self.session.save() prev_key = self.session.session_key self.session.flush() self.assertFalse(self.session.exists(prev_key)) self.assertNotEqual(self.session.session_key, prev_key) self.assertIsNone(self.session.session_key) self.assertTrue(self.session.modified) self.assertTrue(self.session.accessed) def test_cycle(self): self.session['a'], self.session['b'] = 'c', 'd' self.session.save() prev_key = self.session.session_key prev_data = list(self.session.items()) self.session.cycle_key() self.assertNotEqual(self.session.session_key, prev_key) self.assertEqual(list(self.session.items()), prev_data) def test_save_doesnt_clear_data(self): self.session['a'] = 'b' self.session.save() self.assertEqual(self.session['a'], 'b') def test_invalid_key(self): # Submitting an invalid session key (either by guessing, or if the db has # removed the key) results in a new key being generated. try: session = self.backend('1') try: session.save() except AttributeError: self.fail( "The session object did not save properly. " "Middleware may be saving cache items without namespaces." ) self.assertNotEqual(session.session_key, '1') self.assertEqual(session.get('cat'), None) session.delete() finally: # Some backends leave a stale cache entry for the invalid # session key; make sure that entry is manually deleted session.delete('1') def test_session_key_is_read_only(self): def set_session_key(session): session.session_key = session._get_new_session_key() self.assertRaises(AttributeError, set_session_key, self.session) # Custom session expiry def test_default_expiry(self): # A normal session has a max age equal to settings self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) # So does a custom session with an idle expiration time of 0 (but it'll # expire at browser close) self.session.set_expiry(0) self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) def test_custom_expiry_seconds(self): modification = timezone.now() self.session.set_expiry(10) date = self.session.get_expiry_date(modification=modification) self.assertEqual(date, modification + timedelta(seconds=10)) age = self.session.get_expiry_age(modification=modification) self.assertEqual(age, 10) def test_custom_expiry_timedelta(self): modification = timezone.now() # Mock timezone.now, because set_expiry calls it on this code path. original_now = timezone.now try: timezone.now = lambda: modification self.session.set_expiry(timedelta(seconds=10)) finally: timezone.now = original_now date = self.session.get_expiry_date(modification=modification) self.assertEqual(date, modification + timedelta(seconds=10)) age = self.session.get_expiry_age(modification=modification) self.assertEqual(age, 10) def test_custom_expiry_datetime(self): modification = timezone.now() self.session.set_expiry(modification + timedelta(seconds=10)) date = self.session.get_expiry_date(modification=modification) self.assertEqual(date, modification + timedelta(seconds=10)) age = self.session.get_expiry_age(modification=modification) self.assertEqual(age, 10) def test_custom_expiry_reset(self): self.session.set_expiry(None) self.session.set_expiry(10) self.session.set_expiry(None) self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE) def test_get_expire_at_browser_close(self): # Tests get_expire_at_browser_close with different settings and different # set_expiry calls with override_settings(SESSION_EXPIRE_AT_BROWSER_CLOSE=False): self.session.set_expiry(10) self.assertFalse(self.session.get_expire_at_browser_close()) self.session.set_expiry(0) self.assertTrue(self.session.get_expire_at_browser_close()) self.session.set_expiry(None) self.assertFalse(self.session.get_expire_at_browser_close()) with override_settings(SESSION_EXPIRE_AT_BROWSER_CLOSE=True): self.session.set_expiry(10) self.assertFalse(self.session.get_expire_at_browser_close()) self.session.set_expiry(0) self.assertTrue(self.session.get_expire_at_browser_close()) self.session.set_expiry(None) self.assertTrue(self.session.get_expire_at_browser_close()) def test_decode(self): # Ensure we can decode what we encode data = {'a test key': 'a test value'} encoded = self.session.encode(data) self.assertEqual(self.session.decode(encoded), data) def test_decode_failure_logged_to_security(self): bad_encode = base64.b64encode(b'flaskdj:alkdjf') with patch_logger('django.security.SuspiciousSession', 'warning') as calls: self.assertEqual({}, self.session.decode(bad_encode)) # check that the failed decode is logged self.assertEqual(len(calls), 1) self.assertIn('corrupted', calls[0]) def test_actual_expiry(self): # this doesn't work with JSONSerializer (serializing timedelta) with override_settings(SESSION_SERIALIZER='django.contrib.sessions.serializers.PickleSerializer'): self.session = self.backend() # reinitialize after overriding settings # Regression test for #19200 old_session_key = None new_session_key = None try: self.session['foo'] = 'bar' self.session.set_expiry(-timedelta(seconds=10)) self.session.save() old_session_key = self.session.session_key # With an expiry date in the past, the session expires instantly. new_session = self.backend(self.session.session_key) new_session_key = new_session.session_key self.assertNotIn('foo', new_session) finally: self.session.delete(old_session_key) self.session.delete(new_session_key) def test_session_load_does_not_create_record(self): """ Loading an unknown session key does not create a session record. Creating session records on load is a DOS vulnerability. """ if self.backend is CookieSession: raise unittest.SkipTest("Cookie backend doesn't have an external store to create records in.") session = self.backend('someunknownkey') session.load() self.assertFalse(session.exists(session.session_key)) # provided unknown key was cycled, not reused self.assertNotEqual(session.session_key, 'someunknownkey') class DatabaseSessionTests(SessionTestsMixin, TestCase): backend = DatabaseSession def test_session_str(self): "Session repr should be the session key." self.session['x'] = 1 self.session.save() session_key = self.session.session_key s = Session.objects.get(session_key=session_key) self.assertEqual(force_text(s), session_key) def test_session_get_decoded(self): """ Test we can use Session.get_decoded to retrieve data stored in normal way """ self.session['x'] = 1 self.session.save() s = Session.objects.get(session_key=self.session.session_key) self.assertEqual(s.get_decoded(), {'x': 1}) def test_sessionmanager_save(self): """ Test SessionManager.save method """ # Create a session self.session['y'] = 1 self.session.save() s = Session.objects.get(session_key=self.session.session_key) # Change it Session.objects.save(s.session_key, {'y': 2}, s.expire_date) # Clear cache, so that it will be retrieved from DB del self.session._session_cache self.assertEqual(self.session['y'], 2) @override_settings(SESSION_ENGINE="django.contrib.sessions.backends.db") def test_clearsessions_command(self): """ Test clearsessions command for clearing expired sessions. """ self.assertEqual(0, Session.objects.count()) # One object in the future self.session['foo'] = 'bar' self.session.set_expiry(3600) self.session.save() # One object in the past other_session = self.backend() other_session['foo'] = 'bar' other_session.set_expiry(-3600) other_session.save() # Two sessions are in the database before clearsessions... self.assertEqual(2, Session.objects.count()) management.call_command('clearsessions') # ... and one is deleted. self.assertEqual(1, Session.objects.count()) @override_settings(USE_TZ=True) class DatabaseSessionWithTimeZoneTests(DatabaseSessionTests): pass class CacheDBSessionTests(SessionTestsMixin, TestCase): backend = CacheDBSession @unittest.skipIf('DummyCache' in settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND'], "Session saving tests require a real cache backend") def test_exists_searches_cache_first(self): self.session.save() with self.assertNumQueries(0): self.assertTrue(self.session.exists(self.session.session_key)) # Some backends might issue a warning @ignore_warnings(module="django.core.cache.backends.base") def test_load_overlong_key(self): self.session._session_key = (string.ascii_letters + string.digits) * 20 self.assertEqual(self.session.load(), {}) @override_settings(SESSION_CACHE_ALIAS='sessions') def test_non_default_cache(self): # 21000 - CacheDB backend should respect SESSION_CACHE_ALIAS. self.assertRaises(InvalidCacheBackendError, self.backend) @override_settings(USE_TZ=True) class CacheDBSessionWithTimeZoneTests(CacheDBSessionTests): pass # Don't need DB flushing for these tests, so can use unittest.TestCase as base class class FileSessionTests(SessionTestsMixin, unittest.TestCase): backend = FileSession def setUp(self): # Do file session tests in an isolated directory, and kill it after we're done. self.original_session_file_path = settings.SESSION_FILE_PATH self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp() # Reset the file session backend's internal caches if hasattr(self.backend, '_storage_path'): del self.backend._storage_path super(FileSessionTests, self).setUp() def tearDown(self): super(FileSessionTests, self).tearDown() settings.SESSION_FILE_PATH = self.original_session_file_path shutil.rmtree(self.temp_session_store) @override_settings( SESSION_FILE_PATH="/if/this/directory/exists/you/have/a/weird/computer") def test_configuration_check(self): del self.backend._storage_path # Make sure the file backend checks for a good storage dir self.assertRaises(ImproperlyConfigured, self.backend) def test_invalid_key_backslash(self): # Ensure we don't allow directory-traversal. # This is tested directly on _key_to_file, as load() will swallow # a SuspiciousOperation in the same way as an IOError - by creating # a new session, making it unclear whether the slashes were detected. self.assertRaises(InvalidSessionKey, self.backend()._key_to_file, "a\\b\\c") def test_invalid_key_forwardslash(self): # Ensure we don't allow directory-traversal self.assertRaises(InvalidSessionKey, self.backend()._key_to_file, "a/b/c") @override_settings(SESSION_ENGINE="django.contrib.sessions.backends.file") def test_clearsessions_command(self): """ Test clearsessions command for clearing expired sessions. """ storage_path = self.backend._get_storage_path() file_prefix = settings.SESSION_COOKIE_NAME def count_sessions(): return len([session_file for session_file in os.listdir(storage_path) if session_file.startswith(file_prefix)]) self.assertEqual(0, count_sessions()) # One object in the future self.session['foo'] = 'bar' self.session.set_expiry(3600) self.session.save() # One object in the past other_session = self.backend() other_session['foo'] = 'bar' other_session.set_expiry(-3600) other_session.save() # Two sessions are in the filesystem before clearsessions... self.assertEqual(2, count_sessions()) management.call_command('clearsessions') # ... and one is deleted. self.assertEqual(1, count_sessions()) class CacheSessionTests(SessionTestsMixin, unittest.TestCase): backend = CacheSession # Some backends might issue a warning @ignore_warnings(module="django.core.cache.backends.base") def test_load_overlong_key(self): self.session._session_key = (string.ascii_letters + string.digits) * 20 self.assertEqual(self.session.load(), {}) def test_default_cache(self): self.session.save() self.assertNotEqual(caches['default'].get(self.session.cache_key), None) @override_settings(CACHES={ 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', }, 'sessions': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'session', }, }, SESSION_CACHE_ALIAS='sessions') def test_non_default_cache(self): # Re-initialize the session backend to make use of overridden settings. self.session = self.backend() self.session.save() self.assertEqual(caches['default'].get(self.session.cache_key), None) self.assertNotEqual(caches['sessions'].get(self.session.cache_key), None) class SessionMiddlewareTests(TestCase): @override_settings(SESSION_COOKIE_SECURE=True) def test_secure_session_cookie(self): request = RequestFactory().get('/') response = HttpResponse('Session test') middleware = SessionMiddleware() # Simulate a request the modifies the session middleware.process_request(request) request.session['hello'] = 'world' # Handle the response through the middleware response = middleware.process_response(request, response) self.assertTrue( response.cookies[settings.SESSION_COOKIE_NAME]['secure']) @override_settings(SESSION_COOKIE_HTTPONLY=True) def test_httponly_session_cookie(self): request = RequestFactory().get('/') response = HttpResponse('Session test') middleware = SessionMiddleware() # Simulate a request the modifies the session middleware.process_request(request) request.session['hello'] = 'world' # Handle the response through the middleware response = middleware.process_response(request, response) self.assertTrue( response.cookies[settings.SESSION_COOKIE_NAME]['httponly']) self.assertIn(http_cookies.Morsel._reserved['httponly'], str(response.cookies[settings.SESSION_COOKIE_NAME])) @override_settings(SESSION_COOKIE_HTTPONLY=False) def test_no_httponly_session_cookie(self): request = RequestFactory().get('/') response = HttpResponse('Session test') middleware = SessionMiddleware() # Simulate a request the modifies the session middleware.process_request(request) request.session['hello'] = 'world' # Handle the response through the middleware response = middleware.process_response(request, response) self.assertFalse(response.cookies[settings.SESSION_COOKIE_NAME]['httponly']) self.assertNotIn(http_cookies.Morsel._reserved['httponly'], str(response.cookies[settings.SESSION_COOKIE_NAME])) def test_session_save_on_500(self): request = RequestFactory().get('/') response = HttpResponse('Horrible error') response.status_code = 500 middleware = SessionMiddleware() # Simulate a request the modifies the session middleware.process_request(request) request.session['hello'] = 'world' # Handle the response through the middleware response = middleware.process_response(request, response) # Check that the value wasn't saved above. self.assertNotIn('hello', request.session.load()) def test_session_delete_on_end(self): request = RequestFactory().get('/') response = HttpResponse('Session test') middleware = SessionMiddleware() # Before deleting, there has to be an existing cookie request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc' # Simulate a request that ends the session middleware.process_request(request) request.session.flush() # Handle the response through the middleware response = middleware.process_response(request, response) # Check that the cookie was deleted, not recreated. # A deleted cookie header looks like: # Set-Cookie: sessionid=; expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ self.assertEqual( 'Set-Cookie: {}={}; expires=Thu, 01-Jan-1970 00:00:00 GMT; ' 'Max-Age=0; Path=/'.format( settings.SESSION_COOKIE_NAME, '""' if sys.version_info >= (3, 5) else '', ), str(response.cookies[settings.SESSION_COOKIE_NAME]) ) @override_settings(SESSION_COOKIE_DOMAIN='.example.local') def test_session_delete_on_end_with_custom_domain(self): request = RequestFactory().get('/') response = HttpResponse('Session test') middleware = SessionMiddleware() # Before deleting, there has to be an existing cookie request.COOKIES[settings.SESSION_COOKIE_NAME] = 'abc' # Simulate a request that ends the session middleware.process_request(request) request.session.flush() # Handle the response through the middleware response = middleware.process_response(request, response) # Check that the cookie was deleted, not recreated. # A deleted cookie header with a custom domain looks like: # Set-Cookie: sessionid=; Domain=.example.local; # expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/ self.assertEqual( 'Set-Cookie: {}={}; Domain=.example.local; expires=Thu, ' '01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/'.format( settings.SESSION_COOKIE_NAME, '""' if sys.version_info >= (3, 5) else '', ), str(response.cookies[settings.SESSION_COOKIE_NAME]) ) def test_flush_empty_without_session_cookie_doesnt_set_cookie(self): request = RequestFactory().get('/') response = HttpResponse('Session test') middleware = SessionMiddleware() # Simulate a request that ends the session middleware.process_request(request) request.session.flush() # Handle the response through the middleware response = middleware.process_response(request, response) # A cookie should not be set. self.assertEqual(response.cookies, {}) # The session is accessed so "Vary: Cookie" should be set. self.assertEqual(response['Vary'], 'Cookie') # Don't need DB flushing for these tests, so can use unittest.TestCase as base class class CookieSessionTests(SessionTestsMixin, unittest.TestCase): backend = CookieSession def test_save(self): """ This test tested exists() in the other session backends, but that doesn't make sense for us. """ pass def test_cycle(self): """ This test tested cycle_key() which would create a new session key for the same session data. But we can't invalidate previously signed cookies (other than letting them expire naturally) so testing for this behavior is meaningless. """ pass @unittest.expectedFailure def test_actual_expiry(self): # The cookie backend doesn't handle non-default expiry dates, see #19201 super(CookieSessionTests, self).test_actual_expiry() Django-1.8.7/tests/sessions_tests/__init__.py0000664000175000017500000000000012625116214020670 0ustar timtim00000000000000Django-1.8.7/tests/update/0000775000175000017500000000000012625116243014765 5ustar timtim00000000000000Django-1.8.7/tests/update/tests.py0000664000175000017500000001200112625116214016471 0ustar timtim00000000000000from __future__ import unicode_literals from django.test import TestCase from .models import A, B, D, Bar, DataPoint, Foo, RelatedPoint class SimpleTest(TestCase): def setUp(self): self.a1 = A.objects.create() self.a2 = A.objects.create() for x in range(20): B.objects.create(a=self.a1) D.objects.create(a=self.a1) def test_nonempty_update(self): """ Test that update changes the right number of rows for a nonempty queryset """ num_updated = self.a1.b_set.update(y=100) self.assertEqual(num_updated, 20) cnt = B.objects.filter(y=100).count() self.assertEqual(cnt, 20) def test_empty_update(self): """ Test that update changes the right number of rows for an empty queryset """ num_updated = self.a2.b_set.update(y=100) self.assertEqual(num_updated, 0) cnt = B.objects.filter(y=100).count() self.assertEqual(cnt, 0) def test_nonempty_update_with_inheritance(self): """ Test that update changes the right number of rows for an empty queryset when the update affects only a base table """ num_updated = self.a1.d_set.update(y=100) self.assertEqual(num_updated, 20) cnt = D.objects.filter(y=100).count() self.assertEqual(cnt, 20) def test_empty_update_with_inheritance(self): """ Test that update changes the right number of rows for an empty queryset when the update affects only a base table """ num_updated = self.a2.d_set.update(y=100) self.assertEqual(num_updated, 0) cnt = D.objects.filter(y=100).count() self.assertEqual(cnt, 0) def test_foreign_key_update_with_id(self): """ Test that update works using _id for foreign keys """ num_updated = self.a1.d_set.update(a_id=self.a2) self.assertEqual(num_updated, 20) self.assertEqual(self.a2.d_set.count(), 20) class AdvancedTests(TestCase): def setUp(self): self.d0 = DataPoint.objects.create(name="d0", value="apple") self.d2 = DataPoint.objects.create(name="d2", value="banana") self.d3 = DataPoint.objects.create(name="d3", value="banana") self.r1 = RelatedPoint.objects.create(name="r1", data=self.d3) def test_update(self): """ Objects are updated by first filtering the candidates into a queryset and then calling the update() method. It executes immediately and returns nothing. """ resp = DataPoint.objects.filter(value="apple").update(name="d1") self.assertEqual(resp, 1) resp = DataPoint.objects.filter(value="apple") self.assertEqual(list(resp), [self.d0]) def test_update_multiple_objects(self): """ We can update multiple objects at once. """ resp = DataPoint.objects.filter(value="banana").update( value="pineapple") self.assertEqual(resp, 2) self.assertEqual(DataPoint.objects.get(name="d2").value, 'pineapple') def test_update_fk(self): """ Foreign key fields can also be updated, although you can only update the object referred to, not anything inside the related object. """ resp = RelatedPoint.objects.filter(name="r1").update(data=self.d0) self.assertEqual(resp, 1) resp = RelatedPoint.objects.filter(data__name="d0") self.assertEqual(list(resp), [self.r1]) def test_update_multiple_fields(self): """ Multiple fields can be updated at once """ resp = DataPoint.objects.filter(value="apple").update( value="fruit", another_value="peach") self.assertEqual(resp, 1) d = DataPoint.objects.get(name="d0") self.assertEqual(d.value, 'fruit') self.assertEqual(d.another_value, 'peach') def test_update_all(self): """ In the rare case you want to update every instance of a model, update() is also a manager method. """ self.assertEqual(DataPoint.objects.update(value='thing'), 3) resp = DataPoint.objects.values('value').distinct() self.assertEqual(list(resp), [{'value': 'thing'}]) def test_update_slice_fail(self): """ We do not support update on already sliced query sets. """ method = DataPoint.objects.all()[:2].update self.assertRaises(AssertionError, method, another_value='another thing') def test_update_respects_to_field(self): """ Update of an FK field which specifies a to_field works. """ a_foo = Foo.objects.create(target='aaa') b_foo = Foo.objects.create(target='bbb') bar = Bar.objects.create(foo=a_foo) self.assertEqual(bar.foo_id, a_foo.target) bar_qs = Bar.objects.filter(pk=bar.pk) self.assertEqual(bar_qs[0].foo_id, a_foo.target) bar_qs.update(foo=b_foo) self.assertEqual(bar_qs[0].foo_id, b_foo.target) Django-1.8.7/tests/update/__init__.py0000664000175000017500000000000012603513307017062 0ustar timtim00000000000000Django-1.8.7/tests/update/models.py0000664000175000017500000000215412625116214016622 0ustar timtim00000000000000""" Tests for the update() queryset method that allows in-place, multi-object updates. """ from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class DataPoint(models.Model): name = models.CharField(max_length=20) value = models.CharField(max_length=20) another_value = models.CharField(max_length=20, blank=True) def __str__(self): return six.text_type(self.name) @python_2_unicode_compatible class RelatedPoint(models.Model): name = models.CharField(max_length=20) data = models.ForeignKey(DataPoint) def __str__(self): return six.text_type(self.name) 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) class Foo(models.Model): target = models.CharField(max_length=10, unique=True) class Bar(models.Model): foo = models.ForeignKey(Foo, to_field='target') Django-1.8.7/tests/fixtures_model_package/0000775000175000017500000000000012625116243020207 5ustar timtim00000000000000Django-1.8.7/tests/fixtures_model_package/models/0000775000175000017500000000000012625116243021472 5ustar timtim00000000000000Django-1.8.7/tests/fixtures_model_package/models/__init__.py0000664000175000017500000000103712625113144023601 0ustar timtim00000000000000from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() def __str__(self): return self.headline class Meta: app_label = 'fixtures_model_package' ordering = ('-pub_date', 'headline') class Book(models.Model): name = models.CharField(max_length=100) class Meta: ordering = ('name',) Django-1.8.7/tests/fixtures_model_package/models/sql/0000775000175000017500000000000012625116243022271 5ustar timtim00000000000000Django-1.8.7/tests/fixtures_model_package/models/sql/book.sql0000664000175000017500000000021712625113144023741 0ustar timtim00000000000000-- Deprecated search path for custom SQL -- remove in Django 1.9 INSERT INTO fixtures_model_package_book (name) VALUES ('My Deprecated Book'); Django-1.8.7/tests/fixtures_model_package/tests.py0000664000175000017500000001034312625116213021721 0ustar timtim00000000000000from __future__ import unicode_literals import warnings from django.core import management from django.db import transaction from django.test import TestCase, TransactionTestCase from django.utils.six import StringIO from .models import Article, Book class SampleTestCase(TestCase): fixtures = ['fixture1.json', 'fixture2.json'] def testClassFixtures(self): "Test cases can load fixture objects into models defined in packages" self.assertEqual(Article.objects.count(), 3) self.assertQuerysetEqual( Article.objects.all(), [ "Django conquers world!", "Copyright is fine the way it is", "Poker has no place on ESPN", ], lambda a: a.headline ) class TestNoInitialDataLoading(TransactionTestCase): available_apps = ['fixtures_model_package'] def test_migrate(self): with transaction.atomic(): Book.objects.all().delete() management.call_command( 'migrate', verbosity=0, load_initial_data=False ) self.assertQuerysetEqual(Book.objects.all(), []) def test_flush(self): # Test presence of fixture (flush called by TransactionTestCase) self.assertQuerysetEqual( Book.objects.all(), [ 'Achieving self-awareness of Python programs' ], lambda a: a.name ) with transaction.atomic(): management.call_command( 'flush', verbosity=0, interactive=False, load_initial_data=False ) self.assertQuerysetEqual(Book.objects.all(), []) class FixtureTestCase(TestCase): def test_initial_data(self): "Fixtures can load initial data into models defined in packages" # migrate introduces 1 initial data object from initial_data.json # this behavior is deprecated and will be removed in Django 1.9 self.assertQuerysetEqual( Book.objects.all(), [ 'Achieving self-awareness of Python programs' ], lambda a: a.name ) def test_loaddata(self): "Fixtures can load data into models defined in packages" # Load fixture 1. Single JSON file, with two objects management.call_command("loaddata", "fixture1.json", verbosity=0) self.assertQuerysetEqual( Article.objects.all(), [ "Time to reform copyright", "Poker has no place on ESPN", ], lambda a: a.headline, ) # Load fixture 2. JSON file imported by default. Overwrites some # existing objects management.call_command("loaddata", "fixture2.json", verbosity=0) self.assertQuerysetEqual( Article.objects.all(), [ "Django conquers world!", "Copyright is fine the way it is", "Poker has no place on ESPN", ], lambda a: a.headline, ) # Load a fixture that doesn't exist with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") management.call_command("loaddata", "unknown.json", verbosity=0) self.assertEqual(len(w), 1) self.assertTrue(w[0].message, "No fixture named 'unknown' found.") self.assertQuerysetEqual( Article.objects.all(), [ "Django conquers world!", "Copyright is fine the way it is", "Poker has no place on ESPN", ], lambda a: a.headline, ) class InitialSQLTests(TestCase): def test_custom_sql(self): """ #14300 -- Verify that custom_sql_for_model searches `app/sql` and not `app/models/sql` (the old location will work until Django 1.9) """ out = StringIO() management.call_command("sqlcustom", "fixtures_model_package", stdout=out) output = out.getvalue() self.assertIn("INSERT INTO fixtures_model_package_book (name) VALUES ('My Book')", output) # value from deprecated search path models/sql (remove in Django 1.9) self.assertIn("Deprecated Book", output) Django-1.8.7/tests/fixtures_model_package/fixtures/0000775000175000017500000000000012625116243022060 5ustar timtim00000000000000Django-1.8.7/tests/fixtures_model_package/fixtures/initial_data.json0000664000175000017500000000026412625113144025374 0ustar timtim00000000000000[ { "pk": "10", "model": "fixtures_model_package.book", "fields": { "name": "Achieving self-awareness of Python programs" } } ] Django-1.8.7/tests/fixtures_model_package/fixtures/fixture2.xml0000664000175000017500000000107212603513307024350 0ustar timtim00000000000000 Poker on TV is great! 2006-06-16 11:00:00 XML identified as leading cause of cancer 2006-06-16 16:00:00 Django-1.8.7/tests/fixtures_model_package/fixtures/fixture1.json0000664000175000017500000000065312603513307024524 0ustar timtim00000000000000[ { "pk": "2", "model": "fixtures_model_package.article", "fields": { "headline": "Poker has no place on ESPN", "pub_date": "2006-06-16 12:00:00" } }, { "pk": "3", "model": "fixtures_model_package.article", "fields": { "headline": "Time to reform copyright", "pub_date": "2006-06-16 13:00:00" } } ] Django-1.8.7/tests/fixtures_model_package/fixtures/fixture2.json0000664000175000017500000000065612603513307024530 0ustar timtim00000000000000[ { "pk": "3", "model": "fixtures_model_package.article", "fields": { "headline": "Copyright is fine the way it is", "pub_date": "2006-06-16 14:00:00" } }, { "pk": "4", "model": "fixtures_model_package.article", "fields": { "headline": "Django conquers world!", "pub_date": "2006-06-16 15:00:00" } } ] Django-1.8.7/tests/fixtures_model_package/__init__.py0000664000175000017500000000000012603513307022304 0ustar timtim00000000000000Django-1.8.7/tests/fixtures_model_package/sql/0000775000175000017500000000000012625116243021006 5ustar timtim00000000000000Django-1.8.7/tests/fixtures_model_package/sql/book.sql0000664000175000017500000000010312625113144022450 0ustar timtim00000000000000INSERT INTO fixtures_model_package_book (name) VALUES ('My Book'); Django-1.8.7/tests/select_related_onetoone/0000775000175000017500000000000012625116243020370 5ustar timtim00000000000000Django-1.8.7/tests/select_related_onetoone/tests.py0000664000175000017500000002437612625116214022116 0ustar timtim00000000000000from __future__ import unicode_literals import unittest from django.core.exceptions import FieldError from django.test import TestCase from .models import ( AdvancedUserStat, Child1, Child2, Child3, Child4, Image, Parent1, Parent2, Product, StatDetails, User, UserProfile, UserStat, UserStatResult, ) class ReverseSelectRelatedTestCase(TestCase): def setUp(self): user = User.objects.create(username="test") UserProfile.objects.create(user=user, state="KS", city="Lawrence") results = UserStatResult.objects.create(results='first results') userstat = UserStat.objects.create(user=user, posts=150, results=results) StatDetails.objects.create(base_stats=userstat, comments=259) user2 = User.objects.create(username="bob") results2 = UserStatResult.objects.create(results='moar results') advstat = AdvancedUserStat.objects.create(user=user2, posts=200, karma=5, results=results2) StatDetails.objects.create(base_stats=advstat, comments=250) p1 = Parent1(name1="Only Parent1") p1.save() c1 = Child1(name1="Child1 Parent1", name2="Child1 Parent2", value=1) c1.save() p2 = Parent2(name2="Child2 Parent2") p2.save() c2 = Child2(name1="Child2 Parent1", parent2=p2, value=2) c2.save() def test_basic(self): with self.assertNumQueries(1): u = User.objects.select_related("userprofile").get(username="test") self.assertEqual(u.userprofile.state, "KS") def test_follow_next_level(self): with self.assertNumQueries(1): u = User.objects.select_related("userstat__results").get(username="test") self.assertEqual(u.userstat.posts, 150) self.assertEqual(u.userstat.results.results, 'first results') def test_follow_two(self): with self.assertNumQueries(1): u = User.objects.select_related("userprofile", "userstat").get(username="test") self.assertEqual(u.userprofile.state, "KS") self.assertEqual(u.userstat.posts, 150) def test_follow_two_next_level(self): with self.assertNumQueries(1): u = User.objects.select_related("userstat__results", "userstat__statdetails").get(username="test") self.assertEqual(u.userstat.results.results, 'first results') self.assertEqual(u.userstat.statdetails.comments, 259) def test_forward_and_back(self): with self.assertNumQueries(1): stat = UserStat.objects.select_related("user__userprofile").get(user__username="test") self.assertEqual(stat.user.userprofile.state, 'KS') self.assertEqual(stat.user.userstat.posts, 150) def test_back_and_forward(self): with self.assertNumQueries(1): u = User.objects.select_related("userstat").get(username="test") self.assertEqual(u.userstat.user.username, 'test') def test_not_followed_by_default(self): with self.assertNumQueries(2): u = User.objects.select_related().get(username="test") self.assertEqual(u.userstat.posts, 150) def test_follow_from_child_class(self): with self.assertNumQueries(1): stat = AdvancedUserStat.objects.select_related('user', 'statdetails').get(posts=200) self.assertEqual(stat.statdetails.comments, 250) self.assertEqual(stat.user.username, 'bob') def test_follow_inheritance(self): with self.assertNumQueries(1): stat = UserStat.objects.select_related('user', 'advanceduserstat').get(posts=200) self.assertEqual(stat.advanceduserstat.posts, 200) self.assertEqual(stat.user.username, 'bob') with self.assertNumQueries(1): self.assertEqual(stat.advanceduserstat.user.username, 'bob') def test_nullable_relation(self): im = Image.objects.create(name="imag1") p1 = Product.objects.create(name="Django Plushie", image=im) p2 = Product.objects.create(name="Talking Django Plushie") with self.assertNumQueries(1): result = sorted(Product.objects.select_related("image"), key=lambda x: x.name) self.assertEqual([p.name for p in result], ["Django Plushie", "Talking Django Plushie"]) self.assertEqual(p1.image, im) # Check for ticket #13839 self.assertIsNone(p2.image) def test_missing_reverse(self): """ Ticket #13839: select_related() should NOT cache None for missing objects on a reverse 1-1 relation. """ with self.assertNumQueries(1): user = User.objects.select_related('userprofile').get(username='bob') with self.assertRaises(UserProfile.DoesNotExist): user.userprofile def test_nullable_missing_reverse(self): """ Ticket #13839: select_related() should NOT cache None for missing objects on a reverse 0-1 relation. """ Image.objects.create(name="imag1") with self.assertNumQueries(1): image = Image.objects.select_related('product').get() with self.assertRaises(Product.DoesNotExist): image.product def test_parent_only(self): with self.assertNumQueries(1): p = Parent1.objects.select_related('child1').get(name1="Only Parent1") with self.assertNumQueries(0): with self.assertRaises(Child1.DoesNotExist): p.child1 def test_multiple_subclass(self): with self.assertNumQueries(1): p = Parent1.objects.select_related('child1').get(name1="Child1 Parent1") self.assertEqual(p.child1.name2, 'Child1 Parent2') def test_onetoone_with_subclass(self): with self.assertNumQueries(1): p = Parent2.objects.select_related('child2').get(name2="Child2 Parent2") self.assertEqual(p.child2.name1, 'Child2 Parent1') def test_onetoone_with_two_subclasses(self): with self.assertNumQueries(1): p = Parent2.objects.select_related('child2', "child2__child3").get(name2="Child2 Parent2") self.assertEqual(p.child2.name1, 'Child2 Parent1') with self.assertRaises(Child3.DoesNotExist): p.child2.child3 p3 = Parent2(name2="Child3 Parent2") p3.save() c2 = Child3(name1="Child3 Parent1", parent2=p3, value=2, value3=3) c2.save() with self.assertNumQueries(1): p = Parent2.objects.select_related('child2', "child2__child3").get(name2="Child3 Parent2") self.assertEqual(p.child2.name1, 'Child3 Parent1') self.assertEqual(p.child2.child3.value3, 3) self.assertEqual(p.child2.child3.value, p.child2.value) self.assertEqual(p.child2.name1, p.child2.child3.name1) def test_multiinheritance_two_subclasses(self): with self.assertNumQueries(1): p = Parent1.objects.select_related('child1', 'child1__child4').get(name1="Child1 Parent1") self.assertEqual(p.child1.name2, 'Child1 Parent2') self.assertEqual(p.child1.name1, p.name1) with self.assertRaises(Child4.DoesNotExist): p.child1.child4 Child4(name1='n1', name2='n2', value=1, value4=4).save() with self.assertNumQueries(1): p = Parent2.objects.select_related('child1', 'child1__child4').get(name2="n2") self.assertEqual(p.name2, 'n2') self.assertEqual(p.child1.name1, 'n1') self.assertEqual(p.child1.name2, p.name2) self.assertEqual(p.child1.value, 1) self.assertEqual(p.child1.child4.name1, p.child1.name1) self.assertEqual(p.child1.child4.name2, p.child1.name2) self.assertEqual(p.child1.child4.value, p.child1.value) self.assertEqual(p.child1.child4.value4, 4) @unittest.expectedFailure def test_inheritance_deferred(self): c = Child4.objects.create(name1='n1', name2='n2', value=1, value4=4) with self.assertNumQueries(1): p = Parent2.objects.select_related('child1').only( 'id2', 'child1__value').get(name2="n2") self.assertEqual(p.id2, c.id2) self.assertEqual(p.child1.value, 1) p = Parent2.objects.select_related('child1').only( 'id2', 'child1__value').get(name2="n2") with self.assertNumQueries(1): self.assertEqual(p.name2, 'n2') p = Parent2.objects.select_related('child1').only( 'id2', 'child1__value').get(name2="n2") with self.assertNumQueries(1): self.assertEqual(p.child1.name2, 'n2') @unittest.expectedFailure def test_inheritance_deferred2(self): c = Child4.objects.create(name1='n1', name2='n2', value=1, value4=4) qs = Parent2.objects.select_related('child1', 'child4').only( 'id2', 'child1__value', 'child1__child4__value4') with self.assertNumQueries(1): p = qs.get(name2="n2") self.assertEqual(p.id2, c.id2) self.assertEqual(p.child1.value, 1) self.assertEqual(p.child1.child4.value4, 4) self.assertEqual(p.child1.child4.id2, c.id2) p = qs.get(name2="n2") with self.assertNumQueries(1): self.assertEqual(p.child1.name2, 'n2') p = qs.get(name2="n2") with self.assertNumQueries(1): self.assertEqual(p.child1.name1, 'n1') with self.assertNumQueries(1): self.assertEqual(p.child1.child4.name1, 'n1') class ReverseSelectRelatedValidationTests(TestCase): """ Rverse related fields should be listed in the validation message when an invalid field is given in select_related(). """ non_relational_error = "Non-relational field given in select_related: '%s'. Choices are: %s" invalid_error = "Invalid field name(s) given in select_related: '%s'. Choices are: %s" def test_reverse_related_validation(self): fields = 'userprofile, userstat' with self.assertRaisesMessage(FieldError, self.invalid_error % ('foobar', fields)): list(User.objects.select_related('foobar')) with self.assertRaisesMessage(FieldError, self.non_relational_error % ('username', fields)): list(User.objects.select_related('username')) Django-1.8.7/tests/select_related_onetoone/__init__.py0000664000175000017500000000000012603513307022465 0ustar timtim00000000000000Django-1.8.7/tests/select_related_onetoone/models.py0000664000175000017500000000452212625113144022225 0ustar timtim00000000000000from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class User(models.Model): username = models.CharField(max_length=100) email = models.EmailField() def __str__(self): return self.username @python_2_unicode_compatible class UserProfile(models.Model): user = models.OneToOneField(User) city = models.CharField(max_length=100) state = models.CharField(max_length=2) def __str__(self): return "%s, %s" % (self.city, self.state) @python_2_unicode_compatible class UserStatResult(models.Model): results = models.CharField(max_length=50) def __str__(self): return 'UserStatResults, results = %s' % (self.results,) @python_2_unicode_compatible class UserStat(models.Model): user = models.OneToOneField(User, primary_key=True) posts = models.IntegerField() results = models.ForeignKey(UserStatResult) def __str__(self): return 'UserStat, posts = %s' % (self.posts,) @python_2_unicode_compatible class StatDetails(models.Model): base_stats = models.OneToOneField(UserStat) comments = models.IntegerField() def __str__(self): return 'StatDetails, comments = %s' % (self.comments,) class AdvancedUserStat(UserStat): karma = models.IntegerField() class Image(models.Model): name = models.CharField(max_length=100) class Product(models.Model): name = models.CharField(max_length=100) image = models.OneToOneField(Image, null=True) @python_2_unicode_compatible class Parent1(models.Model): name1 = models.CharField(max_length=50) def __str__(self): return self.name1 @python_2_unicode_compatible class Parent2(models.Model): # Avoid having two "id" fields in the Child1 subclass id2 = models.AutoField(primary_key=True) name2 = models.CharField(max_length=50) def __str__(self): return self.name2 @python_2_unicode_compatible class Child1(Parent1, Parent2): value = models.IntegerField() def __str__(self): return self.name1 @python_2_unicode_compatible class Child2(Parent1): parent2 = models.OneToOneField(Parent2) value = models.IntegerField() def __str__(self): return self.name1 class Child3(Child2): value3 = models.IntegerField() class Child4(Child1): value4 = models.IntegerField() Django-1.8.7/tests/builtin_server/0000775000175000017500000000000012625116243016537 5ustar timtim00000000000000Django-1.8.7/tests/builtin_server/tests.py0000664000175000017500000001215612625116213020255 0ustar timtim00000000000000from __future__ import unicode_literals import sys import traceback from io import BytesIO from unittest import TestCase from wsgiref import simple_server # If data is too large, socket will choke, so write chunks no larger than 32MB # at a time. The rationale behind the 32MB can be found on Django's Trac: # https://code.djangoproject.com/ticket/5596#comment:4 MAX_SOCKET_CHUNK_SIZE = 32 * 1024 * 1024 # 32 MB class ServerHandler(simple_server.ServerHandler, object): error_status = str("500 INTERNAL SERVER ERROR") def write(self, data): """'write()' callable as specified by PEP 3333""" assert isinstance(data, bytes), "write() argument must be bytestring" if not self.status: raise AssertionError("write() before start_response()") elif not self.headers_sent: # Before the first output, send the stored headers self.bytes_sent = len(data) # make sure we know content-length self.send_headers() else: self.bytes_sent += len(data) # XXX check Content-Length and truncate if too many bytes written? data = BytesIO(data) for chunk in iter(lambda: data.read(MAX_SOCKET_CHUNK_SIZE), b''): self._write(chunk) self._flush() def error_output(self, environ, start_response): super(ServerHandler, self).error_output(environ, start_response) return ['\n'.join(traceback.format_exception(*sys.exc_info()))] # Backport of http://hg.python.org/cpython/rev/d5af1b235dab. See #16241. # This can be removed when support for Python <= 2.7.3 is deprecated. def finish_response(self): try: if not self.result_is_file() or not self.sendfile(): for data in self.result: self.write(data) self.finish_content() finally: self.close() class DummyHandler(object): def log_request(self, *args, **kwargs): pass class FileWrapperHandler(ServerHandler): def __init__(self, *args, **kwargs): super(FileWrapperHandler, self).__init__(*args, **kwargs) self.request_handler = DummyHandler() self._used_sendfile = False def sendfile(self): self._used_sendfile = True return True def wsgi_app(environ, start_response): start_response(str('200 OK'), [(str('Content-Type'), str('text/plain'))]) return [b'Hello World!'] def wsgi_app_file_wrapper(environ, start_response): start_response(str('200 OK'), [(str('Content-Type'), str('text/plain'))]) return environ['wsgi.file_wrapper'](BytesIO(b'foo')) class WSGIFileWrapperTests(TestCase): """ Test that the wsgi.file_wrapper works for the builting server. Tests for #9659: wsgi.file_wrapper in the builtin server. We need to mock a couple of handlers and keep track of what gets called when using a couple kinds of WSGI apps. """ def test_file_wrapper_uses_sendfile(self): env = {'SERVER_PROTOCOL': 'HTTP/1.0'} handler = FileWrapperHandler(None, BytesIO(), BytesIO(), env) handler.run(wsgi_app_file_wrapper) self.assertTrue(handler._used_sendfile) self.assertEqual(handler.stdout.getvalue(), b'') self.assertEqual(handler.stderr.getvalue(), b'') def test_file_wrapper_no_sendfile(self): env = {'SERVER_PROTOCOL': 'HTTP/1.0'} handler = FileWrapperHandler(None, BytesIO(), BytesIO(), env) handler.run(wsgi_app) self.assertFalse(handler._used_sendfile) self.assertEqual(handler.stdout.getvalue().splitlines()[-1], b'Hello World!') self.assertEqual(handler.stderr.getvalue(), b'') class WriteChunkCounterHandler(ServerHandler): """ Server handler that counts the number of chunks written after headers were sent. Used to make sure large response body chunking works properly. """ def __init__(self, *args, **kwargs): super(WriteChunkCounterHandler, self).__init__(*args, **kwargs) self.request_handler = DummyHandler() self.headers_written = False self.write_chunk_counter = 0 def send_headers(self): super(WriteChunkCounterHandler, self).send_headers() self.headers_written = True def _write(self, data): if self.headers_written: self.write_chunk_counter += 1 self.stdout.write(data) def send_big_data_app(environ, start_response): start_response(str('200 OK'), [(str('Content-Type'), str('text/plain'))]) # Return a blob of data that is 1.5 times the maximum chunk size. return [b'x' * (MAX_SOCKET_CHUNK_SIZE + MAX_SOCKET_CHUNK_SIZE // 2)] class ServerHandlerChunksProperly(TestCase): """ Test that the ServerHandler chunks data properly. Tests for #18972: The logic that performs the math to break data into 32MB (MAX_SOCKET_CHUNK_SIZE) chunks was flawed, BUT it didn't actually cause any problems. """ def test_chunked_data(self): env = {'SERVER_PROTOCOL': 'HTTP/1.0'} handler = WriteChunkCounterHandler(None, BytesIO(), BytesIO(), env) handler.run(send_big_data_app) self.assertEqual(handler.write_chunk_counter, 2) Django-1.8.7/tests/builtin_server/__init__.py0000664000175000017500000000000012603513307020634 0ustar timtim00000000000000Django-1.8.7/tests/string_lookup/0000775000175000017500000000000012625116243016402 5ustar timtim00000000000000Django-1.8.7/tests/string_lookup/tests.py0000664000175000017500000000501512625116214020115 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.test import TestCase from .models import Article, Bar, Base, Child, Foo, Whiz class StringLookupTests(TestCase): def test_string_form_referencing(self): """ Regression test for #1661 and #1662 Check that string form referencing of models works, both as pre and post reference, on all RelatedField types. """ f1 = Foo(name="Foo1") f1.save() f2 = Foo(name="Foo2") f2.save() w1 = Whiz(name="Whiz1") w1.save() b1 = Bar(name="Bar1", normal=f1, fwd=w1, back=f2) b1.save() self.assertEqual(b1.normal, f1) self.assertEqual(b1.fwd, w1) self.assertEqual(b1.back, f2) base1 = Base(name="Base1") base1.save() child1 = Child(name="Child1", parent=base1) child1.save() self.assertEqual(child1.parent, base1) def test_unicode_chars_in_queries(self): """ Regression tests for #3937 make sure we can use unicode characters in queries. If these tests fail on MySQL, it's a problem with the test setup. A properly configured UTF-8 database can handle this. """ fx = Foo(name='Bjorn', friend='François') fx.save() self.assertEqual(Foo.objects.get(friend__contains='\xe7'), fx) # We can also do the above query using UTF-8 strings. self.assertEqual(Foo.objects.get(friend__contains=b'\xc3\xa7'), fx) def test_queries_on_textfields(self): """ Regression tests for #5087 make sure we can perform queries on TextFields. """ a = Article(name='Test', text='The quick brown fox jumps over the lazy dog.') a.save() self.assertEqual(Article.objects.get(text__exact='The quick brown fox jumps over the lazy dog.'), a) self.assertEqual(Article.objects.get(text__contains='quick brown fox'), a) def test_ipaddress_on_postgresql(self): """ Regression test for #708 "like" queries on IP address fields require casting with HOST() (on PostgreSQL). """ a = Article(name='IP test', text='The body', submitted_from='192.0.2.100') a.save() self.assertEqual(repr(Article.objects.filter(submitted_from__contains='192.0.2')), repr([a])) # Test that the searches do not match the subnet mask (/32 in this case) self.assertEqual(Article.objects.filter(submitted_from__contains='32').count(), 0) Django-1.8.7/tests/string_lookup/__init__.py0000664000175000017500000000000012603513307020477 0ustar timtim00000000000000Django-1.8.7/tests/string_lookup/models.py0000664000175000017500000000267512625116214020247 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Foo(models.Model): name = models.CharField(max_length=50) friend = models.CharField(max_length=50, blank=True) def __str__(self): return "Foo %s" % self.name @python_2_unicode_compatible class Bar(models.Model): name = models.CharField(max_length=50) normal = models.ForeignKey(Foo, related_name='normal_foo') fwd = models.ForeignKey("Whiz") back = models.ForeignKey("Foo") def __str__(self): return "Bar %s" % self.place.name @python_2_unicode_compatible class Whiz(models.Model): name = models.CharField(max_length=50) def __str__(self): return "Whiz %s" % self.name @python_2_unicode_compatible class Child(models.Model): parent = models.OneToOneField('Base') name = models.CharField(max_length=50) def __str__(self): return "Child %s" % self.name @python_2_unicode_compatible class Base(models.Model): name = models.CharField(max_length=50) def __str__(self): return "Base %s" % self.name @python_2_unicode_compatible class Article(models.Model): name = models.CharField(max_length=50) text = models.TextField() submitted_from = models.GenericIPAddressField(blank=True, null=True) def __str__(self): return "Article %s" % self.name Django-1.8.7/tests/or_lookups/0000775000175000017500000000000012625116243015677 5ustar timtim00000000000000Django-1.8.7/tests/or_lookups/tests.py0000664000175000017500000001671012625113144017415 0ustar timtim00000000000000from __future__ import unicode_literals from datetime import datetime from operator import attrgetter from django.db.models import Q from django.test import TestCase from .models import Article class OrLookupsTests(TestCase): def setUp(self): self.a1 = Article.objects.create( headline='Hello', pub_date=datetime(2005, 11, 27) ).pk self.a2 = Article.objects.create( headline='Goodbye', pub_date=datetime(2005, 11, 28) ).pk self.a3 = Article.objects.create( headline='Hello and goodbye', pub_date=datetime(2005, 11, 29) ).pk def test_filter_or(self): self.assertQuerysetEqual( Article.objects.filter(headline__startswith='Hello') | Article.objects.filter(headline__startswith='Goodbye'), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.filter(headline__contains='Hello') | Article.objects.filter(headline__contains='bye'), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.filter(headline__iexact='Hello') | Article.objects.filter(headline__contains='ood'), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__startswith='Goodbye')), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline") ) def test_stages(self): # You can shorten this syntax with code like the following, which is # especially useful if building the query in stages: articles = Article.objects.all() self.assertQuerysetEqual( articles.filter(headline__startswith='Hello') & articles.filter(headline__startswith='Goodbye'), [] ) self.assertQuerysetEqual( articles.filter(headline__startswith='Hello') & articles.filter(headline__contains='bye'), [ 'Hello and goodbye' ], attrgetter("headline") ) def test_pk_q(self): self.assertQuerysetEqual( Article.objects.filter(Q(pk=self.a1) | Q(pk=self.a2)), [ 'Hello', 'Goodbye' ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.filter(Q(pk=self.a1) | Q(pk=self.a2) | Q(pk=self.a3)), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline"), ) def test_pk_in(self): self.assertQuerysetEqual( Article.objects.filter(pk__in=[self.a1, self.a2, self.a3]), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline"), ) self.assertQuerysetEqual( Article.objects.filter(pk__in=(self.a1, self.a2, self.a3)), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline"), ) self.assertQuerysetEqual( Article.objects.filter(pk__in=[self.a1, self.a2, self.a3, 40000]), [ 'Hello', 'Goodbye', 'Hello and goodbye' ], attrgetter("headline"), ) def test_q_negated(self): # Q objects can be negated self.assertQuerysetEqual( Article.objects.filter(Q(pk=self.a1) | ~Q(pk=self.a2)), [ 'Hello', 'Hello and goodbye' ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.filter(~Q(pk=self.a1) & ~Q(pk=self.a2)), [ 'Hello and goodbye' ], attrgetter("headline"), ) # This allows for more complex queries than filter() and exclude() # alone would allow self.assertQuerysetEqual( Article.objects.filter(Q(pk=self.a1) & (~Q(pk=self.a2) | Q(pk=self.a3))), [ 'Hello' ], attrgetter("headline"), ) def test_complex_filter(self): # The 'complex_filter' method supports framework features such as # 'limit_choices_to' which normally take a single dictionary of lookup # arguments but need to support arbitrary queries via Q objects too. self.assertQuerysetEqual( Article.objects.complex_filter({'pk': self.a1}), [ 'Hello' ], attrgetter("headline"), ) self.assertQuerysetEqual( Article.objects.complex_filter(Q(pk=self.a1) | Q(pk=self.a2)), [ 'Hello', 'Goodbye' ], attrgetter("headline"), ) def test_empty_in(self): # Passing "in" an empty list returns no results ... self.assertQuerysetEqual( Article.objects.filter(pk__in=[]), [] ) # ... but can return results if we OR it with another query. self.assertQuerysetEqual( Article.objects.filter(Q(pk__in=[]) | Q(headline__icontains='goodbye')), [ 'Goodbye', 'Hello and goodbye' ], attrgetter("headline"), ) def test_q_and(self): # Q arg objects are ANDed self.assertQuerysetEqual( Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')), [ 'Hello and goodbye' ], attrgetter("headline") ) # Q arg AND order is irrelevant self.assertQuerysetEqual( Article.objects.filter(Q(headline__contains='bye'), headline__startswith='Hello'), [ 'Hello and goodbye' ], attrgetter("headline"), ) self.assertQuerysetEqual( Article.objects.filter(Q(headline__startswith='Hello') & Q(headline__startswith='Goodbye')), [] ) def test_q_exclude(self): self.assertQuerysetEqual( Article.objects.exclude(Q(headline__startswith='Hello')), [ 'Goodbye' ], attrgetter("headline") ) def test_other_arg_queries(self): # Try some arg queries with operations other than filter. self.assertEqual( Article.objects.get(Q(headline__startswith='Hello'), Q(headline__contains='bye')).headline, 'Hello and goodbye' ) self.assertEqual( Article.objects.filter(Q(headline__startswith='Hello') | Q(headline__contains='bye')).count(), 3 ) self.assertQuerysetEqual( Article.objects.filter(Q(headline__startswith='Hello'), Q(headline__contains='bye')).values(), [ {"headline": "Hello and goodbye", "id": self.a3, "pub_date": datetime(2005, 11, 29)}, ], lambda o: o, ) self.assertEqual( Article.objects.filter(Q(headline__startswith='Hello')).in_bulk([self.a1, self.a2]), {self.a1: Article.objects.get(pk=self.a1)} ) Django-1.8.7/tests/or_lookups/__init__.py0000664000175000017500000000000012603513307017774 0ustar timtim00000000000000Django-1.8.7/tests/or_lookups/models.py0000664000175000017500000000123012625116214017526 0ustar timtim00000000000000""" OR lookups To perform an OR lookup, or a lookup that combines ANDs and ORs, combine ``QuerySet`` objects using ``&`` and ``|`` operators. Alternatively, use positional arguments, and pass one or more expressions of clauses using the variable ``django.db.models.Q`` (or any object with an ``add_to_query`` method). """ from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=50) pub_date = models.DateTimeField() class Meta: ordering = ('pub_date',) def __str__(self): return self.headline Django-1.8.7/tests/test_client/0000775000175000017500000000000012625116243016020 5ustar timtim00000000000000Django-1.8.7/tests/test_client/views.py0000664000175000017500000002466312625116214017540 0ustar timtim00000000000000from xml.dom.minidom import parseString from django.contrib.auth.decorators import login_required, permission_required from django.core import mail from django.forms import fields from django.forms.forms import Form, ValidationError from django.forms.formsets import BaseFormSet, formset_factory from django.http import ( HttpResponse, HttpResponseBadRequest, HttpResponseNotAllowed, HttpResponseNotFound, HttpResponseRedirect, ) from django.shortcuts import render_to_response 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') c = Context({'var': request.GET.get('var', 42)}) return HttpResponse(t.render(c)) def trace_view(request): """ A simple view that expects a TRACE request and echoes its status line. TRACE requests should not have an entity; the view will return a 400 status response if it is present. """ if request.method.upper() != "TRACE": return HttpResponseNotAllowed("TRACE") elif request.body: return HttpResponseBadRequest("TRACE requests MUST NOT include an entity") else: protocol = request.META["SERVER_PROTOCOL"] t = Template( '{{ method }} {{ uri }} {{ version }}', name="TRACE Template", ) c = Context({ 'method': request.method, 'uri': request.path, 'version': protocol, }) 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 """ if request.method == 'POST': if request.POST: t = Template('Data received: {{ data }} is the value.', name='POST Template') c = Context({'data': request.POST['value']}) else: t = Template('Viewing POST page.', name='Empty POST Template') c = Context() else: t = Template('Viewing GET page.', name='Empty GET Template') c = Context() 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""" if request.method == 'POST': root = parseString(request.body) first_book = root.firstChild.firstChild title, author = [n.firstChild.nodeValue for n in first_book.childNodes] t = Template("{{ title }} - {{ author }}", name="Book template") c = Context({"title": title, "author": author}) else: t = Template("GET request.", name="Book GET template") c = Context() return HttpResponse(t.render(c)) def redirect_view(request): "A view that redirects all requests to the GET view" if request.GET: query = '?' + urlencode(request.GET, True) else: query = '' return HttpResponseRedirect('/get_view/' + query) def view_with_secure(request): "A view that indicates if the request was secure" response = HttpResponse() response.test_was_secure_request = request.is_secure() 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('/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') TestChoices = ( ('a', 'First Choice'), ('b', 'Second Choice'), ('c', 'Third Choice'), ('d', 'Fourth Choice'), ('e', 'Fifth Choice') ) class TestForm(Form): text = fields.CharField() email = fields.EmailField() value = fields.IntegerField() single = fields.ChoiceField(choices=TestChoices) multi = fields.MultipleChoiceField(choices=TestChoices) def clean(self): cleaned_data = self.cleaned_data if cleaned_data.get("text") == "Raise non-field error": raise ValidationError("Non-field error.") return cleaned_data def form_view(request): "A view that tests a simple form" if request.method == 'POST': form = TestForm(request.POST) if form.is_valid(): t = Template('Valid POST data.', name='Valid POST Template') c = Context() else: t = Template('Invalid POST data. {{ form.errors }}', name='Invalid POST Template') c = Context({'form': form}) else: form = TestForm(request.GET) t = Template('Viewing base form. {{ form }}.', name='Form GET Template') c = Context({'form': form}) return HttpResponse(t.render(c)) def form_view_with_template(request): "A view that tests a simple form" if request.method == 'POST': form = TestForm(request.POST) if form.is_valid(): message = 'POST data OK' else: message = 'POST data has errors' else: form = TestForm() message = 'GET form page' return render_to_response('form_view.html', { 'form': form, 'message': message } ) class BaseTestFormSet(BaseFormSet): def clean(self): """Checks that no two email addresses are the same.""" if any(self.errors): # Don't bother validating the formset unless each form is valid return emails = [] for i in range(0, self.total_form_count()): form = self.forms[i] email = form.cleaned_data['email'] if email in emails: raise ValidationError( "Forms in a set must have distinct email addresses." ) emails.append(email) TestFormSet = formset_factory(TestForm, BaseTestFormSet) def formset_view(request): "A view that tests a simple formset" if request.method == 'POST': formset = TestFormSet(request.POST) if formset.is_valid(): t = Template('Valid POST data.', name='Valid POST Template') c = Context() else: t = Template('Invalid POST data. {{ my_formset.errors }}', name='Invalid POST Template') c = Context({'my_formset': formset}) else: formset = TestForm(request.GET) t = Template('Viewing base formset. {{ my_formset }}.', name='Formset GET Template') 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') c = Context({'user': request.user}) 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') c = Context({'user': request.user}) 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. ' 'Username is {{ user.username }}. ' 'Permissions are {{ user.get_all_permissions }}.', name='Permissions Template') c = Context({'user': request.user}) return HttpResponse(t.render(c)) 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): t = Template('This is a login protected test using a method. ' 'Username is {{ user.username }}.', name='Login Method Template') c = Context({'user': request.user}) return HttpResponse(t.render(c)) @method_decorator(permission_required('permission_not_granted')) def permission_protected_view(self, request): t = Template('This is a permission protected test using a method. ' 'Username is {{ user.username }}. ' 'Permissions are {{ user.get_all_permissions }}.', name='Permissions Template') c = Context({'user': request.user}) return HttpResponse(t.render(c)) _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' t = Template('This is a view that modifies the session.', name='Session Modifying View Template') 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", "This is a test email", "from@example.com", ['first@example.com', 'second@example.com']).send() return HttpResponse("Mail sent") def mass_mail_sending_view(request): m1 = mail.EmailMessage( 'First Test message', 'This is the first test email', 'from@example.com', ['first@example.com', 'second@example.com']) m2 = mail.EmailMessage( 'Second Test message', 'This is the second test email', 'from@example.com', ['second@example.com', 'third@example.com']) c = mail.get_connection() c.send_messages([m1, m2]) return HttpResponse("Mail sent") def django_project_redirect(request): return HttpResponseRedirect('https://www.djangoproject.com/') Django-1.8.7/tests/test_client/urls.py0000664000175000017500000000411212625116214017353 0ustar timtim00000000000000from django.conf.urls import url from django.contrib.auth import views as auth_views from django.views.generic import RedirectView from . import views urlpatterns = [ url(r'^get_view/$', views.get_view, name='get_view'), url(r'^post_view/$', views.post_view), url(r'^trace_view/$', views.trace_view), url(r'^header_view/$', views.view_with_header), url(r'^raw_post_view/$', views.raw_post_view), url(r'^redirect_view/$', views.redirect_view), url(r'^secure_view/$', views.view_with_secure), url(r'^permanent_redirect_view/$', RedirectView.as_view(url='/get_view/', permanent=True)), url(r'^temporary_redirect_view/$', RedirectView.as_view(url='/get_view/', permanent=False)), url(r'^http_redirect_view/$', RedirectView.as_view(url='/secure_view/', permanent=True)), url(r'^https_redirect_view/$', RedirectView.as_view(url='https://testserver/secure_view/', permanent=True)), url(r'^double_redirect_view/$', views.double_redirect_view), url(r'^bad_view/$', views.bad_view), url(r'^form_view/$', views.form_view), url(r'^form_view_with_template/$', views.form_view_with_template), url(r'^formset_view/$', views.formset_view), url(r'^login_protected_view/$', views.login_protected_view), url(r'^login_protected_method_view/$', views.login_protected_method_view), url(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect), url(r'^permission_protected_view/$', views.permission_protected_view), url(r'^permission_protected_view_exception/$', views.permission_protected_view_exception), url(r'^permission_protected_method_view/$', views.permission_protected_method_view), url(r'^session_view/$', views.session_view), url(r'^broken_view/$', views.broken_view), url(r'^mail_sending_view/$', views.mail_sending_view), url(r'^mass_mail_sending_view/$', views.mass_mail_sending_view), url(r'^django_project_redirect/$', views.django_project_redirect), url(r'^accounts/login/$', auth_views.login, {'template_name': 'login.html'}), url(r'^accounts/logout/$', auth_views.logout), ] Django-1.8.7/tests/test_client/tests.py0000664000175000017500000006205012625116214017535 0ustar timtim00000000000000# -*- coding: utf-8 -*- """ Testing using the Test Client The test client is a class that can act like a simple browser for testing purposes. It allows the user to compose GET and POST requests, and obtain the response that the server gave to those requests. The server Response objects are annotated with the details of the contexts and templates that were rendered during the process of serving the request. ``Client`` objects are stateful - they will retain cookie (and thus session) details for the lifetime of the ``Client`` instance. This is not intended as a replacement for Twill, Selenium, or other browser automation frameworks - it is here to allow testing against the contexts and templates produced by a view, rather than the HTML rendered to the end-user. """ from __future__ import unicode_literals from django.core import mail from django.http import HttpResponse from django.test import Client, RequestFactory, TestCase, override_settings from .views import get_view, post_view, trace_view @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF='test_client.urls',) class ClientTest(TestCase): fixtures = ['testdata.json'] def test_get_view(self): "GET a view" # The data is ignored, but let's check it doesn't crash the system # anyway. data = {'var': '\xf2'} response = self.client.get('/get_view/', data) # Check some response details self.assertContains(response, 'This is a test') self.assertEqual(response.context['var'], '\xf2') self.assertEqual(response.templates[0].name, 'GET Template') def test_get_post_view(self): "GET a view that normally expects POSTs" response = self.client.get('/post_view/', {}) # Check some response details self.assertEqual(response.status_code, 200) self.assertEqual(response.templates[0].name, 'Empty GET Template') self.assertTemplateUsed(response, 'Empty GET Template') self.assertTemplateNotUsed(response, 'Empty POST Template') def test_empty_post(self): "POST an empty dictionary to a view" response = self.client.post('/post_view/', {}) # Check some response details self.assertEqual(response.status_code, 200) self.assertEqual(response.templates[0].name, 'Empty POST Template') self.assertTemplateNotUsed(response, 'Empty GET Template') self.assertTemplateUsed(response, 'Empty POST Template') def test_post(self): "POST some data to a view" post_data = { 'value': 37 } response = self.client.post('/post_view/', post_data) # Check some response details self.assertEqual(response.status_code, 200) self.assertEqual(response.context['data'], '37') self.assertEqual(response.templates[0].name, 'POST Template') self.assertContains(response, 'Data received') def test_trace(self): """TRACE a view""" response = self.client.trace('/trace_view/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['method'], 'TRACE') self.assertEqual(response.templates[0].name, 'TRACE Template') def test_response_headers(self): "Check the value of HTTP headers returned in a response" response = self.client.get("/header_view/") self.assertEqual(response['X-DJANGO-TEST'], 'Slartibartfast') def test_response_attached_request(self): """ Check that the returned response has a ``request`` attribute with the originating environ dict and a ``wsgi_request`` with the originating ``WSGIRequest`` instance. """ response = self.client.get("/header_view/") self.assertTrue(hasattr(response, 'request')) self.assertTrue(hasattr(response, 'wsgi_request')) for key, value in response.request.items(): self.assertIn(key, response.wsgi_request.environ) self.assertEqual(response.wsgi_request.environ[key], value) def test_response_resolver_match(self): """ The response contains a ResolverMatch instance. """ response = self.client.get('/header_view/') self.assertTrue(hasattr(response, 'resolver_match')) def test_response_resolver_match_redirect_follow(self): """ The response ResolverMatch instance contains the correct information when following redirects. """ response = self.client.get('/redirect_view/', follow=True) self.assertEqual(response.resolver_match.url_name, 'get_view') def test_response_resolver_match_regular_view(self): """ The response ResolverMatch instance contains the correct information when accessing a regular view. """ response = self.client.get('/get_view/') self.assertEqual(response.resolver_match.url_name, 'get_view') def test_raw_post(self): "POST raw data (with a content type) to a view" test_doc = """BlinkMalcolm Gladwell""" response = self.client.post("/raw_post_view/", test_doc, content_type="text/xml") self.assertEqual(response.status_code, 200) self.assertEqual(response.templates[0].name, "Book template") self.assertEqual(response.content, b"Blink - Malcolm Gladwell") def test_insecure(self): "GET a URL through http" response = self.client.get('/secure_view/', secure=False) self.assertFalse(response.test_was_secure_request) self.assertEqual(response.test_server_port, '80') def test_secure(self): "GET a URL through https" response = self.client.get('/secure_view/', secure=True) self.assertTrue(response.test_was_secure_request) self.assertEqual(response.test_server_port, '443') def test_redirect(self): "GET a URL that redirects elsewhere" response = self.client.get('/redirect_view/') # Check that the response was a 302 (redirect) and that # assertRedirect() understands to put an implicit http://testserver/ in # front of non-absolute URLs. self.assertRedirects(response, '/get_view/') host = 'django.testserver' client_providing_host = Client(HTTP_HOST=host) response = client_providing_host.get('/redirect_view/') # Check that the response was a 302 (redirect) with absolute URI self.assertRedirects(response, '/get_view/', host=host) def test_redirect_with_query(self): "GET a URL that redirects with given GET parameters" response = self.client.get('/redirect_view/', {'var': 'value'}) # Check if parameters are intact self.assertRedirects(response, 'http://testserver/get_view/?var=value') def test_permanent_redirect(self): "GET a URL that redirects permanently elsewhere" response = self.client.get('/permanent_redirect_view/') # Check that the response was a 301 (permanent redirect) self.assertRedirects(response, 'http://testserver/get_view/', status_code=301) client_providing_host = Client(HTTP_HOST='django.testserver') response = client_providing_host.get('/permanent_redirect_view/') # Check that the response was a 301 (permanent redirect) with absolute URI self.assertRedirects(response, 'http://django.testserver/get_view/', status_code=301) def test_temporary_redirect(self): "GET a URL that does a non-permanent redirect" response = self.client.get('/temporary_redirect_view/') # Check that the response was a 302 (non-permanent redirect) self.assertRedirects(response, 'http://testserver/get_view/', status_code=302) def test_redirect_to_strange_location(self): "GET a URL that redirects to a non-200 page" response = self.client.get('/double_redirect_view/') # Check that the response was a 302, and that # the attempt to get the redirection location returned 301 when retrieved self.assertRedirects(response, 'http://testserver/permanent_redirect_view/', target_status_code=301) def test_follow_redirect(self): "A URL that redirects can be followed to termination." response = self.client.get('/double_redirect_view/', follow=True) self.assertRedirects(response, 'http://testserver/get_view/', status_code=302, target_status_code=200) self.assertEqual(len(response.redirect_chain), 2) def test_redirect_http(self): "GET a URL that redirects to an http URI" response = self.client.get('/http_redirect_view/', follow=True) self.assertFalse(response.test_was_secure_request) def test_redirect_https(self): "GET a URL that redirects to an https URI" response = self.client.get('/https_redirect_view/', follow=True) self.assertTrue(response.test_was_secure_request) def test_notfound_response(self): "GET a URL that responds as '404:Not Found'" response = self.client.get('/bad_view/') # Check that the response was a 404, and that the content contains MAGIC self.assertContains(response, 'MAGIC', status_code=404) def test_valid_form(self): "POST valid data to a form" post_data = { 'text': 'Hello World', 'email': 'foo@example.com', 'value': 37, 'single': 'b', 'multi': ('b', 'c', 'e') } response = self.client.post('/form_view/', post_data) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "Valid POST Template") def test_valid_form_with_hints(self): "GET a form, providing hints in the GET data" hints = { 'text': 'Hello World', 'multi': ('b', 'c', 'e') } response = self.client.get('/form_view/', data=hints) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "Form GET Template") # Check that the multi-value data has been rolled out ok self.assertContains(response, 'Select a valid choice.', 0) def test_incomplete_data_form(self): "POST incomplete data to a form" post_data = { 'text': 'Hello World', 'value': 37 } response = self.client.post('/form_view/', post_data) self.assertContains(response, 'This field is required.', 3) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "Invalid POST Template") self.assertFormError(response, 'form', 'email', 'This field is required.') self.assertFormError(response, 'form', 'single', 'This field is required.') self.assertFormError(response, 'form', 'multi', 'This field is required.') def test_form_error(self): "POST erroneous data to a form" post_data = { 'text': 'Hello World', 'email': 'not an email address', 'value': 37, 'single': 'b', 'multi': ('b', 'c', 'e') } response = self.client.post('/form_view/', post_data) self.assertEqual(response.status_code, 200) self.assertTemplateUsed(response, "Invalid POST Template") self.assertFormError(response, 'form', 'email', 'Enter a valid email address.') def test_valid_form_with_template(self): "POST valid data to a form using multiple templates" post_data = { 'text': 'Hello World', 'email': 'foo@example.com', 'value': 37, 'single': 'b', 'multi': ('b', 'c', 'e') } response = self.client.post('/form_view_with_template/', post_data) self.assertContains(response, 'POST data OK') self.assertTemplateUsed(response, "form_view.html") self.assertTemplateUsed(response, 'base.html') self.assertTemplateNotUsed(response, "Valid POST Template") def test_incomplete_data_form_with_template(self): "POST incomplete data to a form using multiple templates" post_data = { 'text': 'Hello World', 'value': 37 } response = self.client.post('/form_view_with_template/', post_data) self.assertContains(response, 'POST data has errors') self.assertTemplateUsed(response, 'form_view.html') self.assertTemplateUsed(response, 'base.html') self.assertTemplateNotUsed(response, "Invalid POST Template") self.assertFormError(response, 'form', 'email', 'This field is required.') self.assertFormError(response, 'form', 'single', 'This field is required.') self.assertFormError(response, 'form', 'multi', 'This field is required.') def test_form_error_with_template(self): "POST erroneous data to a form using multiple templates" post_data = { 'text': 'Hello World', 'email': 'not an email address', 'value': 37, 'single': 'b', 'multi': ('b', 'c', 'e') } response = self.client.post('/form_view_with_template/', post_data) self.assertContains(response, 'POST data has errors') self.assertTemplateUsed(response, "form_view.html") self.assertTemplateUsed(response, 'base.html') self.assertTemplateNotUsed(response, "Invalid POST Template") self.assertFormError(response, 'form', 'email', 'Enter a valid email address.') def test_unknown_page(self): "GET an invalid URL" response = self.client.get('/unknown_view/') # Check that the response was a 404 self.assertEqual(response.status_code, 404) def test_url_parameters(self): "Make sure that URL ;-parameters are not stripped." response = self.client.get('/unknown_view/;some-parameter') # Check that the path in the response includes it (ignore that it's a 404) self.assertEqual(response.request['PATH_INFO'], '/unknown_view/;some-parameter') def test_view_with_login(self): "Request a page that is protected with @login_required" # Get the page without logging in. Should result in 302. response = self.client.get('/login_protected_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/login_protected_view/') # Log in login = self.client.login(username='testclient', password='password') self.assertTrue(login, 'Could not log in') # Request a page that requires a login response = self.client.get('/login_protected_view/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['user'].username, 'testclient') def test_view_with_method_login(self): "Request a page that is protected with a @login_required method" # Get the page without logging in. Should result in 302. response = self.client.get('/login_protected_method_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/login_protected_method_view/') # Log in login = self.client.login(username='testclient', password='password') self.assertTrue(login, 'Could not log in') # Request a page that requires a login response = self.client.get('/login_protected_method_view/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['user'].username, 'testclient') def test_view_with_login_and_custom_redirect(self): "Request a page that is protected with @login_required(redirect_field_name='redirect_to')" # Get the page without logging in. Should result in 302. response = self.client.get('/login_protected_view_custom_redirect/') self.assertRedirects(response, 'http://testserver/accounts/login/?redirect_to=/login_protected_view_custom_redirect/') # Log in login = self.client.login(username='testclient', password='password') self.assertTrue(login, 'Could not log in') # Request a page that requires a login response = self.client.get('/login_protected_view_custom_redirect/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['user'].username, 'testclient') def test_view_with_bad_login(self): "Request a page that is protected with @login, but use bad credentials" login = self.client.login(username='otheruser', password='nopassword') self.assertFalse(login) def test_view_with_inactive_login(self): "Request a page that is protected with @login, but use an inactive login" login = self.client.login(username='inactive', password='password') self.assertFalse(login) def test_logout(self): "Request a logout after logging in" # Log in self.client.login(username='testclient', password='password') # Request a page that requires a login response = self.client.get('/login_protected_view/') self.assertEqual(response.status_code, 200) self.assertEqual(response.context['user'].username, 'testclient') # Log out self.client.logout() # Request a page that requires a login response = self.client.get('/login_protected_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/login_protected_view/') @override_settings(SESSION_ENGINE="django.contrib.sessions.backends.signed_cookies") def test_logout_cookie_sessions(self): self.test_logout() def test_view_with_permissions(self): "Request a page that is protected with @permission_required" # Get the page without logging in. Should result in 302. response = self.client.get('/permission_protected_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_view/') # Log in login = self.client.login(username='testclient', password='password') self.assertTrue(login, 'Could not log in') # Log in with wrong permissions. Should result in 302. response = self.client.get('/permission_protected_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_view/') # TODO: Log in with right permissions and request the page again def test_view_with_permissions_exception(self): "Request a page that is protected with @permission_required but raises an exception" # Get the page without logging in. Should result in 403. response = self.client.get('/permission_protected_view_exception/') self.assertEqual(response.status_code, 403) # Log in login = self.client.login(username='testclient', password='password') self.assertTrue(login, 'Could not log in') # Log in with wrong permissions. Should result in 403. response = self.client.get('/permission_protected_view_exception/') self.assertEqual(response.status_code, 403) def test_view_with_method_permissions(self): "Request a page that is protected with a @permission_required method" # Get the page without logging in. Should result in 302. response = self.client.get('/permission_protected_method_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_method_view/') # Log in login = self.client.login(username='testclient', password='password') self.assertTrue(login, 'Could not log in') # Log in with wrong permissions. Should result in 302. response = self.client.get('/permission_protected_method_view/') self.assertRedirects(response, 'http://testserver/accounts/login/?next=/permission_protected_method_view/') # TODO: Log in with right permissions and request the page again def test_external_redirect(self): response = self.client.get('/django_project_redirect/') self.assertRedirects(response, 'https://www.djangoproject.com/', fetch_redirect_response=False) def test_session_modifying_view(self): "Request a page that modifies the session" # Session value isn't set initially try: self.client.session['tobacconist'] self.fail("Shouldn't have a session value") except KeyError: pass self.client.post('/session_view/') # Check that the session was modified self.assertEqual(self.client.session['tobacconist'], 'hovercraft') def test_view_with_exception(self): "Request a page that is known to throw an error" self.assertRaises(KeyError, self.client.get, "/broken_view/") # Try the same assertion, a different way try: self.client.get('/broken_view/') self.fail('Should raise an error') except KeyError: pass def test_mail_sending(self): "Test that mail is redirected to a dummy outbox during test setup" response = self.client.get('/mail_sending_view/') self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 1) self.assertEqual(mail.outbox[0].subject, 'Test message') self.assertEqual(mail.outbox[0].body, 'This is a test email') self.assertEqual(mail.outbox[0].from_email, 'from@example.com') self.assertEqual(mail.outbox[0].to[0], 'first@example.com') self.assertEqual(mail.outbox[0].to[1], 'second@example.com') def test_mass_mail_sending(self): "Test that mass mail is redirected to a dummy outbox during test setup" response = self.client.get('/mass_mail_sending_view/') self.assertEqual(response.status_code, 200) self.assertEqual(len(mail.outbox), 2) self.assertEqual(mail.outbox[0].subject, 'First Test message') self.assertEqual(mail.outbox[0].body, 'This is the first test email') self.assertEqual(mail.outbox[0].from_email, 'from@example.com') self.assertEqual(mail.outbox[0].to[0], 'first@example.com') self.assertEqual(mail.outbox[0].to[1], 'second@example.com') self.assertEqual(mail.outbox[1].subject, 'Second Test message') self.assertEqual(mail.outbox[1].body, 'This is the second test email') self.assertEqual(mail.outbox[1].from_email, 'from@example.com') self.assertEqual(mail.outbox[1].to[0], 'second@example.com') self.assertEqual(mail.outbox[1].to[1], 'third@example.com') @override_settings( MIDDLEWARE_CLASSES=('django.middleware.csrf.CsrfViewMiddleware',), ROOT_URLCONF='test_client.urls', ) class CSRFEnabledClientTests(TestCase): def test_csrf_enabled_client(self): "A client can be instantiated with CSRF checks enabled" csrf_client = Client(enforce_csrf_checks=True) # The normal client allows the post response = self.client.post('/post_view/', {}) self.assertEqual(response.status_code, 200) # The CSRF-enabled client rejects it response = csrf_client.post('/post_view/', {}) self.assertEqual(response.status_code, 403) class CustomTestClient(Client): i_am_customized = "Yes" class CustomTestClientTest(TestCase): client_class = CustomTestClient def test_custom_test_client(self): """A test case can specify a custom class for self.client.""" self.assertEqual(hasattr(self.client, "i_am_customized"), True) _generic_view = lambda request: HttpResponse(status=200) @override_settings(ROOT_URLCONF='test_client.urls') class RequestFactoryTest(TestCase): """Tests for the request factory.""" # A mapping between names of HTTP/1.1 methods and their test views. http_methods_and_views = ( ('get', get_view), ('post', post_view), ('put', _generic_view), ('patch', _generic_view), ('delete', _generic_view), ('head', _generic_view), ('options', _generic_view), ('trace', trace_view), ) def setUp(self): self.request_factory = RequestFactory() def test_request_factory(self): """The request factory implements all the HTTP/1.1 methods.""" for method_name, view in self.http_methods_and_views: method = getattr(self.request_factory, method_name) request = method('/somewhere/') response = view(request) self.assertEqual(response.status_code, 200) def test_get_request_from_factory(self): """ The request factory returns a templated response for a GET request. """ request = self.request_factory.get('/somewhere/') response = get_view(request) self.assertEqual(response.status_code, 200) self.assertContains(response, 'This is a test') def test_trace_request_from_factory(self): """The request factory returns an echo response for a TRACE request.""" url_path = '/somewhere/' request = self.request_factory.trace(url_path) response = trace_view(request) protocol = request.META["SERVER_PROTOCOL"] echoed_request_line = "TRACE {} {}".format(url_path, protocol) self.assertEqual(response.status_code, 200) self.assertContains(response, echoed_request_line) Django-1.8.7/tests/test_client/fixtures/0000775000175000017500000000000012625116243017671 5ustar timtim00000000000000Django-1.8.7/tests/test_client/fixtures/testdata.json0000664000175000017500000000332012625113144022370 0ustar timtim00000000000000[ { "pk": "1", "model": "auth.user", "fields": { "username": "testclient", "first_name": "Test", "last_name": "Client", "is_active": true, "is_superuser": false, "is_staff": false, "last_login": "2006-12-17 07:03:31", "groups": [], "user_permissions": [], "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", "email": "testclient@example.com", "date_joined": "2006-12-17 07:03:31" } }, { "pk": "2", "model": "auth.user", "fields": { "username": "inactive", "first_name": "Inactive", "last_name": "User", "is_active": false, "is_superuser": false, "is_staff": false, "last_login": "2006-12-17 07:03:31", "groups": [], "user_permissions": [], "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", "email": "testclient@example.com", "date_joined": "2006-12-17 07:03:31" } }, { "pk": "3", "model": "auth.user", "fields": { "username": "staff", "first_name": "Staff", "last_name": "Member", "is_active": true, "is_superuser": false, "is_staff": true, "last_login": "2006-12-17 07:03:31", "groups": [], "user_permissions": [], "password": "sha1$6efc0$f93efe9fd7542f25a7be94871ea45aa95de57161", "email": "testclient@example.com", "date_joined": "2006-12-17 07:03:31" } } ]Django-1.8.7/tests/test_client/__init__.py0000664000175000017500000000000012603513307020115 0ustar timtim00000000000000Django-1.8.7/tests/sites_framework/0000775000175000017500000000000012625116243016707 5ustar timtim00000000000000Django-1.8.7/tests/sites_framework/migrations/0000775000175000017500000000000012625116243021063 5ustar timtim00000000000000Django-1.8.7/tests/sites_framework/migrations/__init__.py0000664000175000017500000000000012625116214023160 0ustar timtim00000000000000Django-1.8.7/tests/sites_framework/migrations/0001_initial.py0000664000175000017500000000316112625116214023525 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('sites', '0001_initial'), ] operations = [ migrations.CreateModel( name='CustomArticle', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=50)), ('places_this_article_should_appear', models.ForeignKey(to='sites.Site')), ], options={ 'abstract': False, }, bases=(models.Model,), ), migrations.CreateModel( name='ExclusiveArticle', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=50)), ('site', models.ForeignKey(to='sites.Site')), ], options={ 'abstract': False, }, bases=(models.Model,), ), migrations.CreateModel( name='SyndicatedArticle', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=50)), ('sites', models.ManyToManyField(to='sites.Site')), ], options={ 'abstract': False, }, bases=(models.Model,), ), ] Django-1.8.7/tests/sites_framework/tests.py0000664000175000017500000000553312625116214020427 0ustar timtim00000000000000from django.apps import apps from django.conf import settings from django.contrib.sites.managers import CurrentSiteManager from django.contrib.sites.models import Site from django.core import checks from django.db import models from django.test import TestCase from .models import ( AbstractArticle, CustomArticle, ExclusiveArticle, SyndicatedArticle, ) class SitesFrameworkTestCase(TestCase): def setUp(self): Site.objects.get_or_create(id=settings.SITE_ID, domain="example.com", name="example.com") Site.objects.create(id=settings.SITE_ID + 1, domain="example2.com", name="example2.com") self._old_models = apps.app_configs['sites_framework'].models.copy() def tearDown(self): apps.app_configs['sites_framework'].models = self._old_models apps.all_models['sites_framework'] = self._old_models apps.clear_cache() def test_site_fk(self): article = ExclusiveArticle.objects.create(title="Breaking News!", site_id=settings.SITE_ID) self.assertEqual(ExclusiveArticle.on_site.all().get(), article) def test_sites_m2m(self): article = SyndicatedArticle.objects.create(title="Fresh News!") article.sites.add(Site.objects.get(id=settings.SITE_ID)) article.sites.add(Site.objects.get(id=settings.SITE_ID + 1)) article2 = SyndicatedArticle.objects.create(title="More News!") article2.sites.add(Site.objects.get(id=settings.SITE_ID + 1)) self.assertEqual(SyndicatedArticle.on_site.all().get(), article) def test_custom_named_field(self): article = CustomArticle.objects.create(title="Tantalizing News!", places_this_article_should_appear_id=settings.SITE_ID) self.assertEqual(CustomArticle.on_site.all().get(), article) def test_invalid_name(self): class InvalidArticle(AbstractArticle): site = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager("places_this_article_should_appear") errors = InvalidArticle.check() expected = [ checks.Error( ("CurrentSiteManager could not find a field named " "'places_this_article_should_appear'."), hint=None, obj=InvalidArticle.on_site, id='sites.E001', ) ] self.assertEqual(errors, expected) def test_invalid_field_type(self): class ConfusedArticle(AbstractArticle): site = models.IntegerField() errors = ConfusedArticle.check() expected = [ checks.Error( "CurrentSiteManager cannot use 'ConfusedArticle.site' as it is not a ForeignKey or ManyToManyField.", hint=None, obj=ConfusedArticle.on_site, id='sites.E002', ) ] self.assertEqual(errors, expected) Django-1.8.7/tests/sites_framework/__init__.py0000664000175000017500000000000012603513307021004 0ustar timtim00000000000000Django-1.8.7/tests/sites_framework/models.py0000664000175000017500000000150412625113144020541 0ustar timtim00000000000000from django.contrib.sites.managers import CurrentSiteManager from django.contrib.sites.models import Site from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class AbstractArticle(models.Model): title = models.CharField(max_length=50) objects = models.Manager() on_site = CurrentSiteManager() class Meta: abstract = True 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") Django-1.8.7/tests/check_framework/0000775000175000017500000000000012625116243016635 5ustar timtim00000000000000Django-1.8.7/tests/check_framework/tests_1_8_compatibility.py0000664000175000017500000000214112625116213023744 0ustar timtim00000000000000from django.core.checks.compatibility.django_1_8_0 import \ check_duplicate_template_settings from django.test import SimpleTestCase from django.test.utils import override_settings class CheckDuplicateTemplateSettingsTest(SimpleTestCase): def test_not_raised_if_no_templates_setting(self): self.assertEqual(check_duplicate_template_settings(None), []) @override_settings( TEMPLATES=[{'BACKEND': 'django.template.backends.django.DjangoTemplates'}], TEMPLATE_DIRS=['/path/to/dirs'], ) def test_duplicate_setting(self): result = check_duplicate_template_settings(None) self.assertEqual(result[0].id, '1_8.W001') @override_settings( TEMPLATES=[{'BACKEND': 'django.template.backends.django.DjangoTemplates'}], TEMPLATE_DIRS=['/path/to/dirs'], TEMPLATE_DEBUG=True, ) def test_multiple_duplicate_settings(self): result = check_duplicate_template_settings(None) self.assertEqual(len(result), 1) self.assertTrue('TEMPLATE_DIRS' in result[0].msg) self.assertTrue('TEMPLATE_DEBUG' in result[0].msg) Django-1.8.7/tests/check_framework/tests.py0000664000175000017500000003123212625116213020347 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import sys from django.apps import apps from django.conf import settings from django.core import checks from django.core.checks import Error, Warning from django.core.checks.compatibility.django_1_7_0 import \ check_1_7_compatibility from django.core.checks.registry import CheckRegistry from django.core.management import call_command from django.core.management.base import CommandError from django.db import models from django.test import TestCase from django.test.utils import override_settings, override_system_checks from django.utils.encoding import force_text from django.utils.six import StringIO from .models import SimpleModel class DummyObj(object): def __repr__(self): return "obj" class SystemCheckFrameworkTests(TestCase): def test_register_and_run_checks(self): def f(**kwargs): calls[0] += 1 return [1, 2, 3] def f2(**kwargs): return [4, ] def f3(**kwargs): return [5, ] calls = [0] # test register as decorator registry = CheckRegistry() registry.register()(f) registry.register("tag1", "tag2")(f2) registry.register("tag2", deploy=True)(f3) # test register as function registry2 = CheckRegistry() registry2.register(f) registry2.register(f2, "tag1", "tag2") registry2.register(f3, "tag2", deploy=True) # check results errors = registry.run_checks() errors2 = registry2.run_checks() self.assertEqual(errors, errors2) self.assertEqual(sorted(errors), [1, 2, 3, 4]) self.assertEqual(calls[0], 2) errors = registry.run_checks(tags=["tag1"]) errors2 = registry2.run_checks(tags=["tag1"]) self.assertEqual(errors, errors2) self.assertEqual(sorted(errors), [4]) errors = registry.run_checks(tags=["tag1", "tag2"], include_deployment_checks=True) errors2 = registry2.run_checks(tags=["tag1", "tag2"], include_deployment_checks=True) self.assertEqual(errors, errors2) self.assertEqual(sorted(errors), [4, 5]) class MessageTests(TestCase): def test_printing(self): e = Error("Message", hint="Hint", obj=DummyObj()) expected = "obj: Message\n\tHINT: Hint" self.assertEqual(force_text(e), expected) def test_printing_no_hint(self): e = Error("Message", hint=None, obj=DummyObj()) expected = "obj: Message" self.assertEqual(force_text(e), expected) def test_printing_no_object(self): e = Error("Message", hint="Hint", obj=None) expected = "?: Message\n\tHINT: Hint" self.assertEqual(force_text(e), expected) def test_printing_with_given_id(self): e = Error("Message", hint="Hint", obj=DummyObj(), id="ID") expected = "obj: (ID) Message\n\tHINT: Hint" self.assertEqual(force_text(e), expected) def test_printing_field_error(self): field = SimpleModel._meta.get_field('field') e = Error("Error", hint=None, obj=field) expected = "check_framework.SimpleModel.field: Error" self.assertEqual(force_text(e), expected) def test_printing_model_error(self): e = Error("Error", hint=None, obj=SimpleModel) expected = "check_framework.SimpleModel: Error" self.assertEqual(force_text(e), expected) def test_printing_manager_error(self): manager = SimpleModel.manager e = Error("Error", hint=None, obj=manager) expected = "check_framework.SimpleModel.manager: Error" self.assertEqual(force_text(e), expected) class Django_1_7_0_CompatibilityChecks(TestCase): @override_settings(MIDDLEWARE_CLASSES=('django.contrib.sessions.middleware.SessionMiddleware',)) def test_middleware_classes_overridden(self): errors = check_1_7_compatibility() self.assertEqual(errors, []) def test_middleware_classes_not_set_explicitly(self): # If MIDDLEWARE_CLASSES was set explicitly, temporarily pretend it wasn't middleware_classes_overridden = False if 'MIDDLEWARE_CLASSES' in settings._wrapped._explicit_settings: middleware_classes_overridden = True settings._wrapped._explicit_settings.remove('MIDDLEWARE_CLASSES') try: errors = check_1_7_compatibility() expected = [ checks.Warning( "MIDDLEWARE_CLASSES is not set.", hint=("Django 1.7 changed the global defaults for the MIDDLEWARE_CLASSES. " "django.contrib.sessions.middleware.SessionMiddleware, " "django.contrib.auth.middleware.AuthenticationMiddleware, and " "django.contrib.messages.middleware.MessageMiddleware were removed from the defaults. " "If your project needs these middleware then you should configure this setting."), obj=None, id='1_7.W001', ) ] self.assertEqual(errors, expected) finally: # Restore settings value if middleware_classes_overridden: settings._wrapped._explicit_settings.add('MIDDLEWARE_CLASSES') def simple_system_check(**kwargs): simple_system_check.kwargs = kwargs return [] def tagged_system_check(**kwargs): tagged_system_check.kwargs = kwargs return [] tagged_system_check.tags = ['simpletag'] def deployment_system_check(**kwargs): deployment_system_check.kwargs = kwargs return [checks.Warning('Deployment Check')] deployment_system_check.tags = ['deploymenttag'] class CheckCommandTests(TestCase): def setUp(self): simple_system_check.kwargs = None tagged_system_check.kwargs = None self.old_stdout, self.old_stderr = sys.stdout, sys.stderr sys.stdout, sys.stderr = StringIO(), StringIO() def tearDown(self): sys.stdout, sys.stderr = self.old_stdout, self.old_stderr @override_system_checks([simple_system_check, tagged_system_check]) def test_simple_call(self): call_command('check') self.assertEqual(simple_system_check.kwargs, {'app_configs': None}) self.assertEqual(tagged_system_check.kwargs, {'app_configs': None}) @override_system_checks([simple_system_check, tagged_system_check]) def test_given_app(self): call_command('check', 'auth', 'admin') auth_config = apps.get_app_config('auth') admin_config = apps.get_app_config('admin') self.assertEqual(simple_system_check.kwargs, {'app_configs': [auth_config, admin_config]}) self.assertEqual(tagged_system_check.kwargs, {'app_configs': [auth_config, admin_config]}) @override_system_checks([simple_system_check, tagged_system_check]) def test_given_tag(self): call_command('check', tags=['simpletag']) self.assertEqual(simple_system_check.kwargs, None) self.assertEqual(tagged_system_check.kwargs, {'app_configs': None}) @override_system_checks([simple_system_check, tagged_system_check]) def test_invalid_tag(self): self.assertRaises(CommandError, call_command, 'check', tags=['missingtag']) @override_system_checks([simple_system_check]) def test_list_tags_empty(self): call_command('check', list_tags=True) self.assertEqual('\n', sys.stdout.getvalue()) @override_system_checks([tagged_system_check]) def test_list_tags(self): call_command('check', list_tags=True) self.assertEqual('simpletag\n', sys.stdout.getvalue()) @override_system_checks([tagged_system_check], deployment_checks=[deployment_system_check]) def test_list_deployment_check_omitted(self): call_command('check', list_tags=True) self.assertEqual('simpletag\n', sys.stdout.getvalue()) @override_system_checks([tagged_system_check], deployment_checks=[deployment_system_check]) def test_list_deployment_check_included(self): call_command('check', deploy=True, list_tags=True) self.assertEqual('deploymenttag\nsimpletag\n', sys.stdout.getvalue()) @override_system_checks([tagged_system_check], deployment_checks=[deployment_system_check]) def test_tags_deployment_check_omitted(self): msg = 'There is no system check with the "deploymenttag" tag.' with self.assertRaisesMessage(CommandError, msg): call_command('check', tags=['deploymenttag']) @override_system_checks([tagged_system_check], deployment_checks=[deployment_system_check]) def test_tags_deployment_check_included(self): call_command('check', deploy=True, tags=['deploymenttag']) self.assertIn('Deployment Check', sys.stderr.getvalue()) def custom_error_system_check(app_configs, **kwargs): return [ Error( 'Error', hint=None, id='myerrorcheck.E001', ) ] def custom_warning_system_check(app_configs, **kwargs): return [ Warning( 'Warning', hint=None, id='mywarningcheck.E001', ) ] class SilencingCheckTests(TestCase): def setUp(self): self.old_stdout, self.old_stderr = sys.stdout, sys.stderr self.stdout, self.stderr = StringIO(), StringIO() sys.stdout, sys.stderr = self.stdout, self.stderr def tearDown(self): sys.stdout, sys.stderr = self.old_stdout, self.old_stderr @override_settings(SILENCED_SYSTEM_CHECKS=['myerrorcheck.E001']) @override_system_checks([custom_error_system_check]) def test_silenced_error(self): out = StringIO() err = StringIO() try: call_command('check', stdout=out, stderr=err) except CommandError: self.fail("The mycheck.E001 check should be silenced.") self.assertEqual(out.getvalue(), '') self.assertEqual( err.getvalue(), 'System check identified some issues:\n\n' 'ERRORS:\n' '?: (myerrorcheck.E001) Error\n\n' 'System check identified 1 issue (0 silenced).\n' ) @override_settings(SILENCED_SYSTEM_CHECKS=['mywarningcheck.E001']) @override_system_checks([custom_warning_system_check]) def test_silenced_warning(self): out = StringIO() err = StringIO() try: call_command('check', stdout=out, stderr=err) except CommandError: self.fail("The mycheck.E001 check should be silenced.") self.assertEqual(out.getvalue(), 'System check identified no issues (1 silenced).\n') self.assertEqual(err.getvalue(), '') class IsolateModelsMixin(object): def setUp(self): self.current_models = apps.all_models[__package__] self.saved_models = set(self.current_models) def tearDown(self): for model in (set(self.current_models) - self.saved_models): del self.current_models[model] apps.clear_cache() class CheckFrameworkReservedNamesTests(IsolateModelsMixin, TestCase): @override_settings( SILENCED_SYSTEM_CHECKS=['models.E20', 'fields.W342'], # ForeignKey(unique=True) INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes', 'check_framework'] ) def test_model_check_method_not_shadowed(self): class ModelWithAttributeCalledCheck(models.Model): check = 42 class ModelWithFieldCalledCheck(models.Model): check = models.IntegerField() class ModelWithRelatedManagerCalledCheck(models.Model): pass class ModelWithDescriptorCalledCheck(models.Model): check = models.ForeignKey(ModelWithRelatedManagerCalledCheck) article = models.ForeignKey(ModelWithRelatedManagerCalledCheck, related_name='check') errors = checks.run_checks() expected = [ Error( "The 'ModelWithAttributeCalledCheck.check()' class method is " "currently overridden by 42.", hint=None, obj=ModelWithAttributeCalledCheck, id='models.E020' ), Error( "The 'ModelWithRelatedManagerCalledCheck.check()' class method is " "currently overridden by %r." % ModelWithRelatedManagerCalledCheck.check, hint=None, obj=ModelWithRelatedManagerCalledCheck, id='models.E020' ), Error( "The 'ModelWithDescriptorCalledCheck.check()' class method is " "currently overridden by %r." % ModelWithDescriptorCalledCheck.check, hint=None, obj=ModelWithDescriptorCalledCheck, id='models.E020' ), ] self.assertEqual(errors, expected) Django-1.8.7/tests/check_framework/test_security.py0000664000175000017500000004074012625116213022117 0ustar timtim00000000000000from django.conf import settings from django.core.checks.security import base, csrf, sessions from django.test import TestCase from django.test.utils import override_settings class CheckSessionCookieSecureTest(TestCase): @property def func(self): from django.core.checks.security.sessions import check_session_cookie_secure return check_session_cookie_secure @override_settings( SESSION_COOKIE_SECURE=False, INSTALLED_APPS=["django.contrib.sessions"], MIDDLEWARE_CLASSES=[]) def test_session_cookie_secure_with_installed_app(self): """ Warn if SESSION_COOKIE_SECURE is off and "django.contrib.sessions" is in INSTALLED_APPS. """ self.assertEqual(self.func(None), [sessions.W010]) @override_settings( SESSION_COOKIE_SECURE=False, INSTALLED_APPS=[], MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_secure_with_middleware(self): """ Warn if SESSION_COOKIE_SECURE is off and "django.contrib.sessions.middleware.SessionMiddleware" is in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), [sessions.W011]) @override_settings( SESSION_COOKIE_SECURE=False, INSTALLED_APPS=["django.contrib.sessions"], MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_secure_both(self): """ If SESSION_COOKIE_SECURE is off and we find both the session app and the middleware, provide one common warning. """ self.assertEqual(self.func(None), [sessions.W012]) @override_settings( SESSION_COOKIE_SECURE=True, INSTALLED_APPS=["django.contrib.sessions"], MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_secure_true(self): """ If SESSION_COOKIE_SECURE is on, there's no warning about it. """ self.assertEqual(self.func(None), []) class CheckSessionCookieHttpOnlyTest(TestCase): @property def func(self): from django.core.checks.security.sessions import check_session_cookie_httponly return check_session_cookie_httponly @override_settings( SESSION_COOKIE_HTTPONLY=False, INSTALLED_APPS=["django.contrib.sessions"], MIDDLEWARE_CLASSES=[]) def test_session_cookie_httponly_with_installed_app(self): """ Warn if SESSION_COOKIE_HTTPONLY is off and "django.contrib.sessions" is in INSTALLED_APPS. """ self.assertEqual(self.func(None), [sessions.W013]) @override_settings( SESSION_COOKIE_HTTPONLY=False, INSTALLED_APPS=[], MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_httponly_with_middleware(self): """ Warn if SESSION_COOKIE_HTTPONLY is off and "django.contrib.sessions.middleware.SessionMiddleware" is in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), [sessions.W014]) @override_settings( SESSION_COOKIE_HTTPONLY=False, INSTALLED_APPS=["django.contrib.sessions"], MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_httponly_both(self): """ If SESSION_COOKIE_HTTPONLY is off and we find both the session app and the middleware, provide one common warning. """ self.assertEqual(self.func(None), [sessions.W015]) @override_settings( SESSION_COOKIE_HTTPONLY=True, INSTALLED_APPS=["django.contrib.sessions"], MIDDLEWARE_CLASSES=["django.contrib.sessions.middleware.SessionMiddleware"]) def test_session_cookie_httponly_true(self): """ If SESSION_COOKIE_HTTPONLY is on, there's no warning about it. """ self.assertEqual(self.func(None), []) class CheckCSRFMiddlewareTest(TestCase): @property def func(self): from django.core.checks.security.csrf import check_csrf_middleware return check_csrf_middleware @override_settings(MIDDLEWARE_CLASSES=[]) def test_no_csrf_middleware(self): """ Warn if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), [csrf.W003]) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"]) def test_with_csrf_middleware(self): self.assertEqual(self.func(None), []) class CheckCSRFCookieSecureTest(TestCase): @property def func(self): from django.core.checks.security.csrf import check_csrf_cookie_secure return check_csrf_cookie_secure @override_settings( MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_SECURE=False) def test_with_csrf_cookie_secure_false(self): """ Warn if CsrfViewMiddleware is in MIDDLEWARE_CLASSES but CSRF_COOKIE_SECURE isn't True. """ self.assertEqual(self.func(None), [csrf.W016]) @override_settings(MIDDLEWARE_CLASSES=[], CSRF_COOKIE_SECURE=False) def test_with_csrf_cookie_secure_false_no_middleware(self): """ No warning if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES, even if CSRF_COOKIE_SECURE is False. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_SECURE=True) def test_with_csrf_cookie_secure_true(self): self.assertEqual(self.func(None), []) class CheckCSRFCookieHttpOnlyTest(TestCase): @property def func(self): from django.core.checks.security.csrf import check_csrf_cookie_httponly return check_csrf_cookie_httponly @override_settings( MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_HTTPONLY=False) def test_with_csrf_cookie_httponly_false(self): """ Warn if CsrfViewMiddleware is in MIDDLEWARE_CLASSES but CSRF_COOKIE_HTTPONLY isn't True. """ self.assertEqual(self.func(None), [csrf.W017]) @override_settings(MIDDLEWARE_CLASSES=[], CSRF_COOKIE_HTTPONLY=False) def test_with_csrf_cookie_httponly_false_no_middleware(self): """ No warning if CsrfViewMiddleware isn't in MIDDLEWARE_CLASSES, even if CSRF_COOKIE_HTTPONLY is False. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.csrf.CsrfViewMiddleware"], CSRF_COOKIE_HTTPONLY=True) def test_with_csrf_cookie_httponly_true(self): self.assertEqual(self.func(None), []) class CheckSecurityMiddlewareTest(TestCase): @property def func(self): from django.core.checks.security.base import check_security_middleware return check_security_middleware @override_settings(MIDDLEWARE_CLASSES=[]) def test_no_security_middleware(self): """ Warn if SecurityMiddleware isn't in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), [base.W001]) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"]) def test_with_security_middleware(self): self.assertEqual(self.func(None), []) class CheckStrictTransportSecurityTest(TestCase): @property def func(self): from django.core.checks.security.base import check_sts return check_sts @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_SECONDS=0) def test_no_sts(self): """ Warn if SECURE_HSTS_SECONDS isn't > 0. """ self.assertEqual(self.func(None), [base.W004]) @override_settings( MIDDLEWARE_CLASSES=[], SECURE_HSTS_SECONDS=0) def test_no_sts_no_middlware(self): """ Don't warn if SECURE_HSTS_SECONDS isn't > 0 and SecurityMiddleware isn't installed. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_SECONDS=3600) def test_with_sts(self): self.assertEqual(self.func(None), []) class CheckStrictTransportSecuritySubdomainsTest(TestCase): @property def func(self): from django.core.checks.security.base import check_sts_include_subdomains return check_sts_include_subdomains @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_INCLUDE_SUBDOMAINS=False, SECURE_HSTS_SECONDS=3600) def test_no_sts_subdomains(self): """ Warn if SECURE_HSTS_INCLUDE_SUBDOMAINS isn't True. """ self.assertEqual(self.func(None), [base.W005]) @override_settings( MIDDLEWARE_CLASSES=[], SECURE_HSTS_INCLUDE_SUBDOMAINS=False, SECURE_HSTS_SECONDS=3600) def test_no_sts_subdomains_no_middlware(self): """ Don't warn if SecurityMiddleware isn't installed. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_SSL_REDIRECT=False, SECURE_HSTS_SECONDS=None) def test_no_sts_subdomains_no_seconds(self): """ Don't warn if SECURE_HSTS_SECONDS isn't set. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_HSTS_INCLUDE_SUBDOMAINS=True, SECURE_HSTS_SECONDS=3600) def test_with_sts_subdomains(self): self.assertEqual(self.func(None), []) class CheckXFrameOptionsMiddlewareTest(TestCase): @property def func(self): from django.core.checks.security.base import check_xframe_options_middleware return check_xframe_options_middleware @override_settings(MIDDLEWARE_CLASSES=[]) def test_middleware_not_installed(self): """ Warn if XFrameOptionsMiddleware isn't in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), [base.W002]) @override_settings(MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"]) def test_middleware_installed(self): self.assertEqual(self.func(None), []) class CheckXFrameOptionsDenyTest(TestCase): @property def func(self): from django.core.checks.security.base import check_xframe_deny return check_xframe_deny @override_settings( MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"], X_FRAME_OPTIONS='SAMEORIGIN', ) def test_x_frame_options_not_deny(self): """ Warn if XFrameOptionsMiddleware is in MIDDLEWARE_CLASSES but X_FRAME_OPTIONS isn't 'DENY'. """ self.assertEqual(self.func(None), [base.W019]) @override_settings(MIDDLEWARE_CLASSES=[], X_FRAME_OPTIONS='SAMEORIGIN') def test_middleware_not_installed(self): """ No error if XFrameOptionsMiddleware isn't in MIDDLEWARE_CLASSES even if X_FRAME_OPTIONS isn't 'DENY'. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.clickjacking.XFrameOptionsMiddleware"], X_FRAME_OPTIONS='DENY', ) def test_xframe_deny(self): self.assertEqual(self.func(None), []) class CheckContentTypeNosniffTest(TestCase): @property def func(self): from django.core.checks.security.base import check_content_type_nosniff return check_content_type_nosniff @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_CONTENT_TYPE_NOSNIFF=False) def test_no_content_type_nosniff(self): """ Warn if SECURE_CONTENT_TYPE_NOSNIFF isn't True. """ self.assertEqual(self.func(None), [base.W006]) @override_settings( MIDDLEWARE_CLASSES=[], SECURE_CONTENT_TYPE_NOSNIFF=False) def test_no_content_type_nosniff_no_middleware(self): """ Don't warn if SECURE_CONTENT_TYPE_NOSNIFF isn't True and SecurityMiddleware isn't in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_CONTENT_TYPE_NOSNIFF=True) def test_with_content_type_nosniff(self): self.assertEqual(self.func(None), []) class CheckXssFilterTest(TestCase): @property def func(self): from django.core.checks.security.base import check_xss_filter return check_xss_filter @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_BROWSER_XSS_FILTER=False) def test_no_xss_filter(self): """ Warn if SECURE_BROWSER_XSS_FILTER isn't True. """ self.assertEqual(self.func(None), [base.W007]) @override_settings( MIDDLEWARE_CLASSES=[], SECURE_BROWSER_XSS_FILTER=False) def test_no_xss_filter_no_middleware(self): """ Don't warn if SECURE_BROWSER_XSS_FILTER isn't True and SecurityMiddleware isn't in MIDDLEWARE_CLASSES. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_BROWSER_XSS_FILTER=True) def test_with_xss_filter(self): self.assertEqual(self.func(None), []) class CheckSSLRedirectTest(TestCase): @property def func(self): from django.core.checks.security.base import check_ssl_redirect return check_ssl_redirect @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_SSL_REDIRECT=False) def test_no_ssl_redirect(self): """ Warn if SECURE_SSL_REDIRECT isn't True. """ self.assertEqual(self.func(None), [base.W008]) @override_settings( MIDDLEWARE_CLASSES=[], SECURE_SSL_REDIRECT=False) def test_no_ssl_redirect_no_middlware(self): """ Don't warn if SECURE_SSL_REDIRECT is False and SecurityMiddleware isn't installed. """ self.assertEqual(self.func(None), []) @override_settings( MIDDLEWARE_CLASSES=["django.middleware.security.SecurityMiddleware"], SECURE_SSL_REDIRECT=True) def test_with_ssl_redirect(self): self.assertEqual(self.func(None), []) class CheckSecretKeyTest(TestCase): @property def func(self): from django.core.checks.security.base import check_secret_key return check_secret_key @override_settings(SECRET_KEY=('abcdefghijklmnopqrstuvwx' * 2) + 'ab') def test_okay_secret_key(self): self.assertEqual(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH) self.assertGreater(len(set(settings.SECRET_KEY)), base.SECRET_KEY_MIN_UNIQUE_CHARACTERS) self.assertEqual(self.func(None), []) @override_settings(SECRET_KEY='') def test_empty_secret_key(self): self.assertEqual(self.func(None), [base.W009]) @override_settings(SECRET_KEY=None) def test_missing_secret_key(self): del settings.SECRET_KEY self.assertEqual(self.func(None), [base.W009]) @override_settings(SECRET_KEY=None) def test_none_secret_key(self): self.assertEqual(self.func(None), [base.W009]) @override_settings(SECRET_KEY=('abcdefghijklmnopqrstuvwx' * 2) + 'a') def test_low_length_secret_key(self): self.assertEqual(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH - 1) self.assertEqual(self.func(None), [base.W009]) @override_settings(SECRET_KEY='abcd' * 20) def test_low_entropy_secret_key(self): self.assertGreater(len(settings.SECRET_KEY), base.SECRET_KEY_MIN_LENGTH) self.assertLess(len(set(settings.SECRET_KEY)), base.SECRET_KEY_MIN_UNIQUE_CHARACTERS) self.assertEqual(self.func(None), [base.W009]) class CheckDebugTest(TestCase): @property def func(self): from django.core.checks.security.base import check_debug return check_debug @override_settings(DEBUG=True) def test_debug_true(self): """ Warn if DEBUG is True. """ self.assertEqual(self.func(None), [base.W018]) @override_settings(DEBUG=False) def test_debug_false(self): self.assertEqual(self.func(None), []) Django-1.8.7/tests/check_framework/test_model_field_deprecation.py0000664000175000017500000000503012625116213025061 0ustar timtim00000000000000from django.core import checks from django.db import models from django.test import SimpleTestCase from .tests import IsolateModelsMixin class TestDeprecatedField(IsolateModelsMixin, SimpleTestCase): def test_default_details(self): class MyField(models.Field): system_check_deprecated_details = {} class Model(models.Model): name = MyField() model = Model() self.assertEqual(model.check(), [ checks.Warning( msg='MyField has been deprecated.', hint=None, obj=Model._meta.get_field('name'), id='fields.WXXX', ) ]) def test_user_specified_details(self): class MyField(models.Field): system_check_deprecated_details = { 'msg': 'This field is deprecated and will be removed soon.', 'hint': 'Use something else.', 'id': 'fields.W999', } class Model(models.Model): name = MyField() model = Model() self.assertEqual(model.check(), [ checks.Warning( msg='This field is deprecated and will be removed soon.', hint='Use something else.', obj=Model._meta.get_field('name'), id='fields.W999', ) ]) class TestRemovedField(IsolateModelsMixin, SimpleTestCase): def test_default_details(self): class MyField(models.Field): system_check_removed_details = {} class Model(models.Model): name = MyField() model = Model() self.assertEqual(model.check(), [ checks.Error( msg='MyField has been removed except for support in historical migrations.', hint=None, obj=Model._meta.get_field('name'), id='fields.EXXX', ) ]) def test_user_specified_details(self): class MyField(models.Field): system_check_removed_details = { 'msg': 'Support for this field is gone.', 'hint': 'Use something else.', 'id': 'fields.E999', } class Model(models.Model): name = MyField() model = Model() self.assertEqual(model.check(), [ checks.Error( msg='Support for this field is gone.', hint='Use something else.', obj=Model._meta.get_field('name'), id='fields.E999', ) ]) Django-1.8.7/tests/check_framework/__init__.py0000664000175000017500000000000012603513307020732 0ustar timtim00000000000000Django-1.8.7/tests/check_framework/models.py0000664000175000017500000000031212625116213020463 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models class SimpleModel(models.Model): field = models.IntegerField() manager = models.manager.Manager() Django-1.8.7/tests/foreign_object/0000775000175000017500000000000012625116243016462 5ustar timtim00000000000000Django-1.8.7/tests/foreign_object/tests.py0000664000175000017500000004322712625116213020203 0ustar timtim00000000000000import datetime from operator import attrgetter from django import forms from django.core.exceptions import FieldError from django.test import TestCase, skipUnlessDBFeature from django.utils import translation from .models import ( Article, ArticleIdea, ArticleTag, ArticleTranslation, Country, Friendship, Group, Membership, NewsArticle, Person, ) # Note that these tests are testing internal implementation details. # ForeignObject is not part of public API. class MultiColumnFKTests(TestCase): def setUp(self): # Creating countries self.usa = Country.objects.create(name="United States of America") self.soviet_union = Country.objects.create(name="Soviet Union") Person() # Creating People self.bob = Person() self.bob.name = 'Bob' self.bob.person_country = self.usa self.bob.save() self.jim = Person.objects.create(name='Jim', person_country=self.usa) self.george = Person.objects.create(name='George', person_country=self.usa) self.jane = Person.objects.create(name='Jane', person_country=self.soviet_union) self.mark = Person.objects.create(name='Mark', person_country=self.soviet_union) self.sam = Person.objects.create(name='Sam', person_country=self.soviet_union) # Creating Groups self.kgb = Group.objects.create(name='KGB', group_country=self.soviet_union) self.cia = Group.objects.create(name='CIA', group_country=self.usa) self.republican = Group.objects.create(name='Republican', group_country=self.usa) self.democrat = Group.objects.create(name='Democrat', group_country=self.usa) def test_get_succeeds_on_multicolumn_match(self): # Membership objects have access to their related Person if both # country_ids match between them membership = Membership.objects.create( membership_country_id=self.usa.id, person_id=self.bob.id, group_id=self.cia.id) person = membership.person self.assertEqual((person.id, person.name), (self.bob.id, "Bob")) def test_get_fails_on_multicolumn_mismatch(self): # Membership objects returns DoesNotExist error when the there is no # Person with the same id and country_id membership = Membership.objects.create( membership_country_id=self.usa.id, person_id=self.jane.id, group_id=self.cia.id) self.assertRaises(Person.DoesNotExist, getattr, membership, 'person') def test_reverse_query_returns_correct_result(self): # Creating a valid membership because it has the same country has the person Membership.objects.create( membership_country_id=self.usa.id, person_id=self.bob.id, group_id=self.cia.id) # Creating an invalid membership because it has a different country has the person Membership.objects.create( membership_country_id=self.soviet_union.id, person_id=self.bob.id, group_id=self.republican.id) self.assertQuerysetEqual( self.bob.membership_set.all(), [ self.cia.id ], attrgetter("group_id") ) def test_query_filters_correctly(self): # Creating a to valid memberships Membership.objects.create( membership_country_id=self.usa.id, person_id=self.bob.id, group_id=self.cia.id) Membership.objects.create( membership_country_id=self.usa.id, person_id=self.jim.id, group_id=self.cia.id) # Creating an invalid membership Membership.objects.create(membership_country_id=self.soviet_union.id, person_id=self.george.id, group_id=self.cia.id) self.assertQuerysetEqual( Membership.objects.filter(person__name__contains='o'), [ self.bob.id ], attrgetter("person_id") ) def test_reverse_query_filters_correctly(self): timemark = datetime.datetime.utcnow() timedelta = datetime.timedelta(days=1) # Creating a to valid memberships Membership.objects.create( membership_country_id=self.usa.id, person_id=self.bob.id, group_id=self.cia.id, date_joined=timemark - timedelta) Membership.objects.create( membership_country_id=self.usa.id, person_id=self.jim.id, group_id=self.cia.id, date_joined=timemark + timedelta) # Creating an invalid membership Membership.objects.create( membership_country_id=self.soviet_union.id, person_id=self.george.id, group_id=self.cia.id, date_joined=timemark + timedelta) self.assertQuerysetEqual( Person.objects.filter(membership__date_joined__gte=timemark), [ 'Jim' ], attrgetter('name') ) def test_forward_in_lookup_filters_correctly(self): Membership.objects.create(membership_country_id=self.usa.id, person_id=self.bob.id, group_id=self.cia.id) Membership.objects.create(membership_country_id=self.usa.id, person_id=self.jim.id, group_id=self.cia.id) # Creating an invalid membership Membership.objects.create( membership_country_id=self.soviet_union.id, person_id=self.george.id, group_id=self.cia.id) self.assertQuerysetEqual( Membership.objects.filter(person__in=[self.george, self.jim]), [ self.jim.id, ], attrgetter('person_id') ) self.assertQuerysetEqual( Membership.objects.filter(person__in=Person.objects.filter(name='Jim')), [ self.jim.id, ], attrgetter('person_id') ) def test_double_nested_query(self): m1 = Membership.objects.create(membership_country_id=self.usa.id, person_id=self.bob.id, group_id=self.cia.id) m2 = Membership.objects.create(membership_country_id=self.usa.id, person_id=self.jim.id, group_id=self.cia.id) Friendship.objects.create(from_friend_country_id=self.usa.id, from_friend_id=self.bob.id, to_friend_country_id=self.usa.id, to_friend_id=self.jim.id) self.assertQuerysetEqual(Membership.objects.filter( person__in=Person.objects.filter( from_friend__in=Friendship.objects.filter( to_friend__in=Person.objects.all()))), [m1], lambda x: x) self.assertQuerysetEqual(Membership.objects.exclude( person__in=Person.objects.filter( from_friend__in=Friendship.objects.filter( to_friend__in=Person.objects.all()))), [m2], lambda x: x) def test_select_related_foreignkey_forward_works(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(1): people = [m.person for m in Membership.objects.select_related('person').order_by('pk')] normal_people = [m.person for m in Membership.objects.all().order_by('pk')] self.assertEqual(people, normal_people) def test_prefetch_foreignkey_forward_works(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(2): people = [ m.person for m in Membership.objects.prefetch_related('person').order_by('pk')] normal_people = [m.person for m in Membership.objects.order_by('pk')] self.assertEqual(people, normal_people) def test_prefetch_foreignkey_reverse_works(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(2): membership_sets = [ list(p.membership_set.all()) for p in Person.objects.prefetch_related('membership_set').order_by('pk')] normal_membership_sets = [list(p.membership_set.all()) for p in Person.objects.order_by('pk')] self.assertEqual(membership_sets, normal_membership_sets) def test_m2m_through_forward_returns_valid_members(self): # We start out by making sure that the Group 'CIA' has no members. self.assertQuerysetEqual( self.cia.members.all(), [] ) Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.cia) # Let's check to make sure that it worked. Bob and Jim should be members of the CIA. self.assertQuerysetEqual( self.cia.members.all(), [ 'Bob', 'Jim' ], attrgetter("name") ) def test_m2m_through_reverse_returns_valid_members(self): # We start out by making sure that Bob is in no groups. self.assertQuerysetEqual( self.bob.groups.all(), [] ) Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.republican) # Bob should be in the CIA and a Republican self.assertQuerysetEqual( self.bob.groups.all(), [ 'CIA', 'Republican' ], attrgetter("name") ) def test_m2m_through_forward_ignores_invalid_members(self): # We start out by making sure that the Group 'CIA' has no members. self.assertQuerysetEqual( self.cia.members.all(), [] ) # Something adds jane to group CIA but Jane is in Soviet Union which isn't CIA's country Membership.objects.create(membership_country=self.usa, person=self.jane, group=self.cia) # There should still be no members in CIA self.assertQuerysetEqual( self.cia.members.all(), [] ) def test_m2m_through_reverse_ignores_invalid_members(self): # We start out by making sure that Jane has no groups. self.assertQuerysetEqual( self.jane.groups.all(), [] ) # Something adds jane to group CIA but Jane is in Soviet Union which isn't CIA's country Membership.objects.create(membership_country=self.usa, person=self.jane, group=self.cia) # Jane should still not be in any groups self.assertQuerysetEqual( self.jane.groups.all(), [] ) def test_m2m_through_on_self_works(self): self.assertQuerysetEqual( self.jane.friends.all(), [] ) Friendship.objects.create( from_friend_country=self.jane.person_country, from_friend=self.jane, to_friend_country=self.george.person_country, to_friend=self.george) self.assertQuerysetEqual( self.jane.friends.all(), ['George'], attrgetter("name") ) def test_m2m_through_on_self_ignores_mismatch_columns(self): self.assertQuerysetEqual(self.jane.friends.all(), []) # Note that we use ids instead of instances. This is because instances on ForeignObject # properties will set all related field off of the given instance Friendship.objects.create( from_friend_id=self.jane.id, to_friend_id=self.george.id, to_friend_country_id=self.jane.person_country_id, from_friend_country_id=self.george.person_country_id) self.assertQuerysetEqual(self.jane.friends.all(), []) def test_prefetch_related_m2m_foward_works(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(2): members_lists = [list(g.members.all()) for g in Group.objects.prefetch_related('members')] normal_members_lists = [list(g.members.all()) for g in Group.objects.all()] self.assertEqual(members_lists, normal_members_lists) def test_prefetch_related_m2m_reverse_works(self): Membership.objects.create(membership_country=self.usa, person=self.bob, group=self.cia) Membership.objects.create(membership_country=self.usa, person=self.jim, group=self.democrat) with self.assertNumQueries(2): groups_lists = [list(p.groups.all()) for p in Person.objects.prefetch_related('groups')] normal_groups_lists = [list(p.groups.all()) for p in Person.objects.all()] self.assertEqual(groups_lists, normal_groups_lists) @translation.override('fi') def test_translations(self): a1 = Article.objects.create(pub_date=datetime.date.today()) at1_fi = ArticleTranslation(article=a1, lang='fi', title='Otsikko', body='Diipadaapa') at1_fi.save() at2_en = ArticleTranslation(article=a1, lang='en', title='Title', body='Lalalalala') at2_en.save() with self.assertNumQueries(1): fetched = Article.objects.select_related('active_translation').get( active_translation__title='Otsikko') self.assertEqual(fetched.active_translation.title, 'Otsikko') a2 = Article.objects.create(pub_date=datetime.date.today()) at2_fi = ArticleTranslation(article=a2, lang='fi', title='Atsikko', body='Diipadaapa', abstract='dipad') at2_fi.save() a3 = Article.objects.create(pub_date=datetime.date.today()) at3_en = ArticleTranslation(article=a3, lang='en', title='A title', body='lalalalala', abstract='lala') at3_en.save() # Test model initialization with active_translation field. a3 = Article(id=a3.id, pub_date=a3.pub_date, active_translation=at3_en) a3.save() self.assertEqual( list(Article.objects.filter(active_translation__abstract=None)), [a1, a3]) self.assertEqual( list(Article.objects.filter(active_translation__abstract=None, active_translation__pk__isnull=False)), [a1]) with translation.override('en'): self.assertEqual( list(Article.objects.filter(active_translation__abstract=None)), [a1, a2]) def test_foreign_key_raises_informative_does_not_exist(self): referrer = ArticleTranslation() with self.assertRaisesMessage(Article.DoesNotExist, 'ArticleTranslation has no article'): referrer.article def test_foreign_key_related_query_name(self): a1 = Article.objects.create(pub_date=datetime.date.today()) ArticleTag.objects.create(article=a1, name="foo") self.assertEqual(Article.objects.filter(tag__name="foo").count(), 1) self.assertEqual(Article.objects.filter(tag__name="bar").count(), 0) with self.assertRaises(FieldError): Article.objects.filter(tags__name="foo") def test_many_to_many_related_query_name(self): a1 = Article.objects.create(pub_date=datetime.date.today()) i1 = ArticleIdea.objects.create(name="idea1") a1.ideas.add(i1) self.assertEqual(Article.objects.filter(idea_things__name="idea1").count(), 1) self.assertEqual(Article.objects.filter(idea_things__name="idea2").count(), 0) with self.assertRaises(FieldError): Article.objects.filter(ideas__name="idea1") @translation.override('fi') def test_inheritance(self): na = NewsArticle.objects.create(pub_date=datetime.date.today()) ArticleTranslation.objects.create( article=na, lang="fi", title="foo", body="bar") self.assertQuerysetEqual( NewsArticle.objects.select_related('active_translation'), [na], lambda x: x ) with self.assertNumQueries(1): self.assertEqual( NewsArticle.objects.select_related( 'active_translation')[0].active_translation.title, "foo") @skipUnlessDBFeature('has_bulk_insert') def test_batch_create_foreign_object(self): """ See: https://code.djangoproject.com/ticket/21566 """ objs = [Person(name="abcd_%s" % i, person_country=self.usa) for i in range(0, 5)] Person.objects.bulk_create(objs, 10) class FormsTests(TestCase): # ForeignObjects should not have any form fields, currently the user needs # to manually deal with the foreignobject relation. class ArticleForm(forms.ModelForm): class Meta: model = Article fields = '__all__' def test_foreign_object_form(self): # A very crude test checking that the non-concrete fields do not get form fields. form = FormsTests.ArticleForm() self.assertIn('id_pub_date', form.as_table()) self.assertNotIn('active_translation', form.as_table()) form = FormsTests.ArticleForm(data={'pub_date': str(datetime.date.today())}) self.assertTrue(form.is_valid()) a = form.save() self.assertEqual(a.pub_date, datetime.date.today()) form = FormsTests.ArticleForm(instance=a, data={'pub_date': '2013-01-01'}) a2 = form.save() self.assertEqual(a.pk, a2.pk) self.assertEqual(a2.pub_date, datetime.date(2013, 1, 1)) Django-1.8.7/tests/foreign_object/__init__.py0000664000175000017500000000000012603513307020557 0ustar timtim00000000000000Django-1.8.7/tests/foreign_object/models.py0000664000175000017500000001320112625116213020311 0ustar timtim00000000000000import datetime from django.db import models from django.db.models.fields.related import \ ReverseSingleRelatedObjectDescriptor from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import get_language @python_2_unicode_compatible class Country(models.Model): # Table Column Fields name = models.CharField(max_length=50) def __str__(self): return self.name @python_2_unicode_compatible class Person(models.Model): # Table Column Fields name = models.CharField(max_length=128) person_country_id = models.IntegerField() # Relation Fields person_country = models.ForeignObject( Country, from_fields=['person_country_id'], to_fields=['id']) friends = models.ManyToManyField('self', through='Friendship', symmetrical=False) class Meta: ordering = ('name',) def __str__(self): return self.name @python_2_unicode_compatible class Group(models.Model): # Table Column Fields name = models.CharField(max_length=128) group_country = models.ForeignKey(Country) members = models.ManyToManyField(Person, related_name='groups', through='Membership') class Meta: ordering = ('name',) def __str__(self): return self.name @python_2_unicode_compatible class Membership(models.Model): # Table Column Fields membership_country = models.ForeignKey(Country) date_joined = models.DateTimeField(default=datetime.datetime.now) invite_reason = models.CharField(max_length=64, null=True) person_id = models.IntegerField() group_id = models.IntegerField() # Relation Fields person = models.ForeignObject( Person, from_fields=['membership_country', 'person_id'], to_fields=['person_country_id', 'id']) group = models.ForeignObject( Group, from_fields=['membership_country', 'group_id'], to_fields=['group_country', 'id']) class Meta: ordering = ('date_joined', 'invite_reason') def __str__(self): return "%s is a member of %s" % (self.person.name, self.group.name) class Friendship(models.Model): # Table Column Fields from_friend_country = models.ForeignKey(Country, related_name="from_friend_country") from_friend_id = models.IntegerField() to_friend_country_id = models.IntegerField() to_friend_id = models.IntegerField() # Relation Fields from_friend = models.ForeignObject( Person, from_fields=['from_friend_country', 'from_friend_id'], to_fields=['person_country_id', 'id'], related_name='from_friend') to_friend_country = models.ForeignObject( Country, from_fields=['to_friend_country_id'], to_fields=['id'], related_name='to_friend_country') to_friend = models.ForeignObject( Person, from_fields=['to_friend_country_id', 'to_friend_id'], to_fields=['person_country_id', 'id'], related_name='to_friend') class ArticleTranslationDescriptor(ReverseSingleRelatedObjectDescriptor): """ The set of articletranslation should not set any local fields. """ def __set__(self, instance, value): if instance is None: raise AttributeError("%s must be accessed via instance" % self.field.name) setattr(instance, self.cache_name, value) if value is not None and not self.field.rel.multiple: setattr(value, self.field.related.get_cache_name(), instance) class ColConstraint(object): # Anything with as_sql() method works in get_extra_restriction(). def __init__(self, alias, col, value): self.alias, self.col, self.value = alias, col, value def as_sql(self, compiler, connection): qn = compiler.quote_name_unless_alias return '%s.%s = %%s' % (qn(self.alias), qn(self.col)), [self.value] class ActiveTranslationField(models.ForeignObject): """ This field will allow querying and fetching the currently active translation for Article from ArticleTranslation. """ requires_unique_target = False def get_extra_restriction(self, where_class, alias, related_alias): return ColConstraint(alias, 'lang', get_language()) def get_extra_descriptor_filter(self): return {'lang': get_language()} def contribute_to_class(self, cls, name): super(ActiveTranslationField, self).contribute_to_class(cls, name) setattr(cls, self.name, ArticleTranslationDescriptor(self)) @python_2_unicode_compatible class Article(models.Model): active_translation = ActiveTranslationField( 'ArticleTranslation', from_fields=['id'], to_fields=['article'], related_name='+', null=True) pub_date = models.DateField() def __str__(self): try: return self.active_translation.title except ArticleTranslation.DoesNotExist: return '[No translation found]' class NewsArticle(Article): pass class ArticleTranslation(models.Model): article = models.ForeignKey(Article) lang = models.CharField(max_length=2) title = models.CharField(max_length=100) body = models.TextField() abstract = models.CharField(max_length=400, null=True) class Meta: unique_together = ('article', 'lang') ordering = ('active_translation__title',) class ArticleTag(models.Model): article = models.ForeignKey(Article, related_name="tags", related_query_name="tag") name = models.CharField(max_length=255) class ArticleIdea(models.Model): articles = models.ManyToManyField(Article, related_name="ideas", related_query_name="idea_things") name = models.CharField(max_length=255) Django-1.8.7/tests/model_regress/0000775000175000017500000000000012625116243016335 5ustar timtim00000000000000Django-1.8.7/tests/model_regress/tests.py0000664000175000017500000002140212625116214020046 0ustar timtim00000000000000from __future__ import unicode_literals import datetime from operator import attrgetter from django.core.exceptions import ValidationError from django.db import router from django.db.models.sql import InsertQuery from django.test import TestCase, skipUnlessDBFeature from django.utils import six from django.utils.timezone import get_fixed_timezone from .models import ( Article, BrokenUnicodeMethod, Department, Event, Model1, Model2, Model3, NonAutoPK, Party, Worker, ) class ModelTests(TestCase): # The bug is that the following queries would raise: # "TypeError: Related Field has invalid lookup: gte" def test_related_gte_lookup(self): """ Regression test for #10153: foreign key __gte lookups. """ Worker.objects.filter(department__gte=0) def test_related_lte_lookup(self): """ Regression test for #10153: foreign key __lte lookups. """ Worker.objects.filter(department__lte=0) def test_sql_insert_compiler_return_id_attribute(self): """ Regression test for #14019: SQLInsertCompiler.as_sql() failure """ db = router.db_for_write(Party) query = InsertQuery(Party) query.insert_values([Party._meta.fields[0]], [], raw=False) # this line will raise an AttributeError without the accompanying fix query.get_compiler(using=db).as_sql() def test_empty_choice(self): # NOTE: Part of the regression test here is merely parsing the model # declaration. The verbose_name, in particular, did not always work. a = Article.objects.create( headline="Look at me!", pub_date=datetime.datetime.now() ) # An empty choice field should return None for the display name. self.assertIs(a.get_status_display(), None) # Empty strings should be returned as Unicode a = Article.objects.get(pk=a.pk) self.assertEqual(a.misc_data, '') self.assertIs(type(a.misc_data), six.text_type) def test_long_textfield(self): # TextFields can hold more than 4000 characters (this was broken in # Oracle). a = Article.objects.create( headline="Really, really big", pub_date=datetime.datetime.now(), article_text="ABCDE" * 1000 ) a = Article.objects.get(pk=a.pk) self.assertEqual(len(a.article_text), 5000) def test_long_unicode_textfield(self): # TextFields can hold more than 4000 bytes also when they are # less than 4000 characters a = Article.objects.create( headline="Really, really big", pub_date=datetime.datetime.now(), article_text='\u05d0\u05d1\u05d2' * 1000 ) a = Article.objects.get(pk=a.pk) self.assertEqual(len(a.article_text), 3000) def test_date_lookup(self): # Regression test for #659 Party.objects.create(when=datetime.datetime(1999, 12, 31)) Party.objects.create(when=datetime.datetime(1998, 12, 31)) Party.objects.create(when=datetime.datetime(1999, 1, 1)) Party.objects.create(when=datetime.datetime(1, 3, 3)) self.assertQuerysetEqual( Party.objects.filter(when__month=2), [] ) self.assertQuerysetEqual( Party.objects.filter(when__month=1), [ datetime.date(1999, 1, 1) ], attrgetter("when") ) self.assertQuerysetEqual( Party.objects.filter(when__month=12), [ datetime.date(1999, 12, 31), datetime.date(1998, 12, 31), ], attrgetter("when"), ordered=False ) self.assertQuerysetEqual( Party.objects.filter(when__year=1998), [ datetime.date(1998, 12, 31), ], attrgetter("when") ) # Regression test for #8510 self.assertQuerysetEqual( Party.objects.filter(when__day="31"), [ datetime.date(1999, 12, 31), datetime.date(1998, 12, 31), ], attrgetter("when"), ordered=False ) self.assertQuerysetEqual( Party.objects.filter(when__month="12"), [ datetime.date(1999, 12, 31), datetime.date(1998, 12, 31), ], attrgetter("when"), ordered=False ) self.assertQuerysetEqual( Party.objects.filter(when__year="1998"), [ datetime.date(1998, 12, 31), ], attrgetter("when") ) # Regression test for #18969 self.assertQuerysetEqual( Party.objects.filter(when__year=1), [ datetime.date(1, 3, 3), ], attrgetter("when") ) self.assertQuerysetEqual( Party.objects.filter(when__year='1'), [ datetime.date(1, 3, 3), ], attrgetter("when") ) def test_date_filter_null(self): # Date filtering was failing with NULL date values in SQLite # (regression test for #3501, among other things). Party.objects.create(when=datetime.datetime(1999, 1, 1)) Party.objects.create() p = Party.objects.filter(when__month=1)[0] self.assertEqual(p.when, datetime.date(1999, 1, 1)) self.assertQuerysetEqual( Party.objects.filter(pk=p.pk).dates("when", "month"), [ 1 ], attrgetter("month") ) def test_get_next_prev_by_field(self): # Check that get_next_by_FIELD and get_previous_by_FIELD don't crash # when we have usecs values stored on the database # # It crashed after the Field.get_db_prep_* refactor, because on most # backends DateTimeFields supports usecs, but DateTimeField.to_python # didn't recognize them. (Note that # Model._get_next_or_previous_by_FIELD coerces values to strings) Event.objects.create(when=datetime.datetime(2000, 1, 1, 16, 0, 0)) Event.objects.create(when=datetime.datetime(2000, 1, 1, 6, 1, 1)) Event.objects.create(when=datetime.datetime(2000, 1, 1, 13, 1, 1)) e = Event.objects.create(when=datetime.datetime(2000, 1, 1, 12, 0, 20, 24)) self.assertEqual( e.get_next_by_when().when, datetime.datetime(2000, 1, 1, 13, 1, 1) ) self.assertEqual( e.get_previous_by_when().when, datetime.datetime(2000, 1, 1, 6, 1, 1) ) def test_primary_key_foreign_key_types(self): # Check Department and Worker (non-default PK type) d = Department.objects.create(id=10, name="IT") w = Worker.objects.create(department=d, name="Full-time") self.assertEqual(six.text_type(w), "Full-time") def test_broken_unicode(self): # Models with broken unicode methods should still have a printable repr b = BrokenUnicodeMethod.objects.create(name="Jerry") self.assertEqual(repr(b), "") @skipUnlessDBFeature("supports_timezones") def test_timezones(self): # Saving an updating with timezone-aware datetime Python objects. # Regression test for #10443. # The idea is that all these creations and saving should work without # crashing. It's not rocket science. dt1 = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=get_fixed_timezone(600)) dt2 = datetime.datetime(2008, 8, 31, 17, 20, tzinfo=get_fixed_timezone(600)) obj = Article.objects.create( headline="A headline", pub_date=dt1, article_text="foo" ) obj.pub_date = dt2 obj.save() self.assertEqual( Article.objects.filter(headline="A headline").update(pub_date=dt1), 1 ) def test_chained_fks(self): """ Regression for #18432: Chained foreign keys with to_field produce incorrect query """ m1 = Model1.objects.create(pkey=1000) m2 = Model2.objects.create(model1=m1) m3 = Model3.objects.create(model2=m2) # this is the actual test for #18432 m3 = Model3.objects.get(model2=1000) m3.model2 class ModelValidationTest(TestCase): def test_pk_validation(self): NonAutoPK.objects.create(name="one") again = NonAutoPK(name="one") self.assertRaises(ValidationError, again.validate_unique) class EvaluateMethodTest(TestCase): """ Regression test for #13640: cannot filter by objects with 'evaluate' attr """ def test_model_with_evaluate_method(self): """ Ensures that you can filter by objects that have an 'evaluate' attr """ dept = Department.objects.create(pk=1, name='abc') dept.evaluate = 'abc' Worker.objects.filter(department=dept) Django-1.8.7/tests/model_regress/__init__.py0000664000175000017500000000000012603513307020432 0ustar timtim00000000000000Django-1.8.7/tests/model_regress/test_pickle.py0000664000175000017500000000666512625116214021230 0ustar timtim00000000000000import datetime import os import pickle import subprocess import sys from django.core.files.temp import NamedTemporaryFile from django.db import DJANGO_VERSION_PICKLE_KEY, models from django.test import TestCase, mock from django.utils._os import npath, upath from django.utils.version import get_version from .models import Article class ModelPickleTestCase(TestCase): def test_missing_django_version_unpickling(self): """ #21430 -- Verifies a warning is raised for models that are unpickled without a Django version """ class MissingDjangoVersion(models.Model): title = models.CharField(max_length=10) def __reduce__(self): reduce_list = super(MissingDjangoVersion, self).__reduce__() data = reduce_list[-1] del data[DJANGO_VERSION_PICKLE_KEY] return reduce_list p = MissingDjangoVersion(title="FooBar") msg = "Pickled model instance's Django version is not specified." with self.assertRaisesMessage(RuntimeWarning, msg): pickle.loads(pickle.dumps(p)) def test_unsupported_unpickle(self): """ #21430 -- Verifies a warning is raised for models that are unpickled with a different Django version than the current """ class DifferentDjangoVersion(models.Model): title = models.CharField(max_length=10) def __reduce__(self): reduce_list = super(DifferentDjangoVersion, self).__reduce__() data = reduce_list[-1] data[DJANGO_VERSION_PICKLE_KEY] = '1.0' return reduce_list p = DifferentDjangoVersion(title="FooBar") msg = "Pickled model instance's Django version 1.0 does not match the current version %s." % get_version() with self.assertRaisesMessage(RuntimeWarning, msg): pickle.loads(pickle.dumps(p)) def test_unpickling_when_appregistrynotready(self): """ #24007 -- Verifies that a pickled model can be unpickled without having to manually setup the apps registry beforehand. """ script_template = """#!/usr/bin/env python import pickle from django.conf import settings data = %r settings.configure(DEBUG=False, INSTALLED_APPS=('model_regress',), SECRET_KEY = "blah") article = pickle.loads(data) print(article.headline)""" a = Article.objects.create( headline="Some object", pub_date=datetime.datetime.now(), article_text="This is an article", ) with NamedTemporaryFile(mode='w+', suffix=".py") as script: script.write(script_template % pickle.dumps(a)) script.flush() # A path to model_regress must be set in PYTHONPATH model_regress_dir = os.path.dirname(upath(__file__)) model_regress_path = os.path.abspath(model_regress_dir) tests_path = os.path.split(model_regress_path)[0] pythonpath = os.environ.get('PYTHONPATH', '') pythonpath = npath(os.pathsep.join([tests_path, pythonpath])) with mock.patch.dict('os.environ', {'PYTHONPATH': pythonpath}): try: result = subprocess.check_output([sys.executable, script.name]) except subprocess.CalledProcessError: self.fail("Unable to reload model pickled data") self.assertEqual(result.strip().decode(), "Some object") Django-1.8.7/tests/model_regress/models.py0000664000175000017500000000430512625116214020172 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible CHOICES = ( (1, 'first'), (2, 'second'), ) @python_2_unicode_compatible class Article(models.Model): headline = models.CharField(max_length=100, default='Default headline') pub_date = models.DateTimeField() status = models.IntegerField(blank=True, null=True, choices=CHOICES) misc_data = models.CharField(max_length=100, blank=True) article_text = models.TextField() class Meta: ordering = ('pub_date', 'headline') # A utf-8 verbose name (Ã…ngström's Articles) to test they are valid. verbose_name = "\xc3\x85ngstr\xc3\xb6m's Articles" def __str__(self): return self.headline class Movie(models.Model): # Test models with non-default primary keys / AutoFields #5218 movie_id = models.AutoField(primary_key=True) name = models.CharField(max_length=60) class Party(models.Model): when = models.DateField(null=True) class Event(models.Model): when = models.DateTimeField() @python_2_unicode_compatible class Department(models.Model): id = models.PositiveIntegerField(primary_key=True) name = models.CharField(max_length=200) def __str__(self): return self.name @python_2_unicode_compatible class Worker(models.Model): department = models.ForeignKey(Department) name = models.CharField(max_length=200) def __str__(self): return self.name @python_2_unicode_compatible class BrokenUnicodeMethod(models.Model): name = models.CharField(max_length=7) def __str__(self): # Intentionally broken (invalid start byte in byte string). return b'Name\xff: %s'.decode() % self.name class NonAutoPK(models.Model): name = models.CharField(max_length=10, primary_key=True) # Chained foreign keys with to_field produce incorrect query #18432 class Model1(models.Model): pkey = models.IntegerField(unique=True, db_index=True) class Model2(models.Model): model1 = models.ForeignKey(Model1, unique=True, to_field='pkey') class Model3(models.Model): model2 = models.ForeignKey(Model2, unique=True, to_field='model1') Django-1.8.7/tests/test_runner_deprecation_app/0000775000175000017500000000000012625116243021270 5ustar timtim00000000000000Django-1.8.7/tests/test_runner_deprecation_app/tests.py0000664000175000017500000000051212603513307023000 0ustar timtim00000000000000import warnings from django.test import TestCase from django.utils.deprecation import RemovedInNextVersionWarning warnings.warn("module-level warning from deprecation_app", RemovedInNextVersionWarning) class DummyTest(TestCase): def test_warn(self): warnings.warn("warning from test", RemovedInNextVersionWarning) Django-1.8.7/tests/test_runner_deprecation_app/__init__.py0000664000175000017500000000000012603513307023365 0ustar timtim00000000000000Django-1.8.7/tests/model_validation/0000775000175000017500000000000012625116243017015 5ustar timtim00000000000000Django-1.8.7/tests/model_validation/tests.py0000664000175000017500000000407112625116214020531 0ustar timtim00000000000000from django.core import management from django.core.checks import Error, run_checks from django.db.models.signals import post_init from django.test import TestCase from django.test.utils import override_settings from django.utils import six class OnPostInit(object): def __call__(self, **kwargs): pass def on_post_init(**kwargs): pass @override_settings( INSTALLED_APPS=['django.contrib.auth', 'django.contrib.contenttypes'], SILENCED_SYSTEM_CHECKS=['fields.W342'], # ForeignKey(unique=True) ) class ModelValidationTest(TestCase): def test_models_validate(self): # All our models should validate properly # Validation Tests: # * choices= Iterable of Iterables # See: https://code.djangoproject.com/ticket/20430 # * related_name='+' doesn't clash with another '+' # See: https://code.djangoproject.com/ticket/21375 management.call_command("check", stdout=six.StringIO()) def test_model_signal(self): unresolved_references = post_init.unresolved_references.copy() post_init.connect(on_post_init, sender='missing-app.Model') post_init.connect(OnPostInit(), sender='missing-app.Model') errors = run_checks() expected = [ Error( "The 'on_post_init' function was connected to the 'post_init' " "signal with a lazy reference to the 'missing-app.Model' " "sender, which has not been installed.", hint=None, obj='model_validation.tests', id='signals.E001', ), Error( "An instance of the 'OnPostInit' class was connected to " "the 'post_init' signal with a lazy reference to the " "'missing-app.Model' sender, which has not been installed.", hint=None, obj='model_validation.tests', id='signals.E001', ) ] self.assertEqual(errors, expected) post_init.unresolved_references = unresolved_references Django-1.8.7/tests/model_validation/__init__.py0000664000175000017500000000000012603513307021112 0ustar timtim00000000000000Django-1.8.7/tests/model_validation/models.py0000664000175000017500000000244212625113144020651 0ustar timtim00000000000000from django.db import models class ThingItem(object): def __init__(self, value, display): self.value = value self.display = display def __iter__(self): return (x for x in [self.value, self.display]) def __len__(self): return 2 class Things(object): def __iter__(self): return (x for x in [ThingItem(1, 2), ThingItem(3, 4)]) class ThingWithIterableChoices(models.Model): # Testing choices= Iterable of Iterables # See: https://code.djangoproject.com/ticket/20430 thing = models.CharField(max_length=100, blank=True, choices=Things()) class Meta: # Models created as unmanaged as these aren't ever queried managed = False class ManyToManyRel(models.Model): thing1 = models.ManyToManyField(ThingWithIterableChoices, related_name='+') thing2 = models.ManyToManyField(ThingWithIterableChoices, related_name='+') class Meta: # Models created as unmanaged as these aren't ever queried managed = False class FKRel(models.Model): thing1 = models.ForeignKey(ThingWithIterableChoices, related_name='+') thing2 = models.ForeignKey(ThingWithIterableChoices, related_name='+') class Meta: # Models created as unmanaged as these aren't ever queried managed = False Django-1.8.7/tests/modeladmin/0000775000175000017500000000000012625116243015614 5ustar timtim00000000000000Django-1.8.7/tests/modeladmin/tests.py0000664000175000017500000015626312625116214017343 0ustar timtim00000000000000from __future__ import unicode_literals from datetime import date from django import forms from django.contrib.admin import BooleanFieldListFilter, SimpleListFilter from django.contrib.admin.options import ( HORIZONTAL, VERTICAL, ModelAdmin, TabularInline, ) from django.contrib.admin.sites import AdminSite from django.contrib.admin.validation import ModelAdminValidator from django.contrib.admin.widgets import AdminDateWidget, AdminRadioSelect from django.core.checks import Error from django.core.exceptions import ImproperlyConfigured from django.forms.models import BaseModelFormSet from django.forms.widgets import Select from django.test import TestCase, ignore_warnings from django.utils import six from django.utils.deprecation import RemovedInDjango19Warning from .models import ( Band, Concert, ValidationTestInlineModel, ValidationTestModel, ) class MockRequest(object): pass class MockSuperUser(object): def has_perm(self, perm): return True request = MockRequest() request.user = MockSuperUser() class ModelAdminTests(TestCase): def setUp(self): self.band = Band.objects.create( name='The Doors', bio='', sign_date=date(1965, 1, 1), ) self.site = AdminSite() # form/fields/fieldsets interaction ############################## def test_default_fields(self): ma = ModelAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date']) self.assertEqual(list(ma.get_fields(request)), ['name', 'bio', 'sign_date']) self.assertEqual(list(ma.get_fields(request, self.band)), ['name', 'bio', 'sign_date']) def test_default_fieldsets(self): # fieldsets_add and fieldsets_change should return a special data structure that # is used in the templates. They should generate the "right thing" whether we # have specified a custom form, the fields argument, or nothing at all. # # Here's the default case. There are no custom form_add/form_change methods, # no fields argument, and no fieldsets argument. ma = ModelAdmin(Band, self.site) self.assertEqual(ma.get_fieldsets(request), [(None, {'fields': ['name', 'bio', 'sign_date']})]) self.assertEqual(ma.get_fieldsets(request, self.band), [(None, {'fields': ['name', 'bio', 'sign_date']})]) def test_get_fieldsets(self): # Test that get_fieldsets is called when figuring out form fields. # Refs #18681. class BandAdmin(ModelAdmin): def get_fieldsets(self, request, obj=None): return [(None, {'fields': ['name', 'bio']})] ma = BandAdmin(Band, self.site) form = ma.get_form(None) self.assertEqual(form._meta.fields, ['name', 'bio']) class InlineBandAdmin(TabularInline): model = Concert fk_name = 'main_band' can_delete = False def get_fieldsets(self, request, obj=None): return [(None, {'fields': ['day', 'transport']})] ma = InlineBandAdmin(Band, self.site) form = ma.get_formset(None).form self.assertEqual(form._meta.fields, ['day', 'transport']) def test_lookup_allowed_allows_nonexistent_lookup(self): """ Ensure that a lookup_allowed allows a parameter whose field lookup doesn't exist. Refs #21129. """ class BandAdmin(ModelAdmin): fields = ['name'] ma = BandAdmin(Band, self.site) self.assertTrue(ma.lookup_allowed('name__nonexistent', 'test_value')) def test_field_arguments(self): # If we specify the fields argument, fieldsets_add and fielsets_change should # just stick the fields into a formsets structure and return it. class BandAdmin(ModelAdmin): fields = ['name'] ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_fields(request)), ['name']) self.assertEqual(list(ma.get_fields(request, self.band)), ['name']) self.assertEqual(ma.get_fieldsets(request), [(None, {'fields': ['name']})]) self.assertEqual(ma.get_fieldsets(request, self.band), [(None, {'fields': ['name']})]) def test_field_arguments_restricted_on_form(self): # If we specify fields or fieldsets, it should exclude fields on the Form class # to the fields specified. This may cause errors to be raised in the db layer if # required model fields aren't in fields/fieldsets, but that's preferable to # ghost errors where you have a field in your Form class that isn't being # displayed because you forgot to add it to fields/fieldsets # Using `fields`. class BandAdmin(ModelAdmin): fields = ['name'] ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name']) self.assertEqual(list(ma.get_form(request, self.band).base_fields), ['name']) # Using `fieldsets`. class BandAdmin(ModelAdmin): fieldsets = [(None, {'fields': ['name']})] ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name']) self.assertEqual(list(ma.get_form(request, self.band).base_fields), ['name']) # Using `exclude`. class BandAdmin(ModelAdmin): exclude = ['bio'] ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date']) # You can also pass a tuple to `exclude`. class BandAdmin(ModelAdmin): exclude = ('bio',) ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date']) # Using `fields` and `exclude`. class BandAdmin(ModelAdmin): fields = ['name', 'bio'] exclude = ['bio'] ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name']) def test_custom_form_meta_exclude_with_readonly(self): """ Ensure that the custom ModelForm's `Meta.exclude` is respected when used in conjunction with `ModelAdmin.readonly_fields` and when no `ModelAdmin.exclude` is defined. Refs #14496. """ # First, with `ModelAdmin` ----------------------- class AdminBandForm(forms.ModelForm): class Meta: model = Band exclude = ['bio'] class BandAdmin(ModelAdmin): readonly_fields = ['name'] form = AdminBandForm ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['sign_date']) # Then, with `InlineModelAdmin` ----------------- class AdminConcertForm(forms.ModelForm): class Meta: model = Concert exclude = ['day'] class ConcertInline(TabularInline): readonly_fields = ['transport'] form = AdminConcertForm fk_name = 'main_band' model = Concert class BandAdmin(ModelAdmin): inlines = [ ConcertInline ] ma = BandAdmin(Band, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['main_band', 'opening_band', 'id', 'DELETE']) def test_custom_form_meta_exclude(self): """ Ensure that the custom ModelForm's `Meta.exclude` is overridden if `ModelAdmin.exclude` or `InlineModelAdmin.exclude` are defined. Refs #14496. """ # First, with `ModelAdmin` ----------------------- class AdminBandForm(forms.ModelForm): class Meta: model = Band exclude = ['bio'] class BandAdmin(ModelAdmin): exclude = ['name'] form = AdminBandForm ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['bio', 'sign_date']) # Then, with `InlineModelAdmin` ----------------- class AdminConcertForm(forms.ModelForm): class Meta: model = Concert exclude = ['day'] class ConcertInline(TabularInline): exclude = ['transport'] form = AdminConcertForm fk_name = 'main_band' model = Concert class BandAdmin(ModelAdmin): inlines = [ ConcertInline ] ma = BandAdmin(Band, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['main_band', 'opening_band', 'day', 'id', 'DELETE']) def test_custom_form_validation(self): # If we specify a form, it should use it allowing custom validation to work # properly. This won't, however, break any of the admin widgets or media. class AdminBandForm(forms.ModelForm): delete = forms.BooleanField() class BandAdmin(ModelAdmin): form = AdminBandForm ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'bio', 'sign_date', 'delete']) self.assertEqual( type(ma.get_form(request).base_fields['sign_date'].widget), AdminDateWidget) def test_form_exclude_kwarg_override(self): """ Ensure that the `exclude` kwarg passed to `ModelAdmin.get_form()` overrides all other declarations. Refs #8999. """ class AdminBandForm(forms.ModelForm): class Meta: model = Band exclude = ['name'] class BandAdmin(ModelAdmin): exclude = ['sign_date'] form = AdminBandForm def get_form(self, request, obj=None, **kwargs): kwargs['exclude'] = ['bio'] return super(BandAdmin, self).get_form(request, obj, **kwargs) ma = BandAdmin(Band, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['name', 'sign_date']) def test_formset_exclude_kwarg_override(self): """ Ensure that the `exclude` kwarg passed to `InlineModelAdmin.get_formset()` overrides all other declarations. Refs #8999. """ class AdminConcertForm(forms.ModelForm): class Meta: model = Concert exclude = ['day'] class ConcertInline(TabularInline): exclude = ['transport'] form = AdminConcertForm fk_name = 'main_band' model = Concert def get_formset(self, request, obj=None, **kwargs): kwargs['exclude'] = ['opening_band'] return super(ConcertInline, self).get_formset(request, obj, **kwargs) class BandAdmin(ModelAdmin): inlines = [ ConcertInline ] ma = BandAdmin(Band, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['main_band', 'day', 'transport', 'id', 'DELETE']) def test_queryset_override(self): # If we need to override the queryset of a ModelChoiceField in our custom form # make sure that RelatedFieldWidgetWrapper doesn't mess that up. band2 = Band(name='The Beatles', bio='', sign_date=date(1962, 1, 1)) band2.save() class ConcertAdmin(ModelAdmin): pass ma = ConcertAdmin(Concert, self.site) form = ma.get_form(request)() self.assertHTMLEqual(str(form["main_band"]), '' % (band2.id, self.band.id)) class AdminConcertForm(forms.ModelForm): def __init__(self, *args, **kwargs): super(AdminConcertForm, self).__init__(*args, **kwargs) self.fields["main_band"].queryset = Band.objects.filter(name='The Doors') class ConcertAdminWithForm(ModelAdmin): form = AdminConcertForm ma = ConcertAdminWithForm(Concert, self.site) form = ma.get_form(request)() self.assertHTMLEqual(str(form["main_band"]), '' % self.band.id) def test_regression_for_ticket_15820(self): """ Ensure that `obj` is passed from `InlineModelAdmin.get_fieldsets()` to `InlineModelAdmin.get_formset()`. """ class CustomConcertForm(forms.ModelForm): class Meta: model = Concert fields = ['day'] class ConcertInline(TabularInline): model = Concert fk_name = 'main_band' def get_formset(self, request, obj=None, **kwargs): if obj: kwargs['form'] = CustomConcertForm return super(ConcertInline, self).get_formset(request, obj, **kwargs) class BandAdmin(ModelAdmin): inlines = [ ConcertInline ] Concert.objects.create(main_band=self.band, opening_band=self.band, day=1) ma = BandAdmin(Band, self.site) inline_instances = ma.get_inline_instances(request) fieldsets = list(inline_instances[0].get_fieldsets(request)) self.assertEqual(fieldsets[0][1]['fields'], ['main_band', 'opening_band', 'day', 'transport']) fieldsets = list(inline_instances[0].get_fieldsets(request, inline_instances[0].model)) self.assertEqual(fieldsets[0][1]['fields'], ['day']) # radio_fields behavior ########################################### def test_default_foreign_key_widget(self): # First, without any radio_fields specified, the widgets for ForeignKey # and fields with choices specified ought to be a basic Select widget. # ForeignKey widgets in the admin are wrapped with RelatedFieldWidgetWrapper so # they need to be handled properly when type checking. For Select fields, all of # the choices lists have a first entry of dashes. cma = ModelAdmin(Concert, self.site) cmafa = cma.get_form(request) self.assertEqual(type(cmafa.base_fields['main_band'].widget.widget), Select) self.assertEqual( list(cmafa.base_fields['main_band'].widget.choices), [('', '---------'), (self.band.id, 'The Doors')]) self.assertEqual( type(cmafa.base_fields['opening_band'].widget.widget), Select) self.assertEqual( list(cmafa.base_fields['opening_band'].widget.choices), [('', '---------'), (self.band.id, 'The Doors')]) self.assertEqual(type(cmafa.base_fields['day'].widget), Select) self.assertEqual(list(cmafa.base_fields['day'].widget.choices), [('', '---------'), (1, 'Fri'), (2, 'Sat')]) self.assertEqual(type(cmafa.base_fields['transport'].widget), Select) self.assertEqual( list(cmafa.base_fields['transport'].widget.choices), [('', '---------'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]) def test_foreign_key_as_radio_field(self): # Now specify all the fields as radio_fields. Widgets should now be # RadioSelect, and the choices list should have a first entry of 'None' if # blank=True for the model field. Finally, the widget should have the # 'radiolist' attr, and 'inline' as well if the field is specified HORIZONTAL. class ConcertAdmin(ModelAdmin): radio_fields = { 'main_band': HORIZONTAL, 'opening_band': VERTICAL, 'day': VERTICAL, 'transport': HORIZONTAL, } cma = ConcertAdmin(Concert, self.site) cmafa = cma.get_form(request) self.assertEqual(type(cmafa.base_fields['main_band'].widget.widget), AdminRadioSelect) self.assertEqual(cmafa.base_fields['main_band'].widget.attrs, {'class': 'radiolist inline'}) self.assertEqual(list(cmafa.base_fields['main_band'].widget.choices), [(self.band.id, 'The Doors')]) self.assertEqual( type(cmafa.base_fields['opening_band'].widget.widget), AdminRadioSelect) self.assertEqual(cmafa.base_fields['opening_band'].widget.attrs, {'class': 'radiolist'}) self.assertEqual( list(cmafa.base_fields['opening_band'].widget.choices), [('', 'None'), (self.band.id, 'The Doors')]) self.assertEqual(type(cmafa.base_fields['day'].widget), AdminRadioSelect) self.assertEqual(cmafa.base_fields['day'].widget.attrs, {'class': 'radiolist'}) self.assertEqual(list(cmafa.base_fields['day'].widget.choices), [(1, 'Fri'), (2, 'Sat')]) self.assertEqual(type(cmafa.base_fields['transport'].widget), AdminRadioSelect) self.assertEqual(cmafa.base_fields['transport'].widget.attrs, {'class': 'radiolist inline'}) self.assertEqual(list(cmafa.base_fields['transport'].widget.choices), [('', 'None'), (1, 'Plane'), (2, 'Train'), (3, 'Bus')]) class AdminConcertForm(forms.ModelForm): class Meta: model = Concert exclude = ('transport',) class ConcertAdmin(ModelAdmin): form = AdminConcertForm ma = ConcertAdmin(Concert, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['main_band', 'opening_band', 'day']) class AdminConcertForm(forms.ModelForm): extra = forms.CharField() class Meta: model = Concert fields = ['extra', 'transport'] class ConcertAdmin(ModelAdmin): form = AdminConcertForm ma = ConcertAdmin(Concert, self.site) self.assertEqual(list(ma.get_form(request).base_fields), ['extra', 'transport']) class ConcertInline(TabularInline): form = AdminConcertForm model = Concert fk_name = 'main_band' can_delete = True class BandAdmin(ModelAdmin): inlines = [ ConcertInline ] ma = BandAdmin(Band, self.site) self.assertEqual( list(list(ma.get_formsets_with_inlines(request))[0][0]().forms[0].fields), ['extra', 'transport', 'id', 'DELETE', 'main_band']) class CheckTestCase(TestCase): def assertIsInvalid(self, model_admin, model, msg, id=None, hint=None, invalid_obj=None): invalid_obj = invalid_obj or model_admin errors = model_admin.check(model=model) expected = [ Error( msg, hint=hint, obj=invalid_obj, id=id, ) ] self.assertEqual(errors, expected) def assertIsInvalidRegexp(self, model_admin, model, msg, id=None, hint=None, invalid_obj=None): """ Same as assertIsInvalid but treats the given msg as a regexp. """ invalid_obj = invalid_obj or model_admin errors = model_admin.check(model=model) self.assertEqual(len(errors), 1) error = errors[0] self.assertEqual(error.hint, hint) self.assertEqual(error.obj, invalid_obj) self.assertEqual(error.id, id) six.assertRegex(self, error.msg, msg) def assertIsValid(self, model_admin, model): errors = model_admin.check(model=model) expected = [] self.assertEqual(errors, expected) class RawIdCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): raw_id_fields = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'raw_id_fields' must be a list or tuple.", 'admin.E001') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): raw_id_fields = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'raw_id_fields[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E002') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): raw_id_fields = ('name',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'raw_id_fields[0]' must be a ForeignKey or ManyToManyField.", 'admin.E003') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): raw_id_fields = ('users',) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class FieldsetsCheckTests(CheckTestCase): def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {'fields': ('name',)}),) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fieldsets' must be a list or tuple.", 'admin.E007') def test_non_iterable_item(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = ({},) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0]' must be a list or tuple.", 'admin.E008') def test_item_not_a_pair(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = ((),) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0]' must be of length 2.", 'admin.E009') def test_second_element_of_item_not_a_dict(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", ()),) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0][1]' must be a dictionary.", 'admin.E010') def test_missing_fields_key(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {}),) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fieldsets[0][1]' must contain the key 'fields'.", 'admin.E011') class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {'fields': ('name',)}),) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) def test_specified_both_fields_and_fieldsets(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = (("General", {'fields': ('name',)}),) fields = ['name'] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "Both 'fieldsets' and 'fields' are specified.", 'admin.E005') def test_duplicate_fields(self): class ValidationTestModelAdmin(ModelAdmin): fieldsets = [(None, {'fields': ['name', 'name']})] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "There are duplicate field(s) in 'fieldsets[0][1]'.", 'admin.E012') def test_fieldsets_with_custom_form_validation(self): class BandAdmin(ModelAdmin): fieldsets = ( ('Band', { 'fields': ('name',) }), ) self.assertIsValid(BandAdmin, Band) class FieldsCheckTests(CheckTestCase): def test_duplicate_fields_in_fields(self): class ValidationTestModelAdmin(ModelAdmin): fields = ['name', 'name'] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fields' contains duplicate field(s).", 'admin.E006') def test_inline(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel fields = 10 class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'fields' must be a list or tuple.", 'admin.E004', invalid_obj=ValidationTestInline) class FormCheckTests(CheckTestCase): def test_invalid_type(self): class FakeForm(object): pass class ValidationTestModelAdmin(ModelAdmin): form = FakeForm self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'form' must inherit from 'BaseModelForm'.", 'admin.E016') def test_fieldsets_with_custom_form_validation(self): class BandAdmin(ModelAdmin): fieldsets = ( ('Band', { 'fields': ('name',) }), ) self.assertIsValid(BandAdmin, Band) def test_valid_case(self): class AdminBandForm(forms.ModelForm): delete = forms.BooleanField() class BandAdmin(ModelAdmin): form = AdminBandForm fieldsets = ( ('Band', { 'fields': ('name', 'bio', 'sign_date', 'delete') }), ) self.assertIsValid(BandAdmin, Band) class FilterVerticalCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): filter_vertical = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'filter_vertical' must be a list or tuple.", 'admin.E017') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): filter_vertical = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'filter_vertical[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E019') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): filter_vertical = ('name',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'filter_vertical[0]' must be a ManyToManyField.", 'admin.E020') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): filter_vertical = ("users",) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class FilterHorizontalCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): filter_horizontal = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'filter_horizontal' must be a list or tuple.", 'admin.E018') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): filter_horizontal = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'filter_horizontal[0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E019') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): filter_horizontal = ('name',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'filter_horizontal[0]' must be a ManyToManyField.", 'admin.E020') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): filter_horizontal = ("users",) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class RadioFieldsCheckTests(CheckTestCase): def test_not_dictionary(self): class ValidationTestModelAdmin(ModelAdmin): radio_fields = () self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'radio_fields' must be a dictionary.", 'admin.E021') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): radio_fields = {'non_existent_field': VERTICAL} self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'radio_fields' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E022') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): radio_fields = {'name': VERTICAL} self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'radio_fields' refers to 'name', which is not an instance " "of ForeignKey, and does not have a 'choices' definition."), 'admin.E023') def test_invalid_value(self): class ValidationTestModelAdmin(ModelAdmin): radio_fields = {"state": None} self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'radio_fields[\"state\"]' must be either admin.HORIZONTAL or admin.VERTICAL.", 'admin.E024') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): radio_fields = {"state": VERTICAL} self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class PrepopulatedFieldsCheckTests(CheckTestCase): def test_not_dictionary(self): class ValidationTestModelAdmin(ModelAdmin): prepopulated_fields = () self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'prepopulated_fields' must be a dictionary.", 'admin.E026') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): prepopulated_fields = {'non_existent_field': ("slug",)} self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'prepopulated_fields' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E027') def test_missing_field_again(self): class ValidationTestModelAdmin(ModelAdmin): prepopulated_fields = {"slug": ('non_existent_field',)} self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'prepopulated_fields[\"slug\"][0]' refers to 'non_existent_field', " "which is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E030') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): prepopulated_fields = {"users": ('name',)} self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'prepopulated_fields' refers to 'users', which must not be " "a DateTimeField, ForeignKey or ManyToManyField."), 'admin.E028') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): prepopulated_fields = {"slug": ('name',)} self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ListDisplayTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): list_display = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_display' must be a list or tuple.", 'admin.E107') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): list_display = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'list_display[0]' refers to 'non_existent_field', which is not a callable, an attribute " "of 'ValidationTestModelAdmin', or an attribute or method on 'modeladmin.ValidationTestModel'."), 'admin.E108') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): list_display = ('users',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_display[0]' must not be a ManyToManyField.", 'admin.E109') def test_valid_case(self): def a_callable(obj): pass class ValidationTestModelAdmin(ModelAdmin): def a_method(self, obj): pass list_display = ('name', 'decade_published_in', 'a_method', a_callable) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ListDisplayLinksCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): list_display_links = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_display_links' must be a list, a tuple, or None.", 'admin.E110') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): list_display_links = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_display_links[0]' refers to 'non_existent_field', which is not defined in 'list_display'.", 'admin.E111') def test_missing_in_list_display(self): class ValidationTestModelAdmin(ModelAdmin): list_display_links = ('name',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_display_links[0]' refers to 'name', which is not defined in 'list_display'.", 'admin.E111') def test_valid_case(self): def a_callable(obj): pass class ValidationTestModelAdmin(ModelAdmin): def a_method(self, obj): pass list_display = ('name', 'decade_published_in', 'a_method', a_callable) list_display_links = ('name', 'decade_published_in', 'a_method', a_callable) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) def test_None_is_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): list_display_links = None self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ListFilterTests(CheckTestCase): def test_list_filter_validation(self): class ValidationTestModelAdmin(ModelAdmin): list_filter = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_filter' must be a list or tuple.", 'admin.E112') def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): list_filter = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_filter[0]' refers to 'non_existent_field', which does not refer to a Field.", 'admin.E116') def test_not_filter(self): class RandomClass(object): pass class ValidationTestModelAdmin(ModelAdmin): list_filter = (RandomClass,) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_filter[0]' must inherit from 'ListFilter'.", 'admin.E113') def test_not_filter_again(self): class RandomClass(object): pass class ValidationTestModelAdmin(ModelAdmin): list_filter = (('is_active', RandomClass),) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_filter[0][1]' must inherit from 'FieldListFilter'.", 'admin.E115') def test_not_filter_again_again(self): class AwesomeFilter(SimpleListFilter): def get_title(self): return 'awesomeness' def get_choices(self, request): return (('bit', 'A bit awesome'), ('very', 'Very awesome'), ) def get_queryset(self, cl, qs): return qs class ValidationTestModelAdmin(ModelAdmin): list_filter = (('is_active', AwesomeFilter),) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_filter[0][1]' must inherit from 'FieldListFilter'.", 'admin.E115') def test_not_associated_with_field_name(self): class ValidationTestModelAdmin(ModelAdmin): list_filter = (BooleanFieldListFilter,) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_filter[0]' must not inherit from 'FieldListFilter'.", 'admin.E114') def test_valid_case(self): class AwesomeFilter(SimpleListFilter): def get_title(self): return 'awesomeness' def get_choices(self, request): return (('bit', 'A bit awesome'), ('very', 'Very awesome'), ) def get_queryset(self, cl, qs): return qs class ValidationTestModelAdmin(ModelAdmin): list_filter = ('is_active', AwesomeFilter, ('is_active', BooleanFieldListFilter), 'no') self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ListPerPageCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestModelAdmin(ModelAdmin): list_per_page = 'hello' self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_per_page' must be an integer.", 'admin.E118') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): list_per_page = 100 self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ListMaxShowAllCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestModelAdmin(ModelAdmin): list_max_show_all = 'hello' self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_max_show_all' must be an integer.", 'admin.E119') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): list_max_show_all = 200 self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class SearchFieldsCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): search_fields = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'search_fields' must be a list or tuple.", 'admin.E126') class DateHierarchyCheckTests(CheckTestCase): def test_missing_field(self): class ValidationTestModelAdmin(ModelAdmin): date_hierarchy = 'non_existent_field' self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'date_hierarchy' refers to 'non_existent_field', which " "is not an attribute of 'modeladmin.ValidationTestModel'."), 'admin.E127') def test_invalid_field_type(self): class ValidationTestModelAdmin(ModelAdmin): date_hierarchy = 'name' self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'date_hierarchy' must be a DateField or DateTimeField.", 'admin.E128') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): date_hierarchy = 'pub_date' self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class OrderingCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): ordering = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'ordering' must be a list or tuple.", 'admin.E031') class ValidationTestModelAdmin(ModelAdmin): ordering = ('non_existent_field',) self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'ordering[0]' refers to 'non_existent_field', which is not an attribute of 'modeladmin.ValidationTestModel'.", 'admin.E033', ) def test_random_marker_not_alone(self): class ValidationTestModelAdmin(ModelAdmin): ordering = ('?', 'name') self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, ("The value of 'ordering' has the random ordering marker '?', but contains " "other fields as well."), 'admin.E032', hint='Either remove the "?", or remove the other fields.') def test_valid_random_marker_case(self): class ValidationTestModelAdmin(ModelAdmin): ordering = ('?',) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) def test_valid_complex_case(self): class ValidationTestModelAdmin(ModelAdmin): ordering = ('band__name',) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): ordering = ('name',) self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ListSelectRelatedCheckTests(CheckTestCase): def test_invalid_type(self): class ValidationTestModelAdmin(ModelAdmin): list_select_related = 1 self.assertIsInvalid(ValidationTestModelAdmin, ValidationTestModel, "The value of 'list_select_related' must be a boolean, tuple or list.", 'admin.E117') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): list_select_related = False self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class SaveAsCheckTests(CheckTestCase): def test_not_boolean(self): class ValidationTestModelAdmin(ModelAdmin): save_as = 1 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'save_as' must be a boolean.", 'admin.E101') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): save_as = True self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class SaveOnTopCheckTests(CheckTestCase): def test_not_boolean(self): class ValidationTestModelAdmin(ModelAdmin): save_on_top = 1 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'save_on_top' must be a boolean.", 'admin.E102') def test_valid_case(self): class ValidationTestModelAdmin(ModelAdmin): save_on_top = True self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class InlinesCheckTests(CheckTestCase): def test_not_iterable(self): class ValidationTestModelAdmin(ModelAdmin): inlines = 10 self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'inlines' must be a list or tuple.", 'admin.E103') def test_not_model_admin(self): class ValidationTestInline(object): pass class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalidRegexp( ValidationTestModelAdmin, ValidationTestModel, r"'.*\.ValidationTestInline' must inherit from 'BaseModelAdmin'\.", 'admin.E104') def test_missing_model_field(self): class ValidationTestInline(TabularInline): pass class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalidRegexp( ValidationTestModelAdmin, ValidationTestModel, r"'.*\.ValidationTestInline' must have a 'model' attribute\.", 'admin.E105') def test_invalid_model_type(self): """ Test if `model` attribute on inline model admin is a models.Model. """ class SomethingBad(object): pass class ValidationTestInline(TabularInline): model = SomethingBad class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalidRegexp( ValidationTestModelAdmin, ValidationTestModel, r"The value of '.*\.ValidationTestInline.model' must be a Model\.", 'admin.E106') def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class FkNameCheckTests(CheckTestCase): def test_missing_field(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel fk_name = 'non_existent_field' class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "'modeladmin.ValidationTestInlineModel' has no field named 'non_existent_field'.", 'admin.E202', invalid_obj=ValidationTestInline) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel fk_name = "parent" class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class ExtraCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel extra = "hello" class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'extra' must be an integer.", 'admin.E203', invalid_obj=ValidationTestInline) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel extra = 2 class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class MaxNumCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel max_num = "hello" class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'max_num' must be an integer.", 'admin.E204', invalid_obj=ValidationTestInline) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel max_num = 2 class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class MinNumCheckTests(CheckTestCase): def test_not_integer(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel min_num = "hello" class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'min_num' must be an integer.", 'admin.E205', invalid_obj=ValidationTestInline) def test_valid_case(self): class ValidationTestInline(TabularInline): model = ValidationTestInlineModel min_num = 2 class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class FormsetCheckTests(CheckTestCase): def test_invalid_type(self): class FakeFormSet(object): pass class ValidationTestInline(TabularInline): model = ValidationTestInlineModel formset = FakeFormSet class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsInvalid( ValidationTestModelAdmin, ValidationTestModel, "The value of 'formset' must inherit from 'BaseModelFormSet'.", 'admin.E206', invalid_obj=ValidationTestInline) def test_valid_case(self): class RealModelFormSet(BaseModelFormSet): pass class ValidationTestInline(TabularInline): model = ValidationTestInlineModel formset = RealModelFormSet class ValidationTestModelAdmin(ModelAdmin): inlines = [ValidationTestInline] self.assertIsValid(ValidationTestModelAdmin, ValidationTestModel) class CustomModelAdminTests(CheckTestCase): @ignore_warnings(category=RemovedInDjango19Warning) def test_deprecation(self): "Deprecated Custom Validator definitions still work with the check framework." class CustomValidator(ModelAdminValidator): def validate_me(self, model_admin, model): raise ImproperlyConfigured('error!') class CustomModelAdmin(ModelAdmin): validator_class = CustomValidator self.assertIsInvalid(CustomModelAdmin, ValidationTestModel, 'error!') class ListDisplayEditableTests(CheckTestCase): def test_list_display_links_is_none(self): """ list_display and list_editable can contain the same values when list_display_links is None """ class ProductAdmin(ModelAdmin): list_display = ['name', 'slug', 'pub_date'] list_editable = list_display list_display_links = None self.assertIsValid(ProductAdmin, ValidationTestModel) def test_list_display_same_as_list_editable(self): """ The first item in list_display can be the same as the first in list_editable """ class ProductAdmin(ModelAdmin): list_display = ['name', 'slug', 'pub_date'] list_editable = ['name', 'slug'] list_display_links = ['pub_date'] self.assertIsValid(ProductAdmin, ValidationTestModel) class ModelAdminPermissionTests(TestCase): class MockUser(object): def has_module_perms(self, app_label): if app_label == "modeladmin": return True return False class MockAddUser(MockUser): def has_perm(self, perm): if perm == "modeladmin.add_band": return True return False class MockChangeUser(MockUser): def has_perm(self, perm): if perm == "modeladmin.change_band": return True return False class MockDeleteUser(MockUser): def has_perm(self, perm): if perm == "modeladmin.delete_band": return True return False def test_has_add_permission(self): """ Ensure that has_add_permission returns True for users who can add objects and False for users who can't. """ ma = ModelAdmin(Band, AdminSite()) request = MockRequest() request.user = self.MockAddUser() self.assertTrue(ma.has_add_permission(request)) request.user = self.MockChangeUser() self.assertFalse(ma.has_add_permission(request)) request.user = self.MockDeleteUser() self.assertFalse(ma.has_add_permission(request)) def test_has_change_permission(self): """ Ensure that has_change_permission returns True for users who can edit objects and False for users who can't. """ ma = ModelAdmin(Band, AdminSite()) request = MockRequest() request.user = self.MockAddUser() self.assertFalse(ma.has_change_permission(request)) request.user = self.MockChangeUser() self.assertTrue(ma.has_change_permission(request)) request.user = self.MockDeleteUser() self.assertFalse(ma.has_change_permission(request)) def test_has_delete_permission(self): """ Ensure that has_delete_permission returns True for users who can delete objects and False for users who can't. """ ma = ModelAdmin(Band, AdminSite()) request = MockRequest() request.user = self.MockAddUser() self.assertFalse(ma.has_delete_permission(request)) request.user = self.MockChangeUser() self.assertFalse(ma.has_delete_permission(request)) request.user = self.MockDeleteUser() self.assertTrue(ma.has_delete_permission(request)) def test_has_module_permission(self): """ Ensure that has_module_permission returns True for users who have any permission for the module and False for users who don't. """ ma = ModelAdmin(Band, AdminSite()) request = MockRequest() request.user = self.MockAddUser() self.assertTrue(ma.has_module_permission(request)) request.user = self.MockChangeUser() self.assertTrue(ma.has_module_permission(request)) request.user = self.MockDeleteUser() self.assertTrue(ma.has_module_permission(request)) original_app_label = ma.opts.app_label ma.opts.app_label = 'anotherapp' try: request.user = self.MockAddUser() self.assertFalse(ma.has_module_permission(request)) request.user = self.MockChangeUser() self.assertFalse(ma.has_module_permission(request)) request.user = self.MockDeleteUser() self.assertFalse(ma.has_module_permission(request)) finally: ma.opts.app_label = original_app_label Django-1.8.7/tests/modeladmin/__init__.py0000664000175000017500000000000012603513307017711 0ustar timtim00000000000000Django-1.8.7/tests/modeladmin/models.py0000664000175000017500000000300312625113144017442 0ustar timtim00000000000000# -*- coding: utf-8 -*- from django.contrib.auth.models import User from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Band(models.Model): name = models.CharField(max_length=100) bio = models.TextField() sign_date = models.DateField() class Meta: ordering = ('name',) def __str__(self): return self.name class Concert(models.Model): main_band = models.ForeignKey(Band, related_name='main_concerts') opening_band = models.ForeignKey(Band, related_name='opening_concerts', blank=True) day = models.CharField(max_length=3, choices=((1, 'Fri'), (2, 'Sat'))) transport = models.CharField(max_length=100, choices=( (1, 'Plane'), (2, 'Train'), (3, 'Bus') ), blank=True) class ValidationTestModel(models.Model): name = models.CharField(max_length=100) slug = models.SlugField() users = models.ManyToManyField(User) state = models.CharField(max_length=2, choices=(("CO", "Colorado"), ("WA", "Washington"))) is_active = models.BooleanField(default=False) pub_date = models.DateTimeField() band = models.ForeignKey(Band) no = models.IntegerField(verbose_name="Number", blank=True, null=True) # This field is intentionally 2 characters long. See #16080. def decade_published_in(self): return self.pub_date.strftime('%Y')[:3] + "0's" class ValidationTestInlineModel(models.Model): parent = models.ForeignKey(ValidationTestModel) Django-1.8.7/tests/invalid_models_tests/0000775000175000017500000000000012625116243017716 5ustar timtim00000000000000Django-1.8.7/tests/invalid_models_tests/test_ordinary_fields.py0000664000175000017500000004532312625116214024511 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals import unittest from django.core.checks import Error, Warning as DjangoWarning from django.db import connection, models from django.test.utils import override_settings from django.utils.timezone import now from .base import IsolatedModelsTestCase class AutoFieldTests(IsolatedModelsTestCase): def test_valid_case(self): class Model(models.Model): id = models.AutoField(primary_key=True) field = Model._meta.get_field('id') errors = field.check() expected = [] self.assertEqual(errors, expected) def test_primary_key(self): # primary_key must be True. Refs #12467. class Model(models.Model): field = models.AutoField(primary_key=False) # Prevent Django from autocreating `id` AutoField, which would # result in an error, because a model must have exactly one # AutoField. another = models.IntegerField(primary_key=True) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( 'AutoFields must set primary_key=True.', hint=None, obj=field, id='fields.E100', ), ] self.assertEqual(errors, expected) class BooleanFieldTests(IsolatedModelsTestCase): def test_nullable_boolean_field(self): class Model(models.Model): field = models.BooleanField(null=True) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( 'BooleanFields do not accept null values.', hint='Use a NullBooleanField instead.', obj=field, id='fields.E110', ), ] self.assertEqual(errors, expected) class CharFieldTests(IsolatedModelsTestCase): def test_valid_field(self): class Model(models.Model): field = models.CharField( max_length=255, choices=[ ('1', 'item1'), ('2', 'item2'), ], db_index=True) field = Model._meta.get_field('field') errors = field.check() expected = [] self.assertEqual(errors, expected) def test_missing_max_length(self): class Model(models.Model): field = models.CharField() field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "CharFields must define a 'max_length' attribute.", hint=None, obj=field, id='fields.E120', ), ] self.assertEqual(errors, expected) def test_negative_max_length(self): class Model(models.Model): field = models.CharField(max_length=-1) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'max_length' must be a positive integer.", hint=None, obj=field, id='fields.E121', ), ] self.assertEqual(errors, expected) def test_bad_max_length_value(self): class Model(models.Model): field = models.CharField(max_length="bad") field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'max_length' must be a positive integer.", hint=None, obj=field, id='fields.E121', ), ] self.assertEqual(errors, expected) def test_non_iterable_choices(self): class Model(models.Model): field = models.CharField(max_length=10, choices='bad') field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'choices' must be an iterable (e.g., a list or tuple).", hint=None, obj=field, id='fields.E004', ), ] self.assertEqual(errors, expected) def test_choices_containing_non_pairs(self): class Model(models.Model): field = models.CharField(max_length=10, choices=[(1, 2, 3), (1, 2, 3)]) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'choices' must be an iterable containing (actual value, human readable name) tuples.", hint=None, obj=field, id='fields.E005', ), ] self.assertEqual(errors, expected) def test_bad_db_index_value(self): class Model(models.Model): field = models.CharField(max_length=10, db_index='bad') field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'db_index' must be None, True or False.", hint=None, obj=field, id='fields.E006', ), ] self.assertEqual(errors, expected) @unittest.skipUnless(connection.vendor == 'mysql', "Test valid only for MySQL") def test_too_long_char_field_under_mysql(self): from django.db.backends.mysql.validation import DatabaseValidation class Model(models.Model): field = models.CharField(unique=True, max_length=256) field = Model._meta.get_field('field') validator = DatabaseValidation(connection=None) errors = validator.check_field(field) expected = [ Error( 'MySQL does not allow unique CharFields to have a max_length > 255.', hint=None, obj=field, id='mysql.E001', ) ] self.assertEqual(errors, expected) class DateFieldTests(IsolatedModelsTestCase): def test_auto_now_and_auto_now_add_raise_error(self): class Model(models.Model): field0 = models.DateTimeField(auto_now=True, auto_now_add=True, default=now) field1 = models.DateTimeField(auto_now=True, auto_now_add=False, default=now) field2 = models.DateTimeField(auto_now=False, auto_now_add=True, default=now) field3 = models.DateTimeField(auto_now=True, auto_now_add=True, default=None) expected = [] checks = [] for i in range(4): field = Model._meta.get_field('field%d' % i) expected.append(Error( "The options auto_now, auto_now_add, and default " "are mutually exclusive. Only one of these options " "may be present.", hint=None, obj=field, id='fields.E160', )) checks.extend(field.check()) self.assertEqual(checks, expected) def test_fix_default_value(self): class Model(models.Model): field_dt = models.DateField(default=now()) field_d = models.DateField(default=now().date()) field_now = models.DateField(default=now) field_dt = Model._meta.get_field('field_dt') field_d = Model._meta.get_field('field_d') field_now = Model._meta.get_field('field_now') errors = field_dt.check() errors.extend(field_d.check()) errors.extend(field_now.check()) # doesn't raise a warning expected = [ DjangoWarning( 'Fixed default value provided.', hint='It seems you set a fixed date / time / datetime ' 'value as default for this field. This may not be ' 'what you want. If you want to have the current date ' 'as default, use `django.utils.timezone.now`', obj=field_dt, id='fields.W161', ), DjangoWarning( 'Fixed default value provided.', hint='It seems you set a fixed date / time / datetime ' 'value as default for this field. This may not be ' 'what you want. If you want to have the current date ' 'as default, use `django.utils.timezone.now`', obj=field_d, id='fields.W161', ) ] maxDiff = self.maxDiff self.maxDiff = None self.assertEqual(errors, expected) self.maxDiff = maxDiff @override_settings(USE_TZ=True) def test_fix_default_value_tz(self): self.test_fix_default_value() class DateTimeFieldTests(IsolatedModelsTestCase): def test_fix_default_value(self): class Model(models.Model): field_dt = models.DateTimeField(default=now()) field_d = models.DateTimeField(default=now().date()) field_now = models.DateTimeField(default=now) field_dt = Model._meta.get_field('field_dt') field_d = Model._meta.get_field('field_d') field_now = Model._meta.get_field('field_now') errors = field_dt.check() errors.extend(field_d.check()) errors.extend(field_now.check()) # doesn't raise a warning expected = [ DjangoWarning( 'Fixed default value provided.', hint='It seems you set a fixed date / time / datetime ' 'value as default for this field. This may not be ' 'what you want. If you want to have the current date ' 'as default, use `django.utils.timezone.now`', obj=field_dt, id='fields.W161', ), DjangoWarning( 'Fixed default value provided.', hint='It seems you set a fixed date / time / datetime ' 'value as default for this field. This may not be ' 'what you want. If you want to have the current date ' 'as default, use `django.utils.timezone.now`', obj=field_d, id='fields.W161', ) ] maxDiff = self.maxDiff self.maxDiff = None self.assertEqual(errors, expected) self.maxDiff = maxDiff @override_settings(USE_TZ=True) def test_fix_default_value_tz(self): self.test_fix_default_value() class DecimalFieldTests(IsolatedModelsTestCase): def test_required_attributes(self): class Model(models.Model): field = models.DecimalField() field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "DecimalFields must define a 'decimal_places' attribute.", hint=None, obj=field, id='fields.E130', ), Error( "DecimalFields must define a 'max_digits' attribute.", hint=None, obj=field, id='fields.E132', ), ] self.assertEqual(errors, expected) def test_negative_max_digits_and_decimal_places(self): class Model(models.Model): field = models.DecimalField(max_digits=-1, decimal_places=-1) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'decimal_places' must be a non-negative integer.", hint=None, obj=field, id='fields.E131', ), Error( "'max_digits' must be a positive integer.", hint=None, obj=field, id='fields.E133', ), ] self.assertEqual(errors, expected) def test_bad_values_of_max_digits_and_decimal_places(self): class Model(models.Model): field = models.DecimalField(max_digits="bad", decimal_places="bad") field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'decimal_places' must be a non-negative integer.", hint=None, obj=field, id='fields.E131', ), Error( "'max_digits' must be a positive integer.", hint=None, obj=field, id='fields.E133', ), ] self.assertEqual(errors, expected) def test_decimal_places_greater_than_max_digits(self): class Model(models.Model): field = models.DecimalField(max_digits=9, decimal_places=10) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'max_digits' must be greater or equal to 'decimal_places'.", hint=None, obj=field, id='fields.E134', ), ] self.assertEqual(errors, expected) def test_valid_field(self): class Model(models.Model): field = models.DecimalField(max_digits=10, decimal_places=10) field = Model._meta.get_field('field') errors = field.check() expected = [] self.assertEqual(errors, expected) class FileFieldTests(IsolatedModelsTestCase): def test_valid_case(self): class Model(models.Model): field = models.FileField(upload_to='somewhere') field = Model._meta.get_field('field') errors = field.check() expected = [] self.assertEqual(errors, expected) def test_unique(self): class Model(models.Model): field = models.FileField(unique=False, upload_to='somewhere') field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'unique' is not a valid argument for a FileField.", hint=None, obj=field, id='fields.E200', ) ] self.assertEqual(errors, expected) def test_primary_key(self): class Model(models.Model): field = models.FileField(primary_key=False, upload_to='somewhere') field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'primary_key' is not a valid argument for a FileField.", hint=None, obj=field, id='fields.E201', ) ] self.assertEqual(errors, expected) class FilePathFieldTests(IsolatedModelsTestCase): def test_forbidden_files_and_folders(self): class Model(models.Model): field = models.FilePathField(allow_files=False, allow_folders=False) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "FilePathFields must have either 'allow_files' or 'allow_folders' set to True.", hint=None, obj=field, id='fields.E140', ), ] self.assertEqual(errors, expected) class GenericIPAddressFieldTests(IsolatedModelsTestCase): def test_non_nullable_blank(self): class Model(models.Model): field = models.GenericIPAddressField(null=False, blank=True) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( ('GenericIPAddressFields cannot have blank=True if null=False, ' 'as blank values are stored as nulls.'), hint=None, obj=field, id='fields.E150', ), ] self.assertEqual(errors, expected) class ImageFieldTests(IsolatedModelsTestCase): def test_pillow_installed(self): try: from PIL import Image # NOQA except ImportError: pillow_installed = False else: pillow_installed = True class Model(models.Model): field = models.ImageField(upload_to='somewhere') field = Model._meta.get_field('field') errors = field.check() expected = [] if pillow_installed else [ Error( 'Cannot use ImageField because Pillow is not installed.', hint=('Get Pillow at https://pypi.python.org/pypi/Pillow ' 'or run command "pip install Pillow".'), obj=field, id='fields.E210', ), ] self.assertEqual(errors, expected) class IntegerFieldTests(IsolatedModelsTestCase): def test_max_length_warning(self): class Model(models.Model): value = models.IntegerField(max_length=2) value = Model._meta.get_field('value') errors = Model.check() expected = [ DjangoWarning( "'max_length' is ignored when used with IntegerField", hint="Remove 'max_length' from field", obj=value, id='fields.W122', ) ] self.assertEqual(errors, expected) class TimeFieldTests(IsolatedModelsTestCase): def test_fix_default_value(self): class Model(models.Model): field_dt = models.TimeField(default=now()) field_t = models.TimeField(default=now().time()) field_now = models.DateField(default=now) field_dt = Model._meta.get_field('field_dt') field_t = Model._meta.get_field('field_t') field_now = Model._meta.get_field('field_now') errors = field_dt.check() errors.extend(field_t.check()) errors.extend(field_now.check()) # doesn't raise a warning expected = [ DjangoWarning( 'Fixed default value provided.', hint='It seems you set a fixed date / time / datetime ' 'value as default for this field. This may not be ' 'what you want. If you want to have the current date ' 'as default, use `django.utils.timezone.now`', obj=field_dt, id='fields.W161', ), DjangoWarning( 'Fixed default value provided.', hint='It seems you set a fixed date / time / datetime ' 'value as default for this field. This may not be ' 'what you want. If you want to have the current date ' 'as default, use `django.utils.timezone.now`', obj=field_t, id='fields.W161', ) ] maxDiff = self.maxDiff self.maxDiff = None self.assertEqual(errors, expected) self.maxDiff = maxDiff @override_settings(USE_TZ=True) def test_fix_default_value_tz(self): self.test_fix_default_value() Django-1.8.7/tests/invalid_models_tests/test_backend_specific.py0000664000175000017500000000357012625113144024565 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals from types import MethodType from django.core.checks import Error from django.db import connection, models from .base import IsolatedModelsTestCase class BackendSpecificChecksTests(IsolatedModelsTestCase): def test_check_field(self): """ Test if backend specific checks are performed. """ error = Error('an error', hint=None) def mock(self, field, **kwargs): return [error] class Model(models.Model): field = models.IntegerField() field = Model._meta.get_field('field') # Mock connection.validation.check_field method. v = connection.validation old_check_field = v.check_field v.check_field = MethodType(mock, v) try: errors = field.check() finally: # Unmock connection.validation.check_field method. v.check_field = old_check_field self.assertEqual(errors, [error]) def test_validate_field(self): """ Errors raised by deprecated `validate_field` method should be collected. """ def mock(self, errors, opts, field): errors.add(opts, "An error!") class Model(models.Model): field = models.IntegerField() field = Model._meta.get_field('field') expected = [ Error( "An error!", hint=None, obj=field, ) ] # Mock connection.validation.validate_field method. v = connection.validation old_validate_field = v.validate_field v.validate_field = MethodType(mock, v) try: errors = field.check() finally: # Unmock connection.validation.validate_field method. v.validate_field = old_validate_field self.assertEqual(errors, expected) Django-1.8.7/tests/invalid_models_tests/test_relative_fields.py0000664000175000017500000014010712625116214024471 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals from django.core.checks import Error, Warning as DjangoWarning from django.db import models from django.test.testcases import skipIfDBFeature from django.test.utils import override_settings from django.utils import six from .base import IsolatedModelsTestCase class RelativeFieldTests(IsolatedModelsTestCase): def test_valid_foreign_key_without_accessor(self): class Target(models.Model): # There would be a clash if Model.field installed an accessor. model = models.IntegerField() class Model(models.Model): field = models.ForeignKey(Target, related_name='+') field = Model._meta.get_field('field') errors = field.check() self.assertEqual(errors, []) def test_foreign_key_to_missing_model(self): # Model names are resolved when a model is being created, so we cannot # test relative fields in isolation and we need to attach them to a # model. class Model(models.Model): foreign_key = models.ForeignKey('Rel1') field = Model._meta.get_field('foreign_key') errors = field.check() expected = [ Error( ("Field defines a relation with model 'Rel1', " "which is either not installed, or is abstract."), hint=None, obj=field, id='fields.E300', ), ] self.assertEqual(errors, expected) def test_many_to_many_to_missing_model(self): class Model(models.Model): m2m = models.ManyToManyField("Rel2") field = Model._meta.get_field('m2m') errors = field.check(from_model=Model) expected = [ Error( ("Field defines a relation with model 'Rel2', " "which is either not installed, or is abstract."), hint=None, obj=field, id='fields.E300', ), ] self.assertEqual(errors, expected) def test_many_to_many_with_useless_options(self): class Model(models.Model): name = models.CharField(max_length=20) class ModelM2M(models.Model): m2m = models.ManyToManyField(Model, null=True, validators=['']) errors = ModelM2M.check() field = ModelM2M._meta.get_field('m2m') expected = [ DjangoWarning( 'null has no effect on ManyToManyField.', hint=None, obj=field, id='fields.W340', ) ] expected.append( DjangoWarning( 'ManyToManyField does not support validators.', hint=None, obj=field, id='fields.W341', ) ) self.assertEqual(errors, expected) def test_ambiguous_relationship_model(self): class Person(models.Model): pass class Group(models.Model): field = models.ManyToManyField('Person', through="AmbiguousRelationship", related_name='tertiary') class AmbiguousRelationship(models.Model): # Too much foreign keys to Person. first_person = models.ForeignKey(Person, related_name="first") second_person = models.ForeignKey(Person, related_name="second") second_model = models.ForeignKey(Group) field = Group._meta.get_field('field') errors = field.check(from_model=Group) expected = [ Error( ("The model is used as an intermediate model by " "'invalid_models_tests.Group.field', but it has more than one " "foreign key to 'Person', which is ambiguous. You must specify " "which foreign key Django should use via the through_fields " "keyword argument."), hint=('If you want to create a recursive relationship, use ' 'ForeignKey("self", symmetrical=False, ' 'through="AmbiguousRelationship").'), obj=field, id='fields.E335', ), ] self.assertEqual(errors, expected) def test_relationship_model_with_foreign_key_to_wrong_model(self): class WrongModel(models.Model): pass class Person(models.Model): pass class Group(models.Model): members = models.ManyToManyField('Person', through="InvalidRelationship") class InvalidRelationship(models.Model): person = models.ForeignKey(Person) wrong_foreign_key = models.ForeignKey(WrongModel) # The last foreign key should point to Group model. field = Group._meta.get_field('members') errors = field.check(from_model=Group) expected = [ Error( ("The model is used as an intermediate model by " "'invalid_models_tests.Group.members', but it does not " "have a foreign key to 'Group' or 'Person'."), hint=None, obj=InvalidRelationship, id='fields.E336', ), ] self.assertEqual(errors, expected) def test_relationship_model_missing_foreign_key(self): class Person(models.Model): pass class Group(models.Model): members = models.ManyToManyField('Person', through="InvalidRelationship") class InvalidRelationship(models.Model): group = models.ForeignKey(Group) # No foreign key to Person field = Group._meta.get_field('members') errors = field.check(from_model=Group) expected = [ Error( ("The model is used as an intermediate model by " "'invalid_models_tests.Group.members', but it does not have " "a foreign key to 'Group' or 'Person'."), hint=None, obj=InvalidRelationship, id='fields.E336', ), ] self.assertEqual(errors, expected) def test_missing_relationship_model(self): class Person(models.Model): pass class Group(models.Model): members = models.ManyToManyField('Person', through="MissingM2MModel") field = Group._meta.get_field('members') errors = field.check(from_model=Group) expected = [ Error( ("Field specifies a many-to-many relation through model " "'MissingM2MModel', which has not been installed."), hint=None, obj=field, id='fields.E331', ), ] self.assertEqual(errors, expected) def test_symmetrical_self_referential_field(self): class Person(models.Model): # Implicit symmetrical=False. friends = models.ManyToManyField('self', through="Relationship") class Relationship(models.Model): first = models.ForeignKey(Person, related_name="rel_from_set") second = models.ForeignKey(Person, related_name="rel_to_set") field = Person._meta.get_field('friends') errors = field.check(from_model=Person) expected = [ Error( 'Many-to-many fields with intermediate tables must not be symmetrical.', hint=None, obj=field, id='fields.E332', ), ] self.assertEqual(errors, expected) def test_too_many_foreign_keys_in_self_referential_model(self): class Person(models.Model): friends = models.ManyToManyField('self', through="InvalidRelationship", symmetrical=False) class InvalidRelationship(models.Model): first = models.ForeignKey(Person, related_name="rel_from_set_2") second = models.ForeignKey(Person, related_name="rel_to_set_2") third = models.ForeignKey(Person, related_name="too_many_by_far") field = Person._meta.get_field('friends') errors = field.check(from_model=Person) expected = [ Error( ("The model is used as an intermediate model by " "'invalid_models_tests.Person.friends', but it has more than two " "foreign keys to 'Person', which is ambiguous. You must specify " "which two foreign keys Django should use via the through_fields " "keyword argument."), hint='Use through_fields to specify which two foreign keys Django should use.', obj=InvalidRelationship, id='fields.E333', ), ] self.assertEqual(errors, expected) def test_symmetric_self_reference_with_intermediate_table(self): class Person(models.Model): # Explicit symmetrical=True. friends = models.ManyToManyField('self', through="Relationship", symmetrical=True) class Relationship(models.Model): first = models.ForeignKey(Person, related_name="rel_from_set") second = models.ForeignKey(Person, related_name="rel_to_set") field = Person._meta.get_field('friends') errors = field.check(from_model=Person) expected = [ Error( 'Many-to-many fields with intermediate tables must not be symmetrical.', hint=None, obj=field, id='fields.E332', ), ] self.assertEqual(errors, expected) def test_symmetric_self_reference_with_intermediate_table_and_through_fields(self): """Using through_fields in a m2m with an intermediate model shouldn't mask its incompatibility with symmetry.""" class Person(models.Model): # Explicit symmetrical=True. friends = models.ManyToManyField('self', symmetrical=True, through="Relationship", through_fields=('first', 'second')) class Relationship(models.Model): first = models.ForeignKey(Person, related_name="rel_from_set") second = models.ForeignKey(Person, related_name="rel_to_set") referee = models.ForeignKey(Person, related_name="referred") field = Person._meta.get_field('friends') errors = field.check(from_model=Person) expected = [ Error( 'Many-to-many fields with intermediate tables must not be symmetrical.', hint=None, obj=field, id='fields.E332', ), ] self.assertEqual(errors, expected) def test_foreign_key_to_abstract_model(self): class AbstractModel(models.Model): class Meta: abstract = True class Model(models.Model): rel_string_foreign_key = models.ForeignKey('AbstractModel') rel_class_foreign_key = models.ForeignKey(AbstractModel) fields = [ Model._meta.get_field('rel_string_foreign_key'), Model._meta.get_field('rel_class_foreign_key'), ] expected_error = Error( "Field defines a relation with model 'AbstractModel', " "which is either not installed, or is abstract.", id='fields.E300', ) for field in fields: expected_error.obj = field errors = field.check() self.assertEqual(errors, [expected_error]) def test_m2m_to_abstract_model(self): class AbstractModel(models.Model): class Meta: abstract = True class Model(models.Model): rel_string_m2m = models.ManyToManyField('AbstractModel') rel_class_m2m = models.ManyToManyField(AbstractModel) fields = [ Model._meta.get_field('rel_string_m2m'), Model._meta.get_field('rel_class_m2m'), ] expected_error = Error( "Field defines a relation with model 'AbstractModel', " "which is either not installed, or is abstract.", id='fields.E300', ) for field in fields: expected_error.obj = field errors = field.check(from_model=Model) self.assertEqual(errors, [expected_error]) def test_unique_m2m(self): class Person(models.Model): name = models.CharField(max_length=5) class Group(models.Model): members = models.ManyToManyField('Person', unique=True) field = Group._meta.get_field('members') errors = field.check(from_model=Group) expected = [ Error( 'ManyToManyFields cannot be unique.', hint=None, obj=field, id='fields.E330', ), ] self.assertEqual(errors, expected) def test_foreign_key_to_non_unique_field(self): class Target(models.Model): bad = models.IntegerField() # No unique=True class Model(models.Model): foreign_key = models.ForeignKey('Target', to_field='bad') field = Model._meta.get_field('foreign_key') errors = field.check() expected = [ Error( "'Target.bad' must set unique=True because it is referenced by a foreign key.", hint=None, obj=field, id='fields.E311', ), ] self.assertEqual(errors, expected) def test_foreign_key_to_non_unique_field_under_explicit_model(self): class Target(models.Model): bad = models.IntegerField() class Model(models.Model): field = models.ForeignKey(Target, to_field='bad') field = Model._meta.get_field('field') errors = field.check() expected = [ Error( "'Target.bad' must set unique=True because it is referenced by a foreign key.", hint=None, obj=field, id='fields.E311', ), ] self.assertEqual(errors, expected) def test_foreign_object_to_non_unique_fields(self): class Person(models.Model): # Note that both fields are not unique. country_id = models.IntegerField() city_id = models.IntegerField() class MMembership(models.Model): person_country_id = models.IntegerField() person_city_id = models.IntegerField() person = models.ForeignObject(Person, from_fields=['person_country_id', 'person_city_id'], to_fields=['country_id', 'city_id']) field = MMembership._meta.get_field('person') errors = field.check() expected = [ Error( ("None of the fields 'country_id', 'city_id' on model 'Person' " "have a unique=True constraint."), hint=None, obj=field, id='fields.E310', ) ] self.assertEqual(errors, expected) def test_on_delete_set_null_on_non_nullable_field(self): class Person(models.Model): pass class Model(models.Model): foreign_key = models.ForeignKey('Person', on_delete=models.SET_NULL) field = Model._meta.get_field('foreign_key') errors = field.check() expected = [ Error( 'Field specifies on_delete=SET_NULL, but cannot be null.', hint='Set null=True argument on the field, or change the on_delete rule.', obj=field, id='fields.E320', ), ] self.assertEqual(errors, expected) def test_on_delete_set_default_without_default_value(self): class Person(models.Model): pass class Model(models.Model): foreign_key = models.ForeignKey('Person', on_delete=models.SET_DEFAULT) field = Model._meta.get_field('foreign_key') errors = field.check() expected = [ Error( 'Field specifies on_delete=SET_DEFAULT, but has no default value.', hint='Set a default value, or change the on_delete rule.', obj=field, id='fields.E321', ), ] self.assertEqual(errors, expected) @skipIfDBFeature('interprets_empty_strings_as_nulls') def test_nullable_primary_key(self): class Model(models.Model): field = models.IntegerField(primary_key=True, null=True) field = Model._meta.get_field('field') errors = field.check() expected = [ Error( 'Primary keys must not have null=True.', hint='Set null=False on the field, or remove primary_key=True argument.', obj=field, id='fields.E007', ), ] self.assertEqual(errors, expected) def test_not_swapped_model(self): class SwappableModel(models.Model): # A model that can be, but isn't swapped out. References to this # model should *not* raise any validation error. class Meta: swappable = 'TEST_SWAPPABLE_MODEL' class Model(models.Model): explicit_fk = models.ForeignKey(SwappableModel, related_name='explicit_fk') implicit_fk = models.ForeignKey('invalid_models_tests.SwappableModel', related_name='implicit_fk') explicit_m2m = models.ManyToManyField(SwappableModel, related_name='explicit_m2m') implicit_m2m = models.ManyToManyField( 'invalid_models_tests.SwappableModel', related_name='implicit_m2m') explicit_fk = Model._meta.get_field('explicit_fk') self.assertEqual(explicit_fk.check(), []) implicit_fk = Model._meta.get_field('implicit_fk') self.assertEqual(implicit_fk.check(), []) explicit_m2m = Model._meta.get_field('explicit_m2m') self.assertEqual(explicit_m2m.check(from_model=Model), []) implicit_m2m = Model._meta.get_field('implicit_m2m') self.assertEqual(implicit_m2m.check(from_model=Model), []) @override_settings(TEST_SWAPPED_MODEL='invalid_models_tests.Replacement') def test_referencing_to_swapped_model(self): class Replacement(models.Model): pass class SwappedModel(models.Model): class Meta: swappable = 'TEST_SWAPPED_MODEL' class Model(models.Model): explicit_fk = models.ForeignKey(SwappedModel, related_name='explicit_fk') implicit_fk = models.ForeignKey('invalid_models_tests.SwappedModel', related_name='implicit_fk') explicit_m2m = models.ManyToManyField(SwappedModel, related_name='explicit_m2m') implicit_m2m = models.ManyToManyField( 'invalid_models_tests.SwappedModel', related_name='implicit_m2m') fields = [ Model._meta.get_field('explicit_fk'), Model._meta.get_field('implicit_fk'), Model._meta.get_field('explicit_m2m'), Model._meta.get_field('implicit_m2m'), ] expected_error = Error( ("Field defines a relation with the model " "'invalid_models_tests.SwappedModel', which has been swapped out."), hint="Update the relation to point at 'settings.TEST_SWAPPED_MODEL'.", id='fields.E301', ) for field in fields: expected_error.obj = field errors = field.check(from_model=Model) self.assertEqual(errors, [expected_error]) def test_related_field_has_invalid_related_name(self): digit = 0 illegal_non_alphanumeric = '!' whitespace = '\t' invalid_related_names = [ '%s_begins_with_digit' % digit, '%s_begins_with_illegal_non_alphanumeric' % illegal_non_alphanumeric, '%s_begins_with_whitespace' % whitespace, 'contains_%s_illegal_non_alphanumeric' % illegal_non_alphanumeric, 'contains_%s_whitespace' % whitespace, 'ends_with_with_illegal_non_alphanumeric_%s' % illegal_non_alphanumeric, 'ends_with_whitespace_%s' % whitespace, 'with', # a Python keyword 'related_name\n', ] # Python 2 crashes on non-ASCII strings. if six.PY3: invalid_related_names.append(',') class Parent(models.Model): pass for invalid_related_name in invalid_related_names: Child = type(str('Child_%s') % str(invalid_related_name), (models.Model,), { 'parent': models.ForeignKey('Parent', related_name=invalid_related_name), '__module__': Parent.__module__, }) field = Child._meta.get_field('parent') errors = Child.check() expected = [ Error( "The name '%s' is invalid related_name for field Child_%s.parent" % (invalid_related_name, invalid_related_name), hint="Related name must be a valid Python identifier or end with a '+'", obj=field, id='fields.E306', ), ] self.assertEqual(errors, expected) def test_related_field_has_valid_related_name(self): lowercase = 'a' uppercase = 'A' digit = 0 related_names = [ '%s_starts_with_lowercase' % lowercase, '%s_tarts_with_uppercase' % uppercase, '_starts_with_underscore', 'contains_%s_digit' % digit, 'ends_with_plus+', '_', '_+', '+', ] # Python 2 crashes on non-ASCII strings. if six.PY3: related_names.extend(['試', '試驗+']) class Parent(models.Model): pass for related_name in related_names: Child = type(str('Child_%s') % str(related_name), (models.Model,), { 'parent': models.ForeignKey('Parent', related_name=related_name), '__module__': Parent.__module__, }) errors = Child.check() self.assertFalse(errors) class AccessorClashTests(IsolatedModelsTestCase): def test_fk_to_integer(self): self._test_accessor_clash( target=models.IntegerField(), relative=models.ForeignKey('Target')) def test_fk_to_fk(self): self._test_accessor_clash( target=models.ForeignKey('Another'), relative=models.ForeignKey('Target')) def test_fk_to_m2m(self): self._test_accessor_clash( target=models.ManyToManyField('Another'), relative=models.ForeignKey('Target')) def test_m2m_to_integer(self): self._test_accessor_clash( target=models.IntegerField(), relative=models.ManyToManyField('Target')) def test_m2m_to_fk(self): self._test_accessor_clash( target=models.ForeignKey('Another'), relative=models.ManyToManyField('Target')) def test_m2m_to_m2m(self): self._test_accessor_clash( target=models.ManyToManyField('Another'), relative=models.ManyToManyField('Target')) def _test_accessor_clash(self, target, relative): class Another(models.Model): pass class Target(models.Model): model_set = target class Model(models.Model): rel = relative errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.rel' clashes with field name 'Target.model_set'.", hint=("Rename field 'Target.model_set', or add/change " "a related_name argument to the definition " "for field 'Model.rel'."), obj=Model._meta.get_field('rel'), id='fields.E302', ), ] self.assertEqual(errors, expected) def test_clash_between_accessors(self): class Target(models.Model): pass class Model(models.Model): foreign = models.ForeignKey(Target) m2m = models.ManyToManyField(Target) errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.foreign' clashes with reverse accessor for 'Model.m2m'.", hint=("Add or change a related_name argument to the definition " "for 'Model.foreign' or 'Model.m2m'."), obj=Model._meta.get_field('foreign'), id='fields.E304', ), Error( "Reverse accessor for 'Model.m2m' clashes with reverse accessor for 'Model.foreign'.", hint=("Add or change a related_name argument to the definition " "for 'Model.m2m' or 'Model.foreign'."), obj=Model._meta.get_field('m2m'), id='fields.E304', ), ] self.assertEqual(errors, expected) def test_m2m_to_m2m_with_inheritance(self): """ Ref #22047. """ class Target(models.Model): pass class Model(models.Model): children = models.ManyToManyField('Child', related_name="m2m_clash", related_query_name="no_clash") class Parent(models.Model): m2m_clash = models.ManyToManyField('Target') class Child(Parent): pass errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.children' clashes with field name 'Child.m2m_clash'.", hint=("Rename field 'Child.m2m_clash', or add/change " "a related_name argument to the definition " "for field 'Model.children'."), obj=Model._meta.get_field('children'), id='fields.E302', ) ] self.assertEqual(errors, expected) class ReverseQueryNameClashTests(IsolatedModelsTestCase): def test_fk_to_integer(self): self._test_reverse_query_name_clash( target=models.IntegerField(), relative=models.ForeignKey('Target')) def test_fk_to_fk(self): self._test_reverse_query_name_clash( target=models.ForeignKey('Another'), relative=models.ForeignKey('Target')) def test_fk_to_m2m(self): self._test_reverse_query_name_clash( target=models.ManyToManyField('Another'), relative=models.ForeignKey('Target')) def test_m2m_to_integer(self): self._test_reverse_query_name_clash( target=models.IntegerField(), relative=models.ManyToManyField('Target')) def test_m2m_to_fk(self): self._test_reverse_query_name_clash( target=models.ForeignKey('Another'), relative=models.ManyToManyField('Target')) def test_m2m_to_m2m(self): self._test_reverse_query_name_clash( target=models.ManyToManyField('Another'), relative=models.ManyToManyField('Target')) def _test_reverse_query_name_clash(self, target, relative): class Another(models.Model): pass class Target(models.Model): model = target class Model(models.Model): rel = relative errors = Model.check() expected = [ Error( "Reverse query name for 'Model.rel' clashes with field name 'Target.model'.", hint=("Rename field 'Target.model', or add/change " "a related_name argument to the definition " "for field 'Model.rel'."), obj=Model._meta.get_field('rel'), id='fields.E303', ), ] self.assertEqual(errors, expected) class ExplicitRelatedNameClashTests(IsolatedModelsTestCase): def test_fk_to_integer(self): self._test_explicit_related_name_clash( target=models.IntegerField(), relative=models.ForeignKey('Target', related_name='clash')) def test_fk_to_fk(self): self._test_explicit_related_name_clash( target=models.ForeignKey('Another'), relative=models.ForeignKey('Target', related_name='clash')) def test_fk_to_m2m(self): self._test_explicit_related_name_clash( target=models.ManyToManyField('Another'), relative=models.ForeignKey('Target', related_name='clash')) def test_m2m_to_integer(self): self._test_explicit_related_name_clash( target=models.IntegerField(), relative=models.ManyToManyField('Target', related_name='clash')) def test_m2m_to_fk(self): self._test_explicit_related_name_clash( target=models.ForeignKey('Another'), relative=models.ManyToManyField('Target', related_name='clash')) def test_m2m_to_m2m(self): self._test_explicit_related_name_clash( target=models.ManyToManyField('Another'), relative=models.ManyToManyField('Target', related_name='clash')) def _test_explicit_related_name_clash(self, target, relative): class Another(models.Model): pass class Target(models.Model): clash = target class Model(models.Model): rel = relative errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.rel' clashes with field name 'Target.clash'.", hint=("Rename field 'Target.clash', or add/change " "a related_name argument to the definition " "for field 'Model.rel'."), obj=Model._meta.get_field('rel'), id='fields.E302', ), Error( "Reverse query name for 'Model.rel' clashes with field name 'Target.clash'.", hint=("Rename field 'Target.clash', or add/change " "a related_name argument to the definition " "for field 'Model.rel'."), obj=Model._meta.get_field('rel'), id='fields.E303', ), ] self.assertEqual(errors, expected) class ExplicitRelatedQueryNameClashTests(IsolatedModelsTestCase): def test_fk_to_integer(self): self._test_explicit_related_query_name_clash( target=models.IntegerField(), relative=models.ForeignKey('Target', related_query_name='clash')) def test_fk_to_fk(self): self._test_explicit_related_query_name_clash( target=models.ForeignKey('Another'), relative=models.ForeignKey('Target', related_query_name='clash')) def test_fk_to_m2m(self): self._test_explicit_related_query_name_clash( target=models.ManyToManyField('Another'), relative=models.ForeignKey('Target', related_query_name='clash')) def test_m2m_to_integer(self): self._test_explicit_related_query_name_clash( target=models.IntegerField(), relative=models.ManyToManyField('Target', related_query_name='clash')) def test_m2m_to_fk(self): self._test_explicit_related_query_name_clash( target=models.ForeignKey('Another'), relative=models.ManyToManyField('Target', related_query_name='clash')) def test_m2m_to_m2m(self): self._test_explicit_related_query_name_clash( target=models.ManyToManyField('Another'), relative=models.ManyToManyField('Target', related_query_name='clash')) def _test_explicit_related_query_name_clash(self, target, relative): class Another(models.Model): pass class Target(models.Model): clash = target class Model(models.Model): rel = relative errors = Model.check() expected = [ Error( "Reverse query name for 'Model.rel' clashes with field name 'Target.clash'.", hint=("Rename field 'Target.clash', or add/change a related_name " "argument to the definition for field 'Model.rel'."), obj=Model._meta.get_field('rel'), id='fields.E303', ), ] self.assertEqual(errors, expected) class SelfReferentialM2MClashTests(IsolatedModelsTestCase): def test_clash_between_accessors(self): class Model(models.Model): first_m2m = models.ManyToManyField('self', symmetrical=False) second_m2m = models.ManyToManyField('self', symmetrical=False) errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.first_m2m' clashes with reverse accessor for 'Model.second_m2m'.", hint=("Add or change a related_name argument to the definition " "for 'Model.first_m2m' or 'Model.second_m2m'."), obj=Model._meta.get_field('first_m2m'), id='fields.E304', ), Error( "Reverse accessor for 'Model.second_m2m' clashes with reverse accessor for 'Model.first_m2m'.", hint=("Add or change a related_name argument to the definition " "for 'Model.second_m2m' or 'Model.first_m2m'."), obj=Model._meta.get_field('second_m2m'), id='fields.E304', ), ] self.assertEqual(errors, expected) def test_accessor_clash(self): class Model(models.Model): model_set = models.ManyToManyField("self", symmetrical=False) errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.model_set' clashes with field name 'Model.model_set'.", hint=("Rename field 'Model.model_set', or add/change " "a related_name argument to the definition " "for field 'Model.model_set'."), obj=Model._meta.get_field('model_set'), id='fields.E302', ), ] self.assertEqual(errors, expected) def test_reverse_query_name_clash(self): class Model(models.Model): model = models.ManyToManyField("self", symmetrical=False) errors = Model.check() expected = [ Error( "Reverse query name for 'Model.model' clashes with field name 'Model.model'.", hint=("Rename field 'Model.model', or add/change a related_name " "argument to the definition for field 'Model.model'."), obj=Model._meta.get_field('model'), id='fields.E303', ), ] self.assertEqual(errors, expected) def test_clash_under_explicit_related_name(self): class Model(models.Model): clash = models.IntegerField() m2m = models.ManyToManyField("self", symmetrical=False, related_name='clash') errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.m2m' clashes with field name 'Model.clash'.", hint=("Rename field 'Model.clash', or add/change a related_name " "argument to the definition for field 'Model.m2m'."), obj=Model._meta.get_field('m2m'), id='fields.E302', ), Error( "Reverse query name for 'Model.m2m' clashes with field name 'Model.clash'.", hint=("Rename field 'Model.clash', or add/change a related_name " "argument to the definition for field 'Model.m2m'."), obj=Model._meta.get_field('m2m'), id='fields.E303', ), ] self.assertEqual(errors, expected) def test_valid_model(self): class Model(models.Model): first = models.ManyToManyField("self", symmetrical=False, related_name='first_accessor') second = models.ManyToManyField("self", symmetrical=False, related_name='second_accessor') errors = Model.check() self.assertEqual(errors, []) class SelfReferentialFKClashTests(IsolatedModelsTestCase): def test_accessor_clash(self): class Model(models.Model): model_set = models.ForeignKey("Model") errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.model_set' clashes with field name 'Model.model_set'.", hint=("Rename field 'Model.model_set', or add/change " "a related_name argument to the definition " "for field 'Model.model_set'."), obj=Model._meta.get_field('model_set'), id='fields.E302', ), ] self.assertEqual(errors, expected) def test_reverse_query_name_clash(self): class Model(models.Model): model = models.ForeignKey("Model") errors = Model.check() expected = [ Error( "Reverse query name for 'Model.model' clashes with field name 'Model.model'.", hint=("Rename field 'Model.model', or add/change " "a related_name argument to the definition " "for field 'Model.model'."), obj=Model._meta.get_field('model'), id='fields.E303', ), ] self.assertEqual(errors, expected) def test_clash_under_explicit_related_name(self): class Model(models.Model): clash = models.CharField(max_length=10) foreign = models.ForeignKey("Model", related_name='clash') errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.foreign' clashes with field name 'Model.clash'.", hint=("Rename field 'Model.clash', or add/change " "a related_name argument to the definition " "for field 'Model.foreign'."), obj=Model._meta.get_field('foreign'), id='fields.E302', ), Error( "Reverse query name for 'Model.foreign' clashes with field name 'Model.clash'.", hint=("Rename field 'Model.clash', or add/change " "a related_name argument to the definition " "for field 'Model.foreign'."), obj=Model._meta.get_field('foreign'), id='fields.E303', ), ] self.assertEqual(errors, expected) class ComplexClashTests(IsolatedModelsTestCase): # New tests should not be included here, because this is a single, # self-contained sanity check, not a test of everything. def test_complex_clash(self): class Target(models.Model): tgt_safe = models.CharField(max_length=10) clash = models.CharField(max_length=10) model = models.CharField(max_length=10) clash1_set = models.CharField(max_length=10) class Model(models.Model): src_safe = models.CharField(max_length=10) foreign_1 = models.ForeignKey(Target, related_name='id') foreign_2 = models.ForeignKey(Target, related_name='src_safe') m2m_1 = models.ManyToManyField(Target, related_name='id') m2m_2 = models.ManyToManyField(Target, related_name='src_safe') errors = Model.check() expected = [ Error( "Reverse accessor for 'Model.foreign_1' clashes with field name 'Target.id'.", hint=("Rename field 'Target.id', or add/change a related_name " "argument to the definition for field 'Model.foreign_1'."), obj=Model._meta.get_field('foreign_1'), id='fields.E302', ), Error( "Reverse query name for 'Model.foreign_1' clashes with field name 'Target.id'.", hint=("Rename field 'Target.id', or add/change a related_name " "argument to the definition for field 'Model.foreign_1'."), obj=Model._meta.get_field('foreign_1'), id='fields.E303', ), Error( "Reverse accessor for 'Model.foreign_1' clashes with reverse accessor for 'Model.m2m_1'.", hint=("Add or change a related_name argument to " "the definition for 'Model.foreign_1' or 'Model.m2m_1'."), obj=Model._meta.get_field('foreign_1'), id='fields.E304', ), Error( "Reverse query name for 'Model.foreign_1' clashes with reverse query name for 'Model.m2m_1'.", hint=("Add or change a related_name argument to " "the definition for 'Model.foreign_1' or 'Model.m2m_1'."), obj=Model._meta.get_field('foreign_1'), id='fields.E305', ), Error( "Reverse accessor for 'Model.foreign_2' clashes with reverse accessor for 'Model.m2m_2'.", hint=("Add or change a related_name argument " "to the definition for 'Model.foreign_2' or 'Model.m2m_2'."), obj=Model._meta.get_field('foreign_2'), id='fields.E304', ), Error( "Reverse query name for 'Model.foreign_2' clashes with reverse query name for 'Model.m2m_2'.", hint=("Add or change a related_name argument to " "the definition for 'Model.foreign_2' or 'Model.m2m_2'."), obj=Model._meta.get_field('foreign_2'), id='fields.E305', ), Error( "Reverse accessor for 'Model.m2m_1' clashes with field name 'Target.id'.", hint=("Rename field 'Target.id', or add/change a related_name " "argument to the definition for field 'Model.m2m_1'."), obj=Model._meta.get_field('m2m_1'), id='fields.E302', ), Error( "Reverse query name for 'Model.m2m_1' clashes with field name 'Target.id'.", hint=("Rename field 'Target.id', or add/change a related_name " "argument to the definition for field 'Model.m2m_1'."), obj=Model._meta.get_field('m2m_1'), id='fields.E303', ), Error( "Reverse accessor for 'Model.m2m_1' clashes with reverse accessor for 'Model.foreign_1'.", hint=("Add or change a related_name argument to the definition " "for 'Model.m2m_1' or 'Model.foreign_1'."), obj=Model._meta.get_field('m2m_1'), id='fields.E304', ), Error( "Reverse query name for 'Model.m2m_1' clashes with reverse query name for 'Model.foreign_1'.", hint=("Add or change a related_name argument to " "the definition for 'Model.m2m_1' or 'Model.foreign_1'."), obj=Model._meta.get_field('m2m_1'), id='fields.E305', ), Error( "Reverse accessor for 'Model.m2m_2' clashes with reverse accessor for 'Model.foreign_2'.", hint=("Add or change a related_name argument to the definition " "for 'Model.m2m_2' or 'Model.foreign_2'."), obj=Model._meta.get_field('m2m_2'), id='fields.E304', ), Error( "Reverse query name for 'Model.m2m_2' clashes with reverse query name for 'Model.foreign_2'.", hint=("Add or change a related_name argument to the definition " "for 'Model.m2m_2' or 'Model.foreign_2'."), obj=Model._meta.get_field('m2m_2'), id='fields.E305', ), ] self.assertEqual(errors, expected) class M2mThroughFieldsTests(IsolatedModelsTestCase): def test_m2m_field_argument_validation(self): """ Tests that ManyToManyField accepts the ``through_fields`` kwarg only if an intermediary table is specified. """ class Fan(models.Model): pass self.assertRaisesMessage( ValueError, 'Cannot specify through_fields without a through model', models.ManyToManyField, Fan, through_fields=('f1', 'f2')) def test_invalid_order(self): """ Tests that mixing up the order of link fields to ManyToManyField.through_fields triggers validation errors. """ class Fan(models.Model): pass class Event(models.Model): invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invitee', 'event')) class Invitation(models.Model): event = models.ForeignKey(Event) invitee = models.ForeignKey(Fan) inviter = models.ForeignKey(Fan, related_name='+') field = Event._meta.get_field('invitees') errors = field.check(from_model=Event) expected = [ Error( ("'Invitation.invitee' is not a foreign key to 'Event'."), hint="Did you mean one of the following foreign keys to 'Event': event?", obj=field, id='fields.E339'), Error( ("'Invitation.event' is not a foreign key to 'Fan'."), hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?", obj=field, id='fields.E339'), ] self.assertEqual(expected, errors) def test_invalid_field(self): """ Tests that providing invalid field names to ManyToManyField.through_fields triggers validation errors. """ class Fan(models.Model): pass class Event(models.Model): invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=('invalid_field_1', 'invalid_field_2')) class Invitation(models.Model): event = models.ForeignKey(Event) invitee = models.ForeignKey(Fan) inviter = models.ForeignKey(Fan, related_name='+') field = Event._meta.get_field('invitees') errors = field.check(from_model=Event) expected = [ Error( ("The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_1'."), hint="Did you mean one of the following foreign keys to 'Event': event?", obj=field, id='fields.E338'), Error( ("The intermediary model 'invalid_models_tests.Invitation' has no field 'invalid_field_2'."), hint="Did you mean one of the following foreign keys to 'Fan': invitee, inviter?", obj=field, id='fields.E338'), ] self.assertEqual(expected, errors) def test_explicit_field_names(self): """ Tests that if ``through_fields`` kwarg is given, it must specify both link fields of the intermediary table. """ class Fan(models.Model): pass class Event(models.Model): invitees = models.ManyToManyField(Fan, through='Invitation', through_fields=(None, 'invitee')) class Invitation(models.Model): event = models.ForeignKey(Event) invitee = models.ForeignKey(Fan) inviter = models.ForeignKey(Fan, related_name='+') field = Event._meta.get_field('invitees') errors = field.check(from_model=Event) expected = [ Error( ("Field specifies 'through_fields' but does not provide the names " "of the two link fields that should be used for the relation " "through model 'invalid_models_tests.Invitation'."), hint=("Make sure you specify 'through_fields' as " "through_fields=('field1', 'field2')"), obj=field, id='fields.E337')] self.assertEqual(expected, errors) Django-1.8.7/tests/invalid_models_tests/__init__.py0000664000175000017500000000000012603513307022013 0ustar timtim00000000000000Django-1.8.7/tests/invalid_models_tests/test_custom_fields.py0000664000175000017500000000115512603513307024167 0ustar timtim00000000000000from django.db import models from .base import IsolatedModelsTestCase class CustomFieldTest(IsolatedModelsTestCase): def test_none_column(self): class NoColumnField(models.AutoField): def db_type(self, connection): # None indicates not to create a column in the database. return None class Model(models.Model): field = NoColumnField(primary_key=True, db_column="other_field") other_field = models.IntegerField() field = Model._meta.get_field('field') errors = field.check() self.assertEqual(errors, []) Django-1.8.7/tests/invalid_models_tests/test_models.py0000664000175000017500000005365112625116214022622 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals import unittest from django.conf import settings from django.core.checks import Error from django.db import connections, models from django.test.utils import override_settings from .base import IsolatedModelsTestCase def get_max_column_name_length(): allowed_len = None db_alias = None for db in settings.DATABASES.keys(): connection = connections[db] max_name_length = connection.ops.max_name_length() if max_name_length is None or connection.features.truncates_names: continue else: if allowed_len is None: allowed_len = max_name_length db_alias = db elif max_name_length < allowed_len: allowed_len = max_name_length db_alias = db return (allowed_len, db_alias) class IndexTogetherTests(IsolatedModelsTestCase): def test_non_iterable(self): class Model(models.Model): class Meta: index_together = 42 errors = Model.check() expected = [ Error( "'index_together' must be a list or tuple.", hint=None, obj=Model, id='models.E008', ), ] self.assertEqual(errors, expected) def test_non_list(self): class Model(models.Model): class Meta: index_together = 'not-a-list' errors = Model.check() expected = [ Error( "'index_together' must be a list or tuple.", hint=None, obj=Model, id='models.E008', ), ] self.assertEqual(errors, expected) def test_list_containing_non_iterable(self): class Model(models.Model): class Meta: index_together = [('a', 'b'), 42] errors = Model.check() expected = [ Error( "All 'index_together' elements must be lists or tuples.", hint=None, obj=Model, id='models.E009', ), ] self.assertEqual(errors, expected) def test_pointing_to_missing_field(self): class Model(models.Model): class Meta: index_together = [ ["missing_field"], ] errors = Model.check() expected = [ Error( "'index_together' refers to the non-existent field 'missing_field'.", hint=None, obj=Model, id='models.E012', ), ] self.assertEqual(errors, expected) def test_pointing_to_non_local_field(self): class Foo(models.Model): field1 = models.IntegerField() class Bar(Foo): field2 = models.IntegerField() class Meta: index_together = [ ["field2", "field1"], ] errors = Bar.check() expected = [ Error( "'index_together' refers to field 'field1' which is not " "local to model 'Bar'.", hint=("This issue may be caused by multi-table inheritance."), obj=Bar, id='models.E016', ), ] self.assertEqual(errors, expected) def test_pointing_to_m2m_field(self): class Model(models.Model): m2m = models.ManyToManyField('self') class Meta: index_together = [ ["m2m"], ] errors = Model.check() expected = [ Error( "'index_together' refers to a ManyToManyField 'm2m', but " "ManyToManyFields are not permitted in 'index_together'.", hint=None, obj=Model, id='models.E013', ), ] self.assertEqual(errors, expected) # unique_together tests are very similar to index_together tests. class UniqueTogetherTests(IsolatedModelsTestCase): def test_non_iterable(self): class Model(models.Model): class Meta: unique_together = 42 errors = Model.check() expected = [ Error( "'unique_together' must be a list or tuple.", hint=None, obj=Model, id='models.E010', ), ] self.assertEqual(errors, expected) def test_list_containing_non_iterable(self): class Model(models.Model): one = models.IntegerField() two = models.IntegerField() class Meta: unique_together = [('a', 'b'), 42] errors = Model.check() expected = [ Error( "All 'unique_together' elements must be lists or tuples.", hint=None, obj=Model, id='models.E011', ), ] self.assertEqual(errors, expected) def test_non_list(self): class Model(models.Model): class Meta: unique_together = 'not-a-list' errors = Model.check() expected = [ Error( "'unique_together' must be a list or tuple.", hint=None, obj=Model, id='models.E010', ), ] self.assertEqual(errors, expected) def test_valid_model(self): class Model(models.Model): one = models.IntegerField() two = models.IntegerField() class Meta: # unique_together can be a simple tuple unique_together = ('one', 'two') errors = Model.check() self.assertEqual(errors, []) def test_pointing_to_missing_field(self): class Model(models.Model): class Meta: unique_together = [ ["missing_field"], ] errors = Model.check() expected = [ Error( "'unique_together' refers to the non-existent field 'missing_field'.", hint=None, obj=Model, id='models.E012', ), ] self.assertEqual(errors, expected) def test_pointing_to_m2m(self): class Model(models.Model): m2m = models.ManyToManyField('self') class Meta: unique_together = [ ["m2m"], ] errors = Model.check() expected = [ Error( "'unique_together' refers to a ManyToManyField 'm2m', but " "ManyToManyFields are not permitted in 'unique_together'.", hint=None, obj=Model, id='models.E013', ), ] self.assertEqual(errors, expected) class FieldNamesTests(IsolatedModelsTestCase): def test_ending_with_underscore(self): class Model(models.Model): field_ = models.CharField(max_length=10) m2m_ = models.ManyToManyField('self') errors = Model.check() expected = [ Error( 'Field names must not end with an underscore.', hint=None, obj=Model._meta.get_field('field_'), id='fields.E001', ), Error( 'Field names must not end with an underscore.', hint=None, obj=Model._meta.get_field('m2m_'), id='fields.E001', ), ] self.assertEqual(errors, expected) max_column_name_length, column_limit_db_alias = get_max_column_name_length() @unittest.skipIf(max_column_name_length is None, "The database doesn't have a column name length limit.") def test_M2M_long_column_name(self): """ #13711 -- Model check for long M2M column names when database has column name length limits. """ allowed_len, db_alias = get_max_column_name_length() # A model with very long name which will be used to set relations to. class VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz(models.Model): title = models.CharField(max_length=11) # Main model for which checks will be performed. class ModelWithLongField(models.Model): m2m_field = models.ManyToManyField( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, related_name="rn1" ) m2m_field2 = models.ManyToManyField( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, related_name="rn2", through='m2msimple' ) m2m_field3 = models.ManyToManyField( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, related_name="rn3", through='m2mcomplex' ) fk = models.ForeignKey(VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, related_name="rn4") # Models used for setting `through` in M2M field. class m2msimple(models.Model): id2 = models.ForeignKey(ModelWithLongField) class m2mcomplex(models.Model): id2 = models.ForeignKey(ModelWithLongField) long_field_name = 'a' * (self.max_column_name_length + 1) models.ForeignKey( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz ).contribute_to_class(m2msimple, long_field_name) models.ForeignKey( VeryLongModelNamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz, db_column=long_field_name ).contribute_to_class(m2mcomplex, long_field_name) errors = ModelWithLongField.check() # First error because of M2M field set on the model with long name. m2m_long_name = "verylongmodelnamezzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz_id" if self.max_column_name_length > len(m2m_long_name): # Some databases support names longer than the test name. expected = [] else: expected = [ Error( 'Autogenerated column name too long for M2M field "%s". ' 'Maximum length is "%s" for database "%s".' % (m2m_long_name, self.max_column_name_length, self.column_limit_db_alias), hint=("Use 'through' to create a separate model for " "M2M and then set column_name using 'db_column'."), obj=ModelWithLongField, id='models.E019', ) ] # Second error because the FK specified in the `through` model # `m2msimple` has auto-genererated name longer than allowed. # There will be no check errors in the other M2M because it # specifies db_column for the FK in `through` model even if the actual # name is longer than the limits of the database. expected.append( Error( 'Autogenerated column name too long for M2M field "%s_id". ' 'Maximum length is "%s" for database "%s".' % (long_field_name, self.max_column_name_length, self.column_limit_db_alias), hint=("Use 'through' to create a separate model for " "M2M and then set column_name using 'db_column'."), obj=ModelWithLongField, id='models.E019', ) ) self.assertEqual(errors, expected) @unittest.skipIf(max_column_name_length is None, "The database doesn't have a column name length limit.") def test_local_field_long_column_name(self): """ #13711 -- Model check for long column names when database does not support long names. """ allowed_len, db_alias = get_max_column_name_length() class ModelWithLongField(models.Model): title = models.CharField(max_length=11) long_field_name = 'a' * (self.max_column_name_length + 1) long_field_name2 = 'b' * (self.max_column_name_length + 1) models.CharField(max_length=11).contribute_to_class(ModelWithLongField, long_field_name) models.CharField(max_length=11, db_column='vlmn').contribute_to_class(ModelWithLongField, long_field_name2) errors = ModelWithLongField.check() # Error because of the field with long name added to the model # without specifying db_column expected = [ Error( 'Autogenerated column name too long for field "%s". ' 'Maximum length is "%s" for database "%s".' % (long_field_name, self.max_column_name_length, self.column_limit_db_alias), hint="Set the column name manually using 'db_column'.", obj=ModelWithLongField, id='models.E018', ) ] self.assertEqual(errors, expected) def test_including_separator(self): class Model(models.Model): some__field = models.IntegerField() errors = Model.check() expected = [ Error( 'Field names must not contain "__".', hint=None, obj=Model._meta.get_field('some__field'), id='fields.E002', ) ] self.assertEqual(errors, expected) def test_pk(self): class Model(models.Model): pk = models.IntegerField() errors = Model.check() expected = [ Error( "'pk' is a reserved word that cannot be used as a field name.", hint=None, obj=Model._meta.get_field('pk'), id='fields.E003', ) ] self.assertEqual(errors, expected) class ShadowingFieldsTests(IsolatedModelsTestCase): def test_multiinheritance_clash(self): class Mother(models.Model): clash = models.IntegerField() class Father(models.Model): clash = models.IntegerField() class Child(Mother, Father): # Here we have two clashed: id (automatic field) and clash, because # both parents define these fields. pass errors = Child.check() expected = [ Error( "The field 'id' from parent model " "'invalid_models_tests.mother' clashes with the field 'id' " "from parent model 'invalid_models_tests.father'.", hint=None, obj=Child, id='models.E005', ), Error( "The field 'clash' from parent model " "'invalid_models_tests.mother' clashes with the field 'clash' " "from parent model 'invalid_models_tests.father'.", hint=None, obj=Child, id='models.E005', ) ] self.assertEqual(errors, expected) def test_inheritance_clash(self): class Parent(models.Model): f_id = models.IntegerField() class Target(models.Model): # This field doesn't result in a clash. f_id = models.IntegerField() class Child(Parent): # This field clashes with parent "f_id" field. f = models.ForeignKey(Target) errors = Child.check() expected = [ Error( "The field 'f' clashes with the field 'f_id' " "from model 'invalid_models_tests.parent'.", hint=None, obj=Child._meta.get_field('f'), id='models.E006', ) ] self.assertEqual(errors, expected) def test_multigeneration_inheritance(self): class GrandParent(models.Model): clash = models.IntegerField() class Parent(GrandParent): pass class Child(Parent): pass class GrandChild(Child): clash = models.IntegerField() errors = GrandChild.check() expected = [ Error( "The field 'clash' clashes with the field 'clash' " "from model 'invalid_models_tests.grandparent'.", hint=None, obj=GrandChild._meta.get_field('clash'), id='models.E006', ) ] self.assertEqual(errors, expected) def test_id_clash(self): class Target(models.Model): pass class Model(models.Model): fk = models.ForeignKey(Target) fk_id = models.IntegerField() errors = Model.check() expected = [ Error( "The field 'fk_id' clashes with the field 'fk' from model " "'invalid_models_tests.model'.", hint=None, obj=Model._meta.get_field('fk_id'), id='models.E006', ) ] self.assertEqual(errors, expected) class OtherModelTests(IsolatedModelsTestCase): def test_unique_primary_key(self): invalid_id = models.IntegerField(primary_key=False) class Model(models.Model): id = invalid_id errors = Model.check() expected = [ Error( "'id' can only be used as a field name if the field also sets " "'primary_key=True'.", hint=None, obj=Model, id='models.E004', ), ] self.assertEqual(errors, expected) def test_ordering_non_iterable(self): class Model(models.Model): class Meta: ordering = "missing_field" errors = Model.check() expected = [ Error( "'ordering' must be a tuple or list " "(even if you want to order by only one field).", hint=None, obj=Model, id='models.E014', ), ] self.assertEqual(errors, expected) def test_non_valid(self): class RelationModel(models.Model): pass class Model(models.Model): relation = models.ManyToManyField(RelationModel) class Meta: ordering = ['relation'] errors = Model.check() expected = [ Error( "'ordering' refers to the non-existent field 'relation'.", hint=None, obj=Model, id='models.E015', ), ] self.assertEqual(errors, expected) def test_ordering_pointing_to_missing_field(self): class Model(models.Model): class Meta: ordering = ("missing_field",) errors = Model.check() expected = [ Error( "'ordering' refers to the non-existent field 'missing_field'.", hint=None, obj=Model, id='models.E015', ) ] self.assertEqual(errors, expected) def test_ordering_pointing_to_missing_foreignkey_field(self): # refs #22711 class Model(models.Model): missing_fk_field = models.IntegerField() class Meta: ordering = ("missing_fk_field_id",) errors = Model.check() expected = [ Error( "'ordering' refers to the non-existent field 'missing_fk_field_id'.", hint=None, obj=Model, id='models.E015', ) ] self.assertEqual(errors, expected) def test_ordering_pointing_to_existing_foreignkey_field(self): # refs #22711 class Parent(models.Model): pass class Child(models.Model): parent = models.ForeignKey(Parent) class Meta: ordering = ("parent_id",) self.assertFalse(Child.check()) @override_settings(TEST_SWAPPED_MODEL_BAD_VALUE='not-a-model') def test_swappable_missing_app_name(self): class Model(models.Model): class Meta: swappable = 'TEST_SWAPPED_MODEL_BAD_VALUE' errors = Model.check() expected = [ Error( "'TEST_SWAPPED_MODEL_BAD_VALUE' is not of the form 'app_label.app_name'.", hint=None, obj=None, id='models.E001', ), ] self.assertEqual(errors, expected) @override_settings(TEST_SWAPPED_MODEL_BAD_MODEL='not_an_app.Target') def test_swappable_missing_app(self): class Model(models.Model): class Meta: swappable = 'TEST_SWAPPED_MODEL_BAD_MODEL' errors = Model.check() expected = [ Error( "'TEST_SWAPPED_MODEL_BAD_MODEL' references 'not_an_app.Target', " 'which has not been installed, or is abstract.', hint=None, obj=None, id='models.E002', ), ] self.assertEqual(errors, expected) def test_two_m2m_through_same_relationship(self): class Person(models.Model): pass class Group(models.Model): primary = models.ManyToManyField(Person, through="Membership", related_name="primary") secondary = models.ManyToManyField(Person, through="Membership", related_name="secondary") class Membership(models.Model): person = models.ForeignKey(Person) group = models.ForeignKey(Group) errors = Group.check() expected = [ Error( "The model has two many-to-many relations through " "the intermediate model 'invalid_models_tests.Membership'.", hint=None, obj=Group, id='models.E003', ) ] self.assertEqual(errors, expected) Django-1.8.7/tests/invalid_models_tests/base.py0000664000175000017500000000115612625113144021202 0ustar timtim00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals from django.apps import apps from django.test import TestCase class IsolatedModelsTestCase(TestCase): def setUp(self): # The unmanaged models need to be removed after the test in order to # prevent bad interactions with the flush operation in other tests. self._old_models = apps.app_configs['invalid_models_tests'].models.copy() def tearDown(self): apps.app_configs['invalid_models_tests'].models = self._old_models apps.all_models['invalid_models_tests'] = self._old_models apps.clear_cache() Django-1.8.7/tests/serializers_regress/0000775000175000017500000000000012625116243017571 5ustar timtim00000000000000Django-1.8.7/tests/serializers_regress/tests.py0000664000175000017500000005567312625116214021323 0ustar timtim00000000000000""" A test spanning all the capabilities of all the serializers. This class defines sample data and a dynamically generated test case that is capable of testing the capabilities of the serializers. This includes all valid data values, plus forward, backwards and self references. """ from __future__ import unicode_literals import datetime import decimal import uuid from unittest import skipUnless from django.core import serializers from django.core.serializers import SerializerDoesNotExist from django.core.serializers.base import DeserializationError from django.core.serializers.xml_serializer import DTDForbidden from django.db import connection, models from django.http import HttpResponse from django.test import TestCase, ignore_warnings, skipUnlessDBFeature from django.utils import six from django.utils.deprecation import RemovedInDjango19Warning from django.utils.functional import curry from .models import ( Anchor, AutoNowDateTimeData, BaseModel, BigIntegerData, BinaryData, BooleanData, BooleanPKData, CharData, CharPKData, ComplexModel, DateData, DateTimeData, DecimalData, DecimalPKData, EmailData, EmailPKData, ExplicitInheritBaseModel, FileData, FilePathData, FilePathPKData, FKData, FKDataNaturalKey, FKDataToField, FKDataToO2O, FKSelfData, FKToUUID, FloatData, FloatPKData, GenericData, GenericIPAddressData, GenericIPAddressPKData, InheritAbstractModel, InheritBaseModel, IntegerData, IntegerPKData, Intermediate, IPAddressData, IPAddressPKData, LengthModel, M2MData, M2MIntermediateData, M2MSelfData, ModifyingSaveData, NaturalKeyAnchor, NullBooleanData, O2OData, PositiveIntegerData, PositiveIntegerPKData, PositiveSmallIntegerData, PositiveSmallIntegerPKData, ProxyBaseModel, ProxyProxyBaseModel, SlugData, SlugPKData, SmallData, SmallPKData, Tag, TextData, TimeData, UniqueAnchor, UUIDData, ) try: import yaml except ImportError: yaml = None # A set of functions that can be used to recreate # test data objects of various kinds. # The save method is a raw base model save, to make # sure that the data in the database matches the # exact test case. def data_create(pk, klass, data): instance = klass(id=pk) instance.data = data models.Model.save_base(instance, raw=True) return [instance] def generic_create(pk, klass, data): instance = klass(id=pk) instance.data = data[0] models.Model.save_base(instance, raw=True) for tag in data[1:]: 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'] instance.left_id = data['left'] if 'extra' in data: instance.extra = data['extra'] 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: # 1) we're testing inheritance, not field behavior, so none # of the field values need to be protected. # 2) saving the child class and having the parent created # automatically is easier than manually creating both. models.Model.save(instance) created = [instance] for klass, field in instance._meta.parents.items(): created.append(klass.objects.get(id=pk)) return created # 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: testcase.assertEqual(bytes(data), bytes(instance.data), "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % ( pk, repr(bytes(data)), type(data), repr(bytes(instance.data)), type(instance.data)) ) else: testcase.assertEqual(data, instance.data, "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)" % ( 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) testcase.assertEqual(data['right'], instance.right_id) if 'extra' in data: testcase.assertEqual(data['extra'], instance.extra) 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(): testcase.assertEqual(value, getattr(instance, key)) # Define some data types. Each data type is # actually a pair of functions; one to create # and one to compare objects of that type data_obj = (data_create, data_compare) generic_obj = (generic_create, generic_compare) fk_obj = (fk_create, fk_compare) m2m_obj = (m2m_create, m2m_compare) im2m_obj = (im2m_create, im2m_compare) im_obj = (im_create, im_compare) o2o_obj = (o2o_create, o2o_compare) pk_obj = (pk_create, pk_compare) inherited_obj = (inherited_create, inherited_compare) uuid_obj = uuid.uuid4() test_data = [ # Format: (data type, PK value, Model Class, data) (data_obj, 1, BinaryData, six.memoryview(b"\x05\xFD\x00")), (data_obj, 2, BinaryData, None), (data_obj, 5, BooleanData, True), (data_obj, 6, BooleanData, False), (data_obj, 10, CharData, "Test Char Data"), (data_obj, 11, CharData, ""), (data_obj, 12, CharData, "None"), (data_obj, 13, CharData, "null"), (data_obj, 14, CharData, "NULL"), (data_obj, 15, CharData, None), # (We use something that will fit into a latin1 database encoding here, # because that is still the default used on many system setups.) (data_obj, 16, CharData, '\xa5'), (data_obj, 20, DateData, datetime.date(2006, 6, 16)), (data_obj, 21, DateData, None), (data_obj, 30, DateTimeData, datetime.datetime(2006, 6, 16, 10, 42, 37)), (data_obj, 31, DateTimeData, None), (data_obj, 40, EmailData, "hovercraft@example.com"), (data_obj, 41, EmailData, None), (data_obj, 42, EmailData, ""), (data_obj, 50, FileData, 'file:///foo/bar/whiz.txt'), # (data_obj, 51, FileData, None), (data_obj, 52, FileData, ""), (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"), (data_obj, 61, FilePathData, None), (data_obj, 62, FilePathData, ""), (data_obj, 70, DecimalData, decimal.Decimal('12.345')), (data_obj, 71, DecimalData, decimal.Decimal('-12.345')), (data_obj, 72, DecimalData, decimal.Decimal('0.0')), (data_obj, 73, DecimalData, None), (data_obj, 74, FloatData, 12.345), (data_obj, 75, FloatData, -12.345), (data_obj, 76, FloatData, 0.0), (data_obj, 77, FloatData, None), (data_obj, 80, IntegerData, 123456789), (data_obj, 81, IntegerData, -123456789), (data_obj, 82, IntegerData, 0), (data_obj, 83, IntegerData, None), # (XX, ImageData (data_obj, 90, IPAddressData, "127.0.0.1"), (data_obj, 91, IPAddressData, None), (data_obj, 95, GenericIPAddressData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"), (data_obj, 96, GenericIPAddressData, None), (data_obj, 100, NullBooleanData, True), (data_obj, 101, NullBooleanData, False), (data_obj, 102, NullBooleanData, None), (data_obj, 120, PositiveIntegerData, 123456789), (data_obj, 121, PositiveIntegerData, None), (data_obj, 130, PositiveSmallIntegerData, 12), (data_obj, 131, PositiveSmallIntegerData, None), (data_obj, 140, SlugData, "this-is-a-slug"), (data_obj, 141, SlugData, None), (data_obj, 142, SlugData, ""), (data_obj, 150, SmallData, 12), (data_obj, 151, SmallData, -12), (data_obj, 152, SmallData, 0), (data_obj, 153, SmallData, None), (data_obj, 160, TextData, """This is a long piece of text. It contains line breaks. Several of them. The end."""), (data_obj, 161, TextData, ""), (data_obj, 162, TextData, None), (data_obj, 170, TimeData, datetime.time(10, 42, 37)), (data_obj, 171, TimeData, None), (generic_obj, 200, GenericData, ['Generic Object 1', 'tag1', 'tag2']), (generic_obj, 201, GenericData, ['Generic Object 2', 'tag2', 'tag3']), (data_obj, 300, Anchor, "Anchor 1"), (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 (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 (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, []), (m2m_obj, 442, M2MSelfData, [440, 441]), (m2m_obj, 443, M2MSelfData, [445, 446]), (m2m_obj, 444, M2MSelfData, [440, 441, 445, 446]), (m2m_obj, 445, M2MSelfData, []), (m2m_obj, 446, M2MSelfData, []), (fk_obj, 450, FKDataToField, "UAnchor 1"), (fk_obj, 451, FKDataToField, "UAnchor 2"), (fk_obj, 452, FKDataToField, None), (fk_obj, 460, FKDataToO2O, 300), (im2m_obj, 470, M2MIntermediateData, None), # testing post- and prereferences and extra fields (im_obj, 480, Intermediate, {'right': 300, 'left': 470}), (im_obj, 481, Intermediate, {'right': 300, 'left': 490}), (im_obj, 482, Intermediate, {'right': 500, 'left': 470}), (im_obj, 483, Intermediate, {'right': 500, 'left': 490}), (im_obj, 484, Intermediate, {'right': 300, 'left': 470, 'extra': "extra"}), (im_obj, 485, Intermediate, {'right': 300, 'left': 490, 'extra': "extra"}), (im_obj, 486, Intermediate, {'right': 500, 'left': 470, 'extra': "extra"}), (im_obj, 487, Intermediate, {'right': 500, 'left': 490, 'extra': "extra"}), (im2m_obj, 490, M2MIntermediateData, []), (data_obj, 500, Anchor, "Anchor 3"), (data_obj, 501, Anchor, "Anchor 4"), (data_obj, 502, UniqueAnchor, "UAnchor 2"), (pk_obj, 601, BooleanPKData, True), (pk_obj, 602, BooleanPKData, False), (pk_obj, 610, CharPKData, "Test Char PKData"), # (pk_obj, 620, DatePKData, datetime.date(2006, 6, 16)), # (pk_obj, 630, DateTimePKData, datetime.datetime(2006, 6, 16, 10, 42, 37)), (pk_obj, 640, EmailPKData, "hovercraft@example.com"), # (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'), (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"), (pk_obj, 670, DecimalPKData, decimal.Decimal('12.345')), (pk_obj, 671, DecimalPKData, decimal.Decimal('-12.345')), (pk_obj, 672, DecimalPKData, decimal.Decimal('0.0')), (pk_obj, 673, FloatPKData, 12.345), (pk_obj, 674, FloatPKData, -12.345), (pk_obj, 675, FloatPKData, 0.0), (pk_obj, 680, IntegerPKData, 123456789), (pk_obj, 681, IntegerPKData, -123456789), (pk_obj, 682, IntegerPKData, 0), # (XX, ImagePKData (pk_obj, 690, IPAddressPKData, "127.0.0.1"), (pk_obj, 695, GenericIPAddressPKData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"), # (pk_obj, 700, NullBooleanPKData, True), # (pk_obj, 701, NullBooleanPKData, False), (pk_obj, 720, PositiveIntegerPKData, 123456789), (pk_obj, 730, PositiveSmallIntegerPKData, 12), (pk_obj, 740, SlugPKData, "this-is-a-slug"), (pk_obj, 750, SmallPKData, 12), (pk_obj, 751, SmallPKData, -12), (pk_obj, 752, SmallPKData, 0), # (pk_obj, 760, TextPKData, """This is a long piece of text. # It contains line breaks. # Several of them. # The end."""), # (pk_obj, 770, TimePKData, datetime.time(10, 42, 37)), # (pk_obj, 790, XMLPKData, ""), (pk_obj, 791, UUIDData, uuid_obj), (fk_obj, 792, FKToUUID, uuid_obj), (data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006, 6, 16, 10, 42, 37)), (data_obj, 810, ModifyingSaveData, 42), (inherited_obj, 900, InheritAbstractModel, {'child_data': 37, 'parent_data': 42}), (inherited_obj, 910, ExplicitInheritBaseModel, {'child_data': 37, 'parent_data': 42}), (inherited_obj, 920, InheritBaseModel, {'child_data': 37, 'parent_data': 42}), (data_obj, 1000, BigIntegerData, 9223372036854775807), (data_obj, 1001, BigIntegerData, -9223372036854775808), (data_obj, 1002, BigIntegerData, 0), (data_obj, 1003, BigIntegerData, None), (data_obj, 1004, LengthModel, 0), (data_obj, 1005, LengthModel, 1), ] natural_key_test_data = [ (data_obj, 1100, NaturalKeyAnchor, "Natural Key Anghor"), (fk_obj, 1101, FKDataNaturalKey, 1100), (fk_obj, 1102, FKDataNaturalKey, None), ] # Because Oracle treats the empty string as NULL, Oracle is expected to fail # when field.empty_strings_allowed is True and the value is None; skip these # tests. if connection.features.interprets_empty_strings_as_nulls: test_data = [data for data in test_data if not (data[0] == data_obj and data[2]._meta.get_field('data').empty_strings_allowed and data[3] is None)] # Regression test for #8651 -- a FK to an object with PK of 0 # This won't work on MySQL since it won't let you create an object # with an autoincrement primary key of 0, if connection.features.allows_auto_pk_0: test_data.extend([ (data_obj, 0, Anchor, "Anchor 0"), (fk_obj, 465, FKData, 0), ]) # Dynamically create serializer tests to ensure that all # registered serializers are automatically tested. @skipUnlessDBFeature('can_defer_constraint_checks') class SerializerTests(TestCase): def test_get_unknown_serializer(self): """ #15889: get_serializer('nonsense') raises a SerializerDoesNotExist """ with self.assertRaises(SerializerDoesNotExist): serializers.get_serializer("nonsense") with self.assertRaises(KeyError): serializers.get_serializer("nonsense") # SerializerDoesNotExist is instantiated with the nonexistent format with self.assertRaises(SerializerDoesNotExist) as cm: serializers.get_serializer("nonsense") self.assertEqual(cm.exception.args, ("nonsense",)) def test_unregister_unknown_serializer(self): with self.assertRaises(SerializerDoesNotExist): serializers.unregister_serializer("nonsense") def test_get_unknown_deserializer(self): with self.assertRaises(SerializerDoesNotExist): serializers.get_deserializer("nonsense") def test_json_deserializer_exception(self): with self.assertRaises(DeserializationError): for obj in serializers.deserialize("json", """[{"pk":1}"""): pass @skipUnless(yaml, "PyYAML not installed") def test_yaml_deserializer_exception(self): with self.assertRaises(DeserializationError): for obj in serializers.deserialize("yaml", "{"): pass def test_serialize_proxy_model(self): BaseModel.objects.create(parent_data=1) base_objects = BaseModel.objects.all() proxy_objects = ProxyBaseModel.objects.all() proxy_proxy_objects = ProxyProxyBaseModel.objects.all() base_data = serializers.serialize("json", base_objects) proxy_data = serializers.serialize("json", proxy_objects) proxy_proxy_data = serializers.serialize("json", proxy_proxy_objects) self.assertEqual(base_data, proxy_data.replace('proxy', '')) self.assertEqual(base_data, proxy_proxy_data.replace('proxy', '')) def serializerTest(format, self): # Create all the objects defined in the test data objects = [] instance_count = {} for (func, pk, klass, datum) in test_data: with connection.constraint_checks_disabled(): objects.extend(func[0](pk, klass, datum)) # Get a count of the number of objects created for each class for klass in instance_count: instance_count[klass] = klass.objects.count() # Add the generic tagged objects to the object list objects.extend(Tag.objects.all()) # Serialize the test database serialized_data = serializers.serialize(format, objects, indent=2) for obj in serializers.deserialize(format, serialized_data): obj.save() # Assert that the deserialized data is the same # as the original source for (func, pk, klass, datum) in test_data: func[1](self, pk, klass, datum) # Assert that the number of objects deserialized is the # same as the number that was serialized. for klass, count in instance_count.items(): self.assertEqual(count, klass.objects.count()) @ignore_warnings(category=RemovedInDjango19Warning) # for use_natural_keys def naturalKeySerializerTest(format, self): # Create all the objects defined in the test data objects = [] instance_count = {} for (func, pk, klass, datum) in natural_key_test_data: with connection.constraint_checks_disabled(): objects.extend(func[0](pk, klass, datum)) # Get a count of the number of objects created for each class for klass in instance_count: instance_count[klass] = klass.objects.count() # use_natural_keys is deprecated and to be removed in Django 1.9 # Serialize the test database serialized_data = serializers.serialize(format, objects, indent=2, use_natural_keys=True) for obj in serializers.deserialize(format, serialized_data): obj.save() # Assert that the deserialized data is the same # as the original source for (func, pk, klass, datum) in natural_key_test_data: func[1](self, pk, klass, datum) # Assert that the number of objects deserialized is the # same as the number that was serialized. 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) # Serialize then deserialize the test database serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1', 'field3')) result = next(serializers.deserialize(format, serialized_data)) # Check that the deserialized object contains data in only the serialized fields. self.assertEqual(result.object.field1, 'first') 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) # Serialize the test database to a stream for stream in (six.StringIO(), HttpResponse()): serializers.serialize(format, [obj], indent=2, stream=stream) # Serialize normally for a comparison string_data = serializers.serialize(format, [obj], indent=2) # Check that the two are the same if isinstance(stream, six.StringIO): self.assertEqual(string_data, stream.getvalue()) else: self.assertEqual(string_data, stream.content.decode('utf-8')) def naturalKeyTest(format, self): book1 = {'data': '978-1590597255', 'title': 'The Definitive Guide to ' 'Django: Web Development Done Right'} book2 = {'data': '978-1590599969', 'title': 'Practical Django Projects'} # Create the books. adrian = NaturalKeyAnchor.objects.create(**book1) james = NaturalKeyAnchor.objects.create(**book2) # Serialize the books. string_data = serializers.serialize(format, NaturalKeyAnchor.objects.all(), indent=2, use_natural_foreign_keys=True, use_natural_primary_keys=True) # Delete one book (to prove that the natural key generation will only # restore the primary keys of books found in the database via the # get_natural_key manager method). james.delete() # Deserialize and test. books = list(serializers.deserialize(format, string_data)) self.assertEqual(len(books), 2) self.assertEqual(books[0].object.title, book1['title']) self.assertEqual(books[0].object.pk, adrian.pk) self.assertEqual(books[1].object.title, book2['title']) self.assertEqual(books[1].object.pk, None) for format in [f for f in serializers.get_serializer_formats() if not isinstance(serializers.get_serializer(f), serializers.BadSerializer) and not f == 'geojson']: setattr(SerializerTests, 'test_' + format + '_serializer', curry(serializerTest, format)) setattr(SerializerTests, 'test_' + format + '_natural_key_serializer', curry(naturalKeySerializerTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_fields', curry(fieldsTest, format)) setattr(SerializerTests, 'test_' + format + '_serializer_natural_keys', curry(naturalKeyTest, format)) if format != 'python': setattr(SerializerTests, 'test_' + format + '_serializer_stream', curry(streamTest, format)) class XmlDeserializerSecurityTests(TestCase): def test_no_dtd(self): """ The XML deserializer shouldn't allow a DTD. This is the most straightforward way to prevent all entity definitions and avoid both external entities and entity-expansion attacks. """ xml = '' with self.assertRaises(DTDForbidden): next(serializers.deserialize('xml', xml)) Django-1.8.7/tests/serializers_regress/__init__.py0000664000175000017500000000000012625113144021665 0ustar timtim00000000000000Django-1.8.7/tests/serializers_regress/models.py0000664000175000017500000002063212625116214021427 0ustar timtim00000000000000""" A test spanning all the capabilities of all the serializers. This class sets up a model for each model field type (except for image types, because of the Pillow dependency). """ from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.contrib.contenttypes.models import ContentType from django.db import models # 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): 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() content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey() class Meta: ordering = ["data"] class GenericData(models.Model): data = models.CharField(max_length=30) tags = GenericRelation(Tag) # The following test classes are all for validation # 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""" data = models.CharField(max_length=30) 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() data = models.CharField(max_length=100, unique=True) title = models.CharField(max_length=100, null=True) 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) 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', 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, through='Intermediate') class Intermediate(models.Model): left = models.ForeignKey(M2MIntermediateData) right = models.ForeignKey(Anchor) extra = models.CharField(max_length=30, blank=True, default="doesn't matter") # The following test classes are for validating the # deserialization of objects that use a user-defined # field as the primary key. # Some of these data types have been commented out # 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) # class DatePKData(models.Model): # data = models.DateField(primary_key=True) # 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): data = models.IPAddressField(primary_key=True) class GenericIPAddressPKData(models.Model): data = models.GenericIPAddressField(primary_key=True) # This is just a Boolean field with null=True, and we can't test a PK value of NULL. # class NullBooleanPKData(models.Model): # data = models.NullBooleanField(primary_key=True) class 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) # class TextPKData(models.Model): # data = models.TextField(primary_key=True) # class TimePKData(models.Model): # data = models.TimeField(primary_key=True) class UUIDData(models.Model): data = models.UUIDField(primary_key=True) class FKToUUID(models.Model): data = models.ForeignKey(UUIDData) class ComplexModel(models.Model): field1 = models.CharField(max_length=10) field2 = models.CharField(max_length=10) field3 = models.CharField(max_length=10) # Tests for handling fields with pre_save functions, or # models with save functions that modify data class AutoNowDateTimeData(models.Model): data = models.DateTimeField(null=True, auto_now=True) class ModifyingSaveData(models.Model): data = models.IntegerField(null=True) def save(self, *args, **kwargs): """ A save method that modifies the data in the object. Verifies that a user-defined save() method isn't called when objects are deserialized (#4459). """ self.data = 666 super(ModifyingSaveData, self).save(*args, **kwargs) # 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() def __len__(self): return self.data Django-1.8.7/tests/ordering/0000775000175000017500000000000012625116243015314 5ustar timtim00000000000000Django-1.8.7/tests/ordering/tests.py0000664000175000017500000002207012625116214017027 0ustar timtim00000000000000from __future__ import unicode_literals from datetime import datetime from operator import attrgetter from django.db.models import F from django.test import TestCase from .models import Article, Author class OrderingTests(TestCase): def setUp(self): self.a1 = Article.objects.create( headline="Article 1", pub_date=datetime(2005, 7, 26) ) self.a2 = Article.objects.create( headline="Article 2", pub_date=datetime(2005, 7, 27) ) self.a3 = Article.objects.create( headline="Article 3", pub_date=datetime(2005, 7, 27) ) self.a4 = Article.objects.create( headline="Article 4", pub_date=datetime(2005, 7, 28) ) def test_default_ordering(self): """ By default, Article.objects.all() orders by pub_date descending, then headline ascending. """ self.assertQuerysetEqual( Article.objects.all(), [ "Article 4", "Article 2", "Article 3", "Article 1", ], attrgetter("headline") ) # Getting a single item should work too: self.assertEqual(Article.objects.all()[0], self.a4) def test_default_ordering_override(self): """ Override ordering with order_by, which is in the same format as the ordering attribute in models. """ self.assertQuerysetEqual( Article.objects.order_by("headline"), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.order_by("pub_date", "-headline"), [ "Article 1", "Article 3", "Article 2", "Article 4", ], attrgetter("headline") ) def test_order_by_override(self): """ Only the last order_by has any effect (since they each override any previous ordering). """ self.assertQuerysetEqual( Article.objects.order_by("id"), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.order_by("id").order_by("-headline"), [ "Article 4", "Article 3", "Article 2", "Article 1", ], attrgetter("headline") ) def test_stop_slicing(self): """ Use the 'stop' part of slicing notation to limit the results. """ self.assertQuerysetEqual( Article.objects.order_by("headline")[:2], [ "Article 1", "Article 2", ], attrgetter("headline") ) def test_stop_start_slicing(self): """ Use the 'stop' and 'start' parts of slicing notation to offset the result list. """ self.assertQuerysetEqual( Article.objects.order_by("headline")[1:3], [ "Article 2", "Article 3", ], attrgetter("headline") ) def test_random_ordering(self): """ Use '?' to order randomly. """ self.assertEqual( len(list(Article.objects.order_by("?"))), 4 ) def test_reversed_ordering(self): """ Ordering can be reversed using the reverse() method on a queryset. This allows you to extract things like "the last two items" (reverse and then take the first two). """ self.assertQuerysetEqual( Article.objects.all().reverse()[:2], [ "Article 1", "Article 3", ], attrgetter("headline") ) def test_reverse_ordering_pure(self): qs1 = Article.objects.order_by(F('headline').asc()) qs2 = qs1.reverse() self.assertQuerysetEqual( qs1, [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) self.assertQuerysetEqual( qs2, [ "Article 4", "Article 3", "Article 2", "Article 1", ], attrgetter("headline") ) def test_extra_ordering(self): """ Ordering can be based on fields included from an 'extra' clause """ self.assertQuerysetEqual( Article.objects.extra(select={"foo": "pub_date"}, order_by=["foo", "headline"]), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) def test_extra_ordering_quoting(self): """ If the extra clause uses an SQL keyword for a name, it will be protected by quoting. """ self.assertQuerysetEqual( Article.objects.extra(select={"order": "pub_date"}, order_by=["order", "headline"]), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) def test_extra_ordering_with_table_name(self): self.assertQuerysetEqual( Article.objects.extra(order_by=['ordering_article.headline']), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.extra(order_by=['-ordering_article.headline']), [ "Article 4", "Article 3", "Article 2", "Article 1", ], attrgetter("headline") ) def test_order_by_pk(self): """ Ensure that 'pk' works as an ordering option in Meta. Refs #8291. """ Author.objects.create(pk=1) Author.objects.create(pk=2) Author.objects.create(pk=3) Author.objects.create(pk=4) self.assertQuerysetEqual( Author.objects.all(), [ 4, 3, 2, 1 ], attrgetter("pk") ) def test_order_by_fk_attname(self): """ Ensure that ordering by a foreign key by its attribute name prevents the query from inheriting it's related model ordering option. Refs #19195. """ for i in range(1, 5): author = Author.objects.create(pk=i) article = getattr(self, "a%d" % (5 - i)) article.author = author article.save(update_fields={'author'}) self.assertQuerysetEqual( Article.objects.order_by('author_id'), [ "Article 4", "Article 3", "Article 2", "Article 1", ], attrgetter("headline") ) def test_order_by_f_expression(self): self.assertQuerysetEqual( Article.objects.order_by(F('headline')), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.order_by(F('headline').asc()), [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) self.assertQuerysetEqual( Article.objects.order_by(F('headline').desc()), [ "Article 4", "Article 3", "Article 2", "Article 1", ], attrgetter("headline") ) def test_order_by_f_expression_duplicates(self): """ A column may only be included once (the first occurrence) so we check to ensure there are no duplicates by inspecting the SQL. """ qs = Article.objects.order_by(F('headline').asc(), F('headline').desc()) sql = str(qs.query).upper() fragment = sql[sql.find('ORDER BY'):] self.assertEqual(fragment.count('HEADLINE'), 1) self.assertQuerysetEqual( qs, [ "Article 1", "Article 2", "Article 3", "Article 4", ], attrgetter("headline") ) qs = Article.objects.order_by(F('headline').desc(), F('headline').asc()) sql = str(qs.query).upper() fragment = sql[sql.find('ORDER BY'):] self.assertEqual(fragment.count('HEADLINE'), 1) self.assertQuerysetEqual( qs, [ "Article 4", "Article 3", "Article 2", "Article 1", ], attrgetter("headline") ) Django-1.8.7/tests/ordering/__init__.py0000664000175000017500000000000012603513307017411 0ustar timtim00000000000000Django-1.8.7/tests/ordering/models.py0000664000175000017500000000176112625116214017154 0ustar timtim00000000000000""" Specifying ordering Specify default ordering for a model using the ``ordering`` attribute, which should be a list or tuple of field names. This tells Django how to order ``QuerySet`` results. If a field name in ``ordering`` starts with a hyphen, that field will be ordered in descending order. Otherwise, it'll be ordered in ascending order. The special-case field name ``"?"`` specifies random order. The ordering attribute is not required. If you leave it off, ordering will be undefined -- not random, just undefined. """ from django.db import models from django.utils.encoding import python_2_unicode_compatible class Author(models.Model): class Meta: ordering = ('-pk',) @python_2_unicode_compatible class Article(models.Model): author = models.ForeignKey(Author, null=True) headline = models.CharField(max_length=100) pub_date = models.DateTimeField() class Meta: ordering = ('-pub_date', 'headline') def __str__(self): return self.headline Django-1.8.7/tests/custom_lookups/0000775000175000017500000000000012625116243016571 5ustar timtim00000000000000Django-1.8.7/tests/custom_lookups/tests.py0000664000175000017500000005347312625116213020316 0ustar timtim00000000000000from __future__ import unicode_literals import contextlib import time import unittest from datetime import date, datetime from django.core.exceptions import FieldError from django.db import connection, models from django.test import TestCase, override_settings from django.utils import timezone from .models import Author, MySQLUnixTimestamp @contextlib.contextmanager def register_lookup(field, *lookups): try: for lookup in lookups: field.register_lookup(lookup) yield finally: for lookup in lookups: field._unregister_lookup(lookup) class Div3Lookup(models.Lookup): lookup_name = 'div3' def as_sql(self, compiler, connection): lhs, params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) return '(%s) %%%% 3 = %s' % (lhs, rhs), params def as_oracle(self, compiler, connection): lhs, params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) return 'mod(%s, 3) = %s' % (lhs, rhs), params class Div3Transform(models.Transform): lookup_name = 'div3' def as_sql(self, compiler, connection): lhs, lhs_params = compiler.compile(self.lhs) return '(%s) %%%% 3' % lhs, lhs_params def as_oracle(self, compiler, connection): lhs, lhs_params = compiler.compile(self.lhs) return 'mod(%s, 3)' % lhs, lhs_params class Div3BilateralTransform(Div3Transform): bilateral = True class Mult3BilateralTransform(models.Transform): bilateral = True lookup_name = 'mult3' def as_sql(self, compiler, connection): lhs, lhs_params = compiler.compile(self.lhs) return '3 * (%s)' % lhs, lhs_params class UpperBilateralTransform(models.Transform): bilateral = True lookup_name = 'upper' def as_sql(self, compiler, connection): lhs, lhs_params = compiler.compile(self.lhs) return 'UPPER(%s)' % lhs, lhs_params class YearTransform(models.Transform): lookup_name = 'year' def as_sql(self, compiler, connection): lhs_sql, params = compiler.compile(self.lhs) return connection.ops.date_extract_sql('year', lhs_sql), params @property def output_field(self): return models.IntegerField() @YearTransform.register_lookup class YearExact(models.lookups.Lookup): lookup_name = 'exact' def as_sql(self, compiler, connection): # We will need to skip the extract part, and instead go # directly with the originating field, that is self.lhs.lhs lhs_sql, lhs_params = self.process_lhs(compiler, connection, self.lhs.lhs) rhs_sql, rhs_params = self.process_rhs(compiler, connection) # Note that we must be careful so that we have params in the # same order as we have the parts in the SQL. params = lhs_params + rhs_params + lhs_params + rhs_params # We use PostgreSQL specific SQL here. Note that we must do the # conversions in SQL instead of in Python to support F() references. return ("%(lhs)s >= (%(rhs)s || '-01-01')::date " "AND %(lhs)s <= (%(rhs)s || '-12-31')::date" % {'lhs': lhs_sql, 'rhs': rhs_sql}, params) @YearTransform.register_lookup class YearLte(models.lookups.LessThanOrEqual): """ The purpose of this lookup is to efficiently compare the year of the field. """ def as_sql(self, compiler, connection): # Skip the YearTransform above us (no possibility for efficient # lookup otherwise). real_lhs = self.lhs.lhs lhs_sql, params = self.process_lhs(compiler, connection, real_lhs) rhs_sql, rhs_params = self.process_rhs(compiler, connection) params.extend(rhs_params) # Build SQL where the integer year is concatenated with last month # and day, then convert that to date. (We try to have SQL like: # WHERE somecol <= '2013-12-31') # but also make it work if the rhs_sql is field reference. return "%s <= (%s || '-12-31')::date" % (lhs_sql, rhs_sql), params class SQLFunc(models.Lookup): def __init__(self, name, *args, **kwargs): super(SQLFunc, self).__init__(*args, **kwargs) self.name = name def as_sql(self, compiler, connection): return '%s()', [self.name] @property def output_field(self): return CustomField() class SQLFuncFactory(object): def __init__(self, name): self.name = name def __call__(self, *args, **kwargs): return SQLFunc(self.name, *args, **kwargs) class CustomField(models.TextField): def get_lookup(self, lookup_name): if lookup_name.startswith('lookupfunc_'): key, name = lookup_name.split('_', 1) return SQLFuncFactory(name) return super(CustomField, self).get_lookup(lookup_name) def get_transform(self, lookup_name): if lookup_name.startswith('transformfunc_'): key, name = lookup_name.split('_', 1) return SQLFuncFactory(name) return super(CustomField, self).get_transform(lookup_name) class CustomModel(models.Model): field = CustomField() # We will register this class temporarily in the test method. class InMonth(models.lookups.Lookup): """ InMonth matches if the column's month is the same as value's month. """ lookup_name = 'inmonth' def as_sql(self, compiler, connection): lhs, lhs_params = self.process_lhs(compiler, connection) rhs, rhs_params = self.process_rhs(compiler, connection) # We need to be careful so that we get the params in right # places. params = lhs_params + rhs_params + lhs_params + rhs_params return ("%s >= date_trunc('month', %s) and " "%s < date_trunc('month', %s) + interval '1 months'" % (lhs, rhs, lhs, rhs), params) class DateTimeTransform(models.Transform): lookup_name = 'as_datetime' @property def output_field(self): return models.DateTimeField() def as_sql(self, compiler, connection): lhs, params = compiler.compile(self.lhs) return 'from_unixtime({})'.format(lhs), params class LookupTests(TestCase): def test_basic_lookup(self): a1 = Author.objects.create(name='a1', age=1) a2 = Author.objects.create(name='a2', age=2) a3 = Author.objects.create(name='a3', age=3) a4 = Author.objects.create(name='a4', age=4) with register_lookup(models.IntegerField, Div3Lookup): self.assertQuerysetEqual( Author.objects.filter(age__div3=0), [a3], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(age__div3=1).order_by('age'), [a1, a4], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(age__div3=2), [a2], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(age__div3=3), [], lambda x: x ) @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific SQL used") def test_birthdate_month(self): a1 = Author.objects.create(name='a1', birthdate=date(1981, 2, 16)) a2 = Author.objects.create(name='a2', birthdate=date(2012, 2, 29)) a3 = Author.objects.create(name='a3', birthdate=date(2012, 1, 31)) a4 = Author.objects.create(name='a4', birthdate=date(2012, 3, 1)) with register_lookup(models.DateField, InMonth): self.assertQuerysetEqual( Author.objects.filter(birthdate__inmonth=date(2012, 1, 15)), [a3], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(birthdate__inmonth=date(2012, 2, 1)), [a2], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(birthdate__inmonth=date(1981, 2, 28)), [a1], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(birthdate__inmonth=date(2012, 3, 12)), [a4], lambda x: x ) self.assertQuerysetEqual( Author.objects.filter(birthdate__inmonth=date(2012, 4, 1)), [], lambda x: x ) def test_div3_extract(self): with register_lookup(models.IntegerField, Div3Transform): a1 = Author.objects.create(name='a1', age=1) a2 = Author.objects.create(name='a2', age=2) a3 = Author.objects.create(name='a3', age=3) a4 = Author.objects.create(name='a4', age=4) baseqs = Author.objects.order_by('name') self.assertQuerysetEqual( baseqs.filter(age__div3=2), [a2], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__lte=3), [a1, a2, a3, a4], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__in=[0, 2]), [a2, a3], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__in=[2, 4]), [a2], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__gte=3), [], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__range=(1, 2)), [a1, a2, a4], lambda x: x) class BilateralTransformTests(TestCase): def test_bilateral_upper(self): with register_lookup(models.CharField, UpperBilateralTransform): Author.objects.bulk_create([ Author(name='Doe'), Author(name='doe'), Author(name='Foo'), ]) self.assertQuerysetEqual( Author.objects.filter(name__upper='doe'), ["", ""], ordered=False) self.assertQuerysetEqual( Author.objects.filter(name__upper__contains='f'), [""], ordered=False) def test_bilateral_inner_qs(self): with register_lookup(models.CharField, UpperBilateralTransform): with self.assertRaises(NotImplementedError): Author.objects.filter(name__upper__in=Author.objects.values_list('name')) def test_div3_bilateral_extract(self): with register_lookup(models.IntegerField, Div3BilateralTransform): a1 = Author.objects.create(name='a1', age=1) a2 = Author.objects.create(name='a2', age=2) a3 = Author.objects.create(name='a3', age=3) a4 = Author.objects.create(name='a4', age=4) baseqs = Author.objects.order_by('name') self.assertQuerysetEqual( baseqs.filter(age__div3=2), [a2], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__lte=3), [a3], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__in=[0, 2]), [a2, a3], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__in=[2, 4]), [a1, a2, a4], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__gte=3), [a1, a2, a3, a4], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__range=(1, 2)), [a1, a2, a4], lambda x: x) def test_bilateral_order(self): with register_lookup(models.IntegerField, Mult3BilateralTransform, Div3BilateralTransform): a1 = Author.objects.create(name='a1', age=1) a2 = Author.objects.create(name='a2', age=2) a3 = Author.objects.create(name='a3', age=3) a4 = Author.objects.create(name='a4', age=4) baseqs = Author.objects.order_by('name') self.assertQuerysetEqual( baseqs.filter(age__mult3__div3=42), # mult3__div3 always leads to 0 [a1, a2, a3, a4], lambda x: x) self.assertQuerysetEqual( baseqs.filter(age__div3__mult3=42), [a3], lambda x: x) def test_bilateral_fexpr(self): with register_lookup(models.IntegerField, Mult3BilateralTransform): a1 = Author.objects.create(name='a1', age=1, average_rating=3.2) a2 = Author.objects.create(name='a2', age=2, average_rating=0.5) a3 = Author.objects.create(name='a3', age=3, average_rating=1.5) a4 = Author.objects.create(name='a4', age=4) baseqs = Author.objects.order_by('name') self.assertQuerysetEqual( baseqs.filter(age__mult3=models.F('age')), [a1, a2, a3, a4], lambda x: x) self.assertQuerysetEqual( # Same as age >= average_rating baseqs.filter(age__mult3__gte=models.F('average_rating')), [a2, a3], lambda x: x) @override_settings(USE_TZ=True) class DateTimeLookupTests(TestCase): @unittest.skipUnless(connection.vendor == 'mysql', "MySQL specific SQL used") def test_datetime_output_field(self): with register_lookup(models.PositiveIntegerField, DateTimeTransform): ut = MySQLUnixTimestamp.objects.create(timestamp=time.time()) y2k = timezone.make_aware(datetime(2000, 1, 1)) self.assertQuerysetEqual( MySQLUnixTimestamp.objects.filter(timestamp__as_datetime__gt=y2k), [ut], lambda x: x) class YearLteTests(TestCase): def setUp(self): models.DateField.register_lookup(YearTransform) self.a1 = Author.objects.create(name='a1', birthdate=date(1981, 2, 16)) self.a2 = Author.objects.create(name='a2', birthdate=date(2012, 2, 29)) self.a3 = Author.objects.create(name='a3', birthdate=date(2012, 1, 31)) self.a4 = Author.objects.create(name='a4', birthdate=date(2012, 3, 1)) def tearDown(self): models.DateField._unregister_lookup(YearTransform) @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific SQL used") def test_year_lte(self): baseqs = Author.objects.order_by('name') self.assertQuerysetEqual( baseqs.filter(birthdate__year__lte=2012), [self.a1, self.a2, self.a3, self.a4], lambda x: x) self.assertQuerysetEqual( baseqs.filter(birthdate__year=2012), [self.a2, self.a3, self.a4], lambda x: x) self.assertNotIn('BETWEEN', str(baseqs.filter(birthdate__year=2012).query)) self.assertQuerysetEqual( baseqs.filter(birthdate__year__lte=2011), [self.a1], lambda x: x) # The non-optimized version works, too. self.assertQuerysetEqual( baseqs.filter(birthdate__year__lt=2012), [self.a1], lambda x: x) @unittest.skipUnless(connection.vendor == 'postgresql', "PostgreSQL specific SQL used") def test_year_lte_fexpr(self): self.a2.age = 2011 self.a2.save() self.a3.age = 2012 self.a3.save() self.a4.age = 2013 self.a4.save() baseqs = Author.objects.order_by('name') self.assertQuerysetEqual( baseqs.filter(birthdate__year__lte=models.F('age')), [self.a3, self.a4], lambda x: x) self.assertQuerysetEqual( baseqs.filter(birthdate__year__lt=models.F('age')), [self.a4], lambda x: x) def test_year_lte_sql(self): # This test will just check the generated SQL for __lte. This # doesn't require running on PostgreSQL and spots the most likely # error - not running YearLte SQL at all. baseqs = Author.objects.order_by('name') self.assertIn( '<= (2011 || ', str(baseqs.filter(birthdate__year__lte=2011).query)) self.assertIn( '-12-31', str(baseqs.filter(birthdate__year__lte=2011).query)) def test_postgres_year_exact(self): baseqs = Author.objects.order_by('name') self.assertIn( '= (2011 || ', str(baseqs.filter(birthdate__year=2011).query)) self.assertIn( '-12-31', str(baseqs.filter(birthdate__year=2011).query)) def test_custom_implementation_year_exact(self): try: # Two ways to add a customized implementation for different backends: # First is MonkeyPatch of the class. def as_custom_sql(self, compiler, connection): lhs_sql, lhs_params = self.process_lhs(compiler, connection, self.lhs.lhs) rhs_sql, rhs_params = self.process_rhs(compiler, connection) params = lhs_params + rhs_params + lhs_params + rhs_params return ("%(lhs)s >= str_to_date(concat(%(rhs)s, '-01-01'), '%%%%Y-%%%%m-%%%%d') " "AND %(lhs)s <= str_to_date(concat(%(rhs)s, '-12-31'), '%%%%Y-%%%%m-%%%%d')" % {'lhs': lhs_sql, 'rhs': rhs_sql}, params) setattr(YearExact, 'as_' + connection.vendor, as_custom_sql) self.assertIn( 'concat(', str(Author.objects.filter(birthdate__year=2012).query)) finally: delattr(YearExact, 'as_' + connection.vendor) try: # The other way is to subclass the original lookup and register the subclassed # lookup instead of the original. class CustomYearExact(YearExact): # This method should be named "as_mysql" for MySQL, "as_postgresql" for postgres # and so on, but as we don't know which DB we are running on, we need to use # setattr. def as_custom_sql(self, compiler, connection): lhs_sql, lhs_params = self.process_lhs(compiler, connection, self.lhs.lhs) rhs_sql, rhs_params = self.process_rhs(compiler, connection) params = lhs_params + rhs_params + lhs_params + rhs_params return ("%(lhs)s >= str_to_date(CONCAT(%(rhs)s, '-01-01'), '%%%%Y-%%%%m-%%%%d') " "AND %(lhs)s <= str_to_date(CONCAT(%(rhs)s, '-12-31'), '%%%%Y-%%%%m-%%%%d')" % {'lhs': lhs_sql, 'rhs': rhs_sql}, params) setattr(CustomYearExact, 'as_' + connection.vendor, CustomYearExact.as_custom_sql) YearTransform.register_lookup(CustomYearExact) self.assertIn( 'CONCAT(', str(Author.objects.filter(birthdate__year=2012).query)) finally: YearTransform._unregister_lookup(CustomYearExact) YearTransform.register_lookup(YearExact) class TrackCallsYearTransform(YearTransform): lookup_name = 'year' call_order = [] def as_sql(self, compiler, connection): lhs_sql, params = compiler.compile(self.lhs) return connection.ops.date_extract_sql('year', lhs_sql), params @property def output_field(self): return models.IntegerField() def get_lookup(self, lookup_name): self.call_order.append('lookup') return super(TrackCallsYearTransform, self).get_lookup(lookup_name) def get_transform(self, lookup_name): self.call_order.append('transform') return super(TrackCallsYearTransform, self).get_transform(lookup_name) class LookupTransformCallOrderTests(TestCase): def test_call_order(self): with register_lookup(models.DateField, TrackCallsYearTransform): # junk lookup - tries lookup, then transform, then fails with self.assertRaises(FieldError): Author.objects.filter(birthdate__year__junk=2012) self.assertEqual(TrackCallsYearTransform.call_order, ['lookup', 'transform']) TrackCallsYearTransform.call_order = [] # junk transform - tries transform only, then fails with self.assertRaises(FieldError): Author.objects.filter(birthdate__year__junk__more_junk=2012) self.assertEqual(TrackCallsYearTransform.call_order, ['transform']) TrackCallsYearTransform.call_order = [] # Just getting the year (implied __exact) - lookup only Author.objects.filter(birthdate__year=2012) self.assertEqual(TrackCallsYearTransform.call_order, ['lookup']) TrackCallsYearTransform.call_order = [] # Just getting the year (explicit __exact) - lookup only Author.objects.filter(birthdate__year__exact=2012) self.assertEqual(TrackCallsYearTransform.call_order, ['lookup']) class CustomisedMethodsTests(TestCase): def test_overridden_get_lookup(self): q = CustomModel.objects.filter(field__lookupfunc_monkeys=3) self.assertIn('monkeys()', str(q.query)) def test_overridden_get_transform(self): q = CustomModel.objects.filter(field__transformfunc_banana=3) self.assertIn('banana()', str(q.query)) def test_overridden_get_lookup_chain(self): q = CustomModel.objects.filter(field__transformfunc_banana__lookupfunc_elephants=3) self.assertIn('elephants()', str(q.query)) def test_overridden_get_transform_chain(self): q = CustomModel.objects.filter(field__transformfunc_banana__transformfunc_pear=3) self.assertIn('pear()', str(q.query)) class SubqueryTransformTests(TestCase): def test_subquery_usage(self): with register_lookup(models.IntegerField, Div3Transform): Author.objects.create(name='a1', age=1) a2 = Author.objects.create(name='a2', age=2) Author.objects.create(name='a3', age=3) Author.objects.create(name='a4', age=4) self.assertQuerysetEqual( Author.objects.order_by('name').filter(id__in=Author.objects.filter(age__div3=2)), [a2], lambda x: x) Django-1.8.7/tests/custom_lookups/__init__.py0000664000175000017500000000000012603513307020666 0ustar timtim00000000000000Django-1.8.7/tests/custom_lookups/models.py0000664000175000017500000000103712603513307020425 0ustar timtim00000000000000from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=20) age = models.IntegerField(null=True) birthdate = models.DateField(null=True) average_rating = models.FloatField(null=True) def __str__(self): return self.name @python_2_unicode_compatible class MySQLUnixTimestamp(models.Model): timestamp = models.PositiveIntegerField() def __str__(self): return self.name Django-1.8.7/tests/from_db_value/0000775000175000017500000000000012625116243016307 5ustar timtim00000000000000Django-1.8.7/tests/from_db_value/tests.py0000664000175000017500000000165112625116213020023 0ustar timtim00000000000000from django.db import connection from django.db.models import Max from django.test import TestCase from .models import Cash, CashModel class FromDBValueTest(TestCase): def setUp(self): CashModel.objects.create(cash='12.50') def test_simple_load(self): instance = CashModel.objects.get() self.assertIsInstance(instance.cash, Cash) def test_values(self): values_list = CashModel.objects.values_list('cash', flat=True) self.assertIsInstance(values_list[0], Cash) def test_aggregation(self): maximum = CashModel.objects.aggregate(m=Max('cash'))['m'] self.assertIsInstance(maximum, Cash) def test_defer(self): instance = CashModel.objects.defer('cash').get() self.assertIsInstance(instance.cash, Cash) def test_connection(self): instance = CashModel.objects.get() self.assertEqual(instance.cash.vendor, connection.vendor) Django-1.8.7/tests/from_db_value/__init__.py0000664000175000017500000000000012625116213020403 0ustar timtim00000000000000Django-1.8.7/tests/from_db_value/models.py0000664000175000017500000000137212625116213020144 0ustar timtim00000000000000import decimal from django.db import models from django.utils.encoding import python_2_unicode_compatible class Cash(decimal.Decimal): currency = 'USD' def __str__(self): s = super(Cash, self).__str__(self) return '%s %s' % (s, self.currency) class CashField(models.DecimalField): def __init__(self, **kwargs): kwargs['max_digits'] = 20 kwargs['decimal_places'] = 2 super(CashField, self).__init__(**kwargs) def from_db_value(self, value, expression, connection, context): cash = Cash(value) cash.vendor = connection.vendor return cash @python_2_unicode_compatible class CashModel(models.Model): cash = CashField() def __str__(self): return str(self.cash) Django-1.8.7/tests/admin_utils/0000775000175000017500000000000012625116243016013 5ustar timtim00000000000000Django-1.8.7/tests/admin_utils/tests.py0000664000175000017500000002750612625116213017536 0ustar timtim00000000000000from __future__ import unicode_literals from datetime import datetime from django import forms from django.conf import settings from django.contrib import admin from django.contrib.admin import helpers from django.contrib.admin.utils import ( NestedObjects, display_for_field, flatten, flatten_fieldsets, label_for_field, lookup_field, ) from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE from django.db import DEFAULT_DB_ALIAS, models from django.test import SimpleTestCase, TestCase from django.utils import six from django.utils.formats import localize from django.utils.safestring import mark_safe from .models import ( Article, Car, Count, Event, EventGuide, Location, Site, Vehicle, ) class NestedObjectsTests(TestCase): """ Tests for ``NestedObject`` utility collection. """ def setUp(self): self.n = NestedObjects(using=DEFAULT_DB_ALIAS) self.objs = [Count.objects.create(num=i) for i in range(5)] def _check(self, target): self.assertEqual(self.n.nested(lambda obj: obj.num), target) def _connect(self, i, j): self.objs[i].parent = self.objs[j] self.objs[i].save() def _collect(self, *indices): self.n.collect([self.objs[i] for i in indices]) def test_unrelated_roots(self): self._connect(2, 1) self._collect(0) self._collect(1) self._check([0, 1, [2]]) def test_siblings(self): self._connect(1, 0) self._connect(2, 0) self._collect(0) self._check([0, [1, 2]]) def test_non_added_parent(self): self._connect(0, 1) self._collect(0) self._check([0]) def test_cyclic(self): self._connect(0, 2) self._connect(1, 0) self._connect(2, 1) self._collect(0) self._check([0, [1, [2]]]) def test_queries(self): self._connect(1, 0) self._connect(2, 0) # 1 query to fetch all children of 0 (1 and 2) # 1 query to fetch all children of 1 and 2 (none) # Should not require additional queries to populate the nested graph. self.assertNumQueries(2, self._collect, 0) def test_on_delete_do_nothing(self): """ Check that the nested collector doesn't query for DO_NOTHING objects. """ n = NestedObjects(using=DEFAULT_DB_ALIAS) objs = [Event.objects.create()] EventGuide.objects.create(event=objs[0]) with self.assertNumQueries(2): # One for Location, one for Guest, and no query for EventGuide n.collect(objs) def test_relation_on_abstract(self): """ #21846 -- Check that `NestedObjects.collect()` doesn't trip (AttributeError) on the special notation for relations on abstract models (related_name that contains %(app_label)s and/or %(class)s). """ n = NestedObjects(using=DEFAULT_DB_ALIAS) Car.objects.create() n.collect([Vehicle.objects.first()]) class UtilsTests(SimpleTestCase): def test_values_from_lookup_field(self): """ Regression test for #12654: lookup_field """ SITE_NAME = 'example.com' TITLE_TEXT = 'Some title' CREATED_DATE = datetime.min ADMIN_METHOD = 'admin method' SIMPLE_FUNCTION = 'function' INSTANCE_ATTRIBUTE = 'attr' class MockModelAdmin(object): def get_admin_value(self, obj): return ADMIN_METHOD simple_function = lambda obj: SIMPLE_FUNCTION site_obj = Site(domain=SITE_NAME) article = Article( site=site_obj, title=TITLE_TEXT, created=CREATED_DATE, ) article.non_field = INSTANCE_ATTRIBUTE verifications = ( ('site', SITE_NAME), ('created', localize(CREATED_DATE)), ('title', TITLE_TEXT), ('get_admin_value', ADMIN_METHOD), (simple_function, SIMPLE_FUNCTION), ('test_from_model', article.test_from_model()), ('non_field', INSTANCE_ATTRIBUTE) ) mock_admin = MockModelAdmin() for name, value in verifications: field, attr, resolved_value = lookup_field(name, article, mock_admin) if field is not None: resolved_value = display_for_field(resolved_value, field) self.assertEqual(value, resolved_value) def test_null_display_for_field(self): """ Regression test for #12550: display_for_field should handle None value. """ display_value = display_for_field(None, models.CharField()) self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE) display_value = display_for_field(None, models.CharField( choices=( (None, "test_none"), ) )) self.assertEqual(display_value, "test_none") display_value = display_for_field(None, models.DateField()) self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE) display_value = display_for_field(None, models.TimeField()) self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE) # Regression test for #13071: NullBooleanField has special # handling. display_value = display_for_field(None, models.NullBooleanField()) expected = 'None' % settings.STATIC_URL self.assertHTMLEqual(display_value, expected) display_value = display_for_field(None, models.DecimalField()) self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE) display_value = display_for_field(None, models.FloatField()) self.assertEqual(display_value, EMPTY_CHANGELIST_VALUE) def test_label_for_field(self): """ Tests for label_for_field """ self.assertEqual( label_for_field("title", Article), "title" ) self.assertEqual( label_for_field("title2", Article), "another name" ) self.assertEqual( label_for_field("title2", Article, return_attr=True), ("another name", None) ) self.assertEqual( label_for_field("__unicode__", Article), "article" ) self.assertEqual( label_for_field("__str__", Article), str("article") ) self.assertRaises( AttributeError, lambda: label_for_field("unknown", Article) ) def test_callable(obj): return "nothing" self.assertEqual( label_for_field(test_callable, Article), "Test callable" ) self.assertEqual( label_for_field(test_callable, Article, return_attr=True), ("Test callable", test_callable) ) self.assertEqual( label_for_field("test_from_model", Article), "Test from model" ) self.assertEqual( label_for_field("test_from_model", Article, return_attr=True), ("Test from model", Article.test_from_model) ) self.assertEqual( label_for_field("test_from_model_with_override", Article), "not What you Expect" ) self.assertEqual( label_for_field(lambda x: "nothing", Article), "--" ) class MockModelAdmin(object): def test_from_model(self, obj): return "nothing" test_from_model.short_description = "not Really the Model" self.assertEqual( label_for_field("test_from_model", Article, model_admin=MockModelAdmin), "not Really the Model" ) self.assertEqual( label_for_field("test_from_model", Article, model_admin=MockModelAdmin, return_attr=True), ("not Really the Model", MockModelAdmin.test_from_model) ) def test_label_for_property(self): # NOTE: cannot use @property decorator, because of # AttributeError: 'property' object has no attribute 'short_description' class MockModelAdmin(object): def my_property(self): return "this if from property" my_property.short_description = 'property short description' test_from_property = property(my_property) self.assertEqual( label_for_field("test_from_property", Article, model_admin=MockModelAdmin), 'property short description' ) def test_related_name(self): """ Regression test for #13963 """ self.assertEqual( label_for_field('location', Event, return_attr=True), ('location', None), ) self.assertEqual( label_for_field('event', Location, return_attr=True), ('awesome event', None), ) self.assertEqual( label_for_field('guest', Event, return_attr=True), ('awesome guest', None), ) def test_logentry_unicode(self): """ Regression test for #15661 """ log_entry = admin.models.LogEntry() log_entry.action_flag = admin.models.ADDITION self.assertTrue( six.text_type(log_entry).startswith('Added ') ) log_entry.action_flag = admin.models.CHANGE self.assertTrue( six.text_type(log_entry).startswith('Changed ') ) log_entry.action_flag = admin.models.DELETION self.assertTrue( six.text_type(log_entry).startswith('Deleted ') ) # Make sure custom action_flags works log_entry.action_flag = 4 self.assertEqual(six.text_type(log_entry), 'LogEntry Object') def test_safestring_in_field_label(self): # safestring should not be escaped class MyForm(forms.Form): text = forms.CharField(label=mark_safe('text')) cb = forms.BooleanField(label=mark_safe('cb')) form = MyForm() self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(), '') self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(), '') # normal strings needs to be escaped class MyForm(forms.Form): text = forms.CharField(label='&text') cb = forms.BooleanField(label='&cb') form = MyForm() self.assertHTMLEqual(helpers.AdminField(form, 'text', is_first=False).label_tag(), '') self.assertHTMLEqual(helpers.AdminField(form, 'cb', is_first=False).label_tag(), '') def test_flatten(self): flat_all = ['url', 'title', 'content', 'sites'] inputs = ( ((), []), (('url', 'title', ('content', 'sites')), flat_all), (('url', 'title', 'content', 'sites'), flat_all), ((('url', 'title'), ('content', 'sites')), flat_all) ) for orig, expected in inputs: self.assertEqual(flatten(orig), expected) def test_flatten_fieldsets(self): """ Regression test for #18051 """ fieldsets = ( (None, { 'fields': ('url', 'title', ('content', 'sites')) }), ) self.assertEqual(flatten_fieldsets(fieldsets), ['url', 'title', 'content', 'sites']) fieldsets = ( (None, { 'fields': ('url', 'title', ['content', 'sites']) }), ) self.assertEqual(flatten_fieldsets(fieldsets), ['url', 'title', 'content', 'sites']) Django-1.8.7/tests/admin_utils/__init__.py0000664000175000017500000000000012603513307020110 0ustar timtim00000000000000Django-1.8.7/tests/admin_utils/models.py0000664000175000017500000000326012625116213017646 0ustar timtim00000000000000from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Site(models.Model): domain = models.CharField(max_length=100) def __str__(self): return self.domain class Article(models.Model): """ A simple Article model for testing """ site = models.ForeignKey(Site, related_name="admin_articles") title = models.CharField(max_length=100) title2 = models.CharField(max_length=100, verbose_name="another name") created = models.DateTimeField() def test_from_model(self): return "nothing" def test_from_model_with_override(self): return "nothing" test_from_model_with_override.short_description = "not What you Expect" @python_2_unicode_compatible class Count(models.Model): num = models.PositiveSmallIntegerField() parent = models.ForeignKey('self', null=True) def __str__(self): return six.text_type(self.num) class Event(models.Model): date = models.DateTimeField(auto_now_add=True) class Location(models.Model): event = models.OneToOneField(Event, verbose_name='awesome event') class Guest(models.Model): event = models.OneToOneField(Event) name = models.CharField(max_length=255) class Meta: verbose_name = "awesome guest" class EventGuide(models.Model): event = models.ForeignKey(Event, on_delete=models.DO_NOTHING) class Vehicle(models.Model): pass class VehicleMixin(Vehicle): vehicle = models.OneToOneField(Vehicle, parent_link=True, related_name='vehicle_%(app_label)s_%(class)s') class Meta: abstract = True class Car(VehicleMixin): pass Django-1.8.7/tests/.coveragerc0000664000175000017500000000037012625116213015621 0ustar timtim00000000000000[run] omit = */django/contrib/*/tests*,*/django/utils/unittest*,*/django/utils/importlib.py,*/django/core/servers/fastcgi.py,*/django/utils/autoreload.py,*/django/utils/dictconfig.py [report] ignore_errors = True [html] directory = coverage_html Django-1.8.7/tests/humanize_tests/0000775000175000017500000000000012625116243016545 5ustar timtim00000000000000Django-1.8.7/tests/humanize_tests/tests.py0000664000175000017500000003155312625116214020266 0ustar timtim00000000000000from __future__ import unicode_literals import datetime from decimal import Decimal from unittest import skipIf from django.contrib.humanize.templatetags import humanize from django.template import Context, Template, defaultfilters from django.test import TestCase, modify_settings, override_settings from django.utils import translation from django.utils.html import escape from django.utils.timezone import get_fixed_timezone, utc from django.utils.translation import ugettext as _ try: import pytz except ImportError: pytz = None # Mock out datetime in some tests so they don't fail occasionally when they # run too slow. Use a fixed datetime for datetime.now(). DST change in # America/Chicago (the default time zone) happened on March 11th in 2012. now = datetime.datetime(2012, 3, 9, 22, 30) class MockDateTime(datetime.datetime): @classmethod def now(cls, tz=None): if tz is None or tz.utcoffset(now) is None: return now else: # equals now.replace(tzinfo=utc) return now.replace(tzinfo=tz) + tz.utcoffset(now) @modify_settings(INSTALLED_APPS={'append': 'django.contrib.humanize'}) class HumanizeTests(TestCase): def humanize_tester(self, test_list, result_list, method, normalize_result_func=escape): for test_content, result in zip(test_list, result_list): t = Template('{%% load humanize %%}{{ test_content|%s }}' % method) rendered = t.render(Context(locals())).strip() self.assertEqual(rendered, normalize_result_func(result), msg="%s test failed, produced '%s', should've produced '%s'" % (method, rendered, result)) def test_ordinal(self): test_list = ('1', '2', '3', '4', '11', '12', '13', '101', '102', '103', '111', 'something else', None) result_list = ('1st', '2nd', '3rd', '4th', '11th', '12th', '13th', '101st', '102nd', '103rd', '111th', 'something else', None) with translation.override('en'): self.humanize_tester(test_list, result_list, 'ordinal') def test_i18n_html_ordinal(self): """Allow html in output on i18n strings""" test_list = ('1', '2', '3', '4', '11', '12', '13', '101', '102', '103', '111', 'something else', None) result_list = ('1er', '2e', '3e', '4e', '11e', '12e', '13e', '101er', '102e', '103e', '111e', 'something else', 'None') with translation.override('fr-fr'): self.humanize_tester(test_list, result_list, 'ordinal', lambda x: x) def test_intcomma(self): test_list = (100, 1000, 10123, 10311, 1000000, 1234567.25, '100', '1000', '10123', '10311', '1000000', '1234567.1234567', Decimal('1234567.1234567'), None) result_list = ('100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.25', '100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.1234567', '1,234,567.1234567', None) with translation.override('en'): self.humanize_tester(test_list, result_list, 'intcomma') def test_l10n_intcomma(self): test_list = (100, 1000, 10123, 10311, 1000000, 1234567.25, '100', '1000', '10123', '10311', '1000000', '1234567.1234567', Decimal('1234567.1234567'), None) result_list = ('100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.25', '100', '1,000', '10,123', '10,311', '1,000,000', '1,234,567.1234567', '1,234,567.1234567', None) with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=False): with translation.override('en'): self.humanize_tester(test_list, result_list, 'intcomma') def test_intcomma_without_number_grouping(self): # Regression for #17414 with translation.override('ja'), self.settings(USE_L10N=True): self.humanize_tester([100], ['100'], 'intcomma') def test_intword(self): test_list = ('100', '1000000', '1200000', '1290000', '1000000000', '2000000000', '6000000000000', '1300000000000000', '3500000000000000000000', '8100000000000000000000000000000000', None) result_list = ('100', '1.0 million', '1.2 million', '1.3 million', '1.0 billion', '2.0 billion', '6.0 trillion', '1.3 quadrillion', '3.5 sextillion', '8.1 decillion', None) with translation.override('en'): self.humanize_tester(test_list, result_list, 'intword') def test_i18n_intcomma(self): test_list = (100, 1000, 10123, 10311, 1000000, 1234567.25, '100', '1000', '10123', '10311', '1000000', None) result_list = ('100', '1.000', '10.123', '10.311', '1.000.000', '1.234.567,25', '100', '1.000', '10.123', '10.311', '1.000.000', None) with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True): with translation.override('de'): self.humanize_tester(test_list, result_list, 'intcomma') def test_i18n_intword(self): test_list = ('100', '1000000', '1200000', '1290000', '1000000000', '2000000000', '6000000000000') result_list = ('100', '1,0 Million', '1,2 Millionen', '1,3 Millionen', '1,0 Milliarde', '2,0 Milliarden', '6,0 Billionen') with self.settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True): with translation.override('de'): self.humanize_tester(test_list, result_list, 'intword') def test_apnumber(self): test_list = [str(x) for x in range(1, 11)] test_list.append(None) result_list = ('one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', '10', None) with translation.override('en'): self.humanize_tester(test_list, result_list, 'apnumber') def test_naturalday(self): today = datetime.date.today() yesterday = today - datetime.timedelta(days=1) tomorrow = today + datetime.timedelta(days=1) someday = today - datetime.timedelta(days=10) notdate = "I'm not a date value" test_list = (today, yesterday, tomorrow, someday, notdate, None) someday_result = defaultfilters.date(someday) result_list = (_('today'), _('yesterday'), _('tomorrow'), someday_result, "I'm not a date value", None) self.humanize_tester(test_list, result_list, 'naturalday') def test_naturalday_tz(self): today = datetime.date.today() tz_one = get_fixed_timezone(-720) tz_two = get_fixed_timezone(720) # Can be today or yesterday date_one = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_one) naturalday_one = humanize.naturalday(date_one) # Can be today or tomorrow date_two = datetime.datetime(today.year, today.month, today.day, tzinfo=tz_two) naturalday_two = humanize.naturalday(date_two) # As 24h of difference they will never be the same self.assertNotEqual(naturalday_one, naturalday_two) @skipIf(pytz is None, "this test requires pytz") def test_naturalday_uses_localtime(self): # Regression for #18504 # This is 2012-03-08HT19:30:00-06:00 in America/Chicago dt = datetime.datetime(2012, 3, 9, 1, 30, tzinfo=utc) orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime try: with override_settings(TIME_ZONE="America/Chicago", USE_TZ=True): with translation.override('en'): self.humanize_tester([dt], ['yesterday'], 'naturalday') finally: humanize.datetime = orig_humanize_datetime def test_naturaltime(self): class naive(datetime.tzinfo): def utcoffset(self, dt): return None test_list = [ now, now - datetime.timedelta(seconds=1), now - datetime.timedelta(seconds=30), now - datetime.timedelta(minutes=1, seconds=30), now - datetime.timedelta(minutes=2), now - datetime.timedelta(hours=1, minutes=30, seconds=30), now - datetime.timedelta(hours=23, minutes=50, seconds=50), now - datetime.timedelta(days=1), now - datetime.timedelta(days=500), now + datetime.timedelta(seconds=1), now + datetime.timedelta(seconds=30), now + datetime.timedelta(minutes=1, seconds=30), now + datetime.timedelta(minutes=2), now + datetime.timedelta(hours=1, minutes=30, seconds=30), now + datetime.timedelta(hours=23, minutes=50, seconds=50), now + datetime.timedelta(days=1), now + datetime.timedelta(days=2, hours=6), now + datetime.timedelta(days=500), now.replace(tzinfo=naive()), now.replace(tzinfo=utc), ] result_list = [ 'now', 'a second ago', '30\xa0seconds ago', 'a minute ago', '2\xa0minutes ago', 'an hour ago', '23\xa0hours ago', '1\xa0day ago', '1\xa0year, 4\xa0months ago', 'a second from now', '30\xa0seconds from now', 'a minute from now', '2\xa0minutes from now', 'an hour from now', '23\xa0hours from now', '1\xa0day from now', '2\xa0days, 6\xa0hours from now', '1\xa0year, 4\xa0months from now', 'now', 'now', ] # Because of the DST change, 2 days and 6 hours after the chosen # date in naive arithmetic is only 2 days and 5 hours after in # aware arithmetic. result_list_with_tz_support = result_list[:] assert result_list_with_tz_support[-4] == '2\xa0days, 6\xa0hours from now' result_list_with_tz_support[-4] == '2\xa0days, 5\xa0hours from now' orig_humanize_datetime, humanize.datetime = humanize.datetime, MockDateTime try: with translation.override('en'): self.humanize_tester(test_list, result_list, 'naturaltime') with override_settings(USE_TZ=True): self.humanize_tester( test_list, result_list_with_tz_support, 'naturaltime') finally: humanize.datetime = orig_humanize_datetime def test_naturaltime_as_documented(self): """ #23340 -- Verify the documented behavior of humanize.naturaltime. """ time_format = '%d %b %Y %H:%M:%S' documented_now = datetime.datetime.strptime('17 Feb 2007 16:30:00', time_format) test_data = ( ('17 Feb 2007 16:30:00', 'now'), ('17 Feb 2007 16:29:31', '29 seconds ago'), ('17 Feb 2007 16:29:00', 'a minute ago'), ('17 Feb 2007 16:25:35', '4 minutes ago'), ('17 Feb 2007 15:30:29', '59 minutes ago'), ('17 Feb 2007 15:30:01', '59 minutes ago'), ('17 Feb 2007 15:30:00', 'an hour ago'), ('17 Feb 2007 13:31:29', '2 hours ago'), ('16 Feb 2007 13:31:29', '1 day, 2 hours ago'), ('16 Feb 2007 13:30:01', '1 day, 2 hours ago'), ('16 Feb 2007 13:30:00', '1 day, 3 hours ago'), ('17 Feb 2007 16:30:30', '30 seconds from now'), ('17 Feb 2007 16:30:29', '29 seconds from now'), ('17 Feb 2007 16:31:00', 'a minute from now'), ('17 Feb 2007 16:34:35', '4 minutes from now'), ('17 Feb 2007 17:30:29', 'an hour from now'), ('17 Feb 2007 18:31:29', '2 hours from now'), ('18 Feb 2007 16:31:29', '1 day from now'), ('26 Feb 2007 18:31:29', '1 week, 2 days from now'), ) class DocumentedMockDateTime(datetime.datetime): @classmethod def now(cls, tz=None): if tz is None or tz.utcoffset(documented_now) is None: return documented_now else: return documented_now.replace(tzinfo=tz) + tz.utcoffset(now) orig_humanize_datetime = humanize.datetime humanize.datetime = DocumentedMockDateTime try: for test_time_string, expected_natural_time in test_data: test_time = datetime.datetime.strptime(test_time_string, time_format) natural_time = humanize.naturaltime(test_time).replace('\xa0', ' ') self.assertEqual(expected_natural_time, natural_time) finally: humanize.datetime = orig_humanize_datetime Django-1.8.7/tests/humanize_tests/__init__.py0000664000175000017500000000000012625116214020642 0ustar timtim00000000000000Django-1.8.7/tests/signing/0000775000175000017500000000000012625116243015141 5ustar timtim00000000000000Django-1.8.7/tests/signing/tests.py0000664000175000017500000001136012625116214016654 0ustar timtim00000000000000from __future__ import unicode_literals import datetime from django.core import signing from django.test import TestCase from django.test.utils import freeze_time from django.utils import six from django.utils.encoding import force_str class TestSigner(TestCase): def test_signature(self): "signature() method should generate a signature" signer = signing.Signer('predictable-secret') signer2 = signing.Signer('predictable-secret2') for s in ( b'hello', b'3098247:529:087:', '\u2019'.encode('utf-8'), ): self.assertEqual( signer.signature(s), signing.base64_hmac(signer.salt + 'signer', s, 'predictable-secret').decode() ) self.assertNotEqual(signer.signature(s), signer2.signature(s)) def test_signature_with_salt(self): "signature(value, salt=...) should work" signer = signing.Signer('predictable-secret', salt='extra-salt') self.assertEqual( signer.signature('hello'), signing.base64_hmac('extra-salt' + 'signer', 'hello', 'predictable-secret').decode() ) self.assertNotEqual( signing.Signer('predictable-secret', salt='one').signature('hello'), signing.Signer('predictable-secret', salt='two').signature('hello')) def test_sign_unsign(self): "sign/unsign should be reversible" signer = signing.Signer('predictable-secret') examples = [ 'q;wjmbk;wkmb', '3098247529087', '3098247:529:087:', 'jkw osanteuh ,rcuh nthu aou oauh ,ud du', '\u2019', ] if six.PY2: examples.append(b'a byte string') for example in examples: signed = signer.sign(example) self.assertIsInstance(signed, str) self.assertNotEqual(force_str(example), signed) self.assertEqual(example, signer.unsign(signed)) def unsign_detects_tampering(self): "unsign should raise an exception if the value has been tampered with" signer = signing.Signer('predictable-secret') value = 'Another string' signed_value = signer.sign(value) transforms = ( lambda s: s.upper(), lambda s: s + 'a', lambda s: 'a' + s[1:], lambda s: s.replace(':', ''), ) self.assertEqual(value, signer.unsign(signed_value)) for transform in transforms: self.assertRaises( signing.BadSignature, signer.unsign, transform(signed_value)) def test_dumps_loads(self): "dumps and loads be reversible for any JSON serializable object" objects = [ ['a', 'list'], 'a unicode string \u2019', {'a': 'dictionary'}, ] if six.PY2: objects.append(b'a byte string') for o in objects: self.assertNotEqual(o, signing.dumps(o)) self.assertEqual(o, signing.loads(signing.dumps(o))) self.assertNotEqual(o, signing.dumps(o, compress=True)) self.assertEqual(o, signing.loads(signing.dumps(o, compress=True))) def test_decode_detects_tampering(self): "loads should raise exception for tampered objects" transforms = ( lambda s: s.upper(), lambda s: s + 'a', lambda s: 'a' + s[1:], lambda s: s.replace(':', ''), ) value = { 'foo': 'bar', 'baz': 1, } encoded = signing.dumps(value) self.assertEqual(value, signing.loads(encoded)) for transform in transforms: self.assertRaises( signing.BadSignature, signing.loads, transform(encoded)) def test_works_with_non_ascii_keys(self): binary_key = b'\xe7' # Set some binary (non-ASCII key) s = signing.Signer(binary_key) self.assertEqual('foo:6NB0fssLW5RQvZ3Y-MTerq2rX7w', s.sign('foo')) class TestTimestampSigner(TestCase): def test_timestamp_signer(self): value = 'hello' with freeze_time(123456789): signer = signing.TimestampSigner('predictable-key') ts = signer.sign(value) self.assertNotEqual(ts, signing.Signer('predictable-key').sign(value)) self.assertEqual(signer.unsign(ts), value) with freeze_time(123456800): self.assertEqual(signer.unsign(ts, max_age=12), value) # max_age parameter can also accept a datetime.timedelta object self.assertEqual(signer.unsign(ts, max_age=datetime.timedelta(seconds=11)), value) self.assertRaises(signing.SignatureExpired, signer.unsign, ts, max_age=10) Django-1.8.7/tests/signing/__init__.py0000664000175000017500000000000012603513307017236 0ustar timtim00000000000000Django-1.8.7/tests/migrations/0000775000175000017500000000000012625116243015657 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_backwards_deps_1/0000775000175000017500000000000012625116243024346 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_backwards_deps_1/0002_second.py0000664000175000017500000000032312603513307026630 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [('migrations', '0001_initial')] operations = [] Django-1.8.7/tests/migrations/test_migrations_backwards_deps_1/0001_initial.py0000664000175000017500000000023712603513307027011 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): operations = [] Django-1.8.7/tests/migrations/test_graph.py0000664000175000017500000002507112625116214020374 0ustar timtim00000000000000from unittest import expectedFailure from django.db.migrations.graph import ( CircularDependencyError, MigrationGraph, NodeNotFoundError, ) from django.test import TestCase from django.utils.encoding import force_text class GraphTests(TestCase): """ Tests the digraph structure. """ def test_simple_graph(self): """ Tests a basic dependency graph: app_a: 0001 <-- 0002 <--- 0003 <-- 0004 / app_b: 0001 <-- 0002 <-/ """ # Build graph graph = MigrationGraph() graph.add_node(("app_a", "0001"), None) graph.add_node(("app_a", "0002"), None) graph.add_node(("app_a", "0003"), None) graph.add_node(("app_a", "0004"), None) graph.add_node(("app_b", "0001"), None) graph.add_node(("app_b", "0002"), None) graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_a", "0003")) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002")) graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001")) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002")) graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001")) # Test root migration case self.assertEqual( graph.forwards_plan(("app_a", "0001")), [('app_a', '0001')], ) # Test branch B only self.assertEqual( graph.forwards_plan(("app_b", "0002")), [("app_b", "0001"), ("app_b", "0002")], ) # Test whole graph self.assertEqual( graph.forwards_plan(("app_a", "0004")), [('app_b', '0001'), ('app_b', '0002'), ('app_a', '0001'), ('app_a', '0002'), ('app_a', '0003'), ('app_a', '0004')], ) # Test reverse to b:0002 self.assertEqual( graph.backwards_plan(("app_b", "0002")), [('app_a', '0004'), ('app_a', '0003'), ('app_b', '0002')], ) # Test roots and leaves self.assertEqual( graph.root_nodes(), [('app_a', '0001'), ('app_b', '0001')], ) self.assertEqual( graph.leaf_nodes(), [('app_a', '0004'), ('app_b', '0002')], ) def test_complex_graph(self): """ Tests a complex dependency graph: app_a: 0001 <-- 0002 <--- 0003 <-- 0004 \ \ / / app_b: 0001 <-\ 0002 <-X / \ \ / app_c: \ 0001 <-- 0002 <- """ # Build graph graph = MigrationGraph() graph.add_node(("app_a", "0001"), None) graph.add_node(("app_a", "0002"), None) graph.add_node(("app_a", "0003"), None) graph.add_node(("app_a", "0004"), None) graph.add_node(("app_b", "0001"), None) graph.add_node(("app_b", "0002"), None) graph.add_node(("app_c", "0001"), None) graph.add_node(("app_c", "0002"), None) graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_a", "0003")) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002")) graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001")) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002")) graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001")) graph.add_dependency("app_a.0004", ("app_a", "0004"), ("app_c", "0002")) graph.add_dependency("app_c.0002", ("app_c", "0002"), ("app_c", "0001")) graph.add_dependency("app_c.0001", ("app_c", "0001"), ("app_b", "0001")) graph.add_dependency("app_c.0002", ("app_c", "0002"), ("app_a", "0002")) # Test branch C only self.assertEqual( graph.forwards_plan(("app_c", "0002")), [('app_b', '0001'), ('app_c', '0001'), ('app_a', '0001'), ('app_a', '0002'), ('app_c', '0002')], ) # Test whole graph self.assertEqual( graph.forwards_plan(("app_a", "0004")), [('app_b', '0001'), ('app_c', '0001'), ('app_a', '0001'), ('app_a', '0002'), ('app_c', '0002'), ('app_b', '0002'), ('app_a', '0003'), ('app_a', '0004')], ) # Test reverse to b:0001 self.assertEqual( graph.backwards_plan(("app_b", "0001")), [('app_a', '0004'), ('app_c', '0002'), ('app_c', '0001'), ('app_a', '0003'), ('app_b', '0002'), ('app_b', '0001')], ) # Test roots and leaves self.assertEqual( graph.root_nodes(), [('app_a', '0001'), ('app_b', '0001'), ('app_c', '0001')], ) self.assertEqual( graph.leaf_nodes(), [('app_a', '0004'), ('app_b', '0002'), ('app_c', '0002')], ) def test_circular_graph(self): """ Tests a circular dependency graph. """ # Build graph graph = MigrationGraph() graph.add_node(("app_a", "0001"), None) graph.add_node(("app_a", "0002"), None) graph.add_node(("app_a", "0003"), None) graph.add_node(("app_b", "0001"), None) graph.add_node(("app_b", "0002"), None) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002")) graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001")) graph.add_dependency("app_a.0001", ("app_a", "0001"), ("app_b", "0002")) graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001")) graph.add_dependency("app_b.0001", ("app_b", "0001"), ("app_a", "0003")) # Test whole graph self.assertRaises( CircularDependencyError, graph.forwards_plan, ("app_a", "0003"), ) def test_circular_graph_2(self): graph = MigrationGraph() graph.add_node(('A', '0001'), None) graph.add_node(('C', '0001'), None) graph.add_node(('B', '0001'), None) graph.add_dependency('A.0001', ('A', '0001'), ('B', '0001')) graph.add_dependency('B.0001', ('B', '0001'), ('A', '0001')) graph.add_dependency('C.0001', ('C', '0001'), ('B', '0001')) self.assertRaises( CircularDependencyError, graph.forwards_plan, ('C', '0001') ) def test_deep_graph(self): graph = MigrationGraph() root = ("app_a", "1") graph.add_node(root, None) expected = [root] for i in range(2, 750): parent = ("app_a", str(i - 1)) child = ("app_a", str(i)) graph.add_node(child, None) graph.add_dependency(str(i), child, parent) expected.append(child) actual = graph.node_map[root].descendants() self.assertEqual(expected[::-1], actual) @expectedFailure def test_recursion_depth(self): graph = MigrationGraph() root = ("app_a", "1") graph.add_node(root, None) expected = [root] for i in range(2, 1000): parent = ("app_a", str(i - 1)) child = ("app_a", str(i)) graph.add_node(child, None) graph.add_dependency(str(i), child, parent) expected.append(child) actual = graph.node_map[root].descendants() self.assertEqual(expected[::-1], actual) def test_plan_invalid_node(self): """ Tests for forwards/backwards_plan of nonexistent node. """ graph = MigrationGraph() message = "Node ('app_b', '0001') not a valid node" with self.assertRaisesMessage(NodeNotFoundError, message): graph.forwards_plan(("app_b", "0001")) with self.assertRaisesMessage(NodeNotFoundError, message): graph.backwards_plan(("app_b", "0001")) def test_missing_parent_nodes(self): """ Tests for missing parent nodes. """ # Build graph graph = MigrationGraph() graph.add_node(("app_a", "0001"), None) graph.add_node(("app_a", "0002"), None) graph.add_node(("app_a", "0003"), None) graph.add_node(("app_b", "0001"), None) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002")) graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001")) msg = "Migration app_a.0001 dependencies reference nonexistent parent node ('app_b', '0002')" with self.assertRaisesMessage(NodeNotFoundError, msg): graph.add_dependency("app_a.0001", ("app_a", "0001"), ("app_b", "0002")) def test_missing_child_nodes(self): """ Tests for missing child nodes. """ # Build graph graph = MigrationGraph() graph.add_node(("app_a", "0001"), None) msg = "Migration app_a.0002 dependencies reference nonexistent child node ('app_a', '0002')" with self.assertRaisesMessage(NodeNotFoundError, msg): graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001")) def test_infinite_loop(self): """ Tests a complex dependency graph: app_a: 0001 <- \ app_b: 0001 <- x 0002 <- / \ app_c: 0001<- <------------- x 0002 And apply squashing on app_c. """ graph = MigrationGraph() graph.add_node(("app_a", "0001"), None) graph.add_node(("app_b", "0001"), None) graph.add_node(("app_b", "0002"), None) graph.add_node(("app_c", "0001_squashed_0002"), None) graph.add_dependency("app_b.0001", ("app_b", "0001"), ("app_c", "0001_squashed_0002")) graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_a", "0001")) graph.add_dependency("app_b.0002", ("app_b", "0002"), ("app_b", "0001")) graph.add_dependency("app_c.0001_squashed_0002", ("app_c", "0001_squashed_0002"), ("app_b", "0002")) with self.assertRaises(CircularDependencyError): graph.forwards_plan(("app_c", "0001_squashed_0002")) def test_stringify(self): graph = MigrationGraph() self.assertEqual(force_text(graph), "Graph: 0 nodes, 0 edges") graph.add_node(("app_a", "0001"), None) graph.add_node(("app_a", "0002"), None) graph.add_node(("app_a", "0003"), None) graph.add_node(("app_b", "0001"), None) graph.add_node(("app_b", "0002"), None) graph.add_dependency("app_a.0002", ("app_a", "0002"), ("app_a", "0001")) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_a", "0002")) graph.add_dependency("app_a.0003", ("app_a", "0003"), ("app_b", "0002")) self.assertEqual(force_text(graph), "Graph: 5 nodes, 3 edges") Django-1.8.7/tests/migrations/faulty_migrations/0000775000175000017500000000000012625116243021417 5ustar timtim00000000000000Django-1.8.7/tests/migrations/faulty_migrations/namespace/0000775000175000017500000000000012625116242023352 5ustar timtim00000000000000Django-1.8.7/tests/migrations/faulty_migrations/namespace/foo/0000775000175000017500000000000012625116243024136 5ustar timtim00000000000000Django-1.8.7/tests/migrations/faulty_migrations/namespace/foo/__init__.py0000664000175000017500000000000012603513307026233 0ustar timtim00000000000000Django-1.8.7/tests/migrations/faulty_migrations/__init__.py0000664000175000017500000000000012603513307023514 0ustar timtim00000000000000Django-1.8.7/tests/migrations/faulty_migrations/file.py0000664000175000017500000000000012603513307022674 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_first/0000775000175000017500000000000012625116243022301 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_first/thefirst.py0000664000175000017500000000142412603513307024502 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_first/__init__.py0000664000175000017500000000000012603513307024376 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_first/second.py0000664000175000017500000000126512625113144024127 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("migrations", "thefirst"), ("migrations2", "0002_second"), ] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_squashed/0000775000175000017500000000000012625116243022767 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed/0002_second.py0000664000175000017500000000120212625113144025245 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("migrations", "0001_initial")] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_squashed/0001_squashed_0002.py0000664000175000017500000000160012625113144026251 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): replaces = [ ("migrations", "0001_initial"), ("migrations", "0002_second"), ] operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("rating", models.IntegerField(default=0)), ], ), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ), ] Django-1.8.7/tests/migrations/test_migrations_squashed/__init__.py0000664000175000017500000000000012603513307025064 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed/0001_initial.py0000664000175000017500000000142412603513307025431 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/related_models_app/0000775000175000017500000000000012625116243021502 5ustar timtim00000000000000Django-1.8.7/tests/migrations/related_models_app/__init__.py0000664000175000017500000000000012625116214023577 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/0000775000175000017500000000000012625116243025070 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/3_squashed_5.py0000664000175000017500000000061212625116214027722 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): replaces = [ ("migrations", "3_auto"), ("migrations", "4_auto"), ("migrations", "5_auto"), ] dependencies = [("migrations", "2_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/2_auto.py0000664000175000017500000000041412625116214026630 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "1_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/7_auto.py0000664000175000017500000000041412625116214026635 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "6_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/__init__.py0000664000175000017500000000000012625116214027165 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/1_auto.py0000664000175000017500000000033512625116214026631 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_erroneous/6_auto.py0000664000175000017500000000041412625116214026634 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "5_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_loader.py0000664000175000017500000003203512625116214020537 0ustar timtim00000000000000from __future__ import unicode_literals from unittest import skipIf from django.db import connection, connections from django.db.migrations.graph import NodeNotFoundError from django.db.migrations.loader import AmbiguityError, MigrationLoader from django.db.migrations.recorder import MigrationRecorder from django.test import TestCase, modify_settings, override_settings from django.utils import six class RecorderTests(TestCase): """ Tests recording migrations as applied or not. """ def test_apply(self): """ Tests marking migrations as applied/unapplied. """ recorder = MigrationRecorder(connection) self.assertEqual( set((x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"), set(), ) recorder.record_applied("myapp", "0432_ponies") self.assertEqual( set((x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"), {("myapp", "0432_ponies")}, ) # That should not affect records of another database recorder_other = MigrationRecorder(connections['other']) self.assertEqual( set((x, y) for (x, y) in recorder_other.applied_migrations() if x == "myapp"), set(), ) recorder.record_unapplied("myapp", "0432_ponies") self.assertEqual( set((x, y) for (x, y) in recorder.applied_migrations() if x == "myapp"), set(), ) class LoaderTests(TestCase): """ Tests the disk and database loader, and running through migrations in memory. """ @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) @modify_settings(INSTALLED_APPS={'append': 'basic'}) def test_load(self): """ Makes sure the loader can load the migrations for the test apps, and then render them out to a new Apps. """ # Load and test the plan migration_loader = MigrationLoader(connection) self.assertEqual( migration_loader.graph.forwards_plan(("migrations", "0002_second")), [ ("migrations", "0001_initial"), ("migrations", "0002_second"), ], ) # Now render it out! project_state = migration_loader.project_state(("migrations", "0002_second")) self.assertEqual(len(project_state.models), 2) author_state = project_state.models["migrations", "author"] self.assertEqual( [x for x, y in author_state.fields], ["id", "name", "slug", "age", "rating"] ) book_state = project_state.models["migrations", "book"] self.assertEqual( [x for x, y in book_state.fields], ["id", "author"] ) # Ensure we've included unmigrated apps in there too self.assertIn("basic", project_state.real_apps) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_unmigdep"}) def test_load_unmigrated_dependency(self): """ Makes sure the loader can load migrations with a dependency on an unmigrated app. """ # Load and test the plan migration_loader = MigrationLoader(connection) self.assertEqual( migration_loader.graph.forwards_plan(("migrations", "0001_initial")), [ ('contenttypes', '0001_initial'), ('auth', '0001_initial'), ("migrations", "0001_initial"), ], ) # Now render it out! project_state = migration_loader.project_state(("migrations", "0001_initial")) self.assertEqual(len([m for a, m in project_state.models if a == "migrations"]), 1) book_state = project_state.models["migrations", "book"] self.assertEqual( [x for x, y in book_state.fields], ["id", "user"] ) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_run_before"}) def test_run_before(self): """ Makes sure the loader uses Migration.run_before. """ # Load and test the plan migration_loader = MigrationLoader(connection) self.assertEqual( migration_loader.graph.forwards_plan(("migrations", "0002_second")), [ ("migrations", "0001_initial"), ("migrations", "0003_third"), ("migrations", "0002_second"), ], ) @override_settings(MIGRATION_MODULES={ "migrations": "migrations.test_migrations_first", "migrations2": "migrations2.test_migrations_2_first", }) @modify_settings(INSTALLED_APPS={'append': 'migrations2'}) def test_first(self): """ Makes sure the '__first__' migrations build correctly. """ migration_loader = MigrationLoader(connection) self.assertEqual( migration_loader.graph.forwards_plan(("migrations", "second")), [ ("migrations", "thefirst"), ("migrations2", "0001_initial"), ("migrations2", "0002_second"), ("migrations", "second"), ], ) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_name_match(self): "Tests prefix name matching" migration_loader = MigrationLoader(connection) self.assertEqual( migration_loader.get_migration_by_prefix("migrations", "0001").name, "0001_initial", ) with self.assertRaises(AmbiguityError): migration_loader.get_migration_by_prefix("migrations", "0") with self.assertRaises(KeyError): migration_loader.get_migration_by_prefix("migrations", "blarg") def test_load_import_error(self): with override_settings(MIGRATION_MODULES={"migrations": "import_error_package"}): with self.assertRaises(ImportError): MigrationLoader(connection) def test_load_module_file(self): with override_settings(MIGRATION_MODULES={"migrations": "migrations.faulty_migrations.file"}): loader = MigrationLoader(connection) self.assertIn( "migrations", loader.unmigrated_apps, "App with migrations module file not in unmigrated apps." ) @skipIf(six.PY2, "PY2 doesn't load empty dirs.") def test_load_empty_dir(self): with override_settings(MIGRATION_MODULES={"migrations": "migrations.faulty_migrations.namespace"}): loader = MigrationLoader(connection) self.assertIn( "migrations", loader.unmigrated_apps, "App missing __init__.py in migrations module not in unmigrated apps." ) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}) def test_loading_squashed(self): "Tests loading a squashed migration" migration_loader = MigrationLoader(connection) recorder = MigrationRecorder(connection) # Loading with nothing applied should just give us the one node self.assertEqual( len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]), 1, ) # However, fake-apply one migration and it should now use the old two recorder.record_applied("migrations", "0001_initial") migration_loader.build_graph() self.assertEqual( len([x for x in migration_loader.graph.nodes if x[0] == "migrations"]), 2, ) recorder.flush() @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed_complex"}) def test_loading_squashed_complex(self): "Tests loading a complex set of squashed migrations" loader = MigrationLoader(connection) recorder = MigrationRecorder(connection) def num_nodes(): plan = set(loader.graph.forwards_plan(('migrations', '7_auto'))) return len(plan - loader.applied_migrations) # Empty database: use squashed migration loader.build_graph() self.assertEqual(num_nodes(), 5) # Starting at 1 or 2 should use the squashed migration too recorder.record_applied("migrations", "1_auto") loader.build_graph() self.assertEqual(num_nodes(), 4) recorder.record_applied("migrations", "2_auto") loader.build_graph() self.assertEqual(num_nodes(), 3) # However, starting at 3 to 5 cannot use the squashed migration recorder.record_applied("migrations", "3_auto") loader.build_graph() self.assertEqual(num_nodes(), 4) recorder.record_applied("migrations", "4_auto") loader.build_graph() self.assertEqual(num_nodes(), 3) # Starting at 5 to 7 we are passed the squashed migrations recorder.record_applied("migrations", "5_auto") loader.build_graph() self.assertEqual(num_nodes(), 2) recorder.record_applied("migrations", "6_auto") loader.build_graph() self.assertEqual(num_nodes(), 1) recorder.record_applied("migrations", "7_auto") loader.build_graph() self.assertEqual(num_nodes(), 0) recorder.flush() @override_settings(MIGRATION_MODULES={ "app1": "migrations.test_migrations_squashed_complex_multi_apps.app1", "app2": "migrations.test_migrations_squashed_complex_multi_apps.app2", }) @modify_settings(INSTALLED_APPS={'append': [ "migrations.test_migrations_squashed_complex_multi_apps.app1", "migrations.test_migrations_squashed_complex_multi_apps.app2", ]}) def test_loading_squashed_complex_multi_apps(self): loader = MigrationLoader(connection) loader.build_graph() plan = set(loader.graph.forwards_plan(('app1', '4_auto'))) expected_plan = { ('app1', '4_auto'), ('app1', '2_squashed_3'), ('app2', '1_squashed_2'), ('app1', '1_auto') } self.assertEqual(plan, expected_plan) @override_settings(MIGRATION_MODULES={ "app1": "migrations.test_migrations_squashed_complex_multi_apps.app1", "app2": "migrations.test_migrations_squashed_complex_multi_apps.app2", }) @modify_settings(INSTALLED_APPS={'append': [ "migrations.test_migrations_squashed_complex_multi_apps.app1", "migrations.test_migrations_squashed_complex_multi_apps.app2", ]}) def test_loading_squashed_complex_multi_apps_partially_applied(self): loader = MigrationLoader(connection) recorder = MigrationRecorder(connection) recorder.record_applied('app1', '1_auto') recorder.record_applied('app1', '2_auto') loader.build_graph() plan = set(loader.graph.forwards_plan(('app1', '4_auto'))) plan = plan - loader.applied_migrations expected_plan = { ('app1', '4_auto'), ('app1', '3_auto'), ('app2', '1_squashed_2'), } self.assertEqual(plan, expected_plan) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed_erroneous"}) def test_loading_squashed_erroneous(self): "Tests loading a complex but erroneous set of squashed migrations" loader = MigrationLoader(connection) recorder = MigrationRecorder(connection) def num_nodes(): plan = set(loader.graph.forwards_plan(('migrations', '7_auto'))) return len(plan - loader.applied_migrations) # Empty database: use squashed migration loader.build_graph() self.assertEqual(num_nodes(), 5) # Starting at 1 or 2 should use the squashed migration too recorder.record_applied("migrations", "1_auto") loader.build_graph() self.assertEqual(num_nodes(), 4) recorder.record_applied("migrations", "2_auto") loader.build_graph() self.assertEqual(num_nodes(), 3) # However, starting at 3 or 4 we'd need to use non-existing migrations msg = ("Migration migrations.6_auto depends on nonexistent node ('migrations', '5_auto'). " "Django tried to replace migration migrations.5_auto with any of " "[migrations.3_squashed_5] but wasn't able to because some of the replaced " "migrations are already applied.") recorder.record_applied("migrations", "3_auto") with self.assertRaisesMessage(NodeNotFoundError, msg): loader.build_graph() recorder.record_applied("migrations", "4_auto") with self.assertRaisesMessage(NodeNotFoundError, msg): loader.build_graph() # Starting at 5 to 7 we are passed the squashed migrations recorder.record_applied("migrations", "5_auto") loader.build_graph() self.assertEqual(num_nodes(), 2) recorder.record_applied("migrations", "6_auto") loader.build_graph() self.assertEqual(num_nodes(), 1) recorder.record_applied("migrations", "7_auto") loader.build_graph() self.assertEqual(num_nodes(), 0) recorder.flush() Django-1.8.7/tests/migrations/test_operations.py0000664000175000017500000026562412625116214021470 0ustar timtim00000000000000from __future__ import unicode_literals import unittest from django.db import connection, migrations, models, transaction from django.db.migrations.migration import Migration from django.db.migrations.state import ProjectState from django.db.models.fields import NOT_PROVIDED from django.db.transaction import atomic from django.db.utils import IntegrityError from django.test import override_settings from django.utils import six from .models import FoodManager, FoodQuerySet from .test_base import MigrationTestBase try: import sqlparse except ImportError: sqlparse = None class OperationTestBase(MigrationTestBase): """ Common functions to help test operations. """ def apply_operations(self, app_label, project_state, operations): migration = Migration('name', app_label) migration.operations = operations with connection.schema_editor() as editor: return migration.apply(project_state, editor) def unapply_operations(self, app_label, project_state, operations): migration = Migration('name', app_label) migration.operations = operations with connection.schema_editor() as editor: return migration.unapply(project_state, editor) def make_test_state(self, app_label, operation, **kwargs): """ Makes a test state using set_up_test_model and returns the original state and the state after the migration is applied. """ project_state = self.set_up_test_model(app_label, **kwargs) new_state = project_state.clone() operation.state_forwards(app_label, new_state) return project_state, new_state def set_up_test_model(self, app_label, second_model=False, third_model=False, related_model=False, mti_model=False, proxy_model=False, manager_model=False, unique_together=False, options=False, db_table=None, index_together=False): """ Creates a test model state and database table. """ # Delete the tables if they already exist table_names = [ # Start with ManyToMany tables '_pony_stables', '_pony_vans', # Then standard model tables '_pony', '_stable', '_van', ] tables = [(app_label + table_name) for table_name in table_names] with connection.cursor() as cursor: table_names = connection.introspection.table_names(cursor) connection.disable_constraint_checking() sql_delete_table = connection.schema_editor().sql_delete_table with transaction.atomic(): for table in tables: if table in table_names: cursor.execute(sql_delete_table % { "table": connection.ops.quote_name(table), }) connection.enable_constraint_checking() # Make the "current" state model_options = { "swappable": "TEST_SWAP_MODEL", "index_together": [["weight", "pink"]] if index_together else [], "unique_together": [["pink", "weight"]] if unique_together else [], } if options: model_options["permissions"] = [("can_groom", "Can groom")] if db_table: model_options["db_table"] = db_table operations = [migrations.CreateModel( "Pony", [ ("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=3)), ("weight", models.FloatField()), ], options=model_options, )] if second_model: operations.append(migrations.CreateModel( "Stable", [ ("id", models.AutoField(primary_key=True)), ] )) if third_model: operations.append(migrations.CreateModel( "Van", [ ("id", models.AutoField(primary_key=True)), ] )) if related_model: operations.append(migrations.CreateModel( "Rider", [ ("id", models.AutoField(primary_key=True)), ("pony", models.ForeignKey("Pony")), ("friend", models.ForeignKey("self")) ], )) if mti_model: operations.append(migrations.CreateModel( "ShetlandPony", fields=[ ('pony_ptr', models.OneToOneField( auto_created=True, primary_key=True, to_field='id', serialize=False, to='Pony', )), ("cuteness", models.IntegerField(default=1)), ], bases=['%s.Pony' % app_label], )) if proxy_model: operations.append(migrations.CreateModel( "ProxyPony", fields=[], options={"proxy": True}, bases=['%s.Pony' % app_label], )) if manager_model: operations.append(migrations.CreateModel( "Food", fields=[ ("id", models.AutoField(primary_key=True)), ], managers=[ ("food_qs", FoodQuerySet.as_manager()), ("food_mgr", FoodManager("a", "b")), ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)), ] )) return self.apply_operations(app_label, ProjectState(), operations) class OperationTests(OperationTestBase): """ Tests running the operations and making sure they do what they say they do. Each test looks at their state changing, and then their database operation - both forwards and backwards. """ def test_create_model(self): """ Tests the CreateModel operation. Most other tests use this operation as part of setup, so check failures here first. """ operation = migrations.CreateModel( "Pony", [ ("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1)), ], ) self.assertEqual(operation.describe(), "Create model Pony") # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards("test_crmo", new_state) self.assertEqual(new_state.models["test_crmo", "pony"].name, "Pony") self.assertEqual(len(new_state.models["test_crmo", "pony"].fields), 2) # Test the database alteration self.assertTableNotExists("test_crmo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crmo", editor, project_state, new_state) self.assertTableExists("test_crmo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmo", editor, new_state, project_state) self.assertTableNotExists("test_crmo_pony") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "CreateModel") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2].keys()), ["fields", "name"]) # And default manager not in set operation = migrations.CreateModel("Foo", fields=[], managers=[("objects", models.Manager())]) definition = operation.deconstruct() self.assertNotIn('managers', definition[2]) def test_create_model_with_unique_after(self): """ Tests the CreateModel operation directly followed by an AlterUniqueTogether (bug #22844 - sqlite remake issues) """ operation1 = migrations.CreateModel( "Pony", [ ("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1)), ], ) operation2 = migrations.CreateModel( "Rider", [ ("id", models.AutoField(primary_key=True)), ("number", models.IntegerField(default=1)), ("pony", models.ForeignKey("test_crmoua.Pony")), ], ) operation3 = migrations.AlterUniqueTogether( "Rider", [ ("number", "pony"), ], ) # Test the database alteration project_state = ProjectState() self.assertTableNotExists("test_crmoua_pony") self.assertTableNotExists("test_crmoua_rider") with connection.schema_editor() as editor: new_state = project_state.clone() operation1.state_forwards("test_crmoua", new_state) operation1.database_forwards("test_crmoua", editor, project_state, new_state) project_state, new_state = new_state, new_state.clone() operation2.state_forwards("test_crmoua", new_state) operation2.database_forwards("test_crmoua", editor, project_state, new_state) project_state, new_state = new_state, new_state.clone() operation3.state_forwards("test_crmoua", new_state) operation3.database_forwards("test_crmoua", editor, project_state, new_state) self.assertTableExists("test_crmoua_pony") self.assertTableExists("test_crmoua_rider") def test_create_model_m2m(self): """ Test the creation of a model with a ManyToMany field and the auto-created "through" model. """ project_state = self.set_up_test_model("test_crmomm") operation = migrations.CreateModel( "Stable", [ ("id", models.AutoField(primary_key=True)), ("ponies", models.ManyToManyField("Pony", related_name="stables")) ] ) # Test the state alteration new_state = project_state.clone() operation.state_forwards("test_crmomm", new_state) # Test the database alteration self.assertTableNotExists("test_crmomm_stable_ponies") with connection.schema_editor() as editor: operation.database_forwards("test_crmomm", editor, project_state, new_state) self.assertTableExists("test_crmomm_stable") self.assertTableExists("test_crmomm_stable_ponies") self.assertColumnNotExists("test_crmomm_stable", "ponies") # Make sure the M2M field actually works with atomic(): Pony = new_state.apps.get_model("test_crmomm", "Pony") Stable = new_state.apps.get_model("test_crmomm", "Stable") stable = Stable.objects.create() p1 = Pony.objects.create(pink=False, weight=4.55) p2 = Pony.objects.create(pink=True, weight=5.43) stable.ponies.add(p1, p2) self.assertEqual(stable.ponies.count(), 2) stable.ponies.all().delete() # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmomm", editor, new_state, project_state) self.assertTableNotExists("test_crmomm_stable") self.assertTableNotExists("test_crmomm_stable_ponies") def test_create_model_inheritance(self): """ Tests the CreateModel operation on a multi-table inheritance setup. """ project_state = self.set_up_test_model("test_crmoih") # Test the state alteration operation = migrations.CreateModel( "ShetlandPony", [ ('pony_ptr', models.OneToOneField( auto_created=True, primary_key=True, to_field='id', serialize=False, to='test_crmoih.Pony', )), ("cuteness", models.IntegerField(default=1)), ], ) new_state = project_state.clone() operation.state_forwards("test_crmoih", new_state) self.assertIn(("test_crmoih", "shetlandpony"), new_state.models) # Test the database alteration self.assertTableNotExists("test_crmoih_shetlandpony") with connection.schema_editor() as editor: operation.database_forwards("test_crmoih", editor, project_state, new_state) self.assertTableExists("test_crmoih_shetlandpony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crmoih", editor, new_state, project_state) self.assertTableNotExists("test_crmoih_shetlandpony") def test_create_proxy_model(self): """ Tests that CreateModel ignores proxy models. """ project_state = self.set_up_test_model("test_crprmo") # Test the state alteration operation = migrations.CreateModel( "ProxyPony", [], options={"proxy": True}, bases=("test_crprmo.Pony", ), ) self.assertEqual(operation.describe(), "Create proxy model ProxyPony") new_state = project_state.clone() operation.state_forwards("test_crprmo", new_state) self.assertIn(("test_crprmo", "proxypony"), new_state.models) # Test the database alteration self.assertTableNotExists("test_crprmo_proxypony") self.assertTableExists("test_crprmo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crprmo", editor, project_state, new_state) self.assertTableNotExists("test_crprmo_proxypony") self.assertTableExists("test_crprmo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crprmo", editor, new_state, project_state) self.assertTableNotExists("test_crprmo_proxypony") self.assertTableExists("test_crprmo_pony") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "CreateModel") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2].keys()), ["bases", "fields", "name", "options"]) def test_create_unmanaged_model(self): """ Tests that CreateModel ignores unmanaged models. """ project_state = self.set_up_test_model("test_crummo") # Test the state alteration operation = migrations.CreateModel( "UnmanagedPony", [], options={"proxy": True}, bases=("test_crummo.Pony", ), ) self.assertEqual(operation.describe(), "Create proxy model UnmanagedPony") new_state = project_state.clone() operation.state_forwards("test_crummo", new_state) self.assertIn(("test_crummo", "unmanagedpony"), new_state.models) # Test the database alteration self.assertTableNotExists("test_crummo_unmanagedpony") self.assertTableExists("test_crummo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crummo", editor, project_state, new_state) self.assertTableNotExists("test_crummo_unmanagedpony") self.assertTableExists("test_crummo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crummo", editor, new_state, project_state) self.assertTableNotExists("test_crummo_unmanagedpony") self.assertTableExists("test_crummo_pony") def test_create_model_managers(self): """ Tests that the managers on a model are set. """ project_state = self.set_up_test_model("test_cmoma") # Test the state alteration operation = migrations.CreateModel( "Food", fields=[ ("id", models.AutoField(primary_key=True)), ], managers=[ ("food_qs", FoodQuerySet.as_manager()), ("food_mgr", FoodManager("a", "b")), ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)), ] ) self.assertEqual(operation.describe(), "Create model Food") new_state = project_state.clone() operation.state_forwards("test_cmoma", new_state) self.assertIn(("test_cmoma", "food"), new_state.models) managers = new_state.models["test_cmoma", "food"].managers self.assertEqual(managers[0][0], "food_qs") self.assertIsInstance(managers[0][1], models.Manager) self.assertEqual(managers[1][0], "food_mgr") self.assertIsInstance(managers[1][1], FoodManager) self.assertEqual(managers[1][1].args, ("a", "b", 1, 2)) self.assertEqual(managers[2][0], "food_mgr_kwargs") self.assertIsInstance(managers[2][1], FoodManager) self.assertEqual(managers[2][1].args, ("x", "y", 3, 4)) def test_delete_model(self): """ Tests the DeleteModel operation. """ project_state = self.set_up_test_model("test_dlmo") # Test the state alteration operation = migrations.DeleteModel("Pony") self.assertEqual(operation.describe(), "Delete model Pony") new_state = project_state.clone() operation.state_forwards("test_dlmo", new_state) self.assertNotIn(("test_dlmo", "pony"), new_state.models) # Test the database alteration self.assertTableExists("test_dlmo_pony") with connection.schema_editor() as editor: operation.database_forwards("test_dlmo", editor, project_state, new_state) self.assertTableNotExists("test_dlmo_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_dlmo", editor, new_state, project_state) self.assertTableExists("test_dlmo_pony") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "DeleteModel") self.assertEqual(definition[1], []) self.assertEqual(list(definition[2]), ["name"]) def test_delete_proxy_model(self): """ Tests the DeleteModel operation ignores proxy models. """ project_state = self.set_up_test_model("test_dlprmo", proxy_model=True) # Test the state alteration operation = migrations.DeleteModel("ProxyPony") new_state = project_state.clone() operation.state_forwards("test_dlprmo", new_state) self.assertIn(("test_dlprmo", "proxypony"), project_state.models) self.assertNotIn(("test_dlprmo", "proxypony"), new_state.models) # Test the database alteration self.assertTableExists("test_dlprmo_pony") self.assertTableNotExists("test_dlprmo_proxypony") with connection.schema_editor() as editor: operation.database_forwards("test_dlprmo", editor, project_state, new_state) self.assertTableExists("test_dlprmo_pony") self.assertTableNotExists("test_dlprmo_proxypony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_dlprmo", editor, new_state, project_state) self.assertTableExists("test_dlprmo_pony") self.assertTableNotExists("test_dlprmo_proxypony") def test_rename_model(self): """ Tests the RenameModel operation. """ project_state = self.set_up_test_model("test_rnmo", related_model=True) # Test the state alteration operation = migrations.RenameModel("Pony", "Horse") self.assertEqual(operation.describe(), "Rename model Pony to Horse") # Test initial state and database self.assertIn(("test_rnmo", "pony"), project_state.models) self.assertNotIn(("test_rnmo", "horse"), project_state.models) self.assertTableExists("test_rnmo_pony") self.assertTableNotExists("test_rnmo_horse") if connection.features.supports_foreign_keys: self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id")) self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id")) # Migrate forwards new_state = project_state.clone() new_state = self.apply_operations("test_rnmo", new_state, [operation]) # Test new state and database self.assertNotIn(("test_rnmo", "pony"), new_state.models) self.assertIn(("test_rnmo", "horse"), new_state.models) # RenameModel also repoints all incoming FKs and M2Ms self.assertEqual("test_rnmo.Horse", new_state.models["test_rnmo", "rider"].fields[1][1].rel.to) self.assertTableNotExists("test_rnmo_pony") self.assertTableExists("test_rnmo_horse") if connection.features.supports_foreign_keys: self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id")) self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id")) # Migrate backwards original_state = self.unapply_operations("test_rnmo", project_state, [operation]) # Test original state and database self.assertIn(("test_rnmo", "pony"), original_state.models) self.assertNotIn(("test_rnmo", "horse"), original_state.models) self.assertEqual("Pony", original_state.models["test_rnmo", "rider"].fields[1][1].rel.to) self.assertTableExists("test_rnmo_pony") self.assertTableNotExists("test_rnmo_horse") if connection.features.supports_foreign_keys: self.assertFKExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_pony", "id")) self.assertFKNotExists("test_rnmo_rider", ["pony_id"], ("test_rnmo_horse", "id")) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RenameModel") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'old_name': "Pony", 'new_name': "Horse"}) def test_rename_model_with_self_referential_fk(self): """ Tests the RenameModel operation on model with self referential FK. """ project_state = self.set_up_test_model("test_rmwsrf", related_model=True) # Test the state alteration operation = migrations.RenameModel("Rider", "HorseRider") self.assertEqual(operation.describe(), "Rename model Rider to HorseRider") new_state = project_state.clone() operation.state_forwards("test_rmwsrf", new_state) self.assertNotIn(("test_rmwsrf", "rider"), new_state.models) self.assertIn(("test_rmwsrf", "horserider"), new_state.models) # Remember, RenameModel also repoints all incoming FKs and M2Ms self.assertEqual("test_rmwsrf.HorseRider", new_state.models["test_rmwsrf", "horserider"].fields[2][1].rel.to) # Test the database alteration self.assertTableExists("test_rmwsrf_rider") self.assertTableNotExists("test_rmwsrf_horserider") if connection.features.supports_foreign_keys: self.assertFKExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id")) self.assertFKNotExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_horserider", "id")) with connection.schema_editor() as editor: operation.database_forwards("test_rmwsrf", editor, project_state, new_state) self.assertTableNotExists("test_rmwsrf_rider") self.assertTableExists("test_rmwsrf_horserider") if connection.features.supports_foreign_keys: self.assertFKNotExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_rider", "id")) self.assertFKExists("test_rmwsrf_horserider", ["friend_id"], ("test_rmwsrf_horserider", "id")) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_rmwsrf", editor, new_state, project_state) self.assertTableExists("test_rmwsrf_rider") self.assertTableNotExists("test_rmwsrf_horserider") if connection.features.supports_foreign_keys: self.assertFKExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_rider", "id")) self.assertFKNotExists("test_rmwsrf_rider", ["friend_id"], ("test_rmwsrf_horserider", "id")) def test_rename_model_with_superclass_fk(self): """ Tests the RenameModel operation on a model which has a superclass that has a foreign key. """ project_state = self.set_up_test_model("test_rmwsc", related_model=True, mti_model=True) # Test the state alteration operation = migrations.RenameModel("ShetlandPony", "LittleHorse") self.assertEqual(operation.describe(), "Rename model ShetlandPony to LittleHorse") new_state = project_state.clone() operation.state_forwards("test_rmwsc", new_state) self.assertNotIn(("test_rmwsc", "shetlandpony"), new_state.models) self.assertIn(("test_rmwsc", "littlehorse"), new_state.models) # RenameModel shouldn't repoint the superclass's relations, only local ones self.assertEqual( project_state.models["test_rmwsc", "rider"].fields[1][1].rel.to, new_state.models["test_rmwsc", "rider"].fields[1][1].rel.to ) # Before running the migration we have a table for Shetland Pony, not Little Horse self.assertTableExists("test_rmwsc_shetlandpony") self.assertTableNotExists("test_rmwsc_littlehorse") if connection.features.supports_foreign_keys: # and the foreign key on rider points to pony, not shetland pony self.assertFKExists("test_rmwsc_rider", ["pony_id"], ("test_rmwsc_pony", "id")) self.assertFKNotExists("test_rmwsc_rider", ["pony_id"], ("test_rmwsc_shetlandpony", "id")) with connection.schema_editor() as editor: operation.database_forwards("test_rmwsc", editor, project_state, new_state) # Now we have a little horse table, not shetland pony self.assertTableNotExists("test_rmwsc_shetlandpony") self.assertTableExists("test_rmwsc_littlehorse") if connection.features.supports_foreign_keys: # but the Foreign keys still point at pony, not little horse self.assertFKExists("test_rmwsc_rider", ["pony_id"], ("test_rmwsc_pony", "id")) self.assertFKNotExists("test_rmwsc_rider", ["pony_id"], ("test_rmwsc_littlehorse", "id")) def test_rename_model_with_self_referential_m2m(self): app_label = "test_rename_model_with_self_referential_m2m" project_state = self.apply_operations(app_label, ProjectState(), operations=[ migrations.CreateModel("ReflexivePony", fields=[ ("ponies", models.ManyToManyField("self")), ]), ]) project_state = self.apply_operations(app_label, project_state, operations=[ migrations.RenameModel("ReflexivePony", "ReflexivePony2"), ]) Pony = project_state.apps.get_model(app_label, "ReflexivePony2") pony = Pony.objects.create() pony.ponies.add(pony) def test_rename_model_with_m2m(self): app_label = "test_rename_model_with_m2m" project_state = self.apply_operations(app_label, ProjectState(), operations=[ migrations.CreateModel("Rider", fields=[]), migrations.CreateModel("Pony", fields=[ ("riders", models.ManyToManyField("Rider")), ]), ]) Pony = project_state.apps.get_model(app_label, "Pony") Rider = project_state.apps.get_model(app_label, "Rider") pony = Pony.objects.create() rider = Rider.objects.create() pony.riders.add(rider) project_state = self.apply_operations(app_label, project_state, operations=[ migrations.RenameModel("Pony", "Pony2"), ]) Pony = project_state.apps.get_model(app_label, "Pony2") Rider = project_state.apps.get_model(app_label, "Rider") pony = Pony.objects.create() rider = Rider.objects.create() pony.riders.add(rider) self.assertEqual(Pony.objects.count(), 2) self.assertEqual(Rider.objects.count(), 2) self.assertEqual(Pony._meta.get_field('riders').rel.through.objects.count(), 2) def test_rename_m2m_target_model(self): app_label = "test_rename_m2m_target_model" project_state = self.apply_operations(app_label, ProjectState(), operations=[ migrations.CreateModel("Rider", fields=[]), migrations.CreateModel("Pony", fields=[ ("riders", models.ManyToManyField("Rider")), ]), ]) Pony = project_state.apps.get_model(app_label, "Pony") Rider = project_state.apps.get_model(app_label, "Rider") pony = Pony.objects.create() rider = Rider.objects.create() pony.riders.add(rider) project_state = self.apply_operations(app_label, project_state, operations=[ migrations.RenameModel("Rider", "Rider2"), ]) Pony = project_state.apps.get_model(app_label, "Pony") Rider = project_state.apps.get_model(app_label, "Rider2") pony = Pony.objects.create() rider = Rider.objects.create() pony.riders.add(rider) self.assertEqual(Pony.objects.count(), 2) self.assertEqual(Rider.objects.count(), 2) self.assertEqual(Pony._meta.get_field('riders').rel.through.objects.count(), 2) def test_add_field(self): """ Tests the AddField operation. """ # Test the state alteration operation = migrations.AddField( "Pony", "height", models.FloatField(null=True, default=5), ) self.assertEqual(operation.describe(), "Add field height to Pony") project_state, new_state = self.make_test_state("test_adfl", operation) self.assertEqual(len(new_state.models["test_adfl", "pony"].fields), 4) field = [ f for n, f in new_state.models["test_adfl", "pony"].fields if n == "height" ][0] self.assertEqual(field.default, 5) # Test the database alteration self.assertColumnNotExists("test_adfl_pony", "height") with connection.schema_editor() as editor: operation.database_forwards("test_adfl", editor, project_state, new_state) self.assertColumnExists("test_adfl_pony", "height") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_adfl", editor, new_state, project_state) self.assertColumnNotExists("test_adfl_pony", "height") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AddField") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["field", "model_name", "name"]) def test_add_charfield(self): """ Tests the AddField operation on TextField. """ project_state = self.set_up_test_model("test_adchfl") Pony = project_state.apps.get_model("test_adchfl", "Pony") pony = Pony.objects.create(weight=42) new_state = self.apply_operations("test_adchfl", project_state, [ migrations.AddField( "Pony", "text", models.CharField(max_length=10, default="some text"), ), migrations.AddField( "Pony", "empty", models.CharField(max_length=10, default=""), ), # If not properly quoted digits would be interpreted as an int. migrations.AddField( "Pony", "digits", models.CharField(max_length=10, default="42"), ), # Manual quoting is fragile and could trip on quotes. Refs #xyz. migrations.AddField( "Pony", "quotes", models.CharField(max_length=10, default='"\'"'), ), ]) Pony = new_state.apps.get_model("test_adchfl", "Pony") pony = Pony.objects.get(pk=pony.pk) self.assertEqual(pony.text, "some text") self.assertEqual(pony.empty, "") self.assertEqual(pony.digits, "42") self.assertEqual(pony.quotes, '"\'"') def test_add_textfield(self): """ Tests the AddField operation on TextField. """ project_state = self.set_up_test_model("test_adtxtfl") Pony = project_state.apps.get_model("test_adtxtfl", "Pony") pony = Pony.objects.create(weight=42) new_state = self.apply_operations("test_adtxtfl", project_state, [ migrations.AddField( "Pony", "text", models.TextField(default="some text"), ), migrations.AddField( "Pony", "empty", models.TextField(default=""), ), # If not properly quoted digits would be interpreted as an int. migrations.AddField( "Pony", "digits", models.TextField(default="42"), ), # Manual quoting is fragile and could trip on quotes. Refs #xyz. migrations.AddField( "Pony", "quotes", models.TextField(default='"\'"'), ), ]) Pony = new_state.apps.get_model("test_adtxtfl", "Pony") pony = Pony.objects.get(pk=pony.pk) self.assertEqual(pony.text, "some text") self.assertEqual(pony.empty, "") self.assertEqual(pony.digits, "42") self.assertEqual(pony.quotes, '"\'"') def test_add_binaryfield(self): """ Tests the AddField operation on TextField/BinaryField. """ project_state = self.set_up_test_model("test_adbinfl") Pony = project_state.apps.get_model("test_adbinfl", "Pony") pony = Pony.objects.create(weight=42) new_state = self.apply_operations("test_adbinfl", project_state, [ migrations.AddField( "Pony", "blob", models.BinaryField(default=b"some text"), ), migrations.AddField( "Pony", "empty", models.BinaryField(default=b""), ), # If not properly quoted digits would be interpreted as an int. migrations.AddField( "Pony", "digits", models.BinaryField(default=b"42"), ), # Manual quoting is fragile and could trip on quotes. Refs #xyz. migrations.AddField( "Pony", "quotes", models.BinaryField(default=b'"\'"'), ), ]) Pony = new_state.apps.get_model("test_adbinfl", "Pony") pony = Pony.objects.get(pk=pony.pk) # SQLite returns buffer/memoryview, cast to bytes for checking. self.assertEqual(bytes(pony.blob), b"some text") self.assertEqual(bytes(pony.empty), b"") self.assertEqual(bytes(pony.digits), b"42") self.assertEqual(bytes(pony.quotes), b'"\'"') def test_column_name_quoting(self): """ Column names that are SQL keywords shouldn't cause problems when used in migrations (#22168). """ project_state = self.set_up_test_model("test_regr22168") operation = migrations.AddField( "Pony", "order", models.IntegerField(default=0), ) new_state = project_state.clone() operation.state_forwards("test_regr22168", new_state) with connection.schema_editor() as editor: operation.database_forwards("test_regr22168", editor, project_state, new_state) self.assertColumnExists("test_regr22168_pony", "order") def test_add_field_preserve_default(self): """ Tests the AddField operation's state alteration when preserve_default = False. """ project_state = self.set_up_test_model("test_adflpd") # Test the state alteration operation = migrations.AddField( "Pony", "height", models.FloatField(null=True, default=4), preserve_default=False, ) new_state = project_state.clone() operation.state_forwards("test_adflpd", new_state) self.assertEqual(len(new_state.models["test_adflpd", "pony"].fields), 4) field = [ f for n, f in new_state.models["test_adflpd", "pony"].fields if n == "height" ][0] self.assertEqual(field.default, NOT_PROVIDED) # Test the database alteration project_state.apps.get_model("test_adflpd", "pony").objects.create( weight=4, ) self.assertColumnNotExists("test_adflpd_pony", "height") with connection.schema_editor() as editor: operation.database_forwards("test_adflpd", editor, project_state, new_state) self.assertColumnExists("test_adflpd_pony", "height") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AddField") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["field", "model_name", "name", "preserve_default"]) def test_add_field_m2m(self): """ Tests the AddField operation with a ManyToManyField. """ project_state = self.set_up_test_model("test_adflmm", second_model=True) # Test the state alteration operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies")) new_state = project_state.clone() operation.state_forwards("test_adflmm", new_state) self.assertEqual(len(new_state.models["test_adflmm", "pony"].fields), 4) # Test the database alteration self.assertTableNotExists("test_adflmm_pony_stables") with connection.schema_editor() as editor: operation.database_forwards("test_adflmm", editor, project_state, new_state) self.assertTableExists("test_adflmm_pony_stables") self.assertColumnNotExists("test_adflmm_pony", "stables") # Make sure the M2M field actually works with atomic(): Pony = new_state.apps.get_model("test_adflmm", "Pony") p = Pony.objects.create(pink=False, weight=4.55) p.stables.create() self.assertEqual(p.stables.count(), 1) p.stables.all().delete() # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_adflmm", editor, new_state, project_state) self.assertTableNotExists("test_adflmm_pony_stables") def test_alter_field_m2m(self): project_state = self.set_up_test_model("test_alflmm", second_model=True) project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies")) ]) Pony = project_state.apps.get_model("test_alflmm", "Pony") self.assertFalse(Pony._meta.get_field('stables').blank) project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AlterField("Pony", "stables", models.ManyToManyField(to="Stable", related_name="ponies", blank=True)) ]) Pony = project_state.apps.get_model("test_alflmm", "Pony") self.assertTrue(Pony._meta.get_field('stables').blank) def test_repoint_field_m2m(self): project_state = self.set_up_test_model("test_alflmm", second_model=True, third_model=True) project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AddField("Pony", "places", models.ManyToManyField("Stable", related_name="ponies")) ]) Pony = project_state.apps.get_model("test_alflmm", "Pony") project_state = self.apply_operations("test_alflmm", project_state, operations=[ migrations.AlterField("Pony", "places", models.ManyToManyField(to="Van", related_name="ponies")) ]) # Ensure the new field actually works Pony = project_state.apps.get_model("test_alflmm", "Pony") p = Pony.objects.create(pink=False, weight=4.55) p.places.create() self.assertEqual(p.places.count(), 1) p.places.all().delete() def test_remove_field_m2m(self): project_state = self.set_up_test_model("test_rmflmm", second_model=True) project_state = self.apply_operations("test_rmflmm", project_state, operations=[ migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies")) ]) self.assertTableExists("test_rmflmm_pony_stables") with_field_state = project_state.clone() operations = [migrations.RemoveField("Pony", "stables")] project_state = self.apply_operations("test_rmflmm", project_state, operations=operations) self.assertTableNotExists("test_rmflmm_pony_stables") # And test reversal self.unapply_operations("test_rmflmm", with_field_state, operations=operations) self.assertTableExists("test_rmflmm_pony_stables") def test_remove_field_m2m_with_through(self): project_state = self.set_up_test_model("test_rmflmmwt", second_model=True) self.assertTableNotExists("test_rmflmmwt_ponystables") project_state = self.apply_operations("test_rmflmmwt", project_state, operations=[ migrations.CreateModel("PonyStables", fields=[ ("pony", models.ForeignKey('test_rmflmmwt.Pony')), ("stable", models.ForeignKey('test_rmflmmwt.Stable')), ]), migrations.AddField("Pony", "stables", models.ManyToManyField("Stable", related_name="ponies", through='test_rmflmmwt.PonyStables')) ]) self.assertTableExists("test_rmflmmwt_ponystables") operations = [migrations.RemoveField("Pony", "stables")] self.apply_operations("test_rmflmmwt", project_state, operations=operations) def test_remove_field(self): """ Tests the RemoveField operation. """ project_state = self.set_up_test_model("test_rmfl") # Test the state alteration operation = migrations.RemoveField("Pony", "pink") self.assertEqual(operation.describe(), "Remove field pink from Pony") new_state = project_state.clone() operation.state_forwards("test_rmfl", new_state) self.assertEqual(len(new_state.models["test_rmfl", "pony"].fields), 2) # Test the database alteration self.assertColumnExists("test_rmfl_pony", "pink") with connection.schema_editor() as editor: operation.database_forwards("test_rmfl", editor, project_state, new_state) self.assertColumnNotExists("test_rmfl_pony", "pink") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_rmfl", editor, new_state, project_state) self.assertColumnExists("test_rmfl_pony", "pink") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RemoveField") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'model_name': "Pony", 'name': 'pink'}) def test_remove_fk(self): """ Tests the RemoveField operation on a foreign key. """ project_state = self.set_up_test_model("test_rfk", related_model=True) self.assertColumnExists("test_rfk_rider", "pony_id") operation = migrations.RemoveField("Rider", "pony") new_state = project_state.clone() operation.state_forwards("test_rfk", new_state) with connection.schema_editor() as editor: operation.database_forwards("test_rfk", editor, project_state, new_state) self.assertColumnNotExists("test_rfk_rider", "pony_id") with connection.schema_editor() as editor: operation.database_backwards("test_rfk", editor, new_state, project_state) self.assertColumnExists("test_rfk_rider", "pony_id") def test_alter_model_table(self): """ Tests the AlterModelTable operation. """ project_state = self.set_up_test_model("test_almota") # Test the state alteration operation = migrations.AlterModelTable("Pony", "test_almota_pony_2") self.assertEqual(operation.describe(), "Rename table for Pony to test_almota_pony_2") new_state = project_state.clone() operation.state_forwards("test_almota", new_state) self.assertEqual(new_state.models["test_almota", "pony"].options["db_table"], "test_almota_pony_2") # Test the database alteration self.assertTableExists("test_almota_pony") self.assertTableNotExists("test_almota_pony_2") with connection.schema_editor() as editor: operation.database_forwards("test_almota", editor, project_state, new_state) self.assertTableNotExists("test_almota_pony") self.assertTableExists("test_almota_pony_2") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_almota", editor, new_state, project_state) self.assertTableExists("test_almota_pony") self.assertTableNotExists("test_almota_pony_2") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterModelTable") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'name': "Pony", 'table': "test_almota_pony_2"}) def test_alter_model_table_noop(self): """ Tests the AlterModelTable operation if the table name is not changed. """ project_state = self.set_up_test_model("test_almota") # Test the state alteration operation = migrations.AlterModelTable("Pony", "test_almota_pony") new_state = project_state.clone() operation.state_forwards("test_almota", new_state) self.assertEqual(new_state.models["test_almota", "pony"].options["db_table"], "test_almota_pony") # Test the database alteration self.assertTableExists("test_almota_pony") with connection.schema_editor() as editor: operation.database_forwards("test_almota", editor, project_state, new_state) self.assertTableExists("test_almota_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_almota", editor, new_state, project_state) self.assertTableExists("test_almota_pony") def test_alter_model_table_m2m(self): """ AlterModelTable should rename auto-generated M2M tables. """ app_label = "test_talflmltlm2m" pony_db_table = 'pony_foo' project_state = self.set_up_test_model(app_label, second_model=True, db_table=pony_db_table) # Add the M2M field first_state = project_state.clone() operation = migrations.AddField("Pony", "stables", models.ManyToManyField("Stable")) operation.state_forwards(app_label, first_state) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, project_state, first_state) original_m2m_table = "%s_%s" % (pony_db_table, "stables") new_m2m_table = "%s_%s" % (app_label, "pony_stables") self.assertTableExists(original_m2m_table) self.assertTableNotExists(new_m2m_table) # Rename the Pony db_table which should also rename the m2m table. second_state = first_state.clone() operation = migrations.AlterModelTable(name='pony', table=None) operation.state_forwards(app_label, second_state) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, first_state, second_state) self.assertTableExists(new_m2m_table) self.assertTableNotExists(original_m2m_table) # And test reversal with connection.schema_editor() as editor: operation.database_backwards(app_label, editor, second_state, first_state) self.assertTableExists(original_m2m_table) self.assertTableNotExists(new_m2m_table) def test_alter_field(self): """ Tests the AlterField operation. """ project_state = self.set_up_test_model("test_alfl") # Test the state alteration operation = migrations.AlterField("Pony", "pink", models.IntegerField(null=True)) self.assertEqual(operation.describe(), "Alter field pink on Pony") new_state = project_state.clone() operation.state_forwards("test_alfl", new_state) self.assertEqual(project_state.models["test_alfl", "pony"].get_field_by_name("pink").null, False) self.assertEqual(new_state.models["test_alfl", "pony"].get_field_by_name("pink").null, True) # Test the database alteration self.assertColumnNotNull("test_alfl_pony", "pink") with connection.schema_editor() as editor: operation.database_forwards("test_alfl", editor, project_state, new_state) self.assertColumnNull("test_alfl_pony", "pink") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alfl", editor, new_state, project_state) self.assertColumnNotNull("test_alfl_pony", "pink") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterField") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["field", "model_name", "name"]) def test_alter_field_pk(self): """ Tests the AlterField operation on primary keys (for things like PostgreSQL's SERIAL weirdness) """ project_state = self.set_up_test_model("test_alflpk") # Test the state alteration operation = migrations.AlterField("Pony", "id", models.IntegerField(primary_key=True)) new_state = project_state.clone() operation.state_forwards("test_alflpk", new_state) self.assertIsInstance(project_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.AutoField) self.assertIsInstance(new_state.models["test_alflpk", "pony"].get_field_by_name("id"), models.IntegerField) # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alflpk", editor, project_state, new_state) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alflpk", editor, new_state, project_state) @unittest.skipUnless(connection.features.supports_foreign_keys, "No FK support") def test_alter_field_pk_fk(self): """ Tests the AlterField operation on primary keys changes any FKs pointing to it. """ project_state = self.set_up_test_model("test_alflpkfk", related_model=True) # Test the state alteration operation = migrations.AlterField("Pony", "id", models.FloatField(primary_key=True)) new_state = project_state.clone() operation.state_forwards("test_alflpkfk", new_state) self.assertIsInstance(project_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.AutoField) self.assertIsInstance(new_state.models["test_alflpkfk", "pony"].get_field_by_name("id"), models.FloatField) def assertIdTypeEqualsFkType(): with connection.cursor() as cursor: id_type, id_null = [ (c.type_code, c.null_ok) for c in connection.introspection.get_table_description(cursor, "test_alflpkfk_pony") if c.name == "id" ][0] fk_type, fk_null = [ (c.type_code, c.null_ok) for c in connection.introspection.get_table_description(cursor, "test_alflpkfk_rider") if c.name == "pony_id" ][0] self.assertEqual(id_type, fk_type) self.assertEqual(id_null, fk_null) assertIdTypeEqualsFkType() # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alflpkfk", editor, project_state, new_state) assertIdTypeEqualsFkType() # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alflpkfk", editor, new_state, project_state) assertIdTypeEqualsFkType() def test_rename_field(self): """ Tests the RenameField operation. """ project_state = self.set_up_test_model("test_rnfl", unique_together=True, index_together=True) # Test the state alteration operation = migrations.RenameField("Pony", "pink", "blue") self.assertEqual(operation.describe(), "Rename field pink on Pony to blue") new_state = project_state.clone() operation.state_forwards("test_rnfl", new_state) self.assertIn("blue", [n for n, f in new_state.models["test_rnfl", "pony"].fields]) self.assertNotIn("pink", [n for n, f in new_state.models["test_rnfl", "pony"].fields]) # Make sure the unique_together has the renamed column too self.assertIn("blue", new_state.models["test_rnfl", "pony"].options['unique_together'][0]) self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].options['unique_together'][0]) # Make sure the index_together has the renamed column too self.assertIn("blue", new_state.models["test_rnfl", "pony"].options['index_together'][0]) self.assertNotIn("pink", new_state.models["test_rnfl", "pony"].options['index_together'][0]) # Test the database alteration self.assertColumnExists("test_rnfl_pony", "pink") self.assertColumnNotExists("test_rnfl_pony", "blue") with connection.schema_editor() as editor: operation.database_forwards("test_rnfl", editor, project_state, new_state) self.assertColumnExists("test_rnfl_pony", "blue") self.assertColumnNotExists("test_rnfl_pony", "pink") # Ensure the unique constraint has been ported over with connection.cursor() as cursor: cursor.execute("INSERT INTO test_rnfl_pony (blue, weight) VALUES (1, 1)") with self.assertRaises(IntegrityError): with atomic(): cursor.execute("INSERT INTO test_rnfl_pony (blue, weight) VALUES (1, 1)") cursor.execute("DELETE FROM test_rnfl_pony") # Ensure the index constraint has been ported over self.assertIndexExists("test_rnfl_pony", ["weight", "blue"]) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_rnfl", editor, new_state, project_state) self.assertColumnExists("test_rnfl_pony", "pink") self.assertColumnNotExists("test_rnfl_pony", "blue") # Ensure the index constraint has been reset self.assertIndexExists("test_rnfl_pony", ["weight", "pink"]) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RenameField") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'model_name': "Pony", 'old_name': "pink", 'new_name': "blue"}) def test_alter_unique_together(self): """ Tests the AlterUniqueTogether operation. """ project_state = self.set_up_test_model("test_alunto") # Test the state alteration operation = migrations.AlterUniqueTogether("Pony", [("pink", "weight")]) self.assertEqual(operation.describe(), "Alter unique_together for Pony (1 constraint(s))") new_state = project_state.clone() operation.state_forwards("test_alunto", new_state) self.assertEqual(len(project_state.models["test_alunto", "pony"].options.get("unique_together", set())), 0) self.assertEqual(len(new_state.models["test_alunto", "pony"].options.get("unique_together", set())), 1) # Make sure we can insert duplicate rows with connection.cursor() as cursor: cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)") cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)") cursor.execute("DELETE FROM test_alunto_pony") # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alunto", editor, project_state, new_state) cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)") with self.assertRaises(IntegrityError): with atomic(): cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)") cursor.execute("DELETE FROM test_alunto_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alunto", editor, new_state, project_state) cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)") cursor.execute("INSERT INTO test_alunto_pony (pink, weight) VALUES (1, 1)") cursor.execute("DELETE FROM test_alunto_pony") # Test flat unique_together operation = migrations.AlterUniqueTogether("Pony", ("pink", "weight")) operation.state_forwards("test_alunto", new_state) self.assertEqual(len(new_state.models["test_alunto", "pony"].options.get("unique_together", set())), 1) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterUniqueTogether") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'name': "Pony", 'unique_together': {("pink", "weight")}}) def test_alter_unique_together_remove(self): operation = migrations.AlterUniqueTogether("Pony", None) self.assertEqual(operation.describe(), "Alter unique_together for Pony (0 constraint(s))") def test_alter_index_together(self): """ Tests the AlterIndexTogether operation. """ project_state = self.set_up_test_model("test_alinto") # Test the state alteration operation = migrations.AlterIndexTogether("Pony", [("pink", "weight")]) self.assertEqual(operation.describe(), "Alter index_together for Pony (1 constraint(s))") new_state = project_state.clone() operation.state_forwards("test_alinto", new_state) self.assertEqual(len(project_state.models["test_alinto", "pony"].options.get("index_together", set())), 0) self.assertEqual(len(new_state.models["test_alinto", "pony"].options.get("index_together", set())), 1) # Make sure there's no matching index self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"]) # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alinto", editor, project_state, new_state) self.assertIndexExists("test_alinto_pony", ["pink", "weight"]) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alinto", editor, new_state, project_state) self.assertIndexNotExists("test_alinto_pony", ["pink", "weight"]) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterIndexTogether") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'name': "Pony", 'index_together': {("pink", "weight")}}) def test_alter_index_together_remove(self): operation = migrations.AlterIndexTogether("Pony", None) self.assertEqual(operation.describe(), "Alter index_together for Pony (0 constraint(s))") def test_alter_model_options(self): """ Tests the AlterModelOptions operation. """ project_state = self.set_up_test_model("test_almoop") # Test the state alteration (no DB alteration to test) operation = migrations.AlterModelOptions("Pony", {"permissions": [("can_groom", "Can groom")]}) self.assertEqual(operation.describe(), "Change Meta options on Pony") new_state = project_state.clone() operation.state_forwards("test_almoop", new_state) self.assertEqual(len(project_state.models["test_almoop", "pony"].options.get("permissions", [])), 0) self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 1) self.assertEqual(new_state.models["test_almoop", "pony"].options["permissions"][0][0], "can_groom") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterModelOptions") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'name': "Pony", 'options': {"permissions": [("can_groom", "Can groom")]}}) def test_alter_model_options_emptying(self): """ Tests that the AlterModelOptions operation removes keys from the dict (#23121) """ project_state = self.set_up_test_model("test_almoop", options=True) # Test the state alteration (no DB alteration to test) operation = migrations.AlterModelOptions("Pony", {}) self.assertEqual(operation.describe(), "Change Meta options on Pony") new_state = project_state.clone() operation.state_forwards("test_almoop", new_state) self.assertEqual(len(project_state.models["test_almoop", "pony"].options.get("permissions", [])), 1) self.assertEqual(len(new_state.models["test_almoop", "pony"].options.get("permissions", [])), 0) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterModelOptions") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'name': "Pony", 'options': {}}) def test_alter_order_with_respect_to(self): """ Tests the AlterOrderWithRespectTo operation. """ project_state = self.set_up_test_model("test_alorwrtto", related_model=True) # Test the state alteration operation = migrations.AlterOrderWithRespectTo("Rider", "pony") self.assertEqual(operation.describe(), "Set order_with_respect_to on Rider to pony") new_state = project_state.clone() operation.state_forwards("test_alorwrtto", new_state) self.assertEqual(project_state.models["test_alorwrtto", "rider"].options.get("order_with_respect_to", None), None) self.assertEqual(new_state.models["test_alorwrtto", "rider"].options.get("order_with_respect_to", None), "pony") # Make sure there's no matching index self.assertColumnNotExists("test_alorwrtto_rider", "_order") # Create some rows before alteration rendered_state = project_state.apps pony = rendered_state.get_model("test_alorwrtto", "Pony").objects.create(weight=50) rendered_state.get_model("test_alorwrtto", "Rider").objects.create(pony=pony, friend_id=1) rendered_state.get_model("test_alorwrtto", "Rider").objects.create(pony=pony, friend_id=2) # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_alorwrtto", editor, project_state, new_state) self.assertColumnExists("test_alorwrtto_rider", "_order") # Check for correct value in rows updated_riders = new_state.apps.get_model("test_alorwrtto", "Rider").objects.all() self.assertEqual(updated_riders[0]._order, 0) self.assertEqual(updated_riders[1]._order, 0) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_alorwrtto", editor, new_state, project_state) self.assertColumnNotExists("test_alorwrtto_rider", "_order") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "AlterOrderWithRespectTo") self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'name': "Rider", 'order_with_respect_to': "pony"}) def test_alter_model_managers(self): """ Tests that the managers on a model are set. """ project_state = self.set_up_test_model("test_almoma") # Test the state alteration operation = migrations.AlterModelManagers( "Pony", managers=[ ("food_qs", FoodQuerySet.as_manager()), ("food_mgr", FoodManager("a", "b")), ("food_mgr_kwargs", FoodManager("x", "y", 3, 4)), ] ) self.assertEqual(operation.describe(), "Change managers on Pony") managers = project_state.models["test_almoma", "pony"].managers self.assertEqual(managers, []) new_state = project_state.clone() operation.state_forwards("test_almoma", new_state) self.assertIn(("test_almoma", "pony"), new_state.models) managers = new_state.models["test_almoma", "pony"].managers self.assertEqual(managers[0][0], "food_qs") self.assertIsInstance(managers[0][1], models.Manager) self.assertEqual(managers[1][0], "food_mgr") self.assertIsInstance(managers[1][1], FoodManager) self.assertEqual(managers[1][1].args, ("a", "b", 1, 2)) self.assertEqual(managers[2][0], "food_mgr_kwargs") self.assertIsInstance(managers[2][1], FoodManager) self.assertEqual(managers[2][1].args, ("x", "y", 3, 4)) def test_alter_model_managers_emptying(self): """ Tests that the managers on a model are set. """ project_state = self.set_up_test_model("test_almomae", manager_model=True) # Test the state alteration operation = migrations.AlterModelManagers("Food", managers=[]) self.assertEqual(operation.describe(), "Change managers on Food") self.assertIn(("test_almomae", "food"), project_state.models) managers = project_state.models["test_almomae", "food"].managers self.assertEqual(managers[0][0], "food_qs") self.assertIsInstance(managers[0][1], models.Manager) self.assertEqual(managers[1][0], "food_mgr") self.assertIsInstance(managers[1][1], FoodManager) self.assertEqual(managers[1][1].args, ("a", "b", 1, 2)) self.assertEqual(managers[2][0], "food_mgr_kwargs") self.assertIsInstance(managers[2][1], FoodManager) self.assertEqual(managers[2][1].args, ("x", "y", 3, 4)) new_state = project_state.clone() operation.state_forwards("test_almomae", new_state) managers = new_state.models["test_almomae", "food"].managers self.assertEqual(managers, []) def test_alter_fk(self): """ Tests that creating and then altering an FK works correctly and deals with the pending SQL (#23091) """ project_state = self.set_up_test_model("test_alfk") # Test adding and then altering the FK in one go create_operation = migrations.CreateModel( name="Rider", fields=[ ("id", models.AutoField(primary_key=True)), ("pony", models.ForeignKey(to="Pony")), ], ) create_state = project_state.clone() create_operation.state_forwards("test_alfk", create_state) alter_operation = migrations.AlterField( model_name='Rider', name='pony', field=models.ForeignKey(editable=False, to="Pony"), ) alter_state = create_state.clone() alter_operation.state_forwards("test_alfk", alter_state) with connection.schema_editor() as editor: create_operation.database_forwards("test_alfk", editor, project_state, create_state) alter_operation.database_forwards("test_alfk", editor, create_state, alter_state) def test_alter_fk_non_fk(self): """ Tests that altering an FK to a non-FK works (#23244) """ # Test the state alteration operation = migrations.AlterField( model_name="Rider", name="pony", field=models.FloatField(), ) project_state, new_state = self.make_test_state("test_afknfk", operation, related_model=True) # Test the database alteration self.assertColumnExists("test_afknfk_rider", "pony_id") self.assertColumnNotExists("test_afknfk_rider", "pony") with connection.schema_editor() as editor: operation.database_forwards("test_afknfk", editor, project_state, new_state) self.assertColumnExists("test_afknfk_rider", "pony") self.assertColumnNotExists("test_afknfk_rider", "pony_id") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_afknfk", editor, new_state, project_state) self.assertColumnExists("test_afknfk_rider", "pony_id") self.assertColumnNotExists("test_afknfk_rider", "pony") @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse") def test_run_sql(self): """ Tests the RunSQL operation. """ project_state = self.set_up_test_model("test_runsql") # Create the operation operation = migrations.RunSQL( # Use a multi-line string with a comment to test splitting on SQLite and MySQL respectively "CREATE TABLE i_love_ponies (id int, special_thing varchar(15));\n" "INSERT INTO i_love_ponies (id, special_thing) VALUES (1, 'i love ponies'); -- this is magic!\n" "INSERT INTO i_love_ponies (id, special_thing) VALUES (2, 'i love django');\n" "UPDATE i_love_ponies SET special_thing = 'Ponies' WHERE special_thing LIKE '%%ponies';" "UPDATE i_love_ponies SET special_thing = 'Django' WHERE special_thing LIKE '%django';", # Run delete queries to test for parameter substitution failure # reported in #23426 "DELETE FROM i_love_ponies WHERE special_thing LIKE '%Django%';" "DELETE FROM i_love_ponies WHERE special_thing LIKE '%%Ponies%%';" "DROP TABLE i_love_ponies", state_operations=[migrations.CreateModel("SomethingElse", [("id", models.AutoField(primary_key=True))])], ) self.assertEqual(operation.describe(), "Raw SQL operation") # Test the state alteration new_state = project_state.clone() operation.state_forwards("test_runsql", new_state) self.assertEqual(len(new_state.models["test_runsql", "somethingelse"].fields), 1) # Make sure there's no table self.assertTableNotExists("i_love_ponies") # Test SQL collection with connection.schema_editor(collect_sql=True) as editor: operation.database_forwards("test_runsql", editor, project_state, new_state) self.assertIn("LIKE '%%ponies';", "\n".join(editor.collected_sql)) operation.database_backwards("test_runsql", editor, project_state, new_state) self.assertIn("LIKE '%%Ponies%%';", "\n".join(editor.collected_sql)) # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_runsql", editor, project_state, new_state) self.assertTableExists("i_love_ponies") # Make sure all the SQL was processed with connection.cursor() as cursor: cursor.execute("SELECT COUNT(*) FROM i_love_ponies") self.assertEqual(cursor.fetchall()[0][0], 2) cursor.execute("SELECT COUNT(*) FROM i_love_ponies WHERE special_thing = 'Django'") self.assertEqual(cursor.fetchall()[0][0], 1) cursor.execute("SELECT COUNT(*) FROM i_love_ponies WHERE special_thing = 'Ponies'") self.assertEqual(cursor.fetchall()[0][0], 1) # And test reversal self.assertTrue(operation.reversible) with connection.schema_editor() as editor: operation.database_backwards("test_runsql", editor, new_state, project_state) self.assertTableNotExists("i_love_ponies") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RunSQL") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["reverse_sql", "sql", "state_operations"]) def test_run_sql_params(self): """ #23426 - RunSQL should accept parameters. """ project_state = self.set_up_test_model("test_runsql") # Create the operation operation = migrations.RunSQL( ["CREATE TABLE i_love_ponies (id int, special_thing varchar(15));"], ["DROP TABLE i_love_ponies"], ) param_operation = migrations.RunSQL( # forwards ( "INSERT INTO i_love_ponies (id, special_thing) VALUES (1, 'Django');", ["INSERT INTO i_love_ponies (id, special_thing) VALUES (2, %s);", ['Ponies']], ("INSERT INTO i_love_ponies (id, special_thing) VALUES (%s, %s);", (3, 'Python',)), ), # backwards [ "DELETE FROM i_love_ponies WHERE special_thing = 'Django';", ["DELETE FROM i_love_ponies WHERE special_thing = 'Ponies';", None], ("DELETE FROM i_love_ponies WHERE id = %s OR special_thing = %s;", [3, 'Python']), ] ) # Make sure there's no table self.assertTableNotExists("i_love_ponies") new_state = project_state.clone() # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_runsql", editor, project_state, new_state) # Test parameter passing with connection.schema_editor() as editor: param_operation.database_forwards("test_runsql", editor, project_state, new_state) # Make sure all the SQL was processed with connection.cursor() as cursor: cursor.execute("SELECT COUNT(*) FROM i_love_ponies") self.assertEqual(cursor.fetchall()[0][0], 3) with connection.schema_editor() as editor: param_operation.database_backwards("test_runsql", editor, new_state, project_state) with connection.cursor() as cursor: cursor.execute("SELECT COUNT(*) FROM i_love_ponies") self.assertEqual(cursor.fetchall()[0][0], 0) # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_runsql", editor, new_state, project_state) self.assertTableNotExists("i_love_ponies") def test_run_sql_params_invalid(self): """ #23426 - RunSQL should fail when a list of statements with an incorrect number of tuples is given. """ project_state = self.set_up_test_model("test_runsql") new_state = project_state.clone() operation = migrations.RunSQL( # forwards [ ["INSERT INTO foo (bar) VALUES ('buz');"] ], # backwards ( ("DELETE FROM foo WHERE bar = 'buz';", 'invalid', 'parameter count'), ), ) with connection.schema_editor() as editor: six.assertRaisesRegex(self, ValueError, "Expected a 2-tuple but got 1", operation.database_forwards, "test_runsql", editor, project_state, new_state) with connection.schema_editor() as editor: six.assertRaisesRegex(self, ValueError, "Expected a 2-tuple but got 3", operation.database_backwards, "test_runsql", editor, new_state, project_state) def test_run_sql_noop(self): """ #24098 - Tests no-op RunSQL operations. """ operation = migrations.RunSQL(migrations.RunSQL.noop, migrations.RunSQL.noop) with connection.schema_editor() as editor: operation.database_forwards("test_runsql", editor, None, None) operation.database_backwards("test_runsql", editor, None, None) def test_run_python(self): """ Tests the RunPython operation """ project_state = self.set_up_test_model("test_runpython", mti_model=True) # Create the operation def inner_method(models, schema_editor): Pony = models.get_model("test_runpython", "Pony") Pony.objects.create(pink=1, weight=3.55) Pony.objects.create(weight=5) def inner_method_reverse(models, schema_editor): Pony = models.get_model("test_runpython", "Pony") Pony.objects.filter(pink=1, weight=3.55).delete() Pony.objects.filter(weight=5).delete() operation = migrations.RunPython(inner_method, reverse_code=inner_method_reverse) self.assertEqual(operation.describe(), "Raw Python operation") # Test the state alteration does nothing new_state = project_state.clone() operation.state_forwards("test_runpython", new_state) self.assertEqual(new_state, project_state) # Test the database alteration self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 2) # Now test reversal self.assertTrue(operation.reversible) with connection.schema_editor() as editor: operation.database_backwards("test_runpython", editor, project_state, new_state) self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 0) # Now test we can't use a string with self.assertRaises(ValueError): migrations.RunPython("print 'ahahaha'") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RunPython") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["code", "reverse_code"]) # Also test reversal fails, with an operation identical to above but without reverse_code set no_reverse_operation = migrations.RunPython(inner_method) self.assertFalse(no_reverse_operation.reversible) with connection.schema_editor() as editor: no_reverse_operation.database_forwards("test_runpython", editor, project_state, new_state) with self.assertRaises(NotImplementedError): no_reverse_operation.database_backwards("test_runpython", editor, new_state, project_state) self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 2) def create_ponies(models, schema_editor): Pony = models.get_model("test_runpython", "Pony") pony1 = Pony.objects.create(pink=1, weight=3.55) self.assertIsNot(pony1.pk, None) pony2 = Pony.objects.create(weight=5) self.assertIsNot(pony2.pk, None) self.assertNotEqual(pony1.pk, pony2.pk) operation = migrations.RunPython(create_ponies) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 4) # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "RunPython") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["code"]) def create_shetlandponies(models, schema_editor): ShetlandPony = models.get_model("test_runpython", "ShetlandPony") pony1 = ShetlandPony.objects.create(weight=4.0) self.assertIsNot(pony1.pk, None) pony2 = ShetlandPony.objects.create(weight=5.0) self.assertIsNot(pony2.pk, None) self.assertNotEqual(pony1.pk, pony2.pk) operation = migrations.RunPython(create_shetlandponies) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) self.assertEqual(project_state.apps.get_model("test_runpython", "Pony").objects.count(), 6) self.assertEqual(project_state.apps.get_model("test_runpython", "ShetlandPony").objects.count(), 2) def test_run_python_atomic(self): """ Tests the RunPython operation correctly handles the "atomic" keyword """ project_state = self.set_up_test_model("test_runpythonatomic", mti_model=True) def inner_method(models, schema_editor): Pony = models.get_model("test_runpythonatomic", "Pony") Pony.objects.create(pink=1, weight=3.55) raise ValueError("Adrian hates ponies.") atomic_migration = Migration("test", "test_runpythonatomic") atomic_migration.operations = [migrations.RunPython(inner_method)] non_atomic_migration = Migration("test", "test_runpythonatomic") non_atomic_migration.operations = [migrations.RunPython(inner_method, atomic=False)] # If we're a fully-transactional database, both versions should rollback if connection.features.can_rollback_ddl: self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: atomic_migration.apply(project_state, editor) self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: non_atomic_migration.apply(project_state, editor) self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) # Otherwise, the non-atomic operation should leave a row there else: self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: atomic_migration.apply(project_state, editor) self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 0) with self.assertRaises(ValueError): with connection.schema_editor() as editor: non_atomic_migration.apply(project_state, editor) self.assertEqual(project_state.apps.get_model("test_runpythonatomic", "Pony").objects.count(), 1) # And deconstruction definition = non_atomic_migration.operations[0].deconstruct() self.assertEqual(definition[0], "RunPython") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["atomic", "code"]) def test_run_python_related_assignment(self): """ #24282 - Tests that model changes to a FK reverse side update the model on the FK side as well. """ def inner_method(models, schema_editor): Author = models.get_model("test_authors", "Author") Book = models.get_model("test_books", "Book") author = Author.objects.create(name="Hemingway") Book.objects.create(title="Old Man and The Sea", author=author) create_author = migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ], options={}, ) create_book = migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("title", models.CharField(max_length=100)), ("author", models.ForeignKey("test_authors.Author")) ], options={}, ) add_hometown = migrations.AddField( "Author", "hometown", models.CharField(max_length=100), ) create_old_man = migrations.RunPython(inner_method, inner_method) project_state = ProjectState() new_state = project_state.clone() with connection.schema_editor() as editor: create_author.state_forwards("test_authors", new_state) create_author.database_forwards("test_authors", editor, project_state, new_state) project_state = new_state new_state = new_state.clone() with connection.schema_editor() as editor: create_book.state_forwards("test_books", new_state) create_book.database_forwards("test_books", editor, project_state, new_state) project_state = new_state new_state = new_state.clone() with connection.schema_editor() as editor: add_hometown.state_forwards("test_authors", new_state) add_hometown.database_forwards("test_authors", editor, project_state, new_state) project_state = new_state new_state = new_state.clone() with connection.schema_editor() as editor: create_old_man.state_forwards("test_books", new_state) create_old_man.database_forwards("test_books", editor, project_state, new_state) def test_run_python_noop(self): """ #24098 - Tests no-op RunPython operations. """ project_state = ProjectState() new_state = project_state.clone() operation = migrations.RunPython(migrations.RunPython.noop, migrations.RunPython.noop) with connection.schema_editor() as editor: operation.database_forwards("test_runpython", editor, project_state, new_state) operation.database_backwards("test_runpython", editor, new_state, project_state) @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse") def test_separate_database_and_state(self): """ Tests the SeparateDatabaseAndState operation. """ project_state = self.set_up_test_model("test_separatedatabaseandstate") # Create the operation database_operation = migrations.RunSQL( "CREATE TABLE i_love_ponies (id int, special_thing int);", "DROP TABLE i_love_ponies;" ) state_operation = migrations.CreateModel("SomethingElse", [("id", models.AutoField(primary_key=True))]) operation = migrations.SeparateDatabaseAndState( state_operations=[state_operation], database_operations=[database_operation] ) self.assertEqual(operation.describe(), "Custom state/database change combination") # Test the state alteration new_state = project_state.clone() operation.state_forwards("test_separatedatabaseandstate", new_state) self.assertEqual(len(new_state.models["test_separatedatabaseandstate", "somethingelse"].fields), 1) # Make sure there's no table self.assertTableNotExists("i_love_ponies") # Test the database alteration with connection.schema_editor() as editor: operation.database_forwards("test_separatedatabaseandstate", editor, project_state, new_state) self.assertTableExists("i_love_ponies") # And test reversal self.assertTrue(operation.reversible) with connection.schema_editor() as editor: operation.database_backwards("test_separatedatabaseandstate", editor, new_state, project_state) self.assertTableNotExists("i_love_ponies") # And deconstruction definition = operation.deconstruct() self.assertEqual(definition[0], "SeparateDatabaseAndState") self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["database_operations", "state_operations"]) class SwappableOperationTests(OperationTestBase): """ Tests that key operations ignore swappable models (we don't want to replicate all of them here, as the functionality is in a common base class anyway) """ available_apps = [ "migrations", "django.contrib.auth", "django.contrib.contenttypes", ] @override_settings(TEST_SWAP_MODEL="migrations.SomeFakeModel") def test_create_ignore_swapped(self): """ Tests that the CreateTable operation ignores swapped models. """ operation = migrations.CreateModel( "Pony", [ ("id", models.AutoField(primary_key=True)), ("pink", models.IntegerField(default=1)), ], options={ "swappable": "TEST_SWAP_MODEL", }, ) # Test the state alteration (it should still be there!) project_state = ProjectState() new_state = project_state.clone() operation.state_forwards("test_crigsw", new_state) self.assertEqual(new_state.models["test_crigsw", "pony"].name, "Pony") self.assertEqual(len(new_state.models["test_crigsw", "pony"].fields), 2) # Test the database alteration self.assertTableNotExists("test_crigsw_pony") with connection.schema_editor() as editor: operation.database_forwards("test_crigsw", editor, project_state, new_state) self.assertTableNotExists("test_crigsw_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_crigsw", editor, new_state, project_state) self.assertTableNotExists("test_crigsw_pony") @override_settings(TEST_SWAP_MODEL="migrations.SomeFakeModel") def test_delete_ignore_swapped(self): """ Tests the DeleteModel operation ignores swapped models. """ operation = migrations.DeleteModel("Pony") project_state, new_state = self.make_test_state("test_dligsw", operation) # Test the database alteration self.assertTableNotExists("test_dligsw_pony") with connection.schema_editor() as editor: operation.database_forwards("test_dligsw", editor, project_state, new_state) self.assertTableNotExists("test_dligsw_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_dligsw", editor, new_state, project_state) self.assertTableNotExists("test_dligsw_pony") @override_settings(TEST_SWAP_MODEL="migrations.SomeFakeModel") def test_add_field_ignore_swapped(self): """ Tests the AddField operation. """ # Test the state alteration operation = migrations.AddField( "Pony", "height", models.FloatField(null=True, default=5), ) project_state, new_state = self.make_test_state("test_adfligsw", operation) # Test the database alteration self.assertTableNotExists("test_adfligsw_pony") with connection.schema_editor() as editor: operation.database_forwards("test_adfligsw", editor, project_state, new_state) self.assertTableNotExists("test_adfligsw_pony") # And test reversal with connection.schema_editor() as editor: operation.database_backwards("test_adfligsw", editor, new_state, project_state) self.assertTableNotExists("test_adfligsw_pony") Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/0000775000175000017500000000000012625116243026753 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app2/0000775000175000017500000000000012625116243027615 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app2/2_auto.py0000664000175000017500000000040612625116214031356 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("app2", "1_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app2/1_squashed_2.py0000664000175000017500000000052612625116214032446 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): replaces = [ ("app2", "1_auto"), ("app2", "2_auto"), ] dependencies = [("app1", "1_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app2/__init__.py0000664000175000017500000000000012625116214031712 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app2/1_auto.py0000664000175000017500000000040612625116214031355 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("app1", "1_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/0000775000175000017500000000000012625116243027614 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/2_auto.py0000664000175000017500000000040612625116214031355 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("app1", "1_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/2_squashed_3.py0000664000175000017500000000055212625116214032446 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): replaces = [ ("app1", "2_auto"), ("app1", "3_auto"), ] dependencies = [("app1", "1_auto"), ("app2", "2_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/__init__.py0000664000175000017500000000000012625116214031711 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/1_auto.py0000664000175000017500000000033512625116214031355 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/4_auto.py0000664000175000017500000000040612625116214031357 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("app1", "3_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/app1/3_auto.py0000664000175000017500000000043212625116214031355 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("app1", "2_auto"), ("app2", "2_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex_multi_apps/__init__.py0000664000175000017500000000000012625116214031050 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_optimizer.py0000664000175000017500000003660712625116214021324 0ustar timtim00000000000000# -*- coding: utf-8 -*- from django.db import migrations, models from django.db.migrations.optimizer import MigrationOptimizer from django.test import TestCase from .models import CustomModelBase, EmptyManager class OptimizerTests(TestCase): """ Tests the migration autodetector. """ def optimize(self, operations): """ Handy shortcut for getting results + number of loops """ optimizer = MigrationOptimizer() return optimizer.optimize(operations), optimizer._iterations def assertOptimizesTo(self, operations, expected, exact=None, less_than=None): result, iterations = self.optimize(operations) result = [repr(f.deconstruct()) for f in result] expected = [repr(f.deconstruct()) for f in expected] self.assertEqual(expected, result) if exact is not None and iterations != exact: raise self.failureException("Optimization did not take exactly %s iterations (it took %s)" % (exact, iterations)) if less_than is not None and iterations >= less_than: raise self.failureException("Optimization did not take less than %s iterations (it took %s)" % (less_than, iterations)) def test_single(self): """ Tests that the optimizer does nothing on a single operation, and that it does it in just one pass. """ self.assertOptimizesTo( [migrations.DeleteModel("Foo")], [migrations.DeleteModel("Foo")], exact=1, ) def test_create_delete_model(self): """ CreateModel and DeleteModel should collapse into nothing. """ self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.DeleteModel("Foo"), ], [], ) def test_create_rename_model(self): """ CreateModel should absorb RenameModels. """ managers = [('objects', EmptyManager())] self.assertOptimizesTo( [ migrations.CreateModel( name="Foo", fields=[("name", models.CharField(max_length=255))], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), migrations.RenameModel("Foo", "Bar"), ], [ migrations.CreateModel( "Bar", [("name", models.CharField(max_length=255))], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ) ], ) def test_rename_model_self(self): """ RenameModels should absorb themselves. """ self.assertOptimizesTo( [ migrations.RenameModel("Foo", "Baa"), migrations.RenameModel("Baa", "Bar"), ], [ migrations.RenameModel("Foo", "Bar"), ], ) def test_create_alter_delete_model(self): """ CreateModel, AlterModelTable, AlterUniqueTogether, and DeleteModel should collapse into nothing. """ self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.AlterModelTable("Foo", "woohoo"), migrations.AlterUniqueTogether("Foo", [["a", "b"]]), migrations.DeleteModel("Foo"), ], [], ) def test_optimize_through_create(self): """ We should be able to optimize away create/delete through a create or delete of a different model, but only if the create operation does not mention the model at all. """ # These should work self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())]), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Bar", [("size", models.IntegerField())]), ], ) self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())]), migrations.DeleteModel("Bar"), migrations.DeleteModel("Foo"), ], [], ) self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())]), migrations.DeleteModel("Foo"), migrations.DeleteModel("Bar"), ], [], ) # This should not work - FK should block it self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo"))]), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("other", models.ForeignKey("testapp.Foo"))]), migrations.DeleteModel("Foo"), ], ) # This should not work - bases should block it self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), migrations.DeleteModel("Foo"), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())], bases=("testapp.Foo", )), migrations.DeleteModel("Foo"), ], ) def test_create_model_add_field(self): """ AddField should optimize into CreateModel. """ managers = [('objects', EmptyManager())] self.assertOptimizesTo( [ migrations.CreateModel( name="Foo", fields=[("name", models.CharField(max_length=255))], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), migrations.AddField("Foo", "age", models.IntegerField()), ], [ migrations.CreateModel( name="Foo", fields=[ ("name", models.CharField(max_length=255)), ("age", models.IntegerField()), ], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), ], ) def test_create_model_add_field_not_through_fk(self): """ AddField should NOT optimize into CreateModel if it's an FK to a model that's between them. """ self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Link", [("url", models.TextField())]), migrations.AddField("Foo", "link", models.ForeignKey("migrations.Link")), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Link", [("url", models.TextField())]), migrations.AddField("Foo", "link", models.ForeignKey("migrations.Link")), ], ) def test_create_model_add_field_not_through_m2m_through(self): """ AddField should NOT optimize into CreateModel if it's an M2M using a through that's created between them. """ # Note: The middle model is not actually a valid through model, # but that doesn't matter, as we never render it. self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("LinkThrough", []), migrations.AddField("Foo", "link", models.ManyToManyField("migrations.Link", through="migrations.LinkThrough")), ], [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("LinkThrough", []), migrations.AddField("Foo", "link", models.ManyToManyField("migrations.Link", through="migrations.LinkThrough")), ], ) def test_create_model_alter_field(self): """ AlterField should optimize into CreateModel. """ managers = [('objects', EmptyManager())] self.assertOptimizesTo( [ migrations.CreateModel( name="Foo", fields=[("name", models.CharField(max_length=255))], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), migrations.AlterField("Foo", "name", models.IntegerField()), ], [ migrations.CreateModel( name="Foo", fields=[ ("name", models.IntegerField()), ], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), ], ) def test_create_model_rename_field(self): """ RenameField should optimize into CreateModel. """ managers = [('objects', EmptyManager())] self.assertOptimizesTo( [ migrations.CreateModel( name="Foo", fields=[("name", models.CharField(max_length=255))], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), migrations.RenameField("Foo", "name", "title"), ], [ migrations.CreateModel( name="Foo", fields=[ ("title", models.CharField(max_length=255)), ], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), ], ) def test_add_field_rename_field(self): """ RenameField should optimize into AddField """ self.assertOptimizesTo( [ migrations.AddField("Foo", "name", models.CharField(max_length=255)), migrations.RenameField("Foo", "name", "title"), ], [ migrations.AddField("Foo", "title", models.CharField(max_length=255)), ], ) def test_alter_field_rename_field(self): """ RenameField should optimize to the other side of AlterField, and into itself. """ self.assertOptimizesTo( [ migrations.AlterField("Foo", "name", models.CharField(max_length=255)), migrations.RenameField("Foo", "name", "title"), migrations.RenameField("Foo", "title", "nom"), ], [ migrations.RenameField("Foo", "name", "nom"), migrations.AlterField("Foo", "nom", models.CharField(max_length=255)), ], ) def test_create_model_remove_field(self): """ RemoveField should optimize into CreateModel. """ managers = [('objects', EmptyManager())] self.assertOptimizesTo( [ migrations.CreateModel( name="Foo", fields=[ ("name", models.CharField(max_length=255)), ("age", models.IntegerField()), ], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), migrations.RemoveField("Foo", "age"), ], [ migrations.CreateModel( name="Foo", fields=[ ("name", models.CharField(max_length=255)), ], options={'verbose_name': 'Foo'}, bases=(CustomModelBase), managers=managers, ), ], ) def test_add_field_alter_field(self): """ AlterField should optimize into AddField. """ self.assertOptimizesTo( [ migrations.AddField("Foo", "age", models.IntegerField()), migrations.AlterField("Foo", "age", models.FloatField(default=2.4)), ], [ migrations.AddField("Foo", name="age", field=models.FloatField(default=2.4)), ], ) def test_add_field_delete_field(self): """ RemoveField should cancel AddField """ self.assertOptimizesTo( [ migrations.AddField("Foo", "age", models.IntegerField()), migrations.RemoveField("Foo", "age"), ], [], ) def test_alter_field_delete_field(self): """ RemoveField should absorb AlterField """ self.assertOptimizesTo( [ migrations.AlterField("Foo", "age", models.IntegerField()), migrations.RemoveField("Foo", "age"), ], [ migrations.RemoveField("Foo", "age"), ], ) def test_optimize_through_fields(self): """ Checks that field-level through checking is working. This should manage to collapse model Foo to nonexistence, and model Bar to a single IntegerField called "width". """ self.assertOptimizesTo( [ migrations.CreateModel("Foo", [("name", models.CharField(max_length=255))]), migrations.CreateModel("Bar", [("size", models.IntegerField())]), migrations.AddField("Foo", "age", models.IntegerField()), migrations.AddField("Bar", "width", models.IntegerField()), migrations.AlterField("Foo", "age", models.IntegerField()), migrations.RenameField("Bar", "size", "dimensions"), migrations.RemoveField("Foo", "age"), migrations.RenameModel("Foo", "Phou"), migrations.RemoveField("Bar", "dimensions"), migrations.RenameModel("Phou", "Fou"), migrations.DeleteModel("Fou"), ], [ migrations.CreateModel("Bar", [("width", models.IntegerField())]), ], ) Django-1.8.7/tests/migrations/test_migrations_conflict/0000775000175000017500000000000012625116243022753 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_conflict/0002_second.py0000664000175000017500000000120212625113144025231 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("migrations", "0001_initial")] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_conflict/__init__.py0000664000175000017500000000000012603513307025050 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_conflict/0002_conflicting_second.py0000664000175000017500000000060012603513307027612 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("migrations", "0001_initial")] operations = [ migrations.CreateModel( "Something", [ ("id", models.AutoField(primary_key=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_conflict/0001_initial.py0000664000175000017500000000142412603513307025415 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_no_default/0000775000175000017500000000000012625116243023272 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_no_default/__init__.py0000664000175000017500000000000012603513307025367 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_no_default/0001_initial.py0000664000175000017500000000106712625116214025737 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='SillyModel', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('silly_field', models.BooleanField(default=False)), ], options={ }, bases=(models.Model,), ), ] Django-1.8.7/tests/migrations/test_migrations_no_ancestor/0000775000175000017500000000000012625116243023464 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_no_ancestor/0002_second.py0000664000175000017500000000122112625113144025743 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("migrations", "0001_initial"), ] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_no_ancestor/__init__.py0000664000175000017500000000000012603513307025561 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_no_ancestor/0002_conflicting_second.py0000664000175000017500000000115112625113144030324 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_no_ancestor/0001_initial.py0000664000175000017500000000142412603513307026126 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/0000775000175000017500000000000012625116243024516 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex/3_squashed_5.py0000664000175000017500000000061212625116214027350 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): replaces = [ ("migrations", "3_auto"), ("migrations", "4_auto"), ("migrations", "5_auto"), ] dependencies = [("migrations", "2_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/2_auto.py0000664000175000017500000000041412625116214026256 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "1_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/7_auto.py0000664000175000017500000000041412625116214026263 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "6_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/__init__.py0000664000175000017500000000000012625116214026613 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_squashed_complex/1_auto.py0000664000175000017500000000033512625116214026257 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/4_auto.py0000664000175000017500000000041412625116214026260 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "3_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/5_auto.py0000664000175000017500000000041412625116214026261 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "4_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/6_auto.py0000664000175000017500000000041412625116214026262 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "5_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations_squashed_complex/3_auto.py0000664000175000017500000000041412625116214026257 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("migrations", "2_auto")] operations = [ migrations.RunPython(migrations.RunPython.noop) ] Django-1.8.7/tests/migrations/test_migrations/0000775000175000017500000000000012625116243021072 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations/0002_second.py0000664000175000017500000000122112625113144023351 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("migrations", "0001_initial"), ] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations/__init__.py0000664000175000017500000000000012603513307023167 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations/0001_initial.py0000664000175000017500000000163112625113144023533 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ), migrations.AlterUniqueTogether( name='author', unique_together=set([('name', 'slug')]), ), ] Django-1.8.7/tests/migrations/test_migrations_no_changes/0000775000175000017500000000000012625116243023256 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_no_changes/0002_second.py0000664000175000017500000000122112625113144025535 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("migrations", "0001_initial"), ] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_no_changes/0003_third.py0000664000175000017500000000170012625116214025400 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('migrations', '0002_second'), ] operations = [ migrations.CreateModel( name='ModelWithCustomBase', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name='UnmigratedModel', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], options={}, bases=(models.Model,), ), migrations.DeleteModel( name='Author', ), migrations.DeleteModel( name='Book', ), ] Django-1.8.7/tests/migrations/test_migrations_no_changes/__init__.py0000664000175000017500000000000012603513307025353 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_no_changes/0001_initial.py0000664000175000017500000000142412603513307025720 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/__init__.py0000664000175000017500000000000012603513307017754 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_autodetector.py0000664000175000017500000027626712625116214022014 0ustar timtim00000000000000# -*- coding: utf-8 -*- from django.conf import settings from django.contrib.auth.models import AbstractBaseUser from django.db import connection, models from django.db.migrations.autodetector import MigrationAutodetector from django.db.migrations.graph import MigrationGraph from django.db.migrations.loader import MigrationLoader from django.db.migrations.questioner import MigrationQuestioner from django.db.migrations.state import ModelState, ProjectState from django.test import TestCase, mock, override_settings from .models import FoodManager, FoodQuerySet class DeconstructableObject(object): """ A custom deconstructable object. """ def deconstruct(self): return self.__module__ + '.' + self.__class__.__name__, [], {} class AutodetectorTests(TestCase): """ Tests the migration autodetector. """ author_empty = ModelState("testapp", "Author", [("id", models.AutoField(primary_key=True))]) author_name = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ]) author_name_null = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200, null=True)), ]) author_name_longer = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=400)), ]) author_name_renamed = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("names", models.CharField(max_length=200)), ]) author_name_default = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200, default='Ada Lovelace')), ]) author_name_deconstructable_1 = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200, default=DeconstructableObject())), ]) author_name_deconstructable_2 = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200, default=DeconstructableObject())), ]) author_name_deconstructable_3 = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200, default=models.IntegerField())), ]) author_name_deconstructable_4 = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200, default=models.IntegerField())), ]) author_custom_pk = ModelState("testapp", "Author", [("pk_field", models.IntegerField(primary_key=True))]) author_with_biography_non_blank = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField()), ("biography", models.TextField()), ]) author_with_biography_blank = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(blank=True)), ("biography", models.TextField(blank=True)), ]) author_with_book = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("book", models.ForeignKey("otherapp.Book")), ]) author_with_book_order_wrt = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("book", models.ForeignKey("otherapp.Book")), ], options={"order_with_respect_to": "book"}) author_renamed_with_book = ModelState("testapp", "Writer", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("book", models.ForeignKey("otherapp.Book")), ]) author_with_publisher_string = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("publisher_name", models.CharField(max_length=200)), ]) author_with_publisher = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("publisher", models.ForeignKey("testapp.Publisher")), ]) author_with_user = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("user", models.ForeignKey("auth.User")), ]) author_with_custom_user = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=200)), ("user", models.ForeignKey("thirdapp.CustomUser")), ]) author_proxy = ModelState("testapp", "AuthorProxy", [], {"proxy": True}, ("testapp.author",)) author_proxy_options = ModelState("testapp", "AuthorProxy", [], { "proxy": True, "verbose_name": "Super Author", }, ("testapp.author", )) author_proxy_notproxy = ModelState("testapp", "AuthorProxy", [], {}, ("testapp.author", )) author_proxy_third = ModelState("thirdapp", "AuthorProxy", [], {"proxy": True}, ("testapp.author", )) author_proxy_proxy = ModelState("testapp", "AAuthorProxyProxy", [], {"proxy": True}, ("testapp.authorproxy", )) author_unmanaged = ModelState("testapp", "AuthorUnmanaged", [], {"managed": False}, ("testapp.author", )) author_unmanaged_managed = ModelState("testapp", "AuthorUnmanaged", [], {}, ("testapp.author", )) author_unmanaged_default_pk = ModelState("testapp", "Author", [("id", models.AutoField(primary_key=True))]) author_unmanaged_custom_pk = ModelState("testapp", "Author", [ ("pk_field", models.IntegerField(primary_key=True)), ]) author_with_m2m = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("publishers", models.ManyToManyField("testapp.Publisher")), ]) author_with_m2m_blank = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("publishers", models.ManyToManyField("testapp.Publisher", blank=True)), ]) author_with_m2m_through = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("publishers", models.ManyToManyField("testapp.Publisher", through="testapp.Contract")), ]) author_with_former_m2m = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("publishers", models.CharField(max_length=100)), ]) author_with_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ], { "permissions": [('can_hire', 'Can hire')], "verbose_name": "Authi", }) author_with_db_table_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ], {"db_table": "author_one"}) author_with_new_db_table_options = ModelState("testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ], {"db_table": "author_two"}) author_renamed_with_db_table_options = ModelState("testapp", "NewAuthor", [ ("id", models.AutoField(primary_key=True)), ], {"db_table": "author_one"}) author_renamed_with_new_db_table_options = ModelState("testapp", "NewAuthor", [ ("id", models.AutoField(primary_key=True)), ], {"db_table": "author_three"}) contract = ModelState("testapp", "Contract", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("publisher", models.ForeignKey("testapp.Publisher")), ]) publisher = ModelState("testapp", "Publisher", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ]) publisher_with_author = ModelState("testapp", "Publisher", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("name", models.CharField(max_length=100)), ]) publisher_with_aardvark_author = ModelState("testapp", "Publisher", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Aardvark")), ("name", models.CharField(max_length=100)), ]) publisher_with_book = ModelState("testapp", "Publisher", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("otherapp.Book")), ("name", models.CharField(max_length=100)), ]) other_pony = ModelState("otherapp", "Pony", [ ("id", models.AutoField(primary_key=True)), ]) other_pony_food = ModelState("otherapp", "Pony", [ ("id", models.AutoField(primary_key=True)), ], managers=[ ('food_qs', FoodQuerySet.as_manager()), ('food_mgr', FoodManager('a', 'b')), ('food_mgr_kwargs', FoodManager('x', 'y', 3, 4)), ]) other_stable = ModelState("otherapp", "Stable", [("id", models.AutoField(primary_key=True))]) third_thing = ModelState("thirdapp", "Thing", [("id", models.AutoField(primary_key=True))]) book = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("title", models.CharField(max_length=200)), ]) book_proxy_fk = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("thirdapp.AuthorProxy")), ("title", models.CharField(max_length=200)), ]) book_migrations_fk = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.UnmigratedModel")), ("title", models.CharField(max_length=200)), ]) book_with_no_author = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("title", models.CharField(max_length=200)), ]) book_with_author_renamed = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Writer")), ("title", models.CharField(max_length=200)), ]) book_with_field_and_author_renamed = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("writer", models.ForeignKey("testapp.Writer")), ("title", models.CharField(max_length=200)), ]) book_with_multiple_authors = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("authors", models.ManyToManyField("testapp.Author")), ("title", models.CharField(max_length=200)), ]) book_with_multiple_authors_through_attribution = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("authors", models.ManyToManyField("testapp.Author", through="otherapp.Attribution")), ("title", models.CharField(max_length=200)), ]) book_foo_together = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("title", models.CharField(max_length=200)), ], { "index_together": {("author", "title")}, "unique_together": {("author", "title")}, }) book_foo_together_2 = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("title", models.CharField(max_length=200)), ], { "index_together": {("title", "author")}, "unique_together": {("title", "author")}, }) book_foo_together_3 = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("newfield", models.IntegerField()), ("author", models.ForeignKey("testapp.Author")), ("title", models.CharField(max_length=200)), ], { "index_together": {("title", "newfield")}, "unique_together": {("title", "newfield")}, }) book_foo_together_4 = ModelState("otherapp", "Book", [ ("id", models.AutoField(primary_key=True)), ("newfield2", models.IntegerField()), ("author", models.ForeignKey("testapp.Author")), ("title", models.CharField(max_length=200)), ], { "index_together": {("title", "newfield2")}, "unique_together": {("title", "newfield2")}, }) attribution = ModelState("otherapp", "Attribution", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("book", models.ForeignKey("otherapp.Book")), ]) edition = ModelState("thirdapp", "Edition", [ ("id", models.AutoField(primary_key=True)), ("book", models.ForeignKey("otherapp.Book")), ]) custom_user = ModelState("thirdapp", "CustomUser", [ ("id", models.AutoField(primary_key=True)), ("username", models.CharField(max_length=255)), ], bases=(AbstractBaseUser, )) custom_user_no_inherit = ModelState("thirdapp", "CustomUser", [ ("id", models.AutoField(primary_key=True)), ("username", models.CharField(max_length=255)), ]) aardvark = ModelState("thirdapp", "Aardvark", [("id", models.AutoField(primary_key=True))]) aardvark_testapp = ModelState("testapp", "Aardvark", [("id", models.AutoField(primary_key=True))]) aardvark_based_on_author = ModelState("testapp", "Aardvark", [], bases=("testapp.Author", )) aardvark_pk_fk_author = ModelState("testapp", "Aardvark", [ ("id", models.OneToOneField("testapp.Author", primary_key=True)), ]) knight = ModelState("eggs", "Knight", [("id", models.AutoField(primary_key=True))]) rabbit = ModelState("eggs", "Rabbit", [ ("id", models.AutoField(primary_key=True)), ("knight", models.ForeignKey("eggs.Knight")), ("parent", models.ForeignKey("eggs.Rabbit")), ], {"unique_together": {("parent", "knight")}}) def repr_changes(self, changes, include_dependencies=False): output = "" for app_label, migrations in sorted(changes.items()): output += " %s:\n" % app_label for migration in migrations: output += " %s\n" % migration.name for operation in migration.operations: output += " %s\n" % operation if include_dependencies: output += " Dependencies:\n" if migration.dependencies: for dep in migration.dependencies: output += " %s\n" % (dep,) else: output += " None\n" return output def assertNumberMigrations(self, changes, app_label, number): if len(changes.get(app_label, [])) != number: self.fail("Incorrect number of migrations (%s) for %s (expected %s)\n%s" % ( len(changes.get(app_label, [])), app_label, number, self.repr_changes(changes), )) def assertMigrationDependencies(self, changes, app_label, index, dependencies): if not changes.get(app_label): self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes))) if len(changes[app_label]) < index + 1: self.fail("No migration at index %s for %s\n%s" % (index, app_label, self.repr_changes(changes))) migration = changes[app_label][index] if set(migration.dependencies) != set(dependencies): self.fail("Migration dependencies mismatch for %s.%s (expected %s):\n%s" % ( app_label, migration.name, dependencies, self.repr_changes(changes, include_dependencies=True), )) def assertOperationTypes(self, changes, app_label, index, types): if not changes.get(app_label): self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes))) if len(changes[app_label]) < index + 1: self.fail("No migration at index %s for %s\n%s" % (index, app_label, self.repr_changes(changes))) migration = changes[app_label][index] real_types = [operation.__class__.__name__ for operation in migration.operations] if types != real_types: self.fail("Operation type mismatch for %s.%s (expected %s):\n%s" % ( app_label, migration.name, types, self.repr_changes(changes), )) def assertOperationAttributes(self, changes, app_label, index, operation_index, **attrs): if not changes.get(app_label): self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes))) if len(changes[app_label]) < index + 1: self.fail("No migration at index %s for %s\n%s" % (index, app_label, self.repr_changes(changes))) migration = changes[app_label][index] if len(changes[app_label]) < index + 1: self.fail("No operation at index %s for %s.%s\n%s" % ( operation_index, app_label, migration.name, self.repr_changes(changes), )) operation = migration.operations[operation_index] for attr, value in attrs.items(): if getattr(operation, attr, None) != value: self.fail("Attribute mismatch for %s.%s op #%s, %s (expected %r, got %r):\n%s" % ( app_label, migration.name, operation_index, attr, value, getattr(operation, attr, None), self.repr_changes(changes), )) def assertOperationFieldAttributes(self, changes, app_label, index, operation_index, **attrs): if not changes.get(app_label): self.fail("No migrations found for %s\n%s" % (app_label, self.repr_changes(changes))) if len(changes[app_label]) < index + 1: self.fail("No migration at index %s for %s\n%s" % (index, app_label, self.repr_changes(changes))) migration = changes[app_label][index] if len(changes[app_label]) < index + 1: self.fail("No operation at index %s for %s.%s\n%s" % ( operation_index, app_label, migration.name, self.repr_changes(changes), )) operation = migration.operations[operation_index] if not hasattr(operation, 'field'): self.fail("No field attribute for %s.%s op #%s." % ( app_label, migration.name, operation_index, )) field = operation.field for attr, value in attrs.items(): if getattr(field, attr, None) != value: self.fail("Field attribute mismatch for %s.%s op #%s, field.%s (expected %r, got %r):\n%s" % ( app_label, migration.name, operation_index, attr, value, getattr(field, attr, None), self.repr_changes(changes), )) def make_project_state(self, model_states): "Shortcut to make ProjectStates from lists of predefined models" project_state = ProjectState() for model_state in model_states: project_state.add_model(model_state.clone()) return project_state def test_arrange_for_graph(self): """Tests auto-naming of migrations for graph matching.""" # Make a fake graph graph = MigrationGraph() graph.add_node(("testapp", "0001_initial"), None) graph.add_node(("testapp", "0002_foobar"), None) graph.add_node(("otherapp", "0001_initial"), None) graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("testapp", "0001_initial")) graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("otherapp", "0001_initial")) # Use project state to make a new migration change set before = self.make_project_state([]) after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Run through arrange_for_graph changes = autodetector.arrange_for_graph(changes, graph) # Make sure there's a new name, deps match, etc. self.assertEqual(changes["testapp"][0].name, "0003_author") self.assertEqual(changes["testapp"][0].dependencies, [("testapp", "0002_foobar")]) self.assertEqual(changes["otherapp"][0].name, "0002_pony_stable") self.assertEqual(changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")]) def test_trim_apps(self): """ Tests that trim does not remove dependencies but does remove unwanted apps. """ # Use project state to make a new migration change set before = self.make_project_state([]) after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable, self.third_thing]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner(defaults={"ask_initial": True})) changes = autodetector._detect_changes() # Run through arrange_for_graph graph = MigrationGraph() changes = autodetector.arrange_for_graph(changes, graph) changes["testapp"][0].dependencies.append(("otherapp", "0001_initial")) changes = autodetector._trim_to_apps(changes, {"testapp"}) # Make sure there's the right set of migrations self.assertEqual(changes["testapp"][0].name, "0001_initial") self.assertEqual(changes["otherapp"][0].name, "0001_initial") self.assertNotIn("thirdapp", changes) def test_custom_migration_name(self): """Tests custom naming of migrations for graph matching.""" # Make a fake graph graph = MigrationGraph() graph.add_node(("testapp", "0001_initial"), None) graph.add_node(("testapp", "0002_foobar"), None) graph.add_node(("otherapp", "0001_initial"), None) graph.add_dependency("testapp.0002_foobar", ("testapp", "0002_foobar"), ("testapp", "0001_initial")) # Use project state to make a new migration change set before = self.make_project_state([]) after = self.make_project_state([self.author_empty, self.other_pony, self.other_stable]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Run through arrange_for_graph migration_name = 'custom_name' changes = autodetector.arrange_for_graph(changes, graph, migration_name) # Make sure there's a new name, deps match, etc. self.assertEqual(changes["testapp"][0].name, "0003_%s" % migration_name) self.assertEqual(changes["testapp"][0].dependencies, [("testapp", "0002_foobar")]) self.assertEqual(changes["otherapp"][0].name, "0002_%s" % migration_name) self.assertEqual(changes["otherapp"][0].dependencies, [("otherapp", "0001_initial")]) def test_new_model(self): """Tests autodetection of new models.""" # Make state before = self.make_project_state([]) after = self.make_project_state([self.other_pony_food]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, "otherapp", 0, 0, name="Pony") self.assertEqual([name for name, mgr in changes['otherapp'][0].operations[0].managers], ['food_qs', 'food_mgr', 'food_mgr_kwargs']) def test_old_model(self): """Tests deletion of old models.""" # Make state before = self.make_project_state([self.author_empty]) after = self.make_project_state([]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["DeleteModel"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author") def test_add_field(self): """Tests autodetection of new fields.""" # Make state before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_name]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="name") def test_remove_field(self): """Tests autodetection of removed fields.""" # Make state before = self.make_project_state([self.author_name]) after = self.make_project_state([self.author_empty]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RemoveField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="name") def test_alter_field(self): """Tests autodetection of new fields.""" # Make state before = self.make_project_state([self.author_name]) after = self.make_project_state([self.author_name_longer]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True) def test_alter_field_to_not_null_with_default(self): """ #23609 - Tests autodetection of nullable to non-nullable alterations. """ class CustomQuestioner(MigrationQuestioner): def ask_not_null_alteration(self, field_name, model_name): raise Exception("Should not have prompted for not null addition") # Make state before = self.make_project_state([self.author_name_null]) after = self.make_project_state([self.author_name_default]) autodetector = MigrationAutodetector(before, after, CustomQuestioner()) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True) self.assertOperationFieldAttributes(changes, "testapp", 0, 0, default='Ada Lovelace') def test_alter_field_to_not_null_without_default(self): """ #23609 - Tests autodetection of nullable to non-nullable alterations. """ class CustomQuestioner(MigrationQuestioner): def ask_not_null_alteration(self, field_name, model_name): # Ignore for now, and let me handle existing rows with NULL # myself (e.g. adding a RunPython or RunSQL operation in the new # migration file before the AlterField operation) return models.NOT_PROVIDED # Make state before = self.make_project_state([self.author_name_null]) after = self.make_project_state([self.author_name]) autodetector = MigrationAutodetector(before, after, CustomQuestioner()) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=True) self.assertOperationFieldAttributes(changes, "testapp", 0, 0, default=models.NOT_PROVIDED) def test_alter_field_to_not_null_oneoff_default(self): """ #23609 - Tests autodetection of nullable to non-nullable alterations. """ class CustomQuestioner(MigrationQuestioner): def ask_not_null_alteration(self, field_name, model_name): # Provide a one-off default now (will be set on all existing rows) return 'Some Name' # Make state before = self.make_project_state([self.author_name_null]) after = self.make_project_state([self.author_name]) autodetector = MigrationAutodetector(before, after, CustomQuestioner()) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="name", preserve_default=False) self.assertOperationFieldAttributes(changes, "testapp", 0, 0, default="Some Name") def test_rename_field(self): """Tests autodetection of renamed fields.""" # Make state before = self.make_project_state([self.author_name]) after = self.make_project_state([self.author_name_renamed]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_rename": True})) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RenameField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="name", new_name="names") def test_rename_model(self): """Tests autodetection of renamed models.""" # Make state before = self.make_project_state([self.author_with_book, self.book]) after = self.make_project_state([self.author_renamed_with_book, self.book_with_author_renamed]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_rename_model": True})) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="Author", new_name="Writer") # Now that RenameModel handles related fields too, there should be # no AlterField for the related field. self.assertNumberMigrations(changes, 'otherapp', 0) def test_rename_model_with_renamed_rel_field(self): """ Tests autodetection of renamed models while simultaneously renaming one of the fields that relate to the renamed model. """ # Make state before = self.make_project_state([self.author_with_book, self.book]) after = self.make_project_state([self.author_renamed_with_book, self.book_with_field_and_author_renamed]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({ "ask_rename": True, "ask_rename_model": True, })) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, old_name="Author", new_name="Writer") # Right number/type of migrations for related field rename? # Alter is already taken care of. self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["RenameField"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, old_name="author", new_name="writer") def test_fk_dependency(self): """Tests that having a ForeignKey automatically adds a dependency.""" # Make state # Note that testapp (author) has no dependencies, # otherapp (book) depends on testapp (author), # thirdapp (edition) depends on otherapp (book) before = self.make_project_state([]) after = self.make_project_state([self.author_name, self.book, self.edition]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertMigrationDependencies(changes, 'testapp', 0, []) # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book") self.assertMigrationDependencies(changes, 'otherapp', 0, [("testapp", "auto_1")]) # Right number/type of migrations? self.assertNumberMigrations(changes, 'thirdapp', 1) self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="Edition") self.assertMigrationDependencies(changes, 'thirdapp', 0, [("otherapp", "auto_1")]) def test_proxy_fk_dependency(self): """Tests that FK dependencies still work on proxy models.""" # Make state # Note that testapp (author) has no dependencies, # otherapp (book) depends on testapp (authorproxy) before = self.make_project_state([]) after = self.make_project_state([self.author_empty, self.author_proxy_third, self.book_proxy_fk]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertMigrationDependencies(changes, 'testapp', 0, []) # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book") self.assertMigrationDependencies(changes, 'otherapp', 0, [("thirdapp", "auto_1")]) # Right number/type of migrations? self.assertNumberMigrations(changes, 'thirdapp', 1) self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="AuthorProxy") self.assertMigrationDependencies(changes, 'thirdapp', 0, [("testapp", "auto_1")]) def test_same_app_no_fk_dependency(self): """ Tests that a migration with a FK between two models of the same app does not have a dependency to itself. """ # Make state before = self.make_project_state([]) after = self.make_project_state([self.author_with_publisher, self.publisher]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel", "AddField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author") self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher") self.assertOperationAttributes(changes, "testapp", 0, 2, name="publisher") self.assertMigrationDependencies(changes, 'testapp', 0, []) def test_circular_fk_dependency(self): """ Tests that having a circular ForeignKey dependency automatically resolves the situation into 2 migrations on one side and 1 on the other. """ # Make state before = self.make_project_state([]) after = self.make_project_state([self.author_with_book, self.book, self.publisher_with_book]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author") self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher") self.assertMigrationDependencies(changes, 'testapp', 0, [("otherapp", "auto_1")]) # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 2) self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"]) self.assertOperationTypes(changes, 'otherapp', 1, ["AddField"]) self.assertMigrationDependencies(changes, 'otherapp', 0, []) self.assertMigrationDependencies(changes, 'otherapp', 1, [("otherapp", "auto_1"), ("testapp", "auto_1")]) def test_same_app_circular_fk_dependency(self): """ Tests that a migration with a FK between two models of the same app does not have a dependency to itself. """ # Make state before = self.make_project_state([]) after = self.make_project_state([self.author_with_publisher, self.publisher_with_author]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel", "AddField"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="Author") self.assertOperationAttributes(changes, "testapp", 0, 1, name="Publisher") self.assertOperationAttributes(changes, "testapp", 0, 2, name="publisher") self.assertMigrationDependencies(changes, 'testapp', 0, []) def test_same_app_circular_fk_dependency_and_unique_together(self): """ #22275 - Tests that a migration with circular FK dependency does not try to create unique together constraint before creating all required fields first. """ # Make state before = self.make_project_state([]) after = self.make_project_state([self.knight, self.rabbit]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'eggs', 1) self.assertOperationTypes(changes, 'eggs', 0, ["CreateModel", "CreateModel", "AlterUniqueTogether"]) self.assertNotIn("unique_together", changes['eggs'][0].operations[0].options) self.assertNotIn("unique_together", changes['eggs'][0].operations[1].options) self.assertMigrationDependencies(changes, 'eggs', 0, []) def test_alter_db_table_add(self): """Tests detection for adding db_table in model's options.""" # Make state before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_with_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelTable"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", table="author_one") def test_alter_db_table_change(self): """Tests detection for changing db_table in model's options'.""" # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_with_new_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelTable"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", table="author_two") def test_alter_db_table_remove(self): """Tests detection for removing db_table in model's options.""" # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_empty]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelTable"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", table=None) def test_alter_db_table_no_changes(self): """ Tests that alter_db_table doesn't generate a migration if no changes have been made. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_with_db_table_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 0) def test_keep_db_table_with_model_change(self): """ Tests when model changes but db_table stays as-is, autodetector must not create more than one operation. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_renamed_with_db_table_options]) autodetector = MigrationAutodetector( before, after, MigrationQuestioner({"ask_rename_model": True}) ) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel"]) self.assertOperationAttributes(changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor") def test_alter_db_table_with_model_change(self): """ Tests when model and db_table changes, autodetector must create two operations. """ # Make state before = self.make_project_state([self.author_with_db_table_options]) after = self.make_project_state([self.author_renamed_with_new_db_table_options]) autodetector = MigrationAutodetector( before, after, MigrationQuestioner({"ask_rename_model": True}) ) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RenameModel", "AlterModelTable"]) self.assertOperationAttributes(changes, "testapp", 0, 0, old_name="Author", new_name="NewAuthor") self.assertOperationAttributes(changes, "testapp", 0, 1, name="newauthor", table="author_three") def test_empty_foo_together(self): """ #23452 - Empty unique/index_together shouldn't generate a migration. """ # Explicitly testing for not specified, since this is the case after # a CreateModel operation w/o any definition on the original model model_state_not_specified = ModelState("a", "model", [("id", models.AutoField(primary_key=True))]) # Explicitly testing for None, since this was the issue in #23452 after # a AlterFooTogether operation with e.g. () as value model_state_none = ModelState("a", "model", [ ("id", models.AutoField(primary_key=True)) ], { "index_together": None, "unique_together": None, }) # Explicitly testing for the empty set, since we now always have sets. # During removal (('col1', 'col2'),) --> () this becomes set([]) model_state_empty = ModelState("a", "model", [ ("id", models.AutoField(primary_key=True)) ], { "index_together": set(), "unique_together": set(), }) def test(from_state, to_state, msg): before = self.make_project_state([from_state]) after = self.make_project_state([to_state]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() if len(changes) > 0: ops = ', '.join(o.__class__.__name__ for o in changes['a'][0].operations) self.fail('Created operation(s) %s from %s' % (ops, msg)) tests = ( (model_state_not_specified, model_state_not_specified, '"not specified" to "not specified"'), (model_state_not_specified, model_state_none, '"not specified" to "None"'), (model_state_not_specified, model_state_empty, '"not specified" to "empty"'), (model_state_none, model_state_not_specified, '"None" to "not specified"'), (model_state_none, model_state_none, '"None" to "None"'), (model_state_none, model_state_empty, '"None" to "empty"'), (model_state_empty, model_state_not_specified, '"empty" to "not specified"'), (model_state_empty, model_state_none, '"empty" to "None"'), (model_state_empty, model_state_empty, '"empty" to "empty"'), ) for t in tests: test(*t) def test_add_foo_together(self): """Tests index/unique_together detection.""" # Make state before = self.make_project_state([self.author_empty, self.book]) after = self.make_project_state([self.author_empty, self.book_foo_together]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether"]) self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together={("author", "title")}) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together={("author", "title")}) def test_remove_foo_together(self): """Tests index/unique_together detection.""" before = self.make_project_state([self.author_empty, self.book_foo_together]) after = self.make_project_state([self.author_empty, self.book]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether"]) self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together=set()) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together=set()) def test_foo_together_remove_fk(self): """Tests unique_together and field removal detection & ordering""" # Make state before = self.make_project_state([self.author_empty, self.book_foo_together]) after = self.make_project_state([self.author_empty, self.book_with_no_author]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, [ "AlterUniqueTogether", "AlterIndexTogether", "RemoveField" ]) self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together=set()) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together=set()) self.assertOperationAttributes(changes, "otherapp", 0, 2, model_name="book", name="author") def test_foo_together_no_changes(self): """ Tests that index/unique_together doesn't generate a migration if no changes have been made. """ # Make state before = self.make_project_state([self.author_empty, self.book_foo_together]) after = self.make_project_state([self.author_empty, self.book_foo_together]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 0) def test_foo_together_ordering(self): """ Tests that index/unique_together also triggers on ordering changes. """ # Make state before = self.make_project_state([self.author_empty, self.book_foo_together]) after = self.make_project_state([self.author_empty, self.book_foo_together_2]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether"]) self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together={("title", "author")}) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together={("title", "author")}) def test_add_field_and_foo_together(self): """ Tests that added fields will be created before using them in index/unique_together. """ before = self.make_project_state([self.author_empty, self.book]) after = self.make_project_state([self.author_empty, self.book_foo_together_3]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["AddField", "AlterUniqueTogether", "AlterIndexTogether"]) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", unique_together={("title", "newfield")}) self.assertOperationAttributes(changes, "otherapp", 0, 2, name="book", index_together={("title", "newfield")}) def test_remove_field_and_foo_together(self): """ Tests that removed fields will be removed after updating index/unique_together. """ before = self.make_project_state([self.author_empty, self.book_foo_together_3]) after = self.make_project_state([self.author_empty, self.book_foo_together]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["AlterUniqueTogether", "AlterIndexTogether", "RemoveField"]) self.assertOperationAttributes(changes, "otherapp", 0, 0, name="book", unique_together={("author", "title")}) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", index_together={("author", "title")}) def test_rename_field_and_foo_together(self): """ Tests that removed fields will be removed after updating index/unique_together. """ before = self.make_project_state([self.author_empty, self.book_foo_together_3]) after = self.make_project_state([self.author_empty, self.book_foo_together_4]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner({"ask_rename": True})) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["RenameField", "AlterUniqueTogether", "AlterIndexTogether"]) self.assertOperationAttributes(changes, "otherapp", 0, 1, name="book", unique_together={ ("title", "newfield2") }) self.assertOperationAttributes(changes, "otherapp", 0, 2, name="book", index_together={("title", "newfield2")}) def test_proxy(self): """Tests that the autodetector correctly deals with proxy models.""" # First, we test adding a proxy model before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_empty, self.author_proxy]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["CreateModel"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="AuthorProxy", options={"proxy": True}) # Now, we test turning a proxy model into a non-proxy model # It should delete the proxy then make the real one before = self.make_project_state([self.author_empty, self.author_proxy]) after = self.make_project_state([self.author_empty, self.author_proxy_notproxy]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["DeleteModel", "CreateModel"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="AuthorProxy") self.assertOperationAttributes(changes, "testapp", 0, 1, name="AuthorProxy", options={}) def test_proxy_custom_pk(self): """ #23415 - The autodetector must correctly deal with custom FK on proxy models. """ # First, we test the default pk field name before = self.make_project_state([]) after = self.make_project_state([self.author_empty, self.author_proxy_third, self.book_proxy_fk]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # The field name the FK on the book model points to self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'id') # Now, we test the custom pk field name before = self.make_project_state([]) after = self.make_project_state([self.author_custom_pk, self.author_proxy_third, self.book_proxy_fk]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # The field name the FK on the book model points to self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'pk_field') def test_unmanaged_create(self): """Tests that the autodetector correctly deals with managed models.""" # First, we test adding an unmanaged model before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_empty, self.author_unmanaged]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="AuthorUnmanaged", options={"managed": False}) def test_unmanaged_to_managed(self): # Now, we test turning an unmanaged model into a managed model before = self.make_project_state([self.author_empty, self.author_unmanaged]) after = self.make_project_state([self.author_empty, self.author_unmanaged_managed]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterModelOptions"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="authorunmanaged", options={}) def test_managed_to_unmanaged(self): # Now, we turn managed to unmanaged. before = self.make_project_state([self.author_empty, self.author_unmanaged_managed]) after = self.make_project_state([self.author_empty, self.author_unmanaged]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="authorunmanaged", options={"managed": False}) def test_unmanaged_custom_pk(self): """ #23415 - The autodetector must correctly deal with custom FK on unmanaged models. """ # First, we test the default pk field name before = self.make_project_state([]) after = self.make_project_state([self.author_unmanaged_default_pk, self.book]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # The field name the FK on the book model points to self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'id') # Now, we test the custom pk field name before = self.make_project_state([]) after = self.make_project_state([self.author_unmanaged_custom_pk, self.book]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # The field name the FK on the book model points to self.assertEqual(changes['otherapp'][0].operations[0].fields[2][1].rel.field_name, 'pk_field') @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser") def test_swappable(self): before = self.make_project_state([self.custom_user]) after = self.make_project_state([self.custom_user, self.author_with_custom_user]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertMigrationDependencies(changes, 'testapp', 0, [("__setting__", "AUTH_USER_MODEL")]) def test_swappable_changed(self): before = self.make_project_state([self.custom_user, self.author_with_user]) with override_settings(AUTH_USER_MODEL="thirdapp.CustomUser"): after = self.make_project_state([self.custom_user, self.author_with_custom_user]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name='user') fk_field = changes['testapp'][0].operations[0].field to_model = '%s.%s' % (fk_field.rel.to._meta.app_label, fk_field.rel.to._meta.object_name) self.assertEqual(to_model, 'thirdapp.CustomUser') def test_add_field_with_default(self): """#22030 - Adding a field with a default should work.""" # Make state before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_name_default]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="name") def test_custom_deconstructable(self): """ Two instances which deconstruct to the same value aren't considered a change. """ before = self.make_project_state([self.author_name_deconstructable_1]) after = self.make_project_state([self.author_name_deconstructable_2]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number of migrations? self.assertEqual(len(changes), 0) def test_deconstruct_field_kwarg(self): """Field instances are handled correctly by nested deconstruction.""" before = self.make_project_state([self.author_name_deconstructable_3]) after = self.make_project_state([self.author_name_deconstructable_4]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() self.assertEqual(changes, {}) def test_deconstruct_type(self): """ #22951 -- Uninstanted classes with deconstruct are correctly returned by deep_deconstruct during serialization. """ author = ModelState( "testapp", "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField( max_length=200, # IntegerField intentionally not instantiated. default=models.IntegerField, )) ], ) # Make state before = self.make_project_state([]) after = self.make_project_state([author]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel"]) def test_replace_string_with_foreignkey(self): """ #22300 - Adding an FK in the same "spot" as a deleted CharField should work. """ # Make state before = self.make_project_state([self.author_with_publisher_string]) after = self.make_project_state([self.author_with_publisher, self.publisher]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "RemoveField", "AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Publisher") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publisher_name") self.assertOperationAttributes(changes, 'testapp', 0, 2, name="publisher") def test_foreign_key_removed_before_target_model(self): """ Removing an FK and the model it targets in the same change must remove the FK field before the model to maintain consistency. """ before = self.make_project_state([self.author_with_publisher, self.publisher]) after = self.make_project_state([self.author_name]) # removes both the model and FK autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["RemoveField", "DeleteModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publisher") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Publisher") def test_add_many_to_many(self): """#22435 - Adding a ManyToManyField should not prompt for a default.""" class CustomQuestioner(MigrationQuestioner): def ask_not_null_addition(self, field_name, model_name): raise Exception("Should not have prompted for not null addition") before = self.make_project_state([self.author_empty, self.publisher]) after = self.make_project_state([self.author_with_m2m, self.publisher]) autodetector = MigrationAutodetector(before, after, CustomQuestioner()) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers") def test_alter_many_to_many(self): before = self.make_project_state([self.author_with_m2m, self.publisher]) after = self.make_project_state([self.author_with_m2m_blank, self.publisher]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers") def test_create_with_through_model(self): """ Adding a m2m with a through model and the models that use it should be ordered correctly. """ before = self.make_project_state([]) after = self.make_project_state([self.author_with_m2m_through, self.publisher, self.contract]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, [ "CreateModel", "CreateModel", "CreateModel", "AddField", "AddField" ]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Contract") self.assertOperationAttributes(changes, 'testapp', 0, 2, name="Publisher") self.assertOperationAttributes(changes, 'testapp', 0, 3, model_name='contract', name='publisher') self.assertOperationAttributes(changes, 'testapp', 0, 4, model_name='author', name='publishers') def test_many_to_many_removed_before_through_model(self): """ Removing a ManyToManyField and the "through" model in the same change must remove the field before the model to maintain consistency. """ before = self.make_project_state([ self.book_with_multiple_authors_through_attribution, self.author_name, self.attribution ]) # Remove both the through model and ManyToMany after = self.make_project_state([self.book_with_no_author, self.author_name]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, ["RemoveField", "RemoveField", "RemoveField", "DeleteModel"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="author", model_name='attribution') self.assertOperationAttributes(changes, 'otherapp', 0, 1, name="book", model_name='attribution') self.assertOperationAttributes(changes, 'otherapp', 0, 2, name="authors", model_name='book') self.assertOperationAttributes(changes, 'otherapp', 0, 3, name='Attribution') def test_many_to_many_removed_before_through_model_2(self): """ Removing a model that contains a ManyToManyField and the "through" model in the same change must remove the field before the model to maintain consistency. """ before = self.make_project_state([ self.book_with_multiple_authors_through_attribution, self.author_name, self.attribution ]) # Remove both the through model and ManyToMany after = self.make_project_state([self.author_name]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "otherapp", 1) self.assertOperationTypes(changes, "otherapp", 0, [ "RemoveField", "RemoveField", "RemoveField", "DeleteModel", "DeleteModel" ]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="author", model_name='attribution') self.assertOperationAttributes(changes, 'otherapp', 0, 1, name="book", model_name='attribution') self.assertOperationAttributes(changes, 'otherapp', 0, 2, name="authors", model_name='book') self.assertOperationAttributes(changes, 'otherapp', 0, 3, name='Attribution') self.assertOperationAttributes(changes, 'otherapp', 0, 4, name='Book') def test_m2m_w_through_multistep_remove(self): """ A model with a m2m field that specifies a "through" model cannot be removed in the same migration as that through model as the schema will pass through an inconsistent state. The autodetector should produce two migrations to avoid this issue. """ before = self.make_project_state([self.author_with_m2m_through, self.publisher, self.contract]) after = self.make_project_state([self.publisher]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, [ "RemoveField", "RemoveField", "RemoveField", "DeleteModel", "DeleteModel" ]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="publishers", model_name='author') self.assertOperationAttributes(changes, "testapp", 0, 1, name="author", model_name='contract') self.assertOperationAttributes(changes, "testapp", 0, 2, name="publisher", model_name='contract') self.assertOperationAttributes(changes, "testapp", 0, 3, name="Author") self.assertOperationAttributes(changes, "testapp", 0, 4, name="Contract") def test_concrete_field_changed_to_many_to_many(self): """ #23938 - Tests that changing a concrete field into a ManyToManyField first removes the concrete field and then adds the m2m field. """ before = self.make_project_state([self.author_with_former_m2m]) after = self.make_project_state([self.author_with_m2m, self.publisher]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["CreateModel", "RemoveField", "AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name='Publisher') self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publishers", model_name='author') self.assertOperationAttributes(changes, 'testapp', 0, 2, name="publishers", model_name='author') def test_many_to_many_changed_to_concrete_field(self): """ #23938 - Tests that changing a ManyToManyField into a concrete field first removes the m2m field and then adds the concrete field. """ before = self.make_project_state([self.author_with_m2m, self.publisher]) after = self.make_project_state([self.author_with_former_m2m]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["RemoveField", "AddField", "DeleteModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers", model_name='author') self.assertOperationAttributes(changes, 'testapp', 0, 1, name="publishers", model_name='author') self.assertOperationAttributes(changes, 'testapp', 0, 2, name='Publisher') self.assertOperationFieldAttributes(changes, 'testapp', 0, 1, max_length=100) def test_non_circular_foreignkey_dependency_removal(self): """ If two models with a ForeignKey from one to the other are removed at the same time, the autodetector should remove them in the correct order. """ before = self.make_project_state([self.author_with_publisher, self.publisher_with_author]) after = self.make_project_state([]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["RemoveField", "RemoveField", "DeleteModel", "DeleteModel"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="publisher", model_name='author') self.assertOperationAttributes(changes, "testapp", 0, 1, name="author", model_name='publisher') self.assertOperationAttributes(changes, "testapp", 0, 2, name="Author") self.assertOperationAttributes(changes, "testapp", 0, 3, name="Publisher") def test_alter_model_options(self): """Changing a model's options should make a change.""" before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_with_options]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"]) self.assertOperationAttributes(changes, "testapp", 0, 0, options={ "permissions": [('can_hire', 'Can hire')], "verbose_name": "Authi", }) # Changing them back to empty should also make a change before = self.make_project_state([self.author_with_options]) after = self.make_project_state([self.author_empty]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="author", options={}) def test_alter_model_options_proxy(self): """Changing a proxy model's options should also make a change.""" before = self.make_project_state([self.author_proxy, self.author_empty]) after = self.make_project_state([self.author_proxy_options, self.author_empty]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "testapp", 1) self.assertOperationTypes(changes, "testapp", 0, ["AlterModelOptions"]) self.assertOperationAttributes(changes, "testapp", 0, 0, name="authorproxy", options={ "verbose_name": "Super Author" }) def test_set_alter_order_with_respect_to(self): """Tests that setting order_with_respect_to adds a field.""" # Make state before = self.make_project_state([self.book, self.author_with_book]) after = self.make_project_state([self.book, self.author_with_book_order_wrt]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterOrderWithRespectTo"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="author", order_with_respect_to="book") def test_add_alter_order_with_respect_to(self): """ Tests that setting order_with_respect_to when adding the FK too does things in the right order. """ # Make state before = self.make_project_state([self.author_name]) after = self.make_project_state([self.book, self.author_with_book_order_wrt]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AlterOrderWithRespectTo"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, model_name="author", name="book") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author", order_with_respect_to="book") def test_remove_alter_order_with_respect_to(self): """ Tests that removing order_with_respect_to when removing the FK too does things in the right order. """ # Make state before = self.make_project_state([self.book, self.author_with_book_order_wrt]) after = self.make_project_state([self.author_name]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AlterOrderWithRespectTo", "RemoveField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="author", order_with_respect_to=None) self.assertOperationAttributes(changes, 'testapp', 0, 1, model_name="author", name="book") def test_add_model_order_with_respect_to(self): """ Tests that setting order_with_respect_to when adding the whole model does things in the right order. """ # Make state before = self.make_project_state([]) after = self.make_project_state([self.book, self.author_with_book_order_wrt]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "AlterOrderWithRespectTo"]) self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author", order_with_respect_to="book") self.assertNotIn("_order", [name for name, field in changes['testapp'][0].operations[0].fields]) def test_alter_model_managers(self): """ Tests that changing the model managers adds a new operation. """ # Make state before = self.make_project_state([self.other_pony]) after = self.make_project_state([self.other_pony_food]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["AlterModelManagers"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="pony") self.assertEqual([name for name, mgr in changes['otherapp'][0].operations[0].managers], ['food_qs', 'food_mgr', 'food_mgr_kwargs']) self.assertEqual(changes['otherapp'][0].operations[0].managers[1][1].args, ('a', 'b', 1, 2)) self.assertEqual(changes['otherapp'][0].operations[0].managers[2][1].args, ('x', 'y', 3, 4)) def test_swappable_first_inheritance(self): """Tests that swappable models get their CreateModel first.""" # Make state before = self.make_project_state([]) after = self.make_project_state([self.custom_user, self.aardvark]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'thirdapp', 1) self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel", "CreateModel"]) self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="CustomUser") self.assertOperationAttributes(changes, 'thirdapp', 0, 1, name="Aardvark") @override_settings(AUTH_USER_MODEL="thirdapp.CustomUser") def test_swappable_first_setting(self): """Tests that swappable models get their CreateModel first.""" # Make state before = self.make_project_state([]) after = self.make_project_state([self.custom_user_no_inherit, self.aardvark]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'thirdapp', 1) self.assertOperationTypes(changes, 'thirdapp', 0, ["CreateModel", "CreateModel"]) self.assertOperationAttributes(changes, 'thirdapp', 0, 0, name="CustomUser") self.assertOperationAttributes(changes, 'thirdapp', 0, 1, name="Aardvark") def test_bases_first(self): """Tests that bases of other models come first.""" # Make state before = self.make_project_state([]) after = self.make_project_state([self.aardvark_based_on_author, self.author_name]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Aardvark") def test_multiple_bases(self): """#23956 - Tests that inheriting models doesn't move *_ptr fields into AddField operations.""" A = ModelState("app", "A", [("a_id", models.AutoField(primary_key=True))]) B = ModelState("app", "B", [("b_id", models.AutoField(primary_key=True))]) C = ModelState("app", "C", [], bases=("app.A", "app.B")) D = ModelState("app", "D", [], bases=("app.A", "app.B")) E = ModelState("app", "E", [], bases=("app.A", "app.B")) # Make state before = self.make_project_state([]) after = self.make_project_state([A, B, C, D, E]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, "app", 1) self.assertOperationTypes(changes, "app", 0, [ "CreateModel", "CreateModel", "CreateModel", "CreateModel", "CreateModel" ]) self.assertOperationAttributes(changes, "app", 0, 0, name="A") self.assertOperationAttributes(changes, "app", 0, 1, name="B") self.assertOperationAttributes(changes, "app", 0, 2, name="C") self.assertOperationAttributes(changes, "app", 0, 3, name="D") self.assertOperationAttributes(changes, "app", 0, 4, name="E") def test_proxy_bases_first(self): """Tests that bases of proxies come first.""" # Make state before = self.make_project_state([]) after = self.make_project_state([self.author_empty, self.author_proxy, self.author_proxy_proxy]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel", "CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="AuthorProxy") self.assertOperationAttributes(changes, 'testapp', 0, 2, name="AAuthorProxyProxy") def test_pk_fk_included(self): """ Tests that a relation used as the primary key is kept as part of CreateModel. """ # Make state before = self.make_project_state([]) after = self.make_project_state([self.aardvark_pk_fk_author, self.author_name]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "CreateModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Author") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="Aardvark") def test_first_dependency(self): """ Tests that a dependency to an app with no migrations uses __first__. """ # Load graph loader = MigrationLoader(connection) # Make state before = self.make_project_state([]) after = self.make_project_state([self.book_migrations_fk]) after.real_apps = ["migrations"] autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes(graph=loader.graph) # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book") self.assertMigrationDependencies(changes, 'otherapp', 0, [("migrations", "__first__")]) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_last_dependency(self): """ Tests that a dependency to an app with existing migrations uses the last migration of that app. """ # Load graph loader = MigrationLoader(connection) # Make state before = self.make_project_state([]) after = self.make_project_state([self.book_migrations_fk]) after.real_apps = ["migrations"] autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes(graph=loader.graph) # Right number/type of migrations? self.assertNumberMigrations(changes, 'otherapp', 1) self.assertOperationTypes(changes, 'otherapp', 0, ["CreateModel"]) self.assertOperationAttributes(changes, 'otherapp', 0, 0, name="Book") self.assertMigrationDependencies(changes, 'otherapp', 0, [("migrations", "0002_second")]) def test_alter_fk_before_model_deletion(self): """ Tests that ForeignKeys are altered _before_ the model they used to refer to are deleted. """ # Make state before = self.make_project_state([self.author_name, self.publisher_with_author]) after = self.make_project_state([self.aardvark_testapp, self.publisher_with_aardvark_author]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["CreateModel", "AlterField", "DeleteModel"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="Aardvark") self.assertOperationAttributes(changes, 'testapp', 0, 1, name="author") self.assertOperationAttributes(changes, 'testapp', 0, 2, name="Author") def test_fk_dependency_other_app(self): """ #23100 - Tests that ForeignKeys correctly depend on other apps' models. """ # Make state before = self.make_project_state([self.author_name, self.book]) after = self.make_project_state([self.author_with_book, self.book]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0, name="book") self.assertMigrationDependencies(changes, 'testapp', 0, [("otherapp", "__first__")]) def test_circular_dependency_mixed_addcreate(self): """ #23315 - Tests that the dependency resolver knows to put all CreateModel before AddField and not become unsolvable. """ address = ModelState("a", "Address", [ ("id", models.AutoField(primary_key=True)), ("country", models.ForeignKey("b.DeliveryCountry")), ]) person = ModelState("a", "Person", [ ("id", models.AutoField(primary_key=True)), ]) apackage = ModelState("b", "APackage", [ ("id", models.AutoField(primary_key=True)), ("person", models.ForeignKey("a.Person")), ]) country = ModelState("b", "DeliveryCountry", [ ("id", models.AutoField(primary_key=True)), ]) # Make state before = self.make_project_state([]) after = self.make_project_state([address, person, apackage, country]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'a', 2) self.assertNumberMigrations(changes, 'b', 1) self.assertOperationTypes(changes, 'a', 0, ["CreateModel", "CreateModel"]) self.assertOperationTypes(changes, 'a', 1, ["AddField"]) self.assertOperationTypes(changes, 'b', 0, ["CreateModel", "CreateModel"]) @override_settings(AUTH_USER_MODEL="a.Tenant") def test_circular_dependency_swappable(self): """ #23322 - Tests that the dependency resolver knows to explicitly resolve swappable models. """ tenant = ModelState("a", "Tenant", [ ("id", models.AutoField(primary_key=True)), ("primary_address", models.ForeignKey("b.Address"))], bases=(AbstractBaseUser, ) ) address = ModelState("b", "Address", [ ("id", models.AutoField(primary_key=True)), ("tenant", models.ForeignKey(settings.AUTH_USER_MODEL)), ]) # Make state before = self.make_project_state([]) after = self.make_project_state([address, tenant]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'a', 2) self.assertOperationTypes(changes, 'a', 0, ["CreateModel"]) self.assertOperationTypes(changes, 'a', 1, ["AddField"]) self.assertMigrationDependencies(changes, 'a', 0, []) self.assertMigrationDependencies(changes, 'a', 1, [('a', 'auto_1'), ('b', 'auto_1')]) # Right number/type of migrations? self.assertNumberMigrations(changes, 'b', 1) self.assertOperationTypes(changes, 'b', 0, ["CreateModel"]) self.assertMigrationDependencies(changes, 'b', 0, [('__setting__', 'AUTH_USER_MODEL')]) @override_settings(AUTH_USER_MODEL="b.Tenant") def test_circular_dependency_swappable2(self): """ #23322 - Tests that the dependency resolver knows to explicitly resolve swappable models but with the swappable not being the first migrated model. """ address = ModelState("a", "Address", [ ("id", models.AutoField(primary_key=True)), ("tenant", models.ForeignKey(settings.AUTH_USER_MODEL)), ]) tenant = ModelState("b", "Tenant", [ ("id", models.AutoField(primary_key=True)), ("primary_address", models.ForeignKey("a.Address"))], bases=(AbstractBaseUser, ) ) # Make state before = self.make_project_state([]) after = self.make_project_state([address, tenant]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'a', 2) self.assertOperationTypes(changes, 'a', 0, ["CreateModel"]) self.assertOperationTypes(changes, 'a', 1, ["AddField"]) self.assertMigrationDependencies(changes, 'a', 0, []) self.assertMigrationDependencies(changes, 'a', 1, [('__setting__', 'AUTH_USER_MODEL'), ('a', 'auto_1')]) # Right number/type of migrations? self.assertNumberMigrations(changes, 'b', 1) self.assertOperationTypes(changes, 'b', 0, ["CreateModel"]) self.assertMigrationDependencies(changes, 'b', 0, [('a', 'auto_1')]) @override_settings(AUTH_USER_MODEL="a.Person") def test_circular_dependency_swappable_self(self): """ #23322 - Tests that the dependency resolver knows to explicitly resolve swappable models. """ person = ModelState("a", "Person", [ ("id", models.AutoField(primary_key=True)), ("parent1", models.ForeignKey(settings.AUTH_USER_MODEL, related_name='children')) ]) # Make state before = self.make_project_state([]) after = self.make_project_state([person]) autodetector = MigrationAutodetector(before, after) changes = autodetector._detect_changes() # Right number/type of migrations? self.assertNumberMigrations(changes, 'a', 1) self.assertOperationTypes(changes, 'a', 0, ["CreateModel"]) self.assertMigrationDependencies(changes, 'a', 0, []) def test_add_blank_textfield_and_charfield(self): """ #23405 - Adding a NOT NULL and blank `CharField` or `TextField` without default should not prompt for a default. """ class CustomQuestioner(MigrationQuestioner): def ask_not_null_addition(self, field_name, model_name): raise Exception("Should not have prompted for not null addition") before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_with_biography_blank]) autodetector = MigrationAutodetector(before, after, CustomQuestioner()) changes = autodetector._detect_changes() self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0) @mock.patch('django.db.migrations.questioner.MigrationQuestioner.ask_not_null_addition') def test_add_non_blank_textfield_and_charfield(self, mocked_ask_method): """ #23405 - Adding a NOT NULL and non-blank `CharField` or `TextField` without default should prompt for a default. """ before = self.make_project_state([self.author_empty]) after = self.make_project_state([self.author_with_biography_non_blank]) autodetector = MigrationAutodetector(before, after, MigrationQuestioner()) changes = autodetector._detect_changes() # need to check for questioner call self.assertTrue(mocked_ask_method.called) self.assertEqual(mocked_ask_method.call_count, 2) self.assertNumberMigrations(changes, 'testapp', 1) self.assertOperationTypes(changes, 'testapp', 0, ["AddField", "AddField"]) self.assertOperationAttributes(changes, 'testapp', 0, 0) Django-1.8.7/tests/migrations/test_commands.py0000664000175000017500000011603612625116214021076 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import codecs import importlib import os import shutil from django.apps import apps from django.core.management import CommandError, call_command from django.db import DatabaseError, connection, models from django.db.migrations import questioner from django.db.migrations.recorder import MigrationRecorder from django.test import ignore_warnings, mock, override_settings from django.utils import six from django.utils.deprecation import RemovedInDjango110Warning from django.utils.encoding import force_text from .models import UnicodeModel, UnserializableModel from .test_base import MigrationTestBase class MigrateTests(MigrationTestBase): """ Tests running the migrate command. """ @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_migrate(self): """ Tests basic usage of the migrate command. """ # Make sure no tables are created self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") self.assertTableNotExists("migrations_book") # Run the migrations to 0001 only call_command("migrate", "migrations", "0001", verbosity=0) # Make sure the right tables exist self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") self.assertTableNotExists("migrations_book") # Run migrations all the way call_command("migrate", verbosity=0) # Make sure the right tables exist self.assertTableExists("migrations_author") self.assertTableNotExists("migrations_tribble") self.assertTableExists("migrations_book") # Unmigrate everything call_command("migrate", "migrations", "zero", verbosity=0) # Make sure it's all gone self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") self.assertTableNotExists("migrations_book") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_migrate_fake_initial(self): """ #24184 - Tests that --fake-initial only works if all tables created in the initial migration of an app exists """ # Make sure no tables are created self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") # Run the migrations to 0001 only call_command("migrate", "migrations", "0001", verbosity=0) # Make sure the right tables exist self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Fake a roll-back call_command("migrate", "migrations", "zero", fake=True, verbosity=0) # Make sure the tables still exist self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Try to run initial migration with self.assertRaises(DatabaseError): call_command("migrate", "migrations", "0001", verbosity=0) # Run initial migration with an explicit --fake-initial out = six.StringIO() with mock.patch('django.core.management.color.supports_color', lambda *args: False): call_command("migrate", "migrations", "0001", fake_initial=True, stdout=out, verbosity=1) self.assertIn( "migrations.0001_initial... faked", out.getvalue().lower() ) # Run migrations all the way call_command("migrate", verbosity=0) # Make sure the right tables exist self.assertTableExists("migrations_author") self.assertTableNotExists("migrations_tribble") self.assertTableExists("migrations_book") # Fake a roll-back call_command("migrate", "migrations", "zero", fake=True, verbosity=0) # Make sure the tables still exist self.assertTableExists("migrations_author") self.assertTableNotExists("migrations_tribble") self.assertTableExists("migrations_book") # Try to run initial migration with self.assertRaises(DatabaseError): call_command("migrate", "migrations", verbosity=0) # Run initial migration with an explicit --fake-initial with self.assertRaises(DatabaseError): # Fails because "migrations_tribble" does not exist but needs to in # order to make --fake-initial work. call_command("migrate", "migrations", fake_initial=True, verbosity=0) # Fake a apply call_command("migrate", "migrations", fake=True, verbosity=0) # Unmigrate everything call_command("migrate", "migrations", "zero", verbosity=0) # Make sure it's all gone self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") self.assertTableNotExists("migrations_book") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_migrate_conflict_exit(self): """ Makes sure that migrate exits if it detects a conflict. """ with self.assertRaisesMessage(CommandError, "Conflicting migrations detected"): call_command("migrate", "migrations") @ignore_warnings(category=RemovedInDjango110Warning) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_migrate_list(self): """ Tests --list output of migrate command """ out = six.StringIO() with mock.patch('django.core.management.color.supports_color', lambda *args: True): call_command("migrate", list=True, stdout=out, verbosity=0, no_color=False) self.assertEqual( '\x1b[1mmigrations\n\x1b[0m' ' [ ] 0001_initial\n' ' [ ] 0002_second\n', out.getvalue().lower() ) call_command("migrate", "migrations", "0001", verbosity=0) out = six.StringIO() # Giving the explicit app_label tests for selective `show_migration_list` in the command call_command("migrate", "migrations", list=True, stdout=out, verbosity=0, no_color=True) self.assertEqual( 'migrations\n' ' [x] 0001_initial\n' ' [ ] 0002_second\n', out.getvalue().lower() ) # Cleanup by unmigrating everything call_command("migrate", "migrations", "zero", verbosity=0) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_showmigrations_list(self): """ Tests --list output of showmigrations command """ out = six.StringIO() with mock.patch('django.core.management.color.supports_color', lambda *args: True): call_command("showmigrations", format='list', stdout=out, verbosity=0, no_color=False) self.assertEqual( '\x1b[1mmigrations\n\x1b[0m' ' [ ] 0001_initial\n' ' [ ] 0002_second\n', out.getvalue().lower() ) call_command("migrate", "migrations", "0001", verbosity=0) out = six.StringIO() # Giving the explicit app_label tests for selective `show_list` in the command call_command("showmigrations", "migrations", format='list', stdout=out, verbosity=0, no_color=True) self.assertEqual( 'migrations\n' ' [x] 0001_initial\n' ' [ ] 0002_second\n', out.getvalue().lower() ) # Cleanup by unmigrating everything call_command("migrate", "migrations", "zero", verbosity=0) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_run_before"}) def test_showmigrations_plan(self): """ Tests --plan output of showmigrations command """ out = six.StringIO() call_command("showmigrations", format='plan', stdout=out) self.assertIn( "[ ] migrations.0001_initial\n" "[ ] migrations.0003_third\n" "[ ] migrations.0002_second", out.getvalue().lower() ) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out, verbosity=2) self.assertIn( "[ ] migrations.0001_initial\n" "[ ] migrations.0003_third ... (migrations.0001_initial)\n" "[ ] migrations.0002_second ... (migrations.0001_initial)", out.getvalue().lower() ) call_command("migrate", "migrations", "0003", verbosity=0) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out) self.assertIn( "[x] migrations.0001_initial\n" "[x] migrations.0003_third\n" "[ ] migrations.0002_second", out.getvalue().lower() ) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out, verbosity=2) self.assertIn( "[x] migrations.0001_initial\n" "[x] migrations.0003_third ... (migrations.0001_initial)\n" "[ ] migrations.0002_second ... (migrations.0001_initial)", out.getvalue().lower() ) # Cleanup by unmigrating everything call_command("migrate", "migrations", "zero", verbosity=0) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_empty"}) def test_showmigrations_plan_no_migrations(self): """ Tests --plan output of showmigrations command without migrations """ out = six.StringIO() call_command("showmigrations", format='plan', stdout=out) self.assertEqual("", out.getvalue().lower()) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out, verbosity=2) self.assertEqual("", out.getvalue().lower()) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed_complex"}) def test_showmigrations_plan_squashed(self): """ Tests --plan output of showmigrations command with squashed migrations. """ out = six.StringIO() call_command("showmigrations", format='plan', stdout=out) self.assertEqual( "[ ] migrations.1_auto\n" "[ ] migrations.2_auto\n" "[ ] migrations.3_squashed_5\n" "[ ] migrations.6_auto\n" "[ ] migrations.7_auto\n", out.getvalue().lower() ) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out, verbosity=2) self.assertEqual( "[ ] migrations.1_auto\n" "[ ] migrations.2_auto ... (migrations.1_auto)\n" "[ ] migrations.3_squashed_5 ... (migrations.2_auto)\n" "[ ] migrations.6_auto ... (migrations.3_squashed_5)\n" "[ ] migrations.7_auto ... (migrations.6_auto)\n", out.getvalue().lower() ) call_command("migrate", "migrations", "3_squashed_5", verbosity=0) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out) self.assertEqual( "[x] migrations.1_auto\n" "[x] migrations.2_auto\n" "[x] migrations.3_squashed_5\n" "[ ] migrations.6_auto\n" "[ ] migrations.7_auto\n", out.getvalue().lower() ) out = six.StringIO() call_command("showmigrations", format='plan', stdout=out, verbosity=2) self.assertEqual( "[x] migrations.1_auto\n" "[x] migrations.2_auto ... (migrations.1_auto)\n" "[x] migrations.3_squashed_5 ... (migrations.2_auto)\n" "[ ] migrations.6_auto ... (migrations.3_squashed_5)\n" "[ ] migrations.7_auto ... (migrations.6_auto)\n", out.getvalue().lower() ) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_sqlmigrate(self): """ Makes sure that sqlmigrate does something. """ # Make sure the output is wrapped in a transaction out = six.StringIO() call_command("sqlmigrate", "migrations", "0001", stdout=out) output = out.getvalue() self.assertIn(connection.ops.start_transaction_sql(), output) self.assertIn(connection.ops.end_transaction_sql(), output) # Test forwards. All the databases agree on CREATE TABLE, at least. out = six.StringIO() call_command("sqlmigrate", "migrations", "0001", stdout=out) self.assertIn("create table", out.getvalue().lower()) # Cannot generate the reverse SQL unless we've applied the migration. call_command("migrate", "migrations", verbosity=0) # And backwards is a DROP TABLE out = six.StringIO() call_command("sqlmigrate", "migrations", "0001", stdout=out, backwards=True) self.assertIn("drop table", out.getvalue().lower()) # Cleanup by unmigrating everything call_command("migrate", "migrations", "zero", verbosity=0) @override_settings( INSTALLED_APPS=[ "migrations.migrations_test_apps.migrated_app", "migrations.migrations_test_apps.migrated_unapplied_app", "migrations.migrations_test_apps.unmigrated_app"]) def test_regression_22823_unmigrated_fk_to_migrated_model(self): """ https://code.djangoproject.com/ticket/22823 Assuming you have 3 apps, `A`, `B`, and `C`, such that: * `A` has migrations * `B` has a migration we want to apply * `C` has no migrations, but has an FK to `A` When we try to migrate "B", an exception occurs because the "B" was not included in the ProjectState that is used to detect soft-applied migrations. """ call_command("migrate", "migrated_unapplied_app", stdout=six.StringIO()) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}) def test_migrate_record_replaced(self): """ Running a single squashed migration should record all of the original replaced migrations as run. """ recorder = MigrationRecorder(connection) out = six.StringIO() call_command("migrate", "migrations", verbosity=0) call_command("showmigrations", "migrations", stdout=out, no_color=True) self.assertEqual( 'migrations\n' ' [x] 0001_squashed_0002 (2 squashed migrations)\n', out.getvalue().lower() ) applied_migrations = recorder.applied_migrations() self.assertIn(("migrations", "0001_initial"), applied_migrations) self.assertIn(("migrations", "0002_second"), applied_migrations) self.assertIn(("migrations", "0001_squashed_0002"), applied_migrations) # Rollback changes call_command("migrate", "migrations", "zero", verbosity=0) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}) def test_migrate_record_squashed(self): """ Running migrate for a squashed migration should record as run if all of the replaced migrations have been run (#25231). """ recorder = MigrationRecorder(connection) recorder.record_applied("migrations", "0001_initial") recorder.record_applied("migrations", "0002_second") out = six.StringIO() call_command("migrate", "migrations", verbosity=0) call_command("showmigrations", "migrations", stdout=out, no_color=True) self.assertEqual( 'migrations\n' ' [x] 0001_squashed_0002 (2 squashed migrations)\n', out.getvalue().lower() ) self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations() ) # No changes were actually applied so there is nothing to rollback class MakeMigrationsTests(MigrationTestBase): """ Tests running the makemigrations command. """ # Because the `import_module` performed in `MigrationLoader` will cache # the migrations package, we can't reuse the same migration package # between tests. This is only a problem for testing, since `makemigrations` # is normally called in its own process. creation_counter = 0 def setUp(self): MakeMigrationsTests.creation_counter += 1 self.migration_dir = os.path.join(self.test_dir, 'migrations_%d' % self.creation_counter) self.migration_pkg = "migrations.migrations_%d" % self.creation_counter self._old_models = apps.app_configs['migrations'].models.copy() def tearDown(self): apps.app_configs['migrations'].models = self._old_models apps.all_models['migrations'] = self._old_models apps.clear_cache() _cwd = os.getcwd() os.chdir(self.test_dir) try: try: self._rmrf(self.migration_dir) except OSError: pass try: self._rmrf(os.path.join(self.test_dir, "test_migrations_path_doesnt_exist")) except OSError: pass finally: os.chdir(_cwd) def _rmrf(self, dname): if os.path.commonprefix([self.test_dir, os.path.abspath(dname)]) != self.test_dir: return shutil.rmtree(dname) def test_files_content(self): self.assertTableNotExists("migrations_unicodemodel") apps.register_model('migrations', UnicodeModel) with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): call_command("makemigrations", "migrations", verbosity=0) init_file = os.path.join(self.migration_dir, "__init__.py") # Check for existing __init__.py file in migrations folder self.assertTrue(os.path.exists(init_file)) with open(init_file, 'r') as fp: content = force_text(fp.read()) self.assertEqual(content, '') initial_file = os.path.join(self.migration_dir, "0001_initial.py") # Check for existing 0001_initial.py file in migration folder self.assertTrue(os.path.exists(initial_file)) with codecs.open(initial_file, 'r', encoding='utf-8') as fp: content = fp.read() self.assertIn('# -*- coding: utf-8 -*-', content) self.assertIn('migrations.CreateModel', content) if six.PY3: self.assertIn('úñí©óðé µóðéø', content) # Meta.verbose_name self.assertIn('úñí©óðé µóðéøß', content) # Meta.verbose_name_plural self.assertIn('ÚÑâÓÃÉ', content) # title.verbose_name self.assertIn('“Ãjáñgóâ€', content) # title.default else: self.assertIn('\\xfa\\xf1\\xed\\xa9\\xf3\\xf0\\xe9 \\xb5\\xf3\\xf0\\xe9\\xf8', content) # Meta.verbose_name self.assertIn('\\xfa\\xf1\\xed\\xa9\\xf3\\xf0\\xe9 \\xb5\\xf3\\xf0\\xe9\\xf8\\xdf', content) # Meta.verbose_name_plural self.assertIn('\\xda\\xd1\\xcd\\xa2\\xd3\\xd0\\xc9', content) # title.verbose_name self.assertIn('\\u201c\\xd0j\\xe1\\xf1g\\xf3\\u201d', content) # title.default def test_failing_migration(self): # If a migration fails to serialize, it shouldn't generate an empty file. #21280 apps.register_model('migrations', UnserializableModel) with six.assertRaisesRegex(self, ValueError, r'Cannot serialize'): with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): call_command("makemigrations", "migrations", verbosity=0) initial_file = os.path.join(self.migration_dir, "0001_initial.py") self.assertFalse(os.path.exists(initial_file)) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigrations_conflict_exit(self): """ Makes sure that makemigrations exits if it detects a conflict. """ with self.assertRaises(CommandError): call_command("makemigrations") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_makemigrations_merge_no_conflict(self): """ Makes sure that makemigrations exits if in merge mode with no conflicts. """ out = six.StringIO() try: call_command("makemigrations", merge=True, stdout=out) except CommandError: self.fail("Makemigrations errored in merge mode with no conflicts") self.assertIn("No conflicts detected to merge.", out.getvalue()) def test_makemigrations_no_app_sys_exit(self): """ Makes sure that makemigrations exits if a non-existent app is specified. """ err = six.StringIO() with self.assertRaises(SystemExit): call_command("makemigrations", "this_app_does_not_exist", stderr=err) self.assertIn("'this_app_does_not_exist' could not be found.", err.getvalue()) def test_makemigrations_empty_no_app_specified(self): """ Makes sure that makemigrations exits if no app is specified with 'empty' mode. """ with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): self.assertRaises(CommandError, call_command, "makemigrations", empty=True) def test_makemigrations_empty_migration(self): """ Makes sure that makemigrations properly constructs an empty migration. """ with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): try: call_command("makemigrations", "migrations", empty=True, verbosity=0) except CommandError: self.fail("Makemigrations errored in creating empty migration for a proper app.") initial_file = os.path.join(self.migration_dir, "0001_initial.py") # Check for existing 0001_initial.py file in migration folder self.assertTrue(os.path.exists(initial_file)) with codecs.open(initial_file, 'r', encoding='utf-8') as fp: content = fp.read() self.assertIn('# -*- coding: utf-8 -*-', content) # Remove all whitespace to check for empty dependencies and operations content = content.replace(' ', '') self.assertIn('dependencies=[\n]', content) self.assertIn('operations=[\n]', content) def test_makemigrations_no_changes_no_apps(self): """ Makes sure that makemigrations exits when there are no changes and no apps are specified. """ out = six.StringIO() call_command("makemigrations", stdout=out) self.assertIn("No changes detected", out.getvalue()) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_changes"}) def test_makemigrations_no_changes(self): """ Makes sure that makemigrations exits when there are no changes to an app. """ out = six.StringIO() call_command("makemigrations", "migrations", stdout=out) self.assertIn("No changes detected in app 'migrations'", out.getvalue()) def test_makemigrations_migrations_announce(self): """ Makes sure that makemigrations announces the migration at the default verbosity level. """ out = six.StringIO() with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): call_command("makemigrations", "migrations", stdout=out) self.assertIn("Migrations for 'migrations'", out.getvalue()) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_ancestor"}) def test_makemigrations_no_common_ancestor(self): """ Makes sure that makemigrations fails to merge migrations with no common ancestor. """ with self.assertRaises(ValueError) as context: call_command("makemigrations", "migrations", merge=True) exception_message = str(context.exception) self.assertIn("Could not find common ancestor of", exception_message) self.assertIn("0002_second", exception_message) self.assertIn("0002_conflicting_second", exception_message) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigrations_interactive_reject(self): """ Makes sure that makemigrations enters and exits interactive mode properly. """ # Monkeypatch interactive questioner to auto reject old_input = questioner.input questioner.input = lambda _: "N" try: call_command("makemigrations", "migrations", merge=True, interactive=True, verbosity=0) merge_file = os.path.join(self.test_dir, 'test_migrations_conflict', '0003_merge.py') self.assertFalse(os.path.exists(merge_file)) except CommandError: self.fail("Makemigrations failed while running interactive questioner") finally: questioner.input = old_input @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigrations_interactive_accept(self): """ Makes sure that makemigrations enters interactive mode and merges properly. """ # Monkeypatch interactive questioner to auto accept old_input = questioner.input questioner.input = lambda _: "y" out = six.StringIO() try: call_command("makemigrations", "migrations", merge=True, interactive=True, stdout=out) merge_file = os.path.join(self.test_dir, 'test_migrations_conflict', '0003_merge.py') self.assertTrue(os.path.exists(merge_file)) os.remove(merge_file) self.assertFalse(os.path.exists(merge_file)) except CommandError: self.fail("Makemigrations failed while running interactive questioner") finally: questioner.input = old_input self.assertIn("Created new merge migration", force_text(out.getvalue())) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigrations_handle_merge(self): """ Makes sure that makemigrations properly merges the conflicting migrations with --noinput. """ out = six.StringIO() call_command("makemigrations", "migrations", merge=True, interactive=False, stdout=out) output = force_text(out.getvalue()) self.assertIn("Merging migrations", output) self.assertIn("Branch 0002_second", output) self.assertIn("Branch 0002_conflicting_second", output) merge_file = os.path.join(self.test_dir, 'test_migrations_conflict', '0003_merge.py') self.assertTrue(os.path.exists(merge_file)) os.remove(merge_file) self.assertFalse(os.path.exists(merge_file)) self.assertIn("Created new merge migration", output) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigration_merge_dry_run(self): """ Makes sure that makemigrations respects --dry-run option when fixing migration conflicts (#24427). """ out = six.StringIO() call_command("makemigrations", "migrations", dry_run=True, merge=True, interactive=False, stdout=out) merge_file = os.path.join(self.test_dir, '0003_merge.py') self.assertFalse(os.path.exists(merge_file)) output = force_text(out.getvalue()) self.assertIn("Merging migrations", output) self.assertIn("Branch 0002_second", output) self.assertIn("Branch 0002_conflicting_second", output) self.assertNotIn("Created new merge migration", output) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigration_merge_dry_run_verbosity_3(self): """ Makes sure that `makemigrations --merge --dry-run` writes the merge migration file to stdout with `verbosity == 3` (#24427). """ out = six.StringIO() call_command("makemigrations", "migrations", dry_run=True, merge=True, interactive=False, stdout=out, verbosity=3) merge_file = os.path.join(self.test_dir, '0003_merge.py') self.assertFalse(os.path.exists(merge_file)) output = force_text(out.getvalue()) self.assertIn("Merging migrations", output) self.assertIn("Branch 0002_second", output) self.assertIn("Branch 0002_conflicting_second", output) self.assertNotIn("Created new merge migration", output) # Additional output caused by verbosity 3 # The complete merge migration file that would be written self.assertIn("# -*- coding: utf-8 -*-", output) self.assertIn("class Migration(migrations.Migration):", output) self.assertIn("dependencies = [", output) self.assertIn("('migrations', '0002_second')", output) self.assertIn("('migrations', '0002_conflicting_second')", output) self.assertIn("operations = [", output) self.assertIn("]", output) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_default"}) def test_makemigrations_dry_run(self): """ Ticket #22676 -- `makemigrations --dry-run` should not ask for defaults. """ class SillyModel(models.Model): silly_field = models.BooleanField(default=False) silly_date = models.DateField() # Added field without a default class Meta: app_label = "migrations" out = six.StringIO() call_command("makemigrations", "migrations", dry_run=True, stdout=out) # Output the expected changes directly, without asking for defaults self.assertIn("Add field silly_date to sillymodel", out.getvalue()) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_default"}) def test_makemigrations_dry_run_verbosity_3(self): """ Ticket #22675 -- Allow `makemigrations --dry-run` to output the migrations file to stdout (with verbosity == 3). """ class SillyModel(models.Model): silly_field = models.BooleanField(default=False) silly_char = models.CharField(default="") class Meta: app_label = "migrations" out = six.StringIO() call_command("makemigrations", "migrations", dry_run=True, stdout=out, verbosity=3) # Normal --dry-run output self.assertIn("- Add field silly_char to sillymodel", out.getvalue()) # Additional output caused by verbosity 3 # The complete migrations file that would be written self.assertIn("# -*- coding: utf-8 -*-", out.getvalue()) self.assertIn("class Migration(migrations.Migration):", out.getvalue()) self.assertIn("dependencies = [", out.getvalue()) self.assertIn("('migrations', '0001_initial'),", out.getvalue()) self.assertIn("migrations.AddField(", out.getvalue()) self.assertIn("model_name='sillymodel',", out.getvalue()) self.assertIn("name='silly_char',", out.getvalue()) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_path_doesnt_exist.foo.bar"}) def test_makemigrations_migrations_modules_path_not_exist(self): """ Ticket #22682 -- Makemigrations fails when specifying custom location for migration files (using MIGRATION_MODULES) if the custom path doesn't already exist. """ class SillyModel(models.Model): silly_field = models.BooleanField(default=False) class Meta: app_label = "migrations" out = six.StringIO() call_command("makemigrations", "migrations", stdout=out) # Command output indicates the migration is created. self.assertIn(" - Create model SillyModel", out.getvalue()) # Migrations file is actually created in the expected path. self.assertTrue(os.path.isfile(os.path.join(self.test_dir, "test_migrations_path_doesnt_exist", "foo", "bar", "0001_initial.py"))) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"}) def test_makemigrations_interactive_by_default(self): """ Makes sure that the user is prompted to merge by default if there are conflicts and merge is True. Answer negative to differentiate it from behavior when --noinput is specified. """ # Monkeypatch interactive questioner to auto reject old_input = questioner.input questioner.input = lambda _: "N" out = six.StringIO() merge_file = os.path.join(self.test_dir, 'test_migrations_conflict', '0003_merge.py') try: call_command("makemigrations", "migrations", merge=True, stdout=out) # This will fail if interactive is False by default self.assertFalse(os.path.exists(merge_file)) except CommandError: self.fail("Makemigrations failed while running interactive questioner") finally: questioner.input = old_input if os.path.exists(merge_file): os.remove(merge_file) self.assertNotIn("Created new merge migration", out.getvalue()) @override_settings( MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_changes"}, INSTALLED_APPS=[ "migrations", "migrations.migrations_test_apps.unspecified_app_with_conflict"]) def test_makemigrations_unspecified_app_with_conflict_no_merge(self): """ Makes sure that makemigrations does not raise a CommandError when an unspecified app has conflicting migrations. """ try: call_command("makemigrations", "migrations", merge=False, verbosity=0) except CommandError: self.fail("Makemigrations fails resolving conflicts in an unspecified app") @override_settings( INSTALLED_APPS=[ "migrations.migrations_test_apps.migrated_app", "migrations.migrations_test_apps.unspecified_app_with_conflict"]) def test_makemigrations_unspecified_app_with_conflict_merge(self): """ Makes sure that makemigrations does not create a merge for an unspecified app even if it has conflicting migrations. """ # Monkeypatch interactive questioner to auto accept old_input = questioner.input questioner.input = lambda _: "y" out = six.StringIO() merge_file = os.path.join(self.test_dir, 'migrations_test_apps', 'unspecified_app_with_conflict', 'migrations', '0003_merge.py') try: call_command("makemigrations", "migrated_app", merge=True, interactive=True, stdout=out) self.assertFalse(os.path.exists(merge_file)) self.assertIn("No conflicts detected to merge.", out.getvalue()) except CommandError: self.fail("Makemigrations fails resolving conflicts in an unspecified app") finally: questioner.input = old_input if os.path.exists(merge_file): os.remove(merge_file) def test_makemigrations_with_custom_name(self): """ Makes sure that makemigrations generate a custom migration. """ def cmd(migration_count, migration_name, *args): with override_settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): try: call_command("makemigrations", "migrations", "--verbosity", "0", "--name", migration_name, *args) except CommandError: self.fail("Makemigrations errored in creating empty migration with custom name for a proper app.") migration_file = os.path.join(self.migration_dir, "%s_%s.py" % (migration_count, migration_name)) # Check for existing migration file in migration folder self.assertTrue(os.path.exists(migration_file)) with codecs.open(migration_file, "r", encoding="utf-8") as fp: content = fp.read() self.assertIn("# -*- coding: utf-8 -*-", content) content = content.replace(" ", "") return content # generate an initial migration migration_name_0001 = "my_initial_migration" content = cmd("0001", migration_name_0001) self.assertIn("dependencies=[\n]", content) # Python 3.3+ importlib caches os.listdir() on some platforms like # Mac OS X (#23850). if hasattr(importlib, 'invalidate_caches'): importlib.invalidate_caches() # generate an empty migration migration_name_0002 = "my_custom_migration" content = cmd("0002", migration_name_0002, "--empty") self.assertIn("dependencies=[\n('migrations','0001_%s'),\n]" % migration_name_0001, content) self.assertIn("operations=[\n]", content) def test_makemigrations_exit(self): """ makemigrations --exit should exit with sys.exit(1) when there are no changes to an app. """ with self.settings(MIGRATION_MODULES={"migrations": self.migration_pkg}): call_command("makemigrations", "--exit", "migrations", verbosity=0) with self.settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_changes"}): with self.assertRaises(SystemExit): call_command("makemigrations", "--exit", "migrations", verbosity=0) class SquashMigrationsTest(MigrationTestBase): """ Tests running the squashmigrations command. """ path = "test_migrations/0001_squashed_0002_second.py" path = os.path.join(MigrationTestBase.test_dir, path) def tearDown(self): if os.path.exists(self.path): os.remove(self.path) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_squashmigrations_squashes(self): """ Tests that squashmigrations squashes migrations. """ call_command("squashmigrations", "migrations", "0002", interactive=False, verbosity=0) self.assertTrue(os.path.exists(self.path)) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_squashmigrations_optimizes(self): """ Tests that squashmigrations optimizes operations. """ out = six.StringIO() call_command("squashmigrations", "migrations", "0002", interactive=False, verbosity=1, stdout=out) self.assertIn("Optimized from 7 operations to 5 operations.", force_text(out.getvalue())) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_ticket_23799_squashmigrations_no_optimize(self): """ Makes sure that squashmigrations --no-optimize really doesn't optimize operations. """ out = six.StringIO() call_command("squashmigrations", "migrations", "0002", interactive=False, verbosity=1, no_optimize=True, stdout=out) self.assertIn("Skipping optimization", force_text(out.getvalue())) Django-1.8.7/tests/migrations/test_multidb.py0000664000175000017500000001537512625116214020741 0ustar timtim00000000000000import unittest from django.db import connection, migrations, models from django.db.migrations.state import ProjectState from django.test import override_settings from .test_operations import OperationTestBase try: import sqlparse except ImportError: sqlparse = None class AgnosticRouter(object): """ A router that doesn't have an opinion regarding migrating. """ def allow_migrate(self, db, app_label, **hints): return None class MigrateNothingRouter(object): """ A router that doesn't allow migrating. """ def allow_migrate(self, db, app_label, **hints): return False class MigrateEverythingRouter(object): """ A router that always allows migrating. """ def allow_migrate(self, db, app_label, **hints): return True class MigrateWhenFooRouter(object): """ A router that allows migrating depending on a hint. """ def allow_migrate(self, db, app_label, **hints): return hints.get('foo', False) class MultiDBOperationTests(OperationTestBase): multi_db = True def _test_create_model(self, app_label, should_run): """ Tests that CreateModel honours multi-db settings. """ operation = migrations.CreateModel( "Pony", [("id", models.AutoField(primary_key=True))], ) # Test the state alteration project_state = ProjectState() new_state = project_state.clone() operation.state_forwards(app_label, new_state) # Test the database alteration self.assertTableNotExists("%s_pony" % app_label) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, project_state, new_state) if should_run: self.assertTableExists("%s_pony" % app_label) else: self.assertTableNotExists("%s_pony" % app_label) # And test reversal with connection.schema_editor() as editor: operation.database_backwards(app_label, editor, new_state, project_state) self.assertTableNotExists("%s_pony" % app_label) @override_settings(DATABASE_ROUTERS=[AgnosticRouter()]) def test_create_model(self): """ Test when router doesn't have an opinion (i.e. CreateModel should run). """ self._test_create_model("test_mltdb_crmo", should_run=True) @override_settings(DATABASE_ROUTERS=[MigrateNothingRouter()]) def test_create_model2(self): """ Test when router returns False (i.e. CreateModel shouldn't run). """ self._test_create_model("test_mltdb_crmo2", should_run=False) @override_settings(DATABASE_ROUTERS=[MigrateEverythingRouter()]) def test_create_model3(self): """ Test when router returns True (i.e. CreateModel should run). """ self._test_create_model("test_mltdb_crmo3", should_run=True) def test_create_model4(self): """ Test multiple routers. """ with override_settings(DATABASE_ROUTERS=[AgnosticRouter(), AgnosticRouter()]): self._test_create_model("test_mltdb_crmo4", should_run=True) with override_settings(DATABASE_ROUTERS=[MigrateNothingRouter(), MigrateEverythingRouter()]): self._test_create_model("test_mltdb_crmo4", should_run=False) with override_settings(DATABASE_ROUTERS=[MigrateEverythingRouter(), MigrateNothingRouter()]): self._test_create_model("test_mltdb_crmo4", should_run=True) def _test_run_sql(self, app_label, should_run, hints=None): with override_settings(DATABASE_ROUTERS=[MigrateEverythingRouter()]): project_state = self.set_up_test_model(app_label) sql = """ INSERT INTO {0}_pony (pink, weight) VALUES (1, 3.55); INSERT INTO {0}_pony (pink, weight) VALUES (3, 5.0); """.format(app_label) operation = migrations.RunSQL(sql, hints=hints or {}) # Test the state alteration does nothing new_state = project_state.clone() operation.state_forwards(app_label, new_state) self.assertEqual(new_state, project_state) # Test the database alteration self.assertEqual(project_state.apps.get_model(app_label, "Pony").objects.count(), 0) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, project_state, new_state) Pony = project_state.apps.get_model(app_label, "Pony") if should_run: self.assertEqual(Pony.objects.count(), 2) else: self.assertEqual(Pony.objects.count(), 0) @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse") @override_settings(DATABASE_ROUTERS=[MigrateNothingRouter()]) def test_run_sql(self): self._test_run_sql("test_mltdb_runsql", should_run=False) @unittest.skipIf(sqlparse is None and connection.features.requires_sqlparse_for_splitting, "Missing sqlparse") @override_settings(DATABASE_ROUTERS=[MigrateWhenFooRouter()]) def test_run_sql2(self): self._test_run_sql("test_mltdb_runsql2", should_run=False) self._test_run_sql("test_mltdb_runsql2", should_run=True, hints={'foo': True}) def _test_run_python(self, app_label, should_run, hints=None): with override_settings(DATABASE_ROUTERS=[MigrateEverythingRouter()]): project_state = self.set_up_test_model(app_label) # Create the operation def inner_method(models, schema_editor): Pony = models.get_model(app_label, "Pony") Pony.objects.create(pink=1, weight=3.55) Pony.objects.create(weight=5) operation = migrations.RunPython(inner_method, hints=hints or {}) # Test the state alteration does nothing new_state = project_state.clone() operation.state_forwards(app_label, new_state) self.assertEqual(new_state, project_state) # Test the database alteration self.assertEqual(project_state.apps.get_model(app_label, "Pony").objects.count(), 0) with connection.schema_editor() as editor: operation.database_forwards(app_label, editor, project_state, new_state) Pony = project_state.apps.get_model(app_label, "Pony") if should_run: self.assertEqual(Pony.objects.count(), 2) else: self.assertEqual(Pony.objects.count(), 0) @override_settings(DATABASE_ROUTERS=[MigrateNothingRouter()]) def test_run_python(self): self._test_run_python("test_mltdb_runpython", should_run=False) @override_settings(DATABASE_ROUTERS=[MigrateWhenFooRouter()]) def test_run_python2(self): self._test_run_python("test_mltdb_runpython2", should_run=False) self._test_run_python("test_mltdb_runpython2", should_run=True, hints={'foo': True}) Django-1.8.7/tests/migrations/migrations_test_apps/0000775000175000017500000000000012625116243022115 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_app/0000775000175000017500000000000012625116243024551 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_app/migrations/0000775000175000017500000000000012625116243026725 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_app/migrations/__init__.py0000664000175000017500000000000012603513307031022 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_app/migrations/0001_initial.py0000664000175000017500000000142412603513307031367 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/migrations_test_apps/migrated_app/__init__.py0000664000175000017500000000000012603513307026646 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_app/models.py0000664000175000017500000000000012603513307026372 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/with_package_model/0000775000175000017500000000000012625116243025723 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/with_package_model/models/0000775000175000017500000000000012625116243027206 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/with_package_model/models/__init__.py0000664000175000017500000000000012603513307031303 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/with_package_model/__init__.py0000664000175000017500000000000012603513307030020 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unmigrated_app/0000775000175000017500000000000012625116243025114 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unmigrated_app/__init__.py0000664000175000017500000000000012603513307027211 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unmigrated_app/models.py0000664000175000017500000000044412625116214026751 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models class SillyModel(models.Model): silly_field = models.BooleanField(default=False) silly_tribble = models.ForeignKey("migrations.Tribble") is_trouble = models.BooleanField(default=True) Django-1.8.7/tests/migrations/migrations_test_apps/migrated_unapplied_app/0000775000175000017500000000000012625116243026612 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_unapplied_app/migrations/0000775000175000017500000000000012625116243030766 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_unapplied_app/migrations/__init__.py0000664000175000017500000000000012603513307033063 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_unapplied_app/migrations/0001_initial.py0000664000175000017500000000107712603513307033434 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "OtherAuthor", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/migrated_unapplied_app/__init__.py0000664000175000017500000000000012603513307030707 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/migrated_unapplied_app/models.py0000664000175000017500000000053312603513307030446 0ustar timtim00000000000000from django.db import models class OtherAuthor(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) slug = models.SlugField(null=True) age = models.IntegerField(default=0) silly_field = models.BooleanField(default=False) class Meta: app_label = "migrated_unapplied_app" Django-1.8.7/tests/migrations/migrations_test_apps/normal/0000775000175000017500000000000012625116243023405 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/normal/__init__.py0000664000175000017500000000000012603513307025502 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/__init__.py0000664000175000017500000000000012603513307024212 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/0000775000175000017500000000000012625116243025000 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/migrations/0000775000175000017500000000000012625116243027154 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/migrations/0003_a3.py0000664000175000017500000000123012625116214030465 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_c', '0002_c2'), ('lookuperror_b', '0002_b2'), ('lookuperror_a', '0002_a2'), ] operations = [ migrations.CreateModel( name='A3', fields=[ ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), ('b2', models.ForeignKey(to='lookuperror_b.B2')), ('c2', models.ForeignKey(to='lookuperror_c.C2')), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/migrations/0004_a4.py0000664000175000017500000000071012625116214030471 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_a', '0003_a3'), ] operations = [ migrations.CreateModel( name='A4', fields=[ ('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/migrations/__init__.py0000664000175000017500000000000012625116214031251 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/migrations/0002_a2.py0000664000175000017500000000071512625116214030472 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_a', '0001_initial'), ] operations = [ migrations.CreateModel( name='A2', fields=[ ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/migrations/0001_initial.py0000664000175000017500000000064212625116214031617 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='A1', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/__init__.py0000664000175000017500000000000012625116214027075 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_a/models.py0000664000175000017500000000037612625116214026641 0ustar timtim00000000000000from django.db import models class A1(models.Model): pass class A2(models.Model): pass class A3(models.Model): b2 = models.ForeignKey('lookuperror_b.B2') c2 = models.ForeignKey('lookuperror_c.C2') class A4(models.Model): pass Django-1.8.7/tests/migrations/migrations_test_apps/without_init_file/0000775000175000017500000000000012625116243025642 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/without_init_file/migrations/0000775000175000017500000000000012625116243030016 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/without_init_file/migrations/.keep0000664000175000017500000000000012603513307030727 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/without_init_file/__init__.py0000664000175000017500000000000012603513307027737 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/0000775000175000017500000000000012625116243025001 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/migrations/0000775000175000017500000000000012625116243027155 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/migrations/__init__.py0000664000175000017500000000000012625116214031252 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/migrations/0002_b2.py0000664000175000017500000000106512625116214030473 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_a', '0002_a2'), ('lookuperror_b', '0001_initial'), ] operations = [ migrations.CreateModel( name='B2', fields=[ ('id', models.AutoField(primary_key=True, verbose_name='ID', auto_created=True, serialize=False)), ('a1', models.ForeignKey(to='lookuperror_a.A1')), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/migrations/0003_b3.py0000664000175000017500000000071012625116214030471 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_b', '0002_b2'), ] operations = [ migrations.CreateModel( name='B3', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, primary_key=True, auto_created=True)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/migrations/0001_initial.py0000664000175000017500000000064212625116214031620 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='B1', fields=[ ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True, verbose_name='ID')), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/__init__.py0000664000175000017500000000000012625116214027076 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_b/models.py0000664000175000017500000000025412625116214026635 0ustar timtim00000000000000from django.db import models class B1(models.Model): pass class B2(models.Model): a1 = models.ForeignKey('lookuperror_a.A1') class B3(models.Model): pass Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/0000775000175000017500000000000012625116243025002 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/migrations/0000775000175000017500000000000012625116243027156 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/migrations/0003_c3.py0000664000175000017500000000071012625116214030473 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_c', '0002_c2'), ] operations = [ migrations.CreateModel( name='C3', fields=[ ('id', models.AutoField(auto_created=True, serialize=False, verbose_name='ID', primary_key=True)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/migrations/__init__.py0000664000175000017500000000000012625116214031253 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/migrations/0002_c2.py0000664000175000017500000000106512625116214030475 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('lookuperror_a', '0002_a2'), ('lookuperror_c', '0001_initial'), ] operations = [ migrations.CreateModel( name='C2', fields=[ ('id', models.AutoField(auto_created=True, verbose_name='ID', primary_key=True, serialize=False)), ('a1', models.ForeignKey(to='lookuperror_a.A1')), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/migrations/0001_initial.py0000664000175000017500000000064212625116214031621 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='C1', fields=[ ('id', models.AutoField(serialize=False, verbose_name='ID', auto_created=True, primary_key=True)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/__init__.py0000664000175000017500000000000012625116214027077 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/lookuperror_c/models.py0000664000175000017500000000025412625116214026636 0ustar timtim00000000000000from django.db import models class C1(models.Model): pass class C2(models.Model): a1 = models.ForeignKey('lookuperror_a.A1') class C3(models.Model): pass Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/0000775000175000017500000000000012625116243023704 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/book_app/0000775000175000017500000000000012625116243025476 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/book_app/migrations/0000775000175000017500000000000012625116243027652 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/book_app/migrations/__init__.py0000664000175000017500000000000012625116214031747 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/book_app/migrations/0001_initial.py0000664000175000017500000000107112625116214032312 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('author_app', '0001_initial'), ] operations = [ migrations.CreateModel( name='Book', fields=[ ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True)), ('title', models.CharField(max_length=50)), ('author', models.ForeignKey('author_app.Author')), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/book_app/__init__.py0000664000175000017500000000000012625116214027573 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/__init__.py0000664000175000017500000000000012625116214026001 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/author_app/0000775000175000017500000000000012625116243026046 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/author_app/migrations/0000775000175000017500000000000012625116243030222 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/author_app/migrations/0002_alter_id.py0000664000175000017500000000074712625116214033026 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ('author_app', '0001_initial'), ('book_app', '0001_initial'), # Forces the book table to alter the FK ] operations = [ migrations.AlterField( model_name='author', name='id', field=models.CharField(max_length=10, primary_key=True), ), ] Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/author_app/migrations/__init__.py0000664000175000017500000000000012625116214032317 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/author_app/migrations/0001_initial.py0000664000175000017500000000071612625116214032667 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Author', fields=[ ('id', models.AutoField(serialize=False, auto_created=True, primary_key=True)), ('name', models.CharField(max_length=50)), ], ), ] Django-1.8.7/tests/migrations/migrations_test_apps/alter_fk/author_app/__init__.py0000664000175000017500000000000012625116214030143 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/0000775000175000017500000000000012625116243030167 5ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0000775000175000017500000000000012625116243032343 5ustar timtim00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0002_second.pyDjango-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0002_sec0000664000175000017500000000110612603513307033475 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("unspecified_app_with_conflict", "0001_initial")] operations = [ migrations.DeleteModel("Tribble"), migrations.RemoveField("Author", "silly_field"), migrations.AddField("Author", "rating", models.IntegerField(default=0)), migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ], ) ] ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/__init__.pyDjango-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/__init__0000664000175000017500000000000012603513307034011 0ustar timtim00000000000000././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0002_conflicting_second.pyDjango-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0002_con0000664000175000017500000000062312603513307033505 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("unspecified_app_with_conflict", "0001_initial")] operations = [ migrations.CreateModel( "Something", [ ("id", models.AutoField(primary_key=True)), ], ) ] ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0001_initial.pyDjango-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/migrations/0001_ini0000664000175000017500000000142412603513307033504 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("fluffy", models.BooleanField(default=True)), ], ) ] Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/__init__.py0000664000175000017500000000000012603513307032264 0ustar timtim00000000000000Django-1.8.7/tests/migrations/migrations_test_apps/unspecified_app_with_conflict/models.py0000664000175000017500000000000012603513307032010 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_writer.py0000664000175000017500000005123112625116214020604 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import datetime import math import os import re import tokenize import unittest import custom_migration_operations.more_operations import custom_migration_operations.operations from django.conf import settings from django.core.validators import EmailValidator, RegexValidator from django.db import migrations, models from django.db.migrations.writer import ( MigrationWriter, OperationWriter, SettingsReference, ) from django.test import SimpleTestCase, TestCase, ignore_warnings from django.utils import datetime_safe, six from django.utils._os import upath from django.utils.deconstruct import deconstructible from django.utils.timezone import FixedOffset, get_default_timezone, utc from django.utils.translation import ugettext_lazy as _ from .models import FoodManager, FoodQuerySet class TestModel1(object): def upload_to(self): return "somewhere dynamic" thing = models.FileField(upload_to=upload_to) class OperationWriterTests(SimpleTestCase): def test_empty_signature(self): operation = custom_migration_operations.operations.TestOperation() buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.TestOperation(\n' '),' ) def test_args_signature(self): operation = custom_migration_operations.operations.ArgsOperation(1, 2) buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.ArgsOperation(\n' ' arg1=1,\n' ' arg2=2,\n' '),' ) def test_kwargs_signature(self): operation = custom_migration_operations.operations.KwargsOperation(kwarg1=1) buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.KwargsOperation(\n' ' kwarg1=1,\n' '),' ) def test_args_kwargs_signature(self): operation = custom_migration_operations.operations.ArgsKwargsOperation(1, 2, kwarg2=4) buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.ArgsKwargsOperation(\n' ' arg1=1,\n' ' arg2=2,\n' ' kwarg2=4,\n' '),' ) def test_nested_args_signature(self): operation = custom_migration_operations.operations.ArgsOperation( custom_migration_operations.operations.ArgsOperation(1, 2), custom_migration_operations.operations.KwargsOperation(kwarg1=3, kwarg2=4) ) buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.ArgsOperation(\n' ' arg1=custom_migration_operations.operations.ArgsOperation(\n' ' arg1=1,\n' ' arg2=2,\n' ' ),\n' ' arg2=custom_migration_operations.operations.KwargsOperation(\n' ' kwarg1=3,\n' ' kwarg2=4,\n' ' ),\n' '),' ) def test_multiline_args_signature(self): operation = custom_migration_operations.operations.ArgsOperation("test\n arg1", "test\narg2") buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, "custom_migration_operations.operations.ArgsOperation(\n" " arg1='test\\n arg1',\n" " arg2='test\\narg2',\n" ")," ) def test_expand_args_signature(self): operation = custom_migration_operations.operations.ExpandArgsOperation([1, 2]) buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.ExpandArgsOperation(\n' ' arg=[\n' ' 1,\n' ' 2,\n' ' ],\n' '),' ) def test_nested_operation_expand_args_signature(self): operation = custom_migration_operations.operations.ExpandArgsOperation( arg=[ custom_migration_operations.operations.KwargsOperation( kwarg1=1, kwarg2=2, ), ] ) buff, imports = OperationWriter(operation, indentation=0).serialize() self.assertEqual(imports, {'import custom_migration_operations.operations'}) self.assertEqual( buff, 'custom_migration_operations.operations.ExpandArgsOperation(\n' ' arg=[\n' ' custom_migration_operations.operations.KwargsOperation(\n' ' kwarg1=1,\n' ' kwarg2=2,\n' ' ),\n' ' ],\n' '),' ) class WriterTests(TestCase): """ Tests the migration writer (makes migration files from Migration instances) """ def safe_exec(self, string, value=None): l = {} try: exec(string, globals(), l) except Exception as e: if value: self.fail("Could not exec %r (from value %r): %s" % (string.strip(), value, e)) else: self.fail("Could not exec %r: %s" % (string.strip(), e)) return l def serialize_round_trip(self, value): string, imports = MigrationWriter.serialize(value) return self.safe_exec("%s\ntest_value_result = %s" % ("\n".join(imports), string), value)['test_value_result'] def assertSerializedEqual(self, value): self.assertEqual(self.serialize_round_trip(value), value) def assertSerializedResultEqual(self, value, target): self.assertEqual(MigrationWriter.serialize(value), target) def assertSerializedFieldEqual(self, value): new_value = self.serialize_round_trip(value) self.assertEqual(value.__class__, new_value.__class__) self.assertEqual(value.max_length, new_value.max_length) self.assertEqual(value.null, new_value.null) self.assertEqual(value.unique, new_value.unique) def test_serialize_numbers(self): self.assertSerializedEqual(1) self.assertSerializedEqual(1.2) self.assertTrue(math.isinf(self.serialize_round_trip(float("inf")))) self.assertTrue(math.isinf(self.serialize_round_trip(float("-inf")))) self.assertTrue(math.isnan(self.serialize_round_trip(float("nan")))) def test_serialize_constants(self): self.assertSerializedEqual(None) self.assertSerializedEqual(True) self.assertSerializedEqual(False) def test_serialize_strings(self): self.assertSerializedEqual(b"foobar") string, imports = MigrationWriter.serialize(b"foobar") self.assertEqual(string, "b'foobar'") self.assertSerializedEqual("föobár") string, imports = MigrationWriter.serialize("foobar") self.assertEqual(string, "'foobar'") def test_serialize_multiline_strings(self): self.assertSerializedEqual(b"foo\nbar") string, imports = MigrationWriter.serialize(b"foo\nbar") self.assertEqual(string, "b'foo\\nbar'") self.assertSerializedEqual("föo\nbár") string, imports = MigrationWriter.serialize("foo\nbar") self.assertEqual(string, "'foo\\nbar'") def test_serialize_collections(self): self.assertSerializedEqual({1: 2}) self.assertSerializedEqual(["a", 2, True, None]) self.assertSerializedEqual({2, 3, "eighty"}) self.assertSerializedEqual({"lalalala": ["yeah", "no", "maybe"]}) self.assertSerializedEqual(_('Hello')) def test_serialize_builtin_types(self): self.assertSerializedEqual([list, tuple, dict, set, frozenset]) self.assertSerializedResultEqual( [list, tuple, dict, set, frozenset], ("[list, tuple, dict, set, frozenset]", set()) ) def test_serialize_functions(self): with six.assertRaisesRegex(self, ValueError, 'Cannot serialize function: lambda'): self.assertSerializedEqual(lambda x: 42) self.assertSerializedEqual(models.SET_NULL) string, imports = MigrationWriter.serialize(models.SET(42)) self.assertEqual(string, 'models.SET(42)') self.serialize_round_trip(models.SET(42)) def test_serialize_datetime(self): self.assertSerializedEqual(datetime.datetime.utcnow()) self.assertSerializedEqual(datetime.datetime.utcnow) self.assertSerializedEqual(datetime.datetime.today()) self.assertSerializedEqual(datetime.datetime.today) self.assertSerializedEqual(datetime.date.today()) self.assertSerializedEqual(datetime.date.today) self.assertSerializedEqual(datetime.datetime.now().time()) self.assertSerializedEqual(datetime.datetime(2014, 1, 1, 1, 1, tzinfo=get_default_timezone())) self.assertSerializedEqual(datetime.datetime(2013, 12, 31, 22, 1, tzinfo=FixedOffset(180))) self.assertSerializedResultEqual( datetime.datetime(2014, 1, 1, 1, 1), ("datetime.datetime(2014, 1, 1, 1, 1)", {'import datetime'}) ) self.assertSerializedResultEqual( datetime.datetime(2012, 1, 1, 1, 1, tzinfo=utc), ( "datetime.datetime(2012, 1, 1, 1, 1, tzinfo=utc)", {'import datetime', 'from django.utils.timezone import utc'}, ) ) def test_serialize_datetime_safe(self): self.assertSerializedResultEqual( datetime_safe.date(2014, 3, 31), ("datetime.date(2014, 3, 31)", {'import datetime'}) ) self.assertSerializedResultEqual( datetime_safe.time(10, 25), ("datetime.time(10, 25)", {'import datetime'}) ) self.assertSerializedResultEqual( datetime_safe.datetime(2014, 3, 31, 16, 4, 31), ("datetime.datetime(2014, 3, 31, 16, 4, 31)", {'import datetime'}) ) def test_serialize_fields(self): self.assertSerializedFieldEqual(models.CharField(max_length=255)) self.assertSerializedFieldEqual(models.TextField(null=True, blank=True)) def test_serialize_settings(self): self.assertSerializedEqual(SettingsReference(settings.AUTH_USER_MODEL, "AUTH_USER_MODEL")) self.assertSerializedResultEqual( SettingsReference("someapp.model", "AUTH_USER_MODEL"), ("settings.AUTH_USER_MODEL", {"from django.conf import settings"}) ) self.assertSerializedResultEqual( ((x, x * x) for x in range(3)), ("((0, 0), (1, 1), (2, 4))", set()) ) def test_serialize_compiled_regex(self): """ Make sure compiled regex can be serialized. """ regex = re.compile(r'^\w+$', re.U) self.assertSerializedEqual(regex) def test_serialize_class_based_validators(self): """ Ticket #22943: Test serialization of class-based validators, including compiled regexes. """ validator = RegexValidator(message="hello") string = MigrationWriter.serialize(validator)[0] self.assertEqual(string, "django.core.validators.RegexValidator(message='hello')") self.serialize_round_trip(validator) # Test with a compiled regex. validator = RegexValidator(regex=re.compile(r'^\w+$', re.U)) string = MigrationWriter.serialize(validator)[0] self.assertEqual(string, "django.core.validators.RegexValidator(regex=re.compile('^\\\\w+$', 32))") self.serialize_round_trip(validator) # Test a string regex with flag validator = RegexValidator(r'^[0-9]+$', flags=re.U) string = MigrationWriter.serialize(validator)[0] self.assertEqual(string, "django.core.validators.RegexValidator('^[0-9]+$', flags=32)") self.serialize_round_trip(validator) # Test message and code validator = RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid') string = MigrationWriter.serialize(validator)[0] self.assertEqual(string, "django.core.validators.RegexValidator('^[-a-zA-Z0-9_]+$', 'Invalid', 'invalid')") self.serialize_round_trip(validator) # Test with a subclass. validator = EmailValidator(message="hello") string = MigrationWriter.serialize(validator)[0] self.assertEqual(string, "django.core.validators.EmailValidator(message='hello')") self.serialize_round_trip(validator) validator = deconstructible(path="migrations.test_writer.EmailValidator")(EmailValidator)(message="hello") string = MigrationWriter.serialize(validator)[0] self.assertEqual(string, "migrations.test_writer.EmailValidator(message='hello')") validator = deconstructible(path="custom.EmailValidator")(EmailValidator)(message="hello") with six.assertRaisesRegex(self, ImportError, "No module named '?custom'?"): MigrationWriter.serialize(validator) validator = deconstructible(path="django.core.validators.EmailValidator2")(EmailValidator)(message="hello") with self.assertRaisesMessage(ValueError, "Could not find object EmailValidator2 in django.core.validators."): MigrationWriter.serialize(validator) def test_serialize_empty_nonempty_tuple(self): """ Ticket #22679: makemigrations generates invalid code for (an empty tuple) default_permissions = () """ empty_tuple = () one_item_tuple = ('a',) many_items_tuple = ('a', 'b', 'c') self.assertSerializedEqual(empty_tuple) self.assertSerializedEqual(one_item_tuple) self.assertSerializedEqual(many_items_tuple) @unittest.skipUnless(six.PY2, "Only applies on Python 2") def test_serialize_direct_function_reference(self): """ Ticket #22436: You cannot use a function straight from its body (e.g. define the method and use it in the same body) """ with self.assertRaises(ValueError): self.serialize_round_trip(TestModel1.thing) def test_serialize_local_function_reference(self): """ Neither py2 or py3 can serialize a reference in a local scope. """ class TestModel2(object): def upload_to(self): return "somewhere dynamic" thing = models.FileField(upload_to=upload_to) with self.assertRaises(ValueError): self.serialize_round_trip(TestModel2.thing) def test_serialize_local_function_reference_message(self): """ Make sure user is seeing which module/function is the issue """ class TestModel2(object): def upload_to(self): return "somewhere dynamic" thing = models.FileField(upload_to=upload_to) with six.assertRaisesRegex(self, ValueError, '^Could not find function upload_to in migrations.test_writer'): self.serialize_round_trip(TestModel2.thing) def test_serialize_managers(self): self.assertSerializedEqual(models.Manager()) self.assertSerializedResultEqual( FoodQuerySet.as_manager(), ('migrations.models.FoodQuerySet.as_manager()', {'import migrations.models'}) ) self.assertSerializedEqual(FoodManager('a', 'b')) self.assertSerializedEqual(FoodManager('x', 'y', c=3, d=4)) def test_serialize_timedelta(self): self.assertSerializedEqual(datetime.timedelta()) self.assertSerializedEqual(datetime.timedelta(minutes=42)) def test_simple_migration(self): """ Tests serializing a simple migration. """ fields = { 'charfield': models.DateTimeField(default=datetime.datetime.utcnow), 'datetimefield': models.DateTimeField(default=datetime.datetime.utcnow), } options = { 'verbose_name': 'My model', 'verbose_name_plural': 'My models', } migration = type(str("Migration"), (migrations.Migration,), { "operations": [ migrations.CreateModel("MyModel", tuple(fields.items()), options, (models.Model,)), migrations.CreateModel("MyModel2", tuple(fields.items()), bases=(models.Model,)), migrations.CreateModel(name="MyModel3", fields=tuple(fields.items()), options=options, bases=(models.Model,)), migrations.DeleteModel("MyModel"), migrations.AddField("OtherModel", "datetimefield", fields["datetimefield"]), ], "dependencies": [("testapp", "some_other_one")], }) writer = MigrationWriter(migration) output = writer.as_string() # It should NOT be unicode. self.assertIsInstance(output, six.binary_type, "Migration as_string returned unicode") # We don't test the output formatting - that's too fragile. # Just make sure it runs for now, and that things look alright. result = self.safe_exec(output) self.assertIn("Migration", result) # In order to preserve compatibility with Python 3.2 unicode literals # prefix shouldn't be added to strings. tokens = tokenize.generate_tokens(six.StringIO(str(output)).readline) for token_type, token_source, (srow, scol), __, line in tokens: if token_type == tokenize.STRING: self.assertFalse( token_source.startswith('u'), "Unicode literal prefix found at %d:%d: %r" % ( srow, scol, line.strip() ) ) # Silence warning on Python 2: Not importing directory # 'tests/migrations/migrations_test_apps/without_init_file/migrations': # missing __init__.py @ignore_warnings(category=ImportWarning) def test_migration_path(self): test_apps = [ 'migrations.migrations_test_apps.normal', 'migrations.migrations_test_apps.with_package_model', 'migrations.migrations_test_apps.without_init_file', ] base_dir = os.path.dirname(os.path.dirname(upath(__file__))) for app in test_apps: with self.modify_settings(INSTALLED_APPS={'append': app}): migration = migrations.Migration('0001_initial', app.split('.')[-1]) expected_path = os.path.join(base_dir, *(app.split('.') + ['migrations', '0001_initial.py'])) writer = MigrationWriter(migration) self.assertEqual(writer.path, expected_path) def test_custom_operation(self): migration = type(str("Migration"), (migrations.Migration,), { "operations": [ custom_migration_operations.operations.TestOperation(), custom_migration_operations.operations.CreateModel(), migrations.CreateModel("MyModel", (), {}, (models.Model,)), custom_migration_operations.more_operations.TestOperation() ], "dependencies": [] }) writer = MigrationWriter(migration) output = writer.as_string() result = self.safe_exec(output) self.assertIn("custom_migration_operations", result) self.assertNotEqual( result['custom_migration_operations'].operations.TestOperation, result['custom_migration_operations'].more_operations.TestOperation ) def test_deconstruct_class_arguments(self): # Yes, it doesn't make sense to use a class as a default for a # CharField. It does make sense for custom fields though, for example # an enumfield that takes the enum class as an argument. class DeconstructableInstances(object): def deconstruct(self): return ('DeconstructableInstances', [], {}) string = MigrationWriter.serialize(models.CharField(default=DeconstructableInstances))[0] self.assertEqual(string, "models.CharField(default=migrations.test_writer.DeconstructableInstances)") Django-1.8.7/tests/migrations/test_migrations_run_before/0000775000175000017500000000000012625116243023300 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_run_before/0002_second.py0000664000175000017500000000073112625113144025564 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("migrations", "0001_initial"), ] operations = [ migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("migrations.Author", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_run_before/0003_third.py0000664000175000017500000000132312603513307025423 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): """ This is a wee bit crazy, but it's just to show that run_before works. """ dependencies = [ ("migrations", "0001_initial"), ] run_before = [ ("migrations", "0002_second"), ] operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(null=True)), ("age", models.IntegerField(default=0)), ], ) ] Django-1.8.7/tests/migrations/test_migrations_run_before/__init__.py0000664000175000017500000000000012603513307025375 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_run_before/0001_initial.py0000664000175000017500000000071412603513307025743 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): operations = [ migrations.CreateModel( "Salamander", [ ("id", models.AutoField(primary_key=True)), ("size", models.IntegerField(default=0)), ("silly_field", models.BooleanField(default=False)), ], ), ] Django-1.8.7/tests/migrations/models.py0000664000175000017500000000336412625116214017520 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.apps.registry import Apps from django.db import models from django.utils import six from django.utils.encoding import python_2_unicode_compatible class CustomModelBase(models.base.ModelBase): pass class ModelWithCustomBase(six.with_metaclass(CustomModelBase, models.Model)): pass @python_2_unicode_compatible class UnicodeModel(models.Model): title = models.CharField('ÚÑâÓÃÉ', max_length=20, default='“Ãjáñgóâ€') class Meta: # Disable auto loading of this model as we load it on our own apps = Apps() verbose_name = 'úñí©óðé µóðéø' verbose_name_plural = 'úñí©óðé µóðéøß' def __str__(self): return self.title class Unserializable(object): """ An object that migration doesn't know how to serialize. """ pass class UnserializableModel(models.Model): title = models.CharField(max_length=20, default=Unserializable()) class Meta: # Disable auto loading of this model as we load it on our own apps = Apps() class UnmigratedModel(models.Model): """ A model that is in a migration-less app (which this app is if its migrations directory has not been repointed) """ pass class EmptyManager(models.Manager): use_in_migrations = True class FoodQuerySet(models.query.QuerySet): pass class BaseFoodManager(models.Manager): def __init__(self, a, b, c=1, d=2): super(BaseFoodManager, self).__init__() self.args = (a, b, c, d) class FoodManager(BaseFoodManager.from_queryset(FoodQuerySet)): use_in_migrations = True class NoMigrationFoodManager(BaseFoodManager.from_queryset(FoodQuerySet)): pass Django-1.8.7/tests/migrations/test_base.py0000664000175000017500000000527412625116214020210 0ustar timtim00000000000000import os from django.db import connection from django.db.migrations.recorder import MigrationRecorder from django.test import TransactionTestCase from django.utils._os import upath class MigrationTestBase(TransactionTestCase): """ Contains an extended set of asserts for testing migrations and schema operations. """ available_apps = ["migrations"] test_dir = os.path.abspath(os.path.dirname(upath(__file__))) def tearDown(self): # Reset applied-migrations state. recorder = MigrationRecorder(connection) recorder.migration_qs.filter(app='migrations').delete() def get_table_description(self, table): with connection.cursor() as cursor: return connection.introspection.get_table_description(cursor, table) def assertTableExists(self, table): with connection.cursor() as cursor: self.assertIn(table, connection.introspection.table_names(cursor)) def assertTableNotExists(self, table): with connection.cursor() as cursor: self.assertNotIn(table, connection.introspection.table_names(cursor)) def assertColumnExists(self, table, column): self.assertIn(column, [c.name for c in self.get_table_description(table)]) def assertColumnNotExists(self, table, column): self.assertNotIn(column, [c.name for c in self.get_table_description(table)]) def assertColumnNull(self, table, column): self.assertEqual([c.null_ok for c in self.get_table_description(table) if c.name == column][0], True) def assertColumnNotNull(self, table, column): self.assertEqual([c.null_ok for c in self.get_table_description(table) if c.name == column][0], False) def assertIndexExists(self, table, columns, value=True): with connection.cursor() as cursor: self.assertEqual( value, any( c["index"] for c in connection.introspection.get_constraints(cursor, table).values() if c['columns'] == list(columns) ), ) def assertIndexNotExists(self, table, columns): return self.assertIndexExists(table, columns, False) def assertFKExists(self, table, columns, to, value=True): with connection.cursor() as cursor: self.assertEqual( value, any( c["foreign_key"] == to for c in connection.introspection.get_constraints(cursor, table).values() if c['columns'] == list(columns) ), ) def assertFKNotExists(self, table, columns, to, value=True): return self.assertFKExists(table, columns, to, False) Django-1.8.7/tests/migrations/test_executor.py0000664000175000017500000005704112625116214021133 0ustar timtim00000000000000from django.apps.registry import apps as global_apps from django.db import connection from django.db.migrations.executor import MigrationExecutor from django.db.migrations.graph import MigrationGraph from django.db.migrations.recorder import MigrationRecorder from django.db.utils import DatabaseError from django.test import TestCase, modify_settings, override_settings from .test_base import MigrationTestBase @modify_settings(INSTALLED_APPS={'append': 'migrations2'}) class ExecutorTests(MigrationTestBase): """ Tests the migration executor (full end-to-end running). Bear in mind that if these are failing you should fix the other test failures first, as they may be propagating into here. """ available_apps = ["migrations", "migrations2", "django.contrib.auth", "django.contrib.contenttypes"] @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_run(self): """ Tests running a simple set of migrations. """ executor = MigrationExecutor(connection) # Let's look at the plan first and make sure it's up to scratch plan = executor.migration_plan([("migrations", "0002_second")]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), (executor.loader.graph.nodes["migrations", "0002_second"], False), ], ) # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_book") # Alright, let's try running it executor.migrate([("migrations", "0002_second")]) # Are the tables there now? self.assertTableExists("migrations_author") self.assertTableExists("migrations_book") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Alright, let's undo what we did plan = executor.migration_plan([("migrations", None)]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0002_second"], True), (executor.loader.graph.nodes["migrations", "0001_initial"], True), ], ) executor.migrate([("migrations", None)]) # Are the tables gone? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_book") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}) def test_run_with_squashed(self): """ Tests running a squashed migration from zero (should ignore what it replaces) """ executor = MigrationExecutor(connection) # Check our leaf node is the squashed one leaves = [key for key in executor.loader.graph.leaf_nodes() if key[0] == "migrations"] self.assertEqual(leaves, [("migrations", "0001_squashed_0002")]) # Check the plan plan = executor.migration_plan([("migrations", "0001_squashed_0002")]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_squashed_0002"], False), ], ) # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_book") # Alright, let's try running it executor.migrate([("migrations", "0001_squashed_0002")]) # Are the tables there now? self.assertTableExists("migrations_author") self.assertTableExists("migrations_book") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Alright, let's undo what we did. Should also just use squashed. plan = executor.migration_plan([("migrations", None)]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_squashed_0002"], True), ], ) executor.migrate([("migrations", None)]) # Are the tables gone? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_book") @override_settings(MIGRATION_MODULES={ "migrations": "migrations.test_migrations", "migrations2": "migrations2.test_migrations_2", }) def test_empty_plan(self): """ Tests that re-planning a full migration of a fully-migrated set doesn't perform spurious unmigrations and remigrations. There was previously a bug where the executor just always performed the backwards plan for applied migrations - which even for the most recent migration in an app, might include other, dependent apps, and these were being unmigrated. """ # Make the initial plan, check it executor = MigrationExecutor(connection) plan = executor.migration_plan([ ("migrations", "0002_second"), ("migrations2", "0001_initial"), ]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), (executor.loader.graph.nodes["migrations", "0002_second"], False), (executor.loader.graph.nodes["migrations2", "0001_initial"], False), ], ) # Fake-apply all migrations executor.migrate([ ("migrations", "0002_second"), ("migrations2", "0001_initial") ], fake=True) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Now plan a second time and make sure it's empty plan = executor.migration_plan([ ("migrations", "0002_second"), ("migrations2", "0001_initial"), ]) self.assertEqual(plan, []) # Erase all the fake records executor.recorder.record_unapplied("migrations2", "0001_initial") executor.recorder.record_unapplied("migrations", "0002_second") executor.recorder.record_unapplied("migrations", "0001_initial") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_soft_apply(self): """ Tests detection of initial migrations already having been applied. """ state = {"faked": None} def fake_storer(phase, migration=None, fake=None): state["faked"] = fake executor = MigrationExecutor(connection, progress_callback=fake_storer) # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") # Run it normally self.assertEqual( executor.migration_plan([("migrations", "0001_initial")]), [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), ], ) executor.migrate([("migrations", "0001_initial")]) # Are the tables there now? self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # We shouldn't have faked that one self.assertEqual(state["faked"], False) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Fake-reverse that executor.migrate([("migrations", None)], fake=True) # Are the tables still there? self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Make sure that was faked self.assertEqual(state["faked"], True) # Finally, migrate forwards; this should fake-apply our initial migration executor.loader.build_graph() self.assertEqual( executor.migration_plan([("migrations", "0001_initial")]), [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), ], ) # Applying the migration should raise a database level error # because we haven't given the --fake-initial option with self.assertRaises(DatabaseError): executor.migrate([("migrations", "0001_initial")]) # Reset the faked state state = {"faked": None} # Allow faking of initial CreateModel operations executor.migrate([("migrations", "0001_initial")], fake_initial=True) self.assertEqual(state["faked"], True) # And migrate back to clean up the database executor.loader.build_graph() executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") @override_settings( MIGRATION_MODULES={ "migrations": "migrations.test_migrations_custom_user", "django.contrib.auth": "django.contrib.auth.migrations", }, AUTH_USER_MODEL="migrations.Author", ) def test_custom_user(self): """ Regression test for #22325 - references to a custom user model defined in the same app are not resolved correctly. """ executor = MigrationExecutor(connection) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") # Migrate forwards executor.migrate([("migrations", "0001_initial")]) self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Make sure the soft-application detection works (#23093) # Change table_names to not return auth_user during this as # it wouldn't be there in a normal run, and ensure migrations.Author # exists in the global app registry temporarily. old_table_names = connection.introspection.table_names connection.introspection.table_names = lambda c: [x for x in old_table_names(c) if x != "auth_user"] migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps global_apps.get_app_config("migrations").models["author"] = migrations_apps.get_model("migrations", "author") try: migration = executor.loader.get_migration("auth", "0001_initial") self.assertEqual(executor.detect_soft_applied(None, migration)[0], True) finally: connection.introspection.table_names = old_table_names del global_apps.get_app_config("migrations").models["author"] # And migrate back to clean up the database executor.loader.build_graph() executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") @override_settings( INSTALLED_APPS=[ "migrations.migrations_test_apps.lookuperror_a", "migrations.migrations_test_apps.lookuperror_b", "migrations.migrations_test_apps.lookuperror_c" ] ) def test_unrelated_model_lookups_forwards(self): """ #24123 - Tests that all models of apps already applied which are unrelated to the first app being applied are part of the initial model state. """ try: executor = MigrationExecutor(connection) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1") executor.migrate([("lookuperror_b", "0003_b3")]) self.assertTableExists("lookuperror_b_b3") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Migrate forwards -- This led to a lookup LookupErrors because # lookuperror_b.B2 is already applied executor.migrate([ ("lookuperror_a", "0004_a4"), ("lookuperror_c", "0003_c3"), ]) self.assertTableExists("lookuperror_a_a4") self.assertTableExists("lookuperror_c_c3") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() finally: # Cleanup executor.migrate([ ("lookuperror_a", None), ("lookuperror_b", None), ("lookuperror_c", None), ]) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1") @override_settings( INSTALLED_APPS=[ "migrations.migrations_test_apps.lookuperror_a", "migrations.migrations_test_apps.lookuperror_b", "migrations.migrations_test_apps.lookuperror_c" ] ) def test_unrelated_model_lookups_backwards(self): """ #24123 - Tests that all models of apps being unapplied which are unrelated to the first app being unapplied are part of the initial model state. """ try: executor = MigrationExecutor(connection) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1") executor.migrate([ ("lookuperror_a", "0004_a4"), ("lookuperror_b", "0003_b3"), ("lookuperror_c", "0003_c3"), ]) self.assertTableExists("lookuperror_b_b3") self.assertTableExists("lookuperror_a_a4") self.assertTableExists("lookuperror_c_c3") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Migrate backwards -- This led to a lookup LookupErrors because # lookuperror_b.B2 is not in the initial state (unrelated to app c) executor.migrate([("lookuperror_a", None)]) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() finally: # Cleanup executor.migrate([ ("lookuperror_b", None), ("lookuperror_c", None) ]) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) def test_process_callback(self): """ #24129 - Tests callback process """ call_args_list = [] def callback(*args): call_args_list.append(args) executor = MigrationExecutor(connection, progress_callback=callback) # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") executor.migrate([ ("migrations", "0001_initial"), ("migrations", "0002_second"), ]) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() executor.migrate([ ("migrations", None), ("migrations", None), ]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") migrations = executor.loader.graph.nodes expected = [ ("render_start", ), ("render_success", ), ("apply_start", migrations['migrations', '0001_initial'], False), ("apply_success", migrations['migrations', '0001_initial'], False), ("apply_start", migrations['migrations', '0002_second'], False), ("apply_success", migrations['migrations', '0002_second'], False), ("render_start", ), ("render_success", ), ("unapply_start", migrations['migrations', '0002_second'], False), ("unapply_success", migrations['migrations', '0002_second'], False), ("unapply_start", migrations['migrations', '0001_initial'], False), ("unapply_success", migrations['migrations', '0001_initial'], False), ] self.assertEqual(call_args_list, expected) @override_settings( INSTALLED_APPS=[ "migrations.migrations_test_apps.alter_fk.author_app", "migrations.migrations_test_apps.alter_fk.book_app", ] ) def test_alter_id_type_with_fk(self): try: executor = MigrationExecutor(connection) self.assertTableNotExists("author_app_author") self.assertTableNotExists("book_app_book") # Apply initial migrations executor.migrate([ ("author_app", "0001_initial"), ("book_app", "0001_initial"), ]) self.assertTableExists("author_app_author") self.assertTableExists("book_app_book") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Apply PK type alteration executor.migrate([("author_app", "0002_alter_id")]) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() finally: # We can't simply unapply the migrations here because there is no # implicit cast from VARCHAR to INT on the database level. with connection.schema_editor() as editor: editor.execute(editor.sql_delete_table % {"table": "book_app_book"}) editor.execute(editor.sql_delete_table % {"table": "author_app_author"}) self.assertTableNotExists("author_app_author") self.assertTableNotExists("book_app_book") @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}) def test_apply_all_replaced_marks_replacement_as_applied(self): """ Applying all replaced migrations marks replacement as applied (#24628). """ recorder = MigrationRecorder(connection) # Place the database in a state where the replaced migrations are # partially applied: 0001 is applied, 0002 is not. recorder.record_applied("migrations", "0001_initial") executor = MigrationExecutor(connection) # Use fake because we don't actually have the first migration # applied, so the second will fail. And there's no need to actually # create/modify tables here, we're just testing the # MigrationRecord, which works the same with or without fake. executor.migrate([("migrations", "0002_second")], fake=True) # Because we've now applied 0001 and 0002 both, their squashed # replacement should be marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), ) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed"}) def test_migrate_marks_replacement_applied_even_if_it_did_nothing(self): """ A new squash migration will be marked as applied even if all its replaced migrations were previously already applied (#24628). """ recorder = MigrationRecorder(connection) # Record all replaced migrations as applied recorder.record_applied("migrations", "0001_initial") recorder.record_applied("migrations", "0002_second") executor = MigrationExecutor(connection) executor.migrate([("migrations", "0001_squashed_0002")]) # Because 0001 and 0002 are both applied, even though this migrate run # didn't apply anything new, their squashed replacement should be # marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), ) class FakeLoader(object): def __init__(self, graph, applied): self.graph = graph self.applied_migrations = applied class FakeMigration(object): """Really all we need is any object with a debug-useful repr.""" def __init__(self, name): self.name = name def __repr__(self): return 'M<%s>' % self.name class ExecutorUnitTests(TestCase): """(More) isolated unit tests for executor methods.""" def test_minimize_rollbacks(self): """ Minimize unnecessary rollbacks in connected apps. When you say "./manage.py migrate appA 0001", rather than migrating to just after appA-0001 in the linearized migration plan (which could roll back migrations in other apps that depend on appA 0001, but don't need to be rolled back since we're not rolling back appA 0001), we migrate to just before appA-0002. """ a1_impl = FakeMigration('a1') a1 = ('a', '1') a2_impl = FakeMigration('a2') a2 = ('a', '2') b1_impl = FakeMigration('b1') b1 = ('b', '1') graph = MigrationGraph() graph.add_node(a1, a1_impl) graph.add_node(a2, a2_impl) graph.add_node(b1, b1_impl) graph.add_dependency(None, b1, a1) graph.add_dependency(None, a2, a1) executor = MigrationExecutor(None) executor.loader = FakeLoader(graph, {a1, b1, a2}) plan = executor.migration_plan({a1}) self.assertEqual(plan, [(a2_impl, True)]) def test_minimize_rollbacks_branchy(self): """ Minimize rollbacks when target has multiple in-app children. a: 1 <---- 3 <--\ \ \- 2 <--- 4 \ \ b: \- 1 <--- 2 """ a1_impl = FakeMigration('a1') a1 = ('a', '1') a2_impl = FakeMigration('a2') a2 = ('a', '2') a3_impl = FakeMigration('a3') a3 = ('a', '3') a4_impl = FakeMigration('a4') a4 = ('a', '4') b1_impl = FakeMigration('b1') b1 = ('b', '1') b2_impl = FakeMigration('b2') b2 = ('b', '2') graph = MigrationGraph() graph.add_node(a1, a1_impl) graph.add_node(a2, a2_impl) graph.add_node(a3, a3_impl) graph.add_node(a4, a4_impl) graph.add_node(b1, b1_impl) graph.add_node(b2, b2_impl) graph.add_dependency(None, a2, a1) graph.add_dependency(None, a3, a1) graph.add_dependency(None, a4, a2) graph.add_dependency(None, a4, a3) graph.add_dependency(None, b2, b1) graph.add_dependency(None, b1, a1) graph.add_dependency(None, b2, a2) executor = MigrationExecutor(None) executor.loader = FakeLoader(graph, {a1, b1, a2, b2, a3, a4}) plan = executor.migration_plan({a1}) should_be_rolled_back = [b2_impl, a4_impl, a2_impl, a3_impl] exp = [(m, True) for m in should_be_rolled_back] self.assertEqual(plan, exp) def test_backwards_nothing_to_do(self): """ If the current state satisfies the given target, do nothing. a: 1 <--- 2 b: \- 1 c: \- 1 If a1 is applied already and a2 is not, and we're asked to migrate to a1, don't apply or unapply b1 or c1, regardless of their current state. """ a1_impl = FakeMigration('a1') a1 = ('a', '1') a2_impl = FakeMigration('a2') a2 = ('a', '2') b1_impl = FakeMigration('b1') b1 = ('b', '1') c1_impl = FakeMigration('c1') c1 = ('c', '1') graph = MigrationGraph() graph.add_node(a1, a1_impl) graph.add_node(a2, a2_impl) graph.add_node(b1, b1_impl) graph.add_node(c1, c1_impl) graph.add_dependency(None, a2, a1) graph.add_dependency(None, b1, a1) graph.add_dependency(None, c1, a1) executor = MigrationExecutor(None) executor.loader = FakeLoader(graph, {a1, b1}) plan = executor.migration_plan({a1}) self.assertEqual(plan, []) Django-1.8.7/tests/migrations/test_migrations_empty/0000775000175000017500000000000012625116243022310 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_empty/__init__.py0000664000175000017500000000000012625116214024405 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_custom_user/0000775000175000017500000000000012625116243023522 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_custom_user/__init__.py0000664000175000017500000000000012603513307025617 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_custom_user/0001_initial.py0000664000175000017500000000137212625116214026166 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( "Author", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=255)), ], ), migrations.CreateModel( "Tribble", [ ("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey(to=settings.AUTH_USER_MODEL, to_field="id")), ], ) ] Django-1.8.7/tests/migrations/test_migrations_unmigdep/0000775000175000017500000000000012625116243022762 5ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_unmigdep/__init__.py0000664000175000017500000000000012603513307025057 0ustar timtim00000000000000Django-1.8.7/tests/migrations/test_migrations_unmigdep/0001_initial.py0000664000175000017500000000070612625113144025425 0ustar timtim00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ ("auth", "__first__"), ] operations = [ migrations.CreateModel( "Book", [ ("id", models.AutoField(primary_key=True)), ("user", models.ForeignKey("auth.User", null=True)), ], ) ] Django-1.8.7/tests/migrations/test_state.py0000664000175000017500000011611712625116214020415 0ustar timtim00000000000000from django.apps.registry import Apps from django.contrib.contenttypes.fields import GenericForeignKey from django.db import models from django.db.migrations.operations import ( AddField, AlterField, DeleteModel, RemoveField, ) from django.db.migrations.state import ( InvalidBasesError, ModelState, ProjectState, get_related_models_recursive, ) from django.test import SimpleTestCase, TestCase, override_settings from django.utils import six from .models import ( FoodManager, FoodQuerySet, ModelWithCustomBase, NoMigrationFoodManager, ) class StateTests(TestCase): """ Tests state construction, rendering and modification by operations. """ def test_create(self): """ Tests making a ProjectState from an Apps """ new_apps = Apps(["migrations"]) class Author(models.Model): name = models.CharField(max_length=255) bio = models.TextField() age = models.IntegerField(blank=True, null=True) class Meta: app_label = "migrations" apps = new_apps unique_together = ["name", "bio"] index_together = ["bio", "age"] class AuthorProxy(Author): class Meta: app_label = "migrations" apps = new_apps proxy = True ordering = ["name"] class SubAuthor(Author): width = models.FloatField(null=True) class Meta: app_label = "migrations" apps = new_apps class Book(models.Model): title = models.CharField(max_length=1000) author = models.ForeignKey(Author) contributors = models.ManyToManyField(Author) class Meta: app_label = "migrations" apps = new_apps verbose_name = "tome" db_table = "test_tome" class Food(models.Model): food_mgr = FoodManager('a', 'b') food_qs = FoodQuerySet.as_manager() food_no_mgr = NoMigrationFoodManager('x', 'y') class Meta: app_label = "migrations" apps = new_apps class FoodNoManagers(models.Model): class Meta: app_label = "migrations" apps = new_apps class FoodNoDefaultManager(models.Model): food_no_mgr = NoMigrationFoodManager('x', 'y') food_mgr = FoodManager('a', 'b') food_qs = FoodQuerySet.as_manager() class Meta: app_label = "migrations" apps = new_apps mgr1 = FoodManager('a', 'b') mgr2 = FoodManager('x', 'y', c=3, d=4) class FoodOrderedManagers(models.Model): # The managers on this model should be ordered by their creation # counter and not by the order in model body food_no_mgr = NoMigrationFoodManager('x', 'y') food_mgr2 = mgr2 food_mgr1 = mgr1 class Meta: app_label = "migrations" apps = new_apps project_state = ProjectState.from_apps(new_apps) author_state = project_state.models['migrations', 'author'] author_proxy_state = project_state.models['migrations', 'authorproxy'] sub_author_state = project_state.models['migrations', 'subauthor'] book_state = project_state.models['migrations', 'book'] food_state = project_state.models['migrations', 'food'] food_no_managers_state = project_state.models['migrations', 'foodnomanagers'] food_no_default_manager_state = project_state.models['migrations', 'foodnodefaultmanager'] food_order_manager_state = project_state.models['migrations', 'foodorderedmanagers'] self.assertEqual(author_state.app_label, "migrations") self.assertEqual(author_state.name, "Author") self.assertEqual([x for x, y in author_state.fields], ["id", "name", "bio", "age"]) self.assertEqual(author_state.fields[1][1].max_length, 255) self.assertEqual(author_state.fields[2][1].null, False) self.assertEqual(author_state.fields[3][1].null, True) self.assertEqual(author_state.options, {"unique_together": {("name", "bio")}, "index_together": {("bio", "age")}}) self.assertEqual(author_state.bases, (models.Model, )) self.assertEqual(book_state.app_label, "migrations") self.assertEqual(book_state.name, "Book") self.assertEqual([x for x, y in book_state.fields], ["id", "title", "author", "contributors"]) self.assertEqual(book_state.fields[1][1].max_length, 1000) self.assertEqual(book_state.fields[2][1].null, False) self.assertEqual(book_state.fields[3][1].__class__.__name__, "ManyToManyField") self.assertEqual(book_state.options, {"verbose_name": "tome", "db_table": "test_tome"}) self.assertEqual(book_state.bases, (models.Model, )) self.assertEqual(author_proxy_state.app_label, "migrations") self.assertEqual(author_proxy_state.name, "AuthorProxy") self.assertEqual(author_proxy_state.fields, []) self.assertEqual(author_proxy_state.options, {"proxy": True, "ordering": ["name"]}) self.assertEqual(author_proxy_state.bases, ("migrations.author", )) self.assertEqual(sub_author_state.app_label, "migrations") self.assertEqual(sub_author_state.name, "SubAuthor") self.assertEqual(len(sub_author_state.fields), 2) self.assertEqual(sub_author_state.bases, ("migrations.author", )) # The default manager is used in migrations self.assertEqual([name for name, mgr in food_state.managers], ['food_mgr']) self.assertTrue(all(isinstance(name, six.text_type) for name, mgr in food_state.managers)) self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2)) # No explicit managers defined. Migrations will fall back to the default self.assertEqual(food_no_managers_state.managers, []) # food_mgr is used in migration but isn't the default mgr, hence add the # default self.assertEqual([name for name, mgr in food_no_default_manager_state.managers], ['food_no_mgr', 'food_mgr']) self.assertTrue(all(isinstance(name, six.text_type) for name, mgr in food_no_default_manager_state.managers)) self.assertEqual(food_no_default_manager_state.managers[0][1].__class__, models.Manager) self.assertIsInstance(food_no_default_manager_state.managers[1][1], FoodManager) self.assertEqual([name for name, mgr in food_order_manager_state.managers], ['food_mgr1', 'food_mgr2']) self.assertTrue(all(isinstance(name, six.text_type) for name, mgr in food_order_manager_state.managers)) self.assertEqual([mgr.args for name, mgr in food_order_manager_state.managers], [('a', 'b', 1, 2), ('x', 'y', 3, 4)]) def test_render(self): """ Tests rendering a ProjectState into an Apps. """ project_state = ProjectState() project_state.add_model(ModelState( app_label="migrations", name="Tag", fields=[ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ("hidden", models.BooleanField()), ], )) project_state.add_model(ModelState( app_label="migrations", name="SubTag", fields=[ ('tag_ptr', models.OneToOneField( auto_created=True, primary_key=True, to_field='id', serialize=False, to='migrations.Tag', )), ("awesome", models.BooleanField()), ], bases=("migrations.Tag",), )) base_mgr = models.Manager() mgr1 = FoodManager('a', 'b') mgr2 = FoodManager('x', 'y', c=3, d=4) project_state.add_model(ModelState( app_label="migrations", name="Food", fields=[ ("id", models.AutoField(primary_key=True)), ], managers=[ # The ordering we really want is objects, mgr1, mgr2 ('default', base_mgr), ('food_mgr2', mgr2), (b'food_mgr1', mgr1), ] )) new_apps = project_state.apps self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field("name").max_length, 100) self.assertEqual(new_apps.get_model("migrations", "Tag")._meta.get_field("hidden").null, False) self.assertEqual(len(new_apps.get_model("migrations", "SubTag")._meta.local_fields), 2) Food = new_apps.get_model("migrations", "Food") managers = sorted(Food._meta.managers) self.assertEqual([mgr.name for _, mgr, _ in managers], ['default', 'food_mgr1', 'food_mgr2']) self.assertTrue(all(isinstance(mgr.name, six.text_type) for _, mgr, _ in managers)) self.assertEqual([mgr.__class__ for _, mgr, _ in managers], [models.Manager, FoodManager, FoodManager]) self.assertIs(managers[0][1], Food._default_manager) def test_render_model_inheritance(self): class Book(models.Model): title = models.CharField(max_length=1000) class Meta: app_label = "migrations" apps = Apps() class Novel(Book): class Meta: app_label = "migrations" apps = Apps() # First, test rendering individually apps = Apps(["migrations"]) # We shouldn't be able to render yet ms = ModelState.from_model(Novel) with self.assertRaises(InvalidBasesError): ms.render(apps) # Once the parent model is in the app registry, it should be fine ModelState.from_model(Book).render(apps) ModelState.from_model(Novel).render(apps) def test_render_model_with_multiple_inheritance(self): class Foo(models.Model): class Meta: app_label = "migrations" apps = Apps() class Bar(models.Model): class Meta: app_label = "migrations" apps = Apps() class FooBar(Foo, Bar): class Meta: app_label = "migrations" apps = Apps() class AbstractSubFooBar(FooBar): class Meta: abstract = True apps = Apps() class SubFooBar(AbstractSubFooBar): class Meta: app_label = "migrations" apps = Apps() apps = Apps(["migrations"]) # We shouldn't be able to render yet ms = ModelState.from_model(FooBar) with self.assertRaises(InvalidBasesError): ms.render(apps) # Once the parent models are in the app registry, it should be fine ModelState.from_model(Foo).render(apps) self.assertSequenceEqual(ModelState.from_model(Foo).bases, [models.Model]) ModelState.from_model(Bar).render(apps) self.assertSequenceEqual(ModelState.from_model(Bar).bases, [models.Model]) ModelState.from_model(FooBar).render(apps) self.assertSequenceEqual(ModelState.from_model(FooBar).bases, ['migrations.foo', 'migrations.bar']) ModelState.from_model(SubFooBar).render(apps) self.assertSequenceEqual(ModelState.from_model(SubFooBar).bases, ['migrations.foobar']) def test_render_project_dependencies(self): """ Tests that the ProjectState render method correctly renders models to account for inter-model base dependencies. """ new_apps = Apps() class A(models.Model): class Meta: app_label = "migrations" apps = new_apps class B(A): class Meta: app_label = "migrations" apps = new_apps class C(B): class Meta: app_label = "migrations" apps = new_apps class D(A): class Meta: app_label = "migrations" apps = new_apps class E(B): class Meta: app_label = "migrations" apps = new_apps proxy = True class F(D): class Meta: app_label = "migrations" apps = new_apps proxy = True # Make a ProjectState and render it project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.add_model(ModelState.from_model(D)) project_state.add_model(ModelState.from_model(E)) project_state.add_model(ModelState.from_model(F)) final_apps = project_state.apps self.assertEqual(len(final_apps.get_models()), 6) # Now make an invalid ProjectState and make sure it fails project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.add_model(ModelState.from_model(F)) with self.assertRaises(InvalidBasesError): project_state.apps def test_render_unique_app_labels(self): """ Tests that the ProjectState render method doesn't raise an ImproperlyConfigured exception about unique labels if two dotted app names have the same last part. """ class A(models.Model): class Meta: app_label = "django.contrib.auth" class B(models.Model): class Meta: app_label = "vendor.auth" # Make a ProjectState and render it project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(project_state.apps.get_models()), 2) def test_add_relations(self): """ #24573 - Adding relations to existing models should reload the referenced models too. """ new_apps = Apps() class A(models.Model): class Meta: app_label = 'something' apps = new_apps class B(A): class Meta: app_label = 'something' apps = new_apps class C(models.Model): class Meta: app_label = 'something' apps = new_apps project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) project_state.add_model(ModelState.from_model(C)) project_state.apps # We need to work with rendered models old_state = project_state.clone() model_a_old = old_state.apps.get_model('something', 'A') model_b_old = old_state.apps.get_model('something', 'B') model_c_old = old_state.apps.get_model('something', 'C') # Check that the relations between the old models are correct self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) operation = AddField('c', 'to_a', models.OneToOneField('something.A', related_name='from_c')) operation.state_forwards('something', project_state) model_a_new = project_state.apps.get_model('something', 'A') model_b_new = project_state.apps.get_model('something', 'B') model_c_new = project_state.apps.get_model('something', 'C') # Check that all models have changed self.assertIsNot(model_a_old, model_a_new) self.assertIsNot(model_b_old, model_b_new) self.assertIsNot(model_c_old, model_c_new) # Check that the relations between the old models still hold self.assertIs(model_a_old._meta.get_field('b').related_model, model_b_old) self.assertIs(model_b_old._meta.get_field('a_ptr').related_model, model_a_old) # Check that the relations between the new models correct self.assertIs(model_a_new._meta.get_field('b').related_model, model_b_new) self.assertIs(model_b_new._meta.get_field('a_ptr').related_model, model_a_new) self.assertIs(model_a_new._meta.get_field('from_c').related_model, model_c_new) self.assertIs(model_c_new._meta.get_field('to_a').related_model, model_a_new) def test_remove_relations(self): """ #24225 - Tests that relations between models are updated while remaining the relations and references for models of an old state. """ new_apps = Apps() class A(models.Model): class Meta: app_label = "something" apps = new_apps class B(models.Model): to_a = models.ForeignKey(A) class Meta: app_label = "something" apps = new_apps def get_model_a(state): return [mod for mod in state.apps.get_models() if mod._meta.model_name == 'a'][0] project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) old_state = project_state.clone() operation = RemoveField("b", "to_a") operation.state_forwards("something", project_state) # Tests that model from old_state still has the relation model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) self.assertEqual(len(model_a_old._meta.related_objects), 1) self.assertEqual(len(model_a_new._meta.related_objects), 0) # Same test for deleted model project_state = ProjectState() project_state.add_model(ModelState.from_model(A)) project_state.add_model(ModelState.from_model(B)) old_state = project_state.clone() operation = DeleteModel("b") operation.state_forwards("something", project_state) model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) self.assertEqual(len(model_a_old._meta.related_objects), 1) self.assertEqual(len(model_a_new._meta.related_objects), 0) def test_self_relation(self): """ #24513 - Modifying an object pointing to itself would cause it to be rendered twice and thus breaking its related M2M through objects. """ class A(models.Model): to_a = models.ManyToManyField('something.A', symmetrical=False) class Meta: app_label = "something" def get_model_a(state): return [mod for mod in state.apps.get_models() if mod._meta.model_name == 'a'][0] project_state = ProjectState() project_state.add_model((ModelState.from_model(A))) self.assertEqual(len(get_model_a(project_state)._meta.related_objects), 1) old_state = project_state.clone() operation = AlterField( model_name="a", name="to_a", field=models.ManyToManyField("something.A", symmetrical=False, blank=True) ) # At this point the model would be rendered twice causing its related # M2M through objects to point to an old copy and thus breaking their # attribute lookup. operation.state_forwards("something", project_state) model_a_old = get_model_a(old_state) model_a_new = get_model_a(project_state) self.assertIsNot(model_a_old, model_a_new) # Tests that the old model's _meta is still consistent field_to_a_old = model_a_old._meta.get_field("to_a") self.assertEqual(field_to_a_old.m2m_field_name(), "from_a") self.assertEqual(field_to_a_old.m2m_reverse_field_name(), "to_a") self.assertIs(field_to_a_old.related_model, model_a_old) self.assertIs(field_to_a_old.rel.through._meta.get_field('to_a').related_model, model_a_old) self.assertIs(field_to_a_old.rel.through._meta.get_field('from_a').related_model, model_a_old) # Tests that the new model's _meta is still consistent field_to_a_new = model_a_new._meta.get_field("to_a") self.assertEqual(field_to_a_new.m2m_field_name(), "from_a") self.assertEqual(field_to_a_new.m2m_reverse_field_name(), "to_a") self.assertIs(field_to_a_new.related_model, model_a_new) self.assertIs(field_to_a_new.rel.through._meta.get_field('to_a').related_model, model_a_new) self.assertIs(field_to_a_new.rel.through._meta.get_field('from_a').related_model, model_a_new) def test_equality(self): """ Tests that == and != are implemented correctly. """ # Test two things that should be equal project_state = ProjectState() project_state.add_model(ModelState( "migrations", "Tag", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100)), ("hidden", models.BooleanField()), ], {}, None, )) project_state.apps # Fill the apps cached property other_state = project_state.clone() self.assertEqual(project_state, project_state) self.assertEqual(project_state, other_state) self.assertEqual(project_state != project_state, False) self.assertEqual(project_state != other_state, False) self.assertNotEqual(project_state.apps, other_state.apps) # Make a very small change (max_len 99) and see if that affects it project_state = ProjectState() project_state.add_model(ModelState( "migrations", "Tag", [ ("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=99)), ("hidden", models.BooleanField()), ], {}, None, )) self.assertNotEqual(project_state, other_state) self.assertEqual(project_state == other_state, False) def test_dangling_references_throw_error(self): new_apps = Apps() class Author(models.Model): name = models.TextField() class Meta: app_label = "migrations" apps = new_apps class Book(models.Model): author = models.ForeignKey(Author) class Meta: app_label = "migrations" apps = new_apps class Magazine(models.Model): authors = models.ManyToManyField(Author) class Meta: app_label = "migrations" apps = new_apps # Make a valid ProjectState and render it project_state = ProjectState() project_state.add_model(ModelState.from_model(Author)) project_state.add_model(ModelState.from_model(Book)) project_state.add_model(ModelState.from_model(Magazine)) self.assertEqual(len(project_state.apps.get_models()), 3) # now make an invalid one with a ForeignKey project_state = ProjectState() project_state.add_model(ModelState.from_model(Book)) with self.assertRaises(ValueError): project_state.apps # and another with ManyToManyField project_state = ProjectState() project_state.add_model(ModelState.from_model(Magazine)) with self.assertRaises(ValueError): project_state.apps def test_real_apps(self): """ Tests that including real apps can resolve dangling FK errors. This test relies on the fact that contenttypes is always loaded. """ new_apps = Apps() class TestModel(models.Model): ct = models.ForeignKey("contenttypes.ContentType") class Meta: app_label = "migrations" apps = new_apps # If we just stick it into an empty state it should fail project_state = ProjectState() project_state.add_model(ModelState.from_model(TestModel)) with self.assertRaises(ValueError): project_state.apps # If we include the real app it should succeed project_state = ProjectState(real_apps=["contenttypes"]) project_state.add_model(ModelState.from_model(TestModel)) rendered_state = project_state.apps self.assertEqual( len([x for x in rendered_state.get_models() if x._meta.app_label == "migrations"]), 1, ) def test_ignore_order_wrt(self): """ Makes sure ProjectState doesn't include OrderWrt fields when making from existing models. """ new_apps = Apps() class Author(models.Model): name = models.TextField() class Meta: app_label = "migrations" apps = new_apps class Book(models.Model): author = models.ForeignKey(Author) class Meta: app_label = "migrations" apps = new_apps order_with_respect_to = "author" # Make a valid ProjectState and render it project_state = ProjectState() project_state.add_model(ModelState.from_model(Author)) project_state.add_model(ModelState.from_model(Book)) self.assertEqual( [name for name, field in project_state.models["migrations", "book"].fields], ["id", "author"], ) def test_manager_refer_correct_model_version(self): """ #24147 - Tests that managers refer to the correct version of a historical model """ project_state = ProjectState() project_state.add_model(ModelState( app_label="migrations", name="Tag", fields=[ ("id", models.AutoField(primary_key=True)), ("hidden", models.BooleanField()), ], managers=[ ('food_mgr', FoodManager('a', 'b')), ('food_qs', FoodQuerySet.as_manager()), ] )) old_model = project_state.apps.get_model('migrations', 'tag') new_state = project_state.clone() operation = RemoveField("tag", "hidden") operation.state_forwards("migrations", new_state) new_model = new_state.apps.get_model('migrations', 'tag') self.assertIsNot(old_model, new_model) self.assertIs(old_model, old_model.food_mgr.model) self.assertIs(old_model, old_model.food_qs.model) self.assertIs(new_model, new_model.food_mgr.model) self.assertIs(new_model, new_model.food_qs.model) self.assertIsNot(old_model.food_mgr, new_model.food_mgr) self.assertIsNot(old_model.food_qs, new_model.food_qs) self.assertIsNot(old_model.food_mgr.model, new_model.food_mgr.model) self.assertIsNot(old_model.food_qs.model, new_model.food_qs.model) class ModelStateTests(TestCase): def test_custom_model_base(self): state = ModelState.from_model(ModelWithCustomBase) self.assertEqual(state.bases, (models.Model,)) def test_bound_field_sanity_check(self): field = models.CharField(max_length=1) field.model = models.Model with self.assertRaisesMessage(ValueError, 'ModelState.fields cannot be bound to a model - "field" is.'): ModelState('app', 'Model', [('field', field)]) def test_fields_immutability(self): """ Tests that rendering a model state doesn't alter its internal fields. """ apps = Apps() field = models.CharField(max_length=1) state = ModelState('app', 'Model', [('name', field)]) Model = state.render(apps) self.assertNotEqual(Model._meta.get_field('name'), field) def test_repr(self): field = models.CharField(max_length=1) state = ModelState('app', 'Model', [('name', field)], bases=['app.A', 'app.B', 'app.C']) self.assertEqual(repr(state), "") project_state = ProjectState() project_state.add_model(state) with self.assertRaisesMessage(InvalidBasesError, "Cannot resolve bases for []"): project_state.apps @override_settings(TEST_SWAPPABLE_MODEL='migrations.SomeFakeModel') def test_create_swappable(self): """ Tests making a ProjectState from an Apps with a swappable model """ new_apps = Apps(['migrations']) class Author(models.Model): name = models.CharField(max_length=255) bio = models.TextField() age = models.IntegerField(blank=True, null=True) class Meta: app_label = 'migrations' apps = new_apps swappable = 'TEST_SWAPPABLE_MODEL' author_state = ModelState.from_model(Author) self.assertEqual(author_state.app_label, 'migrations') self.assertEqual(author_state.name, 'Author') self.assertEqual([x for x, y in author_state.fields], ['id', 'name', 'bio', 'age']) self.assertEqual(author_state.fields[1][1].max_length, 255) self.assertEqual(author_state.fields[2][1].null, False) self.assertEqual(author_state.fields[3][1].null, True) self.assertEqual(author_state.options, {'swappable': 'TEST_SWAPPABLE_MODEL'}) self.assertEqual(author_state.bases, (models.Model, )) self.assertEqual(author_state.managers, []) @override_settings(TEST_SWAPPABLE_MODEL='migrations.SomeFakeModel') def test_custom_manager_swappable(self): """ Tests making a ProjectState from unused models with custom managers """ new_apps = Apps(['migrations']) class Food(models.Model): food_mgr = FoodManager('a', 'b') food_qs = FoodQuerySet.as_manager() food_no_mgr = NoMigrationFoodManager('x', 'y') class Meta: app_label = "migrations" apps = new_apps swappable = 'TEST_SWAPPABLE_MODEL' food_state = ModelState.from_model(Food) # The default manager is used in migrations self.assertEqual([name for name, mgr in food_state.managers], ['food_mgr']) self.assertEqual(food_state.managers[0][1].args, ('a', 'b', 1, 2)) class RelatedModelsTests(SimpleTestCase): def setUp(self): self.apps = Apps(['migrations.related_models_app']) def create_model(self, name, foreign_keys=[], bases=(), abstract=False, proxy=False): test_name = 'related_models_app' assert not (abstract and proxy) meta_contents = { 'abstract': abstract, 'app_label': test_name, 'apps': self.apps, 'proxy': proxy, } meta = type(str("Meta"), tuple(), meta_contents) if not bases: bases = (models.Model,) body = { 'Meta': meta, '__module__': "__fake__", } fname_base = fname = '%s_%%d' % name.lower() for i, fk in enumerate(foreign_keys, 1): fname = fname_base % i body[fname] = fk return type(name, bases, body) def assertRelated(self, model, needle): self.assertEqual( get_related_models_recursive(model), {(n._meta.app_label, n._meta.model_name) for n in needle}, ) def test_unrelated(self): A = self.create_model("A") B = self.create_model("B") self.assertRelated(A, []) self.assertRelated(B, []) def test_direct_fk(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('B')]) B = self.create_model("B") self.assertRelated(A, [B]) self.assertRelated(B, [A]) def test_direct_hidden_fk(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('B', related_name='+')]) B = self.create_model("B") self.assertRelated(A, [B]) self.assertRelated(B, [A]) def test_nested_fk(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('B')]) B = self.create_model("B", foreign_keys=[models.ForeignKey('C')]) C = self.create_model("C") self.assertRelated(A, [B, C]) self.assertRelated(B, [A, C]) self.assertRelated(C, [A, B]) def test_two_sided(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('B')]) B = self.create_model("B", foreign_keys=[models.ForeignKey('A')]) self.assertRelated(A, [B]) self.assertRelated(B, [A]) def test_circle(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('B')]) B = self.create_model("B", foreign_keys=[models.ForeignKey('C')]) C = self.create_model("C", foreign_keys=[models.ForeignKey('A')]) self.assertRelated(A, [B, C]) self.assertRelated(B, [A, C]) self.assertRelated(C, [A, B]) def test_base(self): A = self.create_model("A") B = self.create_model("B", bases=(A,)) self.assertRelated(A, [B]) self.assertRelated(B, [A]) def test_nested_base(self): A = self.create_model("A") B = self.create_model("B", bases=(A,)) C = self.create_model("C", bases=(B,)) self.assertRelated(A, [B, C]) self.assertRelated(B, [A, C]) self.assertRelated(C, [A, B]) def test_multiple_bases(self): A = self.create_model("A") B = self.create_model("B") C = self.create_model("C", bases=(A, B,)) self.assertRelated(A, [B, C]) self.assertRelated(B, [A, C]) self.assertRelated(C, [A, B]) def test_multiple_nested_bases(self): A = self.create_model("A") B = self.create_model("B") C = self.create_model("C", bases=(A, B,)) D = self.create_model("D") E = self.create_model("E", bases=(D,)) F = self.create_model("F", bases=(C, E,)) Y = self.create_model("Y") Z = self.create_model("Z", bases=(Y,)) self.assertRelated(A, [B, C, D, E, F]) self.assertRelated(B, [A, C, D, E, F]) self.assertRelated(C, [A, B, D, E, F]) self.assertRelated(D, [A, B, C, E, F]) self.assertRelated(E, [A, B, C, D, F]) self.assertRelated(F, [A, B, C, D, E]) self.assertRelated(Y, [Z]) self.assertRelated(Z, [Y]) def test_base_to_base_fk(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('Y')]) B = self.create_model("B", bases=(A,)) Y = self.create_model("Y") Z = self.create_model("Z", bases=(Y,)) self.assertRelated(A, [B, Y, Z]) self.assertRelated(B, [A, Y, Z]) self.assertRelated(Y, [A, B, Z]) self.assertRelated(Z, [A, B, Y]) def test_base_to_subclass_fk(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('Z')]) B = self.create_model("B", bases=(A,)) Y = self.create_model("Y") Z = self.create_model("Z", bases=(Y,)) self.assertRelated(A, [B, Y, Z]) self.assertRelated(B, [A, Y, Z]) self.assertRelated(Y, [A, B, Z]) self.assertRelated(Z, [A, B, Y]) def test_direct_m2m(self): A = self.create_model("A", foreign_keys=[models.ManyToManyField('B')]) B = self.create_model("B") self.assertRelated(A, [A.a_1.through, B]) self.assertRelated(B, [A, A.a_1.through]) def test_direct_m2m_self(self): A = self.create_model("A", foreign_keys=[models.ManyToManyField('A')]) self.assertRelated(A, [A.a_1.through]) def test_intermediate_m2m_self(self): A = self.create_model("A", foreign_keys=[models.ManyToManyField('A', through='T')]) T = self.create_model("T", foreign_keys=[models.ForeignKey('A'), models.ForeignKey('A')]) self.assertRelated(A, [T]) self.assertRelated(T, [A]) def test_intermediate_m2m(self): A = self.create_model("A", foreign_keys=[models.ManyToManyField('B', through='T')]) B = self.create_model("B") T = self.create_model("T", foreign_keys=[models.ForeignKey('A'), models.ForeignKey('B')]) self.assertRelated(A, [B, T]) self.assertRelated(B, [A, T]) self.assertRelated(T, [A, B]) def test_intermediate_m2m_extern_fk(self): A = self.create_model("A", foreign_keys=[models.ManyToManyField('B', through='T')]) B = self.create_model("B") Z = self.create_model("Z") T = self.create_model("T", foreign_keys=[ models.ForeignKey('A'), models.ForeignKey('B'), models.ForeignKey('Z'), ]) self.assertRelated(A, [B, T, Z]) self.assertRelated(B, [A, T, Z]) self.assertRelated(T, [A, B, Z]) self.assertRelated(Z, [A, B, T]) def test_intermediate_m2m_base(self): A = self.create_model("A", foreign_keys=[models.ManyToManyField('B', through='T')]) B = self.create_model("B") S = self.create_model("S") T = self.create_model("T", foreign_keys=[models.ForeignKey('A'), models.ForeignKey('B')], bases=(S,)) self.assertRelated(A, [B, S, T]) self.assertRelated(B, [A, S, T]) self.assertRelated(S, [A, B, T]) self.assertRelated(T, [A, B, S]) def test_generic_fk(self): A = self.create_model("A", foreign_keys=[models.ForeignKey('B'), GenericForeignKey()]) B = self.create_model("B", foreign_keys=[models.ForeignKey('C')]) self.assertRelated(A, [B]) self.assertRelated(B, [A]) def test_abstract_base(self): A = self.create_model("A", abstract=True) B = self.create_model("B", bases=(A,)) self.assertRelated(A, [B]) self.assertRelated(B, []) def test_nested_abstract_base(self): A = self.create_model("A", abstract=True) B = self.create_model("B", bases=(A,), abstract=True) C = self.create_model("C", bases=(B,)) self.assertRelated(A, [B, C]) self.assertRelated(B, [C]) self.assertRelated(C, []) def test_proxy_base(self): A = self.create_model("A") B = self.create_model("B", bases=(A,), proxy=True) self.assertRelated(A, [B]) self.assertRelated(B, []) def test_nested_proxy_base(self): A = self.create_model("A") B = self.create_model("B", bases=(A,), proxy=True) C = self.create_model("C", bases=(B,), proxy=True) self.assertRelated(A, [B, C]) self.assertRelated(B, [C]) self.assertRelated(C, []) def test_multiple_mixed_bases(self): A = self.create_model("A", abstract=True) M = self.create_model("M") P = self.create_model("P") Q = self.create_model("Q", bases=(P,), proxy=True) Z = self.create_model("Z", bases=(A, M, Q)) # M has a pointer O2O field p_ptr to P self.assertRelated(A, [M, P, Q, Z]) self.assertRelated(M, [P, Q, Z]) self.assertRelated(P, [M, Q, Z]) self.assertRelated(Q, [M, P, Z]) self.assertRelated(Z, [M, P, Q]) Django-1.8.7/tests/generic_relations/0000775000175000017500000000000012625116243017177 5ustar timtim00000000000000Django-1.8.7/tests/generic_relations/tests.py0000664000175000017500000006162512625116213020722 0ustar timtim00000000000000from __future__ import unicode_literals from django import forms from django.contrib.contenttypes.forms import generic_inlineformset_factory from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldError from django.db import IntegrityError from django.db.models import Q from django.test import TestCase from django.utils import six from .models import ( AllowsNullGFK, Animal, Comparison, ConcreteRelatedModel, ForConcreteModelModel, ForProxyModelModel, Gecko, ManualPK, Mineral, ProxyRelatedModel, Rock, TaggedItem, ValuableTaggedItem, Vegetable, ) class GenericRelationsTests(TestCase): def setUp(self): self.lion = Animal.objects.create( common_name="Lion", latin_name="Panthera leo") self.platypus = Animal.objects.create( common_name="Platypus", latin_name="Ornithorhynchus anatinus") Vegetable.objects.create(name="Eggplant", is_yucky=True) self.bacon = Vegetable.objects.create(name="Bacon", is_yucky=False) self.quartz = Mineral.objects.create(name="Quartz", hardness=7) # Tagging stuff. self.bacon.tags.create(tag="fatty") self.bacon.tags.create(tag="salty") self.lion.tags.create(tag="yellow") self.lion.tags.create(tag="hairy") # Original list of tags: self.comp_func = lambda obj: ( obj.tag, obj.content_type.model_class(), obj.object_id ) def test_generic_update_or_create_when_created(self): """ Should be able to use update_or_create from the generic related manager to create a tag. Refs #23611. """ count = self.bacon.tags.count() tag, created = self.bacon.tags.update_or_create(tag='stinky') self.assertTrue(created) self.assertEqual(count + 1, self.bacon.tags.count()) def test_generic_update_or_create_when_updated(self): """ Should be able to use update_or_create from the generic related manager to update a tag. Refs #23611. """ count = self.bacon.tags.count() tag = self.bacon.tags.create(tag='stinky') self.assertEqual(count + 1, self.bacon.tags.count()) tag, created = self.bacon.tags.update_or_create(defaults={'tag': 'juicy'}, id=tag.id) self.assertFalse(created) self.assertEqual(count + 1, self.bacon.tags.count()) self.assertEqual(tag.tag, 'juicy') def test_generic_get_or_create_when_created(self): """ Should be able to use get_or_create from the generic related manager to create a tag. Refs #23611. """ count = self.bacon.tags.count() tag, created = self.bacon.tags.get_or_create(tag='stinky') self.assertTrue(created) self.assertEqual(count + 1, self.bacon.tags.count()) def test_generic_get_or_create_when_exists(self): """ Should be able to use get_or_create from the generic related manager to get a tag. Refs #23611. """ count = self.bacon.tags.count() tag = self.bacon.tags.create(tag="stinky") self.assertEqual(count + 1, self.bacon.tags.count()) tag, created = self.bacon.tags.get_or_create(id=tag.id, defaults={'tag': 'juicy'}) self.assertFalse(created) self.assertEqual(count + 1, self.bacon.tags.count()) # shouldn't had changed the tag self.assertEqual(tag.tag, 'stinky') def test_generic_relations_m2m_mimic(self): """ Objects with declared GenericRelations can be tagged directly -- the API mimics the many-to-many API. """ self.assertQuerysetEqual(self.lion.tags.all(), [ "", "" ]) self.assertQuerysetEqual(self.bacon.tags.all(), [ "", "" ]) def test_access_content_object(self): """ Test accessing the content object like a foreign key. """ tagged_item = TaggedItem.objects.get(tag="salty") self.assertEqual(tagged_item.content_object, self.bacon) def test_query_content_object(self): qs = TaggedItem.objects.filter( animal__isnull=False).order_by('animal__common_name', 'tag') self.assertQuerysetEqual( qs, ["", ""] ) mpk = ManualPK.objects.create(id=1) mpk.tags.create(tag='mpk') qs = TaggedItem.objects.filter( Q(animal__isnull=False) | Q(manualpk__id=1)).order_by('tag') self.assertQuerysetEqual( qs, ["hairy", "mpk", "yellow"], lambda x: x.tag) def test_exclude_generic_relations(self): """ Test lookups over an object without GenericRelations. """ # Recall that the Mineral class doesn't have an explicit GenericRelation # defined. That's OK, because you can create TaggedItems explicitly. # However, excluding GenericRelations means your lookups have to be a # bit more explicit. TaggedItem.objects.create(content_object=self.quartz, tag="shiny") TaggedItem.objects.create(content_object=self.quartz, tag="clearish") ctype = ContentType.objects.get_for_model(self.quartz) q = TaggedItem.objects.filter( content_type__pk=ctype.id, object_id=self.quartz.id ) self.assertQuerysetEqual(q, [ "", "" ]) def test_access_via_content_type(self): """ Test lookups through content type. """ self.lion.delete() self.platypus.tags.create(tag="fatty") ctype = ContentType.objects.get_for_model(self.platypus) self.assertQuerysetEqual( Animal.objects.filter(tags__content_type=ctype), [""]) def test_set_foreign_key(self): """ You can set a generic foreign key in the way you'd expect. """ tag1 = TaggedItem.objects.create(content_object=self.quartz, tag="shiny") tag1.content_object = self.platypus tag1.save() self.assertQuerysetEqual( self.platypus.tags.all(), [""]) def test_queries_across_generic_relations(self): """ Queries across generic relations respect the content types. Even though there are two TaggedItems with a tag of "fatty", this query only pulls out the one with the content type related to Animals. """ self.assertQuerysetEqual(Animal.objects.order_by('common_name'), [ "", "" ]) def test_queries_content_type_restriction(self): """ Create another fatty tagged instance with different PK to ensure there is a content type restriction in the generated queries below. """ mpk = ManualPK.objects.create(id=self.lion.pk) mpk.tags.create(tag="fatty") self.platypus.tags.create(tag="fatty") self.assertQuerysetEqual( Animal.objects.filter(tags__tag='fatty'), [""]) self.assertQuerysetEqual( Animal.objects.exclude(tags__tag='fatty'), [""]) def test_object_deletion_with_generic_relation(self): """ If you delete an object with an explicit Generic relation, the related objects are deleted when the source object is deleted. """ self.assertQuerysetEqual(TaggedItem.objects.all(), [ ('fatty', Vegetable, self.bacon.pk), ('hairy', Animal, self.lion.pk), ('salty', Vegetable, self.bacon.pk), ('yellow', Animal, self.lion.pk) ], self.comp_func ) self.lion.delete() self.assertQuerysetEqual(TaggedItem.objects.all(), [ ('fatty', Vegetable, self.bacon.pk), ('salty', Vegetable, self.bacon.pk), ], self.comp_func ) def test_object_deletion_without_generic_relation(self): """ If Generic Relation is not explicitly defined, any related objects remain after deletion of the source object. """ TaggedItem.objects.create(content_object=self.quartz, tag="clearish") quartz_pk = self.quartz.pk self.quartz.delete() self.assertQuerysetEqual(TaggedItem.objects.all(), [ ('clearish', Mineral, quartz_pk), ('fatty', Vegetable, self.bacon.pk), ('hairy', Animal, self.lion.pk), ('salty', Vegetable, self.bacon.pk), ('yellow', Animal, self.lion.pk), ], self.comp_func ) def test_tag_deletion_related_objects_unaffected(self): """ If you delete a tag, the objects using the tag are unaffected (other than losing a tag). """ ctype = ContentType.objects.get_for_model(self.lion) tag = TaggedItem.objects.get( content_type__pk=ctype.id, object_id=self.lion.id, tag="hairy") tag.delete() self.assertQuerysetEqual(self.lion.tags.all(), [""]) self.assertQuerysetEqual(TaggedItem.objects.all(), [ ('fatty', Vegetable, self.bacon.pk), ('salty', Vegetable, self.bacon.pk), ('yellow', Animal, self.lion.pk) ], self.comp_func ) def test_assign_with_queryset(self): # Ensure that querysets used in reverse GFK assignments are pre-evaluated # so their value isn't affected by the clearing operation in # ManyRelatedObjectsDescriptor.__set__. Refs #19816. bacon = Vegetable.objects.create(name="Bacon", is_yucky=False) bacon.tags.create(tag="fatty") bacon.tags.create(tag="salty") self.assertEqual(2, bacon.tags.count()) qs = bacon.tags.filter(tag="fatty") bacon.tags = qs self.assertEqual(1, bacon.tags.count()) self.assertEqual(1, qs.count()) def test_generic_relation_related_name_default(self): # Test that GenericRelation by default isn't usable from # the reverse side. with self.assertRaises(FieldError): TaggedItem.objects.filter(vegetable__isnull=True) def test_multiple_gfk(self): # Simple tests for multiple GenericForeignKeys # only uses one model, since the above tests should be sufficient. tiger = Animal.objects.create(common_name="tiger") cheetah = Animal.objects.create(common_name="cheetah") bear = Animal.objects.create(common_name="bear") # Create directly Comparison.objects.create( first_obj=cheetah, other_obj=tiger, comparative="faster" ) Comparison.objects.create( first_obj=tiger, other_obj=cheetah, comparative="cooler" ) # Create using GenericRelation tiger.comparisons.create(other_obj=bear, comparative="cooler") tiger.comparisons.create(other_obj=cheetah, comparative="stronger") self.assertQuerysetEqual(cheetah.comparisons.all(), [ "" ]) # Filtering works self.assertQuerysetEqual(tiger.comparisons.filter(comparative="cooler"), [ "", "", ], ordered=False) # Filtering and deleting works subjective = ["cooler"] tiger.comparisons.filter(comparative__in=subjective).delete() self.assertQuerysetEqual(Comparison.objects.all(), [ "", "" ], ordered=False) # If we delete cheetah, Comparisons with cheetah as 'first_obj' will be # deleted since Animal has an explicit GenericRelation to Comparison # through first_obj. Comparisons with cheetah as 'other_obj' will not # be deleted. cheetah.delete() self.assertQuerysetEqual(Comparison.objects.all(), [ "" ]) def test_gfk_subclasses(self): # GenericForeignKey should work with subclasses (see #8309) quartz = Mineral.objects.create(name="Quartz", hardness=7) valuedtag = ValuableTaggedItem.objects.create( content_object=quartz, tag="shiny", value=10 ) self.assertEqual(valuedtag.content_object, quartz) def test_generic_inline_formsets(self): GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1) formset = GenericFormSet() self.assertHTMLEqual(''.join(form.as_p() for form in formset.forms), """

    """) formset = GenericFormSet(instance=Animal()) self.assertHTMLEqual(''.join(form.as_p() for form in formset.forms), """

    """) platypus = Animal.objects.create( common_name="Platypus", latin_name="Ornithorhynchus anatinus" ) platypus.tags.create(tag="shiny") GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1) formset = GenericFormSet(instance=platypus) tagged_item_id = TaggedItem.objects.get( tag='shiny', object_id=platypus.id ).id self.assertHTMLEqual(''.join(form.as_p() for form in formset.forms), """

    """ % tagged_item_id) lion = Animal.objects.create(common_name="Lion", latin_name="Panthera leo") formset = GenericFormSet(instance=lion, prefix='x') self.assertHTMLEqual(''.join(form.as_p() for form in formset.forms), """

    """) def test_gfk_manager(self): # GenericForeignKey should not use the default manager (which may filter objects) #16048 tailless = Gecko.objects.create(has_tail=False) tag = TaggedItem.objects.create(content_object=tailless, tag="lizard") self.assertEqual(tag.content_object, tailless) def test_subclasses_with_gen_rel(self): """ Test that concrete model subclasses with generic relations work correctly (ticket 11263). """ granite = Rock.objects.create(name='granite', hardness=5) TaggedItem.objects.create(content_object=granite, tag="countertop") self.assertEqual(Rock.objects.filter(tags__tag="countertop").count(), 1) def test_generic_inline_formsets_initial(self): """ Test for #17927 Initial values support for BaseGenericInlineFormSet. """ quartz = Mineral.objects.create(name="Quartz", hardness=7) GenericFormSet = generic_inlineformset_factory(TaggedItem, extra=1) ctype = ContentType.objects.get_for_model(quartz) initial_data = [{ 'tag': 'lizard', 'content_type': ctype.pk, 'object_id': quartz.pk, }] formset = GenericFormSet(initial=initial_data) self.assertEqual(formset.forms[0].initial, initial_data[0]) def test_get_or_create(self): # get_or_create should work with virtual fields (content_object) quartz = Mineral.objects.create(name="Quartz", hardness=7) tag, created = TaggedItem.objects.get_or_create(tag="shiny", defaults={'content_object': quartz}) self.assertTrue(created) self.assertEqual(tag.tag, "shiny") self.assertEqual(tag.content_object.id, quartz.id) def test_update_or_create_defaults(self): # update_or_create should work with virtual fields (content_object) quartz = Mineral.objects.create(name="Quartz", hardness=7) diamond = Mineral.objects.create(name="Diamond", hardness=7) tag, created = TaggedItem.objects.update_or_create(tag="shiny", defaults={'content_object': quartz}) self.assertTrue(created) self.assertEqual(tag.content_object.id, quartz.id) tag, created = TaggedItem.objects.update_or_create(tag="shiny", defaults={'content_object': diamond}) self.assertFalse(created) self.assertEqual(tag.content_object.id, diamond.id) def test_query_content_type(self): msg = "Field 'content_object' does not generate an automatic reverse relation" with self.assertRaisesMessage(FieldError, msg): TaggedItem.objects.get(content_object='') def test_unsaved_instance_on_generic_foreign_key(self): """ Assigning an unsaved object to GenericForeignKey should raise an exception on model.save(). """ quartz = Mineral(name="Quartz", hardness=7) with self.assertRaises(IntegrityError): TaggedItem.objects.create(tag="shiny", content_object=quartz) class CustomWidget(forms.TextInput): pass class TaggedItemForm(forms.ModelForm): class Meta: model = TaggedItem fields = '__all__' widgets = {'tag': CustomWidget} class GenericInlineFormsetTest(TestCase): def test_generic_inlineformset_factory(self): """ Regression for #14572: Using base forms with widgets defined in Meta should not raise errors. """ Formset = generic_inlineformset_factory(TaggedItem, TaggedItemForm) form = Formset().forms[0] self.assertIsInstance(form['tag'].field.widget, CustomWidget) def test_save_new_uses_form_save(self): """ Regression for #16260: save_new should call form.save() """ class SaveTestForm(forms.ModelForm): def save(self, *args, **kwargs): self.instance.saved_by = "custom method" return super(SaveTestForm, self).save(*args, **kwargs) Formset = generic_inlineformset_factory( ForProxyModelModel, fields='__all__', form=SaveTestForm) instance = ProxyRelatedModel.objects.create() data = { 'form-TOTAL_FORMS': '1', 'form-INITIAL_FORMS': '0', 'form-MAX_NUM_FORMS': '', 'form-0-title': 'foo', } formset = Formset(data, instance=instance, prefix='form') self.assertTrue(formset.is_valid()) new_obj = formset.save()[0] self.assertEqual(new_obj.saved_by, "custom method") def test_save_new_for_proxy(self): Formset = generic_inlineformset_factory(ForProxyModelModel, fields='__all__', for_concrete_model=False) instance = ProxyRelatedModel.objects.create() data = { 'form-TOTAL_FORMS': '1', 'form-INITIAL_FORMS': '0', 'form-MAX_NUM_FORMS': '', 'form-0-title': 'foo', } formset = Formset(data, instance=instance, prefix='form') self.assertTrue(formset.is_valid()) new_obj, = formset.save() self.assertEqual(new_obj.obj, instance) def test_save_new_for_concrete(self): Formset = generic_inlineformset_factory(ForProxyModelModel, fields='__all__', for_concrete_model=True) instance = ProxyRelatedModel.objects.create() data = { 'form-TOTAL_FORMS': '1', 'form-INITIAL_FORMS': '0', 'form-MAX_NUM_FORMS': '', 'form-0-title': 'foo', } formset = Formset(data, instance=instance, prefix='form') self.assertTrue(formset.is_valid()) new_obj, = formset.save() self.assertNotIsInstance(new_obj.obj, ProxyRelatedModel) class ProxyRelatedModelTest(TestCase): def test_default_behavior(self): """ The default for for_concrete_model should be True """ base = ForConcreteModelModel() base.obj = rel = ProxyRelatedModel.objects.create() base.save() base = ForConcreteModelModel.objects.get(pk=base.pk) rel = ConcreteRelatedModel.objects.get(pk=rel.pk) self.assertEqual(base.obj, rel) def test_works_normally(self): """ When for_concrete_model is False, we should still be able to get an instance of the concrete class. """ base = ForProxyModelModel() base.obj = rel = ConcreteRelatedModel.objects.create() base.save() base = ForProxyModelModel.objects.get(pk=base.pk) self.assertEqual(base.obj, rel) def test_proxy_is_returned(self): """ Instances of the proxy should be returned when for_concrete_model is False. """ base = ForProxyModelModel() base.obj = ProxyRelatedModel.objects.create() base.save() base = ForProxyModelModel.objects.get(pk=base.pk) self.assertIsInstance(base.obj, ProxyRelatedModel) def test_query(self): base = ForProxyModelModel() base.obj = rel = ConcreteRelatedModel.objects.create() base.save() self.assertEqual(rel, ConcreteRelatedModel.objects.get(bases__id=base.id)) def test_query_proxy(self): base = ForProxyModelModel() base.obj = rel = ProxyRelatedModel.objects.create() base.save() self.assertEqual(rel, ProxyRelatedModel.objects.get(bases__id=base.id)) def test_generic_relation(self): base = ForProxyModelModel() base.obj = ProxyRelatedModel.objects.create() base.save() base = ForProxyModelModel.objects.get(pk=base.pk) rel = ProxyRelatedModel.objects.get(pk=base.obj.pk) self.assertEqual(base, rel.bases.get()) def test_generic_relation_set(self): base = ForProxyModelModel() base.obj = ConcreteRelatedModel.objects.create() base.save() newrel = ConcreteRelatedModel.objects.create() newrel.bases = [base] newrel = ConcreteRelatedModel.objects.get(pk=newrel.pk) self.assertEqual(base, newrel.bases.get()) class TestInitWithNoneArgument(TestCase): def test_none_not_allowed(self): # TaggedItem requires a content_type, initializing with None should # raise a ValueError. with six.assertRaisesRegex(self, ValueError, 'Cannot assign None: "TaggedItem.content_type" does not allow null values'): TaggedItem(content_object=None) def test_none_allowed(self): # AllowsNullGFK doesn't require a content_type, so None argument should # also be allowed. AllowsNullGFK(content_object=None) Django-1.8.7/tests/generic_relations/__init__.py0000664000175000017500000000000012603513307021274 0ustar timtim00000000000000Django-1.8.7/tests/generic_relations/models.py0000664000175000017500000001020612625116213021030 0ustar timtim00000000000000""" Generic relations Generic relations let an object have a foreign key to any object through a content-type/object-id field. A ``GenericForeignKey`` field can point to any object, be it animal, vegetable, or mineral. The canonical example is tags (although this example implementation is *far* from complete). """ from __future__ import unicode_literals from django.contrib.contenttypes.fields import ( GenericForeignKey, GenericRelation, ) from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class TaggedItem(models.Model): """A tag on an item.""" tag = models.SlugField() content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey() class Meta: ordering = ["tag", "content_type__model"] def __str__(self): return self.tag class ValuableTaggedItem(TaggedItem): value = models.PositiveIntegerField() class AbstractComparison(models.Model): comparative = models.CharField(max_length=50) content_type1 = models.ForeignKey(ContentType, related_name="comparative1_set") object_id1 = models.PositiveIntegerField() first_obj = GenericForeignKey(ct_field="content_type1", fk_field="object_id1") @python_2_unicode_compatible class Comparison(AbstractComparison): """ A model that tests having multiple GenericForeignKeys. One is defined through an inherited abstract model and one defined directly on this class. """ content_type2 = models.ForeignKey(ContentType, related_name="comparative2_set") object_id2 = models.PositiveIntegerField() other_obj = GenericForeignKey(ct_field="content_type2", fk_field="object_id2") def __str__(self): return "%s is %s than %s" % (self.first_obj, self.comparative, self.other_obj) @python_2_unicode_compatible class Animal(models.Model): common_name = models.CharField(max_length=150) latin_name = models.CharField(max_length=150) tags = GenericRelation(TaggedItem, related_query_name='animal') comparisons = GenericRelation(Comparison, object_id_field="object_id1", content_type_field="content_type1") def __str__(self): return self.common_name @python_2_unicode_compatible class Vegetable(models.Model): name = models.CharField(max_length=150) is_yucky = models.BooleanField(default=True) tags = GenericRelation(TaggedItem) def __str__(self): return self.name @python_2_unicode_compatible class Mineral(models.Model): name = models.CharField(max_length=150) hardness = models.PositiveSmallIntegerField() # note the lack of an explicit GenericRelation here... def __str__(self): return self.name class GeckoManager(models.Manager): def get_queryset(self): return super(GeckoManager, self).get_queryset().filter(has_tail=True) class Gecko(models.Model): has_tail = models.BooleanField(default=False) objects = GeckoManager() # To test fix for #11263 class Rock(Mineral): tags = GenericRelation(TaggedItem) class ManualPK(models.Model): id = models.IntegerField(primary_key=True) tags = GenericRelation(TaggedItem, related_query_name='manualpk') class ForProxyModelModel(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() obj = GenericForeignKey(for_concrete_model=False) title = models.CharField(max_length=255, null=True) class ForConcreteModelModel(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() obj = GenericForeignKey() class ConcreteRelatedModel(models.Model): bases = GenericRelation(ForProxyModelModel, for_concrete_model=False) class ProxyRelatedModel(ConcreteRelatedModel): class Meta: proxy = True # To test fix for #7551 class AllowsNullGFK(models.Model): content_type = models.ForeignKey(ContentType, null=True) object_id = models.PositiveIntegerField(null=True) content_object = GenericForeignKey() Django-1.8.7/tests/db_functions/0000775000175000017500000000000012625116243016160 5ustar timtim00000000000000Django-1.8.7/tests/db_functions/tests.py0000664000175000017500000002535712625116213017705 0ustar timtim00000000000000from __future__ import unicode_literals from unittest import skipUnless from django.db import connection from django.db.models import CharField, TextField, Value as V from django.db.models.functions import ( Coalesce, Concat, ConcatPair, Length, Lower, Substr, Upper, ) from django.test import TestCase from django.utils import six, timezone from .models import Article, Author lorem_ipsum = """ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.""" class FunctionTests(TestCase): def test_coalesce(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.annotate(display_name=Coalesce('alias', 'name')) self.assertQuerysetEqual( authors.order_by('name'), [ 'smithj', 'Rhonda', ], lambda a: a.display_name ) with self.assertRaisesMessage(ValueError, 'Coalesce must take at least two expressions'): Author.objects.annotate(display_name=Coalesce('alias')) def test_coalesce_mixed_values(self): a1 = Author.objects.create(name='John Smith', alias='smithj') a2 = Author.objects.create(name='Rhonda') ar1 = Article.objects.create( title="How to Django", text=lorem_ipsum, written=timezone.now(), ) ar1.authors.add(a1) ar1.authors.add(a2) # mixed Text and Char article = Article.objects.annotate( headline=Coalesce('summary', 'text', output_field=TextField()), ) self.assertQuerysetEqual( article.order_by('title'), [ lorem_ipsum, ], lambda a: a.headline ) # mixed Text and Char wrapped article = Article.objects.annotate( headline=Coalesce(Lower('summary'), Lower('text'), output_field=TextField()), ) self.assertQuerysetEqual( article.order_by('title'), [ lorem_ipsum.lower(), ], lambda a: a.headline ) def test_coalesce_ordering(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.order_by(Coalesce('alias', 'name')) self.assertQuerysetEqual( authors, [ 'Rhonda', 'John Smith', ], lambda a: a.name ) authors = Author.objects.order_by(Coalesce('alias', 'name').asc()) self.assertQuerysetEqual( authors, [ 'Rhonda', 'John Smith', ], lambda a: a.name ) authors = Author.objects.order_by(Coalesce('alias', 'name').desc()) self.assertQuerysetEqual( authors, [ 'John Smith', 'Rhonda', ], lambda a: a.name ) def test_concat(self): Author.objects.create(name='Jayden') Author.objects.create(name='John Smith', alias='smithj', goes_by='John') Author.objects.create(name='Margaret', goes_by='Maggie') Author.objects.create(name='Rhonda', alias='adnohR') authors = Author.objects.annotate(joined=Concat('alias', 'goes_by')) self.assertQuerysetEqual( authors.order_by('name'), [ '', 'smithjJohn', 'Maggie', 'adnohR', ], lambda a: a.joined ) with self.assertRaisesMessage(ValueError, 'Concat must take at least two expressions'): Author.objects.annotate(joined=Concat('alias')) def test_concat_many(self): Author.objects.create(name='Jayden') Author.objects.create(name='John Smith', alias='smithj', goes_by='John') Author.objects.create(name='Margaret', goes_by='Maggie') Author.objects.create(name='Rhonda', alias='adnohR') authors = Author.objects.annotate( joined=Concat('name', V(' ('), 'goes_by', V(')'), output_field=CharField()), ) self.assertQuerysetEqual( authors.order_by('name'), [ 'Jayden ()', 'John Smith (John)', 'Margaret (Maggie)', 'Rhonda ()', ], lambda a: a.joined ) def test_concat_mixed_char_text(self): Article.objects.create(title='The Title', text=lorem_ipsum, written=timezone.now()) article = Article.objects.annotate( title_text=Concat('title', V(' - '), 'text', output_field=TextField()), ).get(title='The Title') self.assertEqual(article.title + ' - ' + article.text, article.title_text) # wrap the concat in something else to ensure that we're still # getting text rather than bytes article = Article.objects.annotate( title_text=Upper(Concat('title', V(' - '), 'text', output_field=TextField())), ).get(title='The Title') expected = article.title + ' - ' + article.text self.assertEqual(expected.upper(), article.title_text) @skipUnless(connection.vendor == 'sqlite', "sqlite specific implementation detail.") def test_concat_coalesce_idempotent(self): pair = ConcatPair(V('a'), V('b')) # Check nodes counts self.assertEqual(len(list(pair.flatten())), 3) self.assertEqual(len(list(pair.coalesce().flatten())), 7) # + 2 Coalesce + 2 Value() self.assertEqual(len(list(pair.flatten())), 3) def test_concat_sql_generation_idempotency(self): qs = Article.objects.annotate(description=Concat('title', V(': '), 'summary')) # Multiple compilations should not alter the generated query. self.assertEqual(str(qs.query), str(qs.all().query)) def test_lower(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.annotate(lower_name=Lower('name')) self.assertQuerysetEqual( authors.order_by('name'), [ 'john smith', 'rhonda', ], lambda a: a.lower_name ) Author.objects.update(name=Lower('name')) self.assertQuerysetEqual( authors.order_by('name'), [ ('john smith', 'john smith'), ('rhonda', 'rhonda'), ], lambda a: (a.lower_name, a.name) ) def test_upper(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.annotate(upper_name=Upper('name')) self.assertQuerysetEqual( authors.order_by('name'), [ 'JOHN SMITH', 'RHONDA', ], lambda a: a.upper_name ) Author.objects.update(name=Upper('name')) self.assertQuerysetEqual( authors.order_by('name'), [ ('JOHN SMITH', 'JOHN SMITH'), ('RHONDA', 'RHONDA'), ], lambda a: (a.upper_name, a.name) ) def test_length(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.annotate( name_length=Length('name'), alias_length=Length('alias')) self.assertQuerysetEqual( authors.order_by('name'), [ (10, 6), (6, None), ], lambda a: (a.name_length, a.alias_length) ) self.assertEqual(authors.filter(alias_length__lte=Length('name')).count(), 1) def test_length_ordering(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='John Smith', alias='smithj1') Author.objects.create(name='Rhonda', alias='ronny') authors = Author.objects.order_by(Length('name'), Length('alias')) self.assertQuerysetEqual( authors, [ ('Rhonda', 'ronny'), ('John Smith', 'smithj'), ('John Smith', 'smithj1'), ], lambda a: (a.name, a.alias) ) def test_substr(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.annotate(name_part=Substr('name', 5, 3)) self.assertQuerysetEqual( authors.order_by('name'), [ ' Sm', 'da', ], lambda a: a.name_part ) authors = Author.objects.annotate(name_part=Substr('name', 2)) self.assertQuerysetEqual( authors.order_by('name'), [ 'ohn Smith', 'honda', ], lambda a: a.name_part ) # if alias is null, set to first 5 lower characters of the name Author.objects.filter(alias__isnull=True).update( alias=Lower(Substr('name', 1, 5)), ) self.assertQuerysetEqual( authors.order_by('name'), [ 'smithj', 'rhond', ], lambda a: a.alias ) def test_substr_start(self): Author.objects.create(name='John Smith', alias='smithj') a = Author.objects.annotate( name_part_1=Substr('name', 1), name_part_2=Substr('name', 2), ).get(alias='smithj') self.assertEqual(a.name_part_1[1:], a.name_part_2) with six.assertRaisesRegex(self, ValueError, "'pos' must be greater than 0"): Author.objects.annotate(raises=Substr('name', 0)) def test_substr_with_expressions(self): Author.objects.create(name='John Smith', alias='smithj') Author.objects.create(name='Rhonda') authors = Author.objects.annotate(name_part=Substr('name', 5, 3)) self.assertQuerysetEqual( authors.order_by('name'), [ ' Sm', 'da', ], lambda a: a.name_part ) def test_nested_function_ordering(self): Author.objects.create(name='John Smith') Author.objects.create(name='Rhonda Simpson', alias='ronny') authors = Author.objects.order_by(Length(Coalesce('alias', 'name'))) self.assertQuerysetEqual( authors, [ 'Rhonda Simpson', 'John Smith', ], lambda a: a.name ) authors = Author.objects.order_by(Length(Coalesce('alias', 'name')).desc()) self.assertQuerysetEqual( authors, [ 'John Smith', 'Rhonda Simpson', ], lambda a: a.name ) Django-1.8.7/tests/db_functions/__init__.py0000664000175000017500000000000012625116213020254 0ustar timtim00000000000000Django-1.8.7/tests/db_functions/models.py0000664000175000017500000000165412625116213020020 0ustar timtim00000000000000""" Tests for built in Function expressions. """ from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Author(models.Model): name = models.CharField(max_length=50) alias = models.CharField(max_length=50, null=True, blank=True) goes_by = models.CharField(max_length=50, null=True, blank=True) def __str__(self): return self.name @python_2_unicode_compatible class Article(models.Model): authors = models.ManyToManyField(Author, related_name='articles') title = models.CharField(max_length=50) summary = models.CharField(max_length=200, null=True, blank=True) text = models.TextField() written = models.DateTimeField() published = models.DateTimeField(null=True, blank=True) views = models.PositiveIntegerField(default=0) def __str__(self): return self.title Django-1.8.7/tests/urls.py0000664000175000017500000000032312625116214015036 0ustar timtim00000000000000"""This urlconf exists because Django expects ROOT_URLCONF to exist. URLs should be added within the test folders, and use TestCase.urls to set them. This helps the tests remain isolated. """ urlpatterns = [] Django-1.8.7/tests/templates/0000775000175000017500000000000012625116243015501 5ustar timtim00000000000000Django-1.8.7/tests/templates/login.html0000664000175000017500000000102312625116214017471 0ustar timtim00000000000000{% extends "base.html" %} {% block title %}Login{% endblock %} {% block content %} {% if form.has_errors %}

    Your username and password didn't match. Please try again.

    {% endif %}
    {{ form.username }}
    {{ form.password }}
    {% endblock %} Django-1.8.7/tests/templates/form_view.html0000664000175000017500000000052212625116214020361 0ustar timtim00000000000000{% extends "base.html" %} {% block title %}Submit data{% endblock %} {% block content %}

    {{ message }}

    {% if form.errors %}

    Please correct the errors below:

    {% endif %}
      {{ form }}
    {% endblock %} Django-1.8.7/tests/templates/base.html0000664000175000017500000000021712625116214017277 0ustar timtim00000000000000

    Django Internal Tests: {% block title %}{% endblock %}

    {% block content %} {% endblock %} Django-1.8.7/tests/templates/extended.html0000664000175000017500000000023212625116214020162 0ustar timtim00000000000000{% extends "base.html" %} {% block title %}Extended template{% endblock %} {% block content %} This is just a template extending the base. {% endblock %} Django-1.8.7/tests/templates/views/0000775000175000017500000000000012625116243016636 5ustar timtim00000000000000Django-1.8.7/tests/templates/views/article_detail.html0000664000175000017500000000003112552167174022473 0ustar timtim00000000000000Article detail template. Django-1.8.7/tests/templates/views/article_archive_month.html0000664000175000017500000000004712625116214024054 0ustar timtim00000000000000This template intentionally left blank Django-1.8.7/tests/templates/views/article_archive_day.html0000664000175000017500000000004712552167174023516 0ustar timtim00000000000000This template intentionally left blank Django-1.8.7/tests/templates/views/article_list.html0000664000175000017500000000002212625116214022172 0ustar timtim00000000000000{{ object_list }} Django-1.8.7/tests/templates/views/urlarticle_form.html0000664000175000017500000000005512552167174022725 0ustar timtim00000000000000UrlArticle form template. {{ form.errors }} Django-1.8.7/tests/templates/views/datearticle_archive_month.html0000664000175000017500000000004712625116214024712 0ustar timtim00000000000000This template intentionally left blank Django-1.8.7/tests/templates/views/article_confirm_delete.html0000664000175000017500000000004712625116214024205 0ustar timtim00000000000000This template intentionally left blank Django-1.8.7/tests/templates/views/urlarticle_detail.html0000664000175000017500000000003412552167174023221 0ustar timtim00000000000000UrlArticle detail template. Django-1.8.7/tests/templates/views/article_form.html0000664000175000017500000000005212552167174022177 0ustar timtim00000000000000Article form template. {{ form.errors }} Django-1.8.7/tests/templates/custom_admin/0000775000175000017500000000000012625116243020163 5ustar timtim00000000000000Django-1.8.7/tests/templates/custom_admin/add_form.html0000664000175000017500000000004712603512250022617 0ustar timtim00000000000000{% extends "admin/change_form.html" %} Django-1.8.7/tests/templates/custom_admin/index.html0000664000175000017500000000020412552167174022164 0ustar timtim00000000000000{% extends "admin/index.html" %} {% block content %} Hello from a custom index template {{ foo }} {{ block.super }} {% endblock %} Django-1.8.7/tests/templates/custom_admin/app_index.html0000664000175000017500000000020212603513307023010 0ustar timtim00000000000000{% extends "admin/app_index.html" %} {% block content %} Hello from a custom app_index template {{ block.super }} {% endblock %} Django-1.8.7/tests/templates/custom_admin/delete_confirmation.html0000664000175000017500000000005712552167174025075 0ustar timtim00000000000000{% extends "admin/delete_confirmation.html" %} Django-1.8.7/tests/templates/custom_admin/delete_selected_confirmation.html0000664000175000017500000000007012603512224026723 0ustar timtim00000000000000{% extends "admin/delete_selected_confirmation.html" %} Django-1.8.7/tests/templates/custom_admin/login.html0000664000175000017500000000017212552167174022171 0ustar timtim00000000000000{% extends "admin/login.html" %} {% block content %} Hello from a custom login template {{ block.super }} {% endblock %} Django-1.8.7/tests/templates/custom_admin/password_change_done.html0000664000175000017500000000023712603512250025221 0ustar timtim00000000000000{% extends "registration/password_change_done.html" %} {% block content %} Hello from a custom password change done template {{ block.super }} {% endblock %} Django-1.8.7/tests/templates/custom_admin/password_change_form.html0000664000175000017500000000025212625116214025240 0ustar timtim00000000000000{% extends "registration/password_change_form.html" %} {% block content %} {{ spam }} Hello from a custom password change form template {{ block.super }} {% endblock %} Django-1.8.7/tests/templates/custom_admin/logout.html0000664000175000017500000000020712603512250022353 0ustar timtim00000000000000{% extends "registration/logged_out.html" %} {% block content %} Hello from a custom logout template {{ block.super }} {% endblock %} Django-1.8.7/tests/templates/custom_admin/change_list.html0000664000175000017500000000022612552167174023341 0ustar timtim00000000000000{% extends "admin/change_list.html" %} {% block extrahead %} {% endblock %} Django-1.8.7/tests/templates/custom_admin/object_history.html0000664000175000017500000000005212552167174024105 0ustar timtim00000000000000{% extends "admin/object_history.html" %} Django-1.8.7/tests/templates/custom_admin/change_form.html0000664000175000017500000000004712552167174023332 0ustar timtim00000000000000{% extends "admin/change_form.html" %} Django-1.8.7/tests/templates/comments/0000775000175000017500000000000012625116243017326 5ustar timtim00000000000000Django-1.8.7/tests/templates/comments/comment_notification_email.txt0000664000175000017500000000013712603512224025442 0ustar timtim00000000000000A comment has been posted on {{ content_object }}. The comment reads as follows: {{ comment }} Django-1.8.7/tests/datatypes/0000775000175000017500000000000012625116243015501 5ustar timtim00000000000000Django-1.8.7/tests/datatypes/tests.py0000664000175000017500000001016212625116213017212 0ustar timtim00000000000000from __future__ import unicode_literals import datetime from django.test import TestCase, skipIfDBFeature from django.utils import six from django.utils.timezone import utc from .models import Donut, RumBaba class DataTypesTestCase(TestCase): def test_boolean_type(self): d = Donut(name='Apple Fritter') self.assertFalse(d.is_frosted) self.assertIsNone(d.has_sprinkles) d.has_sprinkles = True self.assertTrue(d.has_sprinkles) d.save() d2 = Donut.objects.get(name='Apple Fritter') self.assertFalse(d2.is_frosted) self.assertTrue(d2.has_sprinkles) def test_date_type(self): d = Donut(name='Apple Fritter') d.baked_date = datetime.date(year=1938, month=6, day=4) d.baked_time = datetime.time(hour=5, minute=30) d.consumed_at = datetime.datetime(year=2007, month=4, day=20, hour=16, minute=19, second=59) d.save() d2 = Donut.objects.get(name='Apple Fritter') self.assertEqual(d2.baked_date, datetime.date(1938, 6, 4)) self.assertEqual(d2.baked_time, datetime.time(5, 30)) self.assertEqual(d2.consumed_at, datetime.datetime(2007, 4, 20, 16, 19, 59)) def test_time_field(self): # Test for ticket #12059: TimeField wrongly handling datetime.datetime object. d = Donut(name='Apple Fritter') d.baked_time = datetime.datetime(year=2007, month=4, day=20, hour=16, minute=19, second=59) d.save() d2 = Donut.objects.get(name='Apple Fritter') self.assertEqual(d2.baked_time, datetime.time(16, 19, 59)) def test_year_boundaries(self): """Year boundary tests (ticket #3689)""" Donut.objects.create(name='Date Test 2007', baked_date=datetime.datetime(year=2007, month=12, day=31), consumed_at=datetime.datetime(year=2007, month=12, day=31, hour=23, minute=59, second=59)) Donut.objects.create(name='Date Test 2006', baked_date=datetime.datetime(year=2006, month=1, day=1), consumed_at=datetime.datetime(year=2006, month=1, day=1)) self.assertEqual("Date Test 2007", Donut.objects.filter(baked_date__year=2007)[0].name) self.assertEqual("Date Test 2006", Donut.objects.filter(baked_date__year=2006)[0].name) Donut.objects.create(name='Apple Fritter', consumed_at=datetime.datetime(year=2007, month=4, day=20, hour=16, minute=19, second=59)) self.assertEqual(['Apple Fritter', 'Date Test 2007'], list(Donut.objects.filter(consumed_at__year=2007).order_by('name').values_list('name', flat=True))) self.assertEqual(0, Donut.objects.filter(consumed_at__year=2005).count()) self.assertEqual(0, Donut.objects.filter(consumed_at__year=2008).count()) def test_textfields_unicode(self): """Regression test for #10238: TextField values returned from the database should be unicode.""" d = Donut.objects.create(name='Jelly Donut', review='Outstanding') newd = Donut.objects.get(id=d.id) self.assertIsInstance(newd.review, six.text_type) @skipIfDBFeature('supports_timezones') def test_error_on_timezone(self): """Regression test for #8354: the MySQL and Oracle backends should raise an error if given a timezone-aware datetime object.""" dt = datetime.datetime(2008, 8, 31, 16, 20, tzinfo=utc) d = Donut(name='Bear claw', consumed_at=dt) self.assertRaises(ValueError, d.save) # ValueError: MySQL backend does not support timezone-aware datetimes. def test_datefield_auto_now_add(self): """Regression test for #10970, auto_now_add for DateField should store a Python datetime.date, not a datetime.datetime""" b = RumBaba.objects.create() # Verify we didn't break DateTimeField behavior self.assertIsInstance(b.baked_timestamp, datetime.datetime) # We need to test this way because datetime.datetime inherits # from datetime.date: self.assertIsInstance(b.baked_date, datetime.date) self.assertNotIsInstance(b.baked_date, datetime.datetime) Django-1.8.7/tests/datatypes/__init__.py0000664000175000017500000000000012603513307017576 0ustar timtim00000000000000Django-1.8.7/tests/datatypes/models.py0000664000175000017500000000154012603513307017334 0ustar timtim00000000000000""" This is a basic model to test saving and loading boolean and date-related types, which in the past were problematic for some database backends. """ from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Donut(models.Model): name = models.CharField(max_length=100) is_frosted = models.BooleanField(default=False) has_sprinkles = models.NullBooleanField() baked_date = models.DateField(null=True) baked_time = models.TimeField(null=True) consumed_at = models.DateTimeField(null=True) review = models.TextField() class Meta: ordering = ('consumed_at',) def __str__(self): return self.name class RumBaba(models.Model): baked_date = models.DateField(auto_now_add=True) baked_timestamp = models.DateTimeField(auto_now_add=True) Django-1.8.7/tests/save_delete_hooks/0000775000175000017500000000000012625116243017166 5ustar timtim00000000000000Django-1.8.7/tests/save_delete_hooks/tests.py0000664000175000017500000000143512603513307020703 0ustar timtim00000000000000from __future__ import unicode_literals from django.test import TestCase from django.utils import six from .models import Person class SaveDeleteHookTests(TestCase): def test_basic(self): p = Person(first_name="John", last_name="Smith") self.assertEqual(p.data, []) p.save() self.assertEqual(p.data, [ "Before save", "After save", ]) self.assertQuerysetEqual( Person.objects.all(), [ "John Smith", ], six.text_type ) p.delete() self.assertEqual(p.data, [ "Before save", "After save", "Before deletion", "After deletion", ]) self.assertQuerysetEqual(Person.objects.all(), []) Django-1.8.7/tests/save_delete_hooks/__init__.py0000664000175000017500000000000012603513307021263 0ustar timtim00000000000000Django-1.8.7/tests/save_delete_hooks/models.py0000664000175000017500000000200612625116214021017 0ustar timtim00000000000000""" Adding hooks before/after saving and deleting To execute arbitrary code around ``save()`` and ``delete()``, just subclass the methods. """ from __future__ import unicode_literals from django.db import models from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Person(models.Model): first_name = models.CharField(max_length=20) last_name = models.CharField(max_length=20) def __init__(self, *args, **kwargs): super(Person, self).__init__(*args, **kwargs) self.data = [] def __str__(self): return "%s %s" % (self.first_name, self.last_name) def save(self, *args, **kwargs): self.data.append("Before save") # Call the "real" save() method super(Person, self).save(*args, **kwargs) self.data.append("After save") def delete(self): self.data.append("Before deletion") # Call the "real" delete() method super(Person, self).delete() self.data.append("After deletion") Django-1.8.7/tests/admin_custom_urls/0000775000175000017500000000000012625116243017232 5ustar timtim00000000000000Django-1.8.7/tests/admin_custom_urls/urls.py0000664000175000017500000000021512625116213020564 0ustar timtim00000000000000from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), ] Django-1.8.7/tests/admin_custom_urls/tests.py0000664000175000017500000001276412625116213020755 0ustar timtim00000000000000from __future__ import unicode_literals from django.contrib.admin.utils import quote from django.core.urlresolvers import reverse from django.template.response import TemplateResponse from django.test import TestCase, override_settings from .models import Action, Car, Person @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF='admin_custom_urls.urls',) class AdminCustomUrlsTest(TestCase): """ Remember that: * The Action model has a CharField PK. * The ModelAdmin for Action customizes the add_view URL, it's '//!add/' """ fixtures = ['users.json', 'actions.json'] def setUp(self): self.client.login(username='super', password='secret') def test_basic_add_GET(self): """ Ensure GET on the add_view works. """ add_url = reverse('admin:admin_custom_urls_action_add') self.assertTrue(add_url.endswith('/!add/')) response = self.client.get(add_url) self.assertIsInstance(response, TemplateResponse) self.assertEqual(response.status_code, 200) def test_add_with_GET_args(self): """ Ensure GET on the add_view plus specifying a field value in the query string works. """ response = self.client.get(reverse('admin:admin_custom_urls_action_add'), {'name': 'My Action'}) self.assertEqual(response.status_code, 200) self.assertContains(response, 'value="My Action"') def test_basic_add_POST(self): """ Ensure POST on add_view works. """ post_data = { '_popup': '1', "name": 'Action added through a popup', "description": "Description of added action", } response = self.client.post(reverse('admin:admin_custom_urls_action_add'), post_data) self.assertEqual(response.status_code, 200) self.assertContains(response, 'dismissAddRelatedObjectPopup') self.assertContains(response, 'Action added through a popup') def test_admin_URLs_no_clash(self): """ Test that some admin URLs work correctly. """ # Should get the change_view for model instance with PK 'add', not show # the add_view response = self.client.get('/admin/admin_custom_urls/action/add/') self.assertEqual(response.status_code, 200) self.assertContains(response, 'Change action') # Ditto, but use reverse() to build the URL url = reverse('admin:%s_action_change' % Action._meta.app_label, args=(quote('add'),)) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Change action') # Should correctly get the change_view for the model instance with the # funny-looking PK (the one with a 'path/to/html/document.html' value) url = reverse('admin:%s_action_change' % Action._meta.app_label, args=(quote("path/to/html/document.html"),)) response = self.client.get(url) self.assertEqual(response.status_code, 200) self.assertContains(response, 'Change action') self.assertContains(response, 'value="path/to/html/document.html"') @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF='admin_custom_urls.urls',) class CustomRedirects(TestCase): fixtures = ['users.json', 'actions.json'] def setUp(self): self.client.login(username='super', password='secret') def test_post_save_add_redirect(self): """ Ensures that ModelAdmin.response_post_save_add() controls the redirection after the 'Save' button has been pressed when adding a new object. Refs 8001, 18310, 19505. """ post_data = {'name': 'John Doe'} self.assertEqual(Person.objects.count(), 0) response = self.client.post( reverse('admin:admin_custom_urls_person_add'), post_data) persons = Person.objects.all() self.assertEqual(len(persons), 1) self.assertRedirects( response, reverse('admin:admin_custom_urls_person_history', args=[persons[0].pk])) def test_post_save_change_redirect(self): """ Ensures that ModelAdmin.response_post_save_change() controls the redirection after the 'Save' button has been pressed when editing an existing object. Refs 8001, 18310, 19505. """ Person.objects.create(name='John Doe') self.assertEqual(Person.objects.count(), 1) person = Person.objects.all()[0] post_data = {'name': 'Jack Doe'} response = self.client.post( reverse('admin:admin_custom_urls_person_change', args=[person.pk]), post_data) self.assertRedirects( response, reverse('admin:admin_custom_urls_person_delete', args=[person.pk])) def test_post_url_continue(self): """ Ensures that the ModelAdmin.response_add()'s parameter `post_url_continue` controls the redirection after an object has been created. """ post_data = {'name': 'SuperFast', '_continue': '1'} self.assertEqual(Car.objects.count(), 0) response = self.client.post( reverse('admin:admin_custom_urls_car_add'), post_data) cars = Car.objects.all() self.assertEqual(len(cars), 1) self.assertRedirects( response, reverse('admin:admin_custom_urls_car_history', args=[cars[0].pk])) Django-1.8.7/tests/admin_custom_urls/fixtures/0000775000175000017500000000000012625116243021103 5ustar timtim00000000000000Django-1.8.7/tests/admin_custom_urls/fixtures/users.json0000664000175000017500000000074012625113144023135 0ustar timtim00000000000000[ { "pk": 100, "model": "auth.user", "fields": { "username": "super", "first_name": "Super", "last_name": "User", "is_active": true, "is_superuser": true, "is_staff": true, "last_login": "2007-05-30 13:20:10", "groups": [], "user_permissions": [], "password": "sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158", "email": "super@example.com", "date_joined": "2007-05-30 13:20:10" } } ] Django-1.8.7/tests/admin_custom_urls/fixtures/actions.json0000664000175000017500000000166612625113144023444 0ustar timtim00000000000000[ { "pk": "delete", "model": "admin_custom_urls.action", "fields": { "description": "Remove things." } }, { "pk": "rename", "model": "admin_custom_urls.action", "fields": { "description": "Gives things other names." } }, { "pk": "add", "model": "admin_custom_urls.action", "fields": { "description": "Add things." } }, { "pk": "path/to/file/", "model": "admin_custom_urls.action", "fields": { "description": "An action with '/' in its name." } }, { "pk": "path/to/html/document.html", "model": "admin_custom_urls.action", "fields": { "description": "An action with a name similar to a HTML doc path." } }, { "pk": "javascript:alert('Hello world');\">Click here", "model": "admin_custom_urls.action", "fields": { "description": "An action with a name suspected of being a XSS attempt" } } ] Django-1.8.7/tests/admin_custom_urls/__init__.py0000664000175000017500000000000012603513307021327 0ustar timtim00000000000000Django-1.8.7/tests/admin_custom_urls/models.py0000664000175000017500000000466212625116213021074 0ustar timtim00000000000000from functools import update_wrapper from django.contrib import admin from django.core.urlresolvers import reverse from django.db import models from django.http import HttpResponseRedirect from django.utils.encoding import python_2_unicode_compatible @python_2_unicode_compatible class Action(models.Model): name = models.CharField(max_length=50, primary_key=True) description = models.CharField(max_length=70) def __str__(self): return self.name class ActionAdmin(admin.ModelAdmin): """ A ModelAdmin for the Action model that changes the URL of the add_view to '//!add/' The Action model has a CharField PK. """ list_display = ('name', 'description') def remove_url(self, name): """ Remove all entries named 'name' from the ModelAdmin instance URL patterns list """ return [url for url in super(ActionAdmin, self).get_urls() if url.name != name] def get_urls(self): # Add the URL of our custom 'add_view' view to the front of the URLs # list. Remove the existing one(s) first from django.conf.urls import url def wrap(view): def wrapper(*args, **kwargs): return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) info = self.model._meta.app_label, self.model._meta.model_name view_name = '%s_%s_add' % info return [ url(r'^!add/$', wrap(self.add_view), name=view_name), ] + self.remove_url(view_name) class Person(models.Model): name = models.CharField(max_length=20) class PersonAdmin(admin.ModelAdmin): def response_post_save_add(self, request, obj): return HttpResponseRedirect( reverse('admin:admin_custom_urls_person_history', args=[obj.pk])) def response_post_save_change(self, request, obj): return HttpResponseRedirect( reverse('admin:admin_custom_urls_person_delete', args=[obj.pk])) class Car(models.Model): name = models.CharField(max_length=20) class CarAdmin(admin.ModelAdmin): def response_add(self, request, obj, post_url_continue=None): return super(CarAdmin, self).response_add( request, obj, post_url_continue=reverse('admin:admin_custom_urls_car_history', args=[obj.pk])) admin.site.register(Action, ActionAdmin) admin.site.register(Person, PersonAdmin) admin.site.register(Car, CarAdmin) Django-1.8.7/tests/admin_inlines/0000775000175000017500000000000012625116243016314 5ustar timtim00000000000000Django-1.8.7/tests/admin_inlines/urls.py0000664000175000017500000000020112625116213017641 0ustar timtim00000000000000from django.conf.urls import include, url from . import admin urlpatterns = [ url(r'^admin/', include(admin.site.urls)), ] Django-1.8.7/tests/admin_inlines/tests.py0000664000175000017500000012422212625116213020030 0ustar timtim00000000000000from __future__ import unicode_literals import warnings from django.contrib.admin import ModelAdmin, TabularInline from django.contrib.admin.helpers import InlineAdminForm from django.contrib.admin.tests import AdminSeleniumWebDriverTestCase from django.contrib.auth.models import Permission, User from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse from django.test import RequestFactory, TestCase, override_settings from django.utils.encoding import force_text from .admin import InnerInline, site as admin_site from .models import ( Author, BinaryTree, Book, Chapter, Child, ChildModel1, ChildModel2, Fashionista, FootNote, Holder, Holder2, Holder3, Holder4, Inner, Inner2, Inner3, Inner4Stacked, Inner4Tabular, Novel, OutfitItem, Parent, ParentModelWithCustomPk, Person, Poll, Profile, ProfileCollection, Question, Sighting, SomeChildModel, SomeParentModel, Teacher, ) INLINE_CHANGELINK_HTML = 'class="inlinechangelink">Change' @override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',), ROOT_URLCONF="admin_inlines.urls") class TestInline(TestCase): fixtures = ['admin-views-users.xml'] def setUp(self): holder = Holder(dummy=13) holder.save() Inner(dummy=42, holder=holder).save() result = self.client.login(username='super', password='secret') self.assertEqual(result, True) self.factory = RequestFactory() def test_can_delete(self): """ can_delete should be passed to inlineformset factory. """ holder = Holder.objects.get(dummy=13) response = self.client.get( reverse('admin:admin_inlines_holder_change', args=(holder.id,)) ) inner_formset = response.context['inline_admin_formsets'][0].formset expected = InnerInline.can_delete actual = inner_formset.can_delete self.assertEqual(expected, actual, 'can_delete must be equal') def test_readonly_stacked_inline_label(self): """Bug #13174.""" holder = Holder.objects.create(dummy=42) Inner.objects.create(holder=holder, dummy=42, readonly='') response = self.client.get( reverse('admin:admin_inlines_holder_change', args=(holder.id,)) ) self.assertContains(response, '') def test_many_to_many_inlines(self): "Autogenerated many-to-many inlines are displayed correctly (#13407)" response = self.client.get(reverse('admin:admin_inlines_author_add')) # The heading for the m2m inline block uses the right text self.assertContains(response, '

    Author-book relationships

    ') # The "add another" label is correct self.assertContains(response, 'Add another Author-book relationship') # The '+' is dropped from the autogenerated form prefix (Author_books+) self.assertContains(response, 'id="id_Author_books-TOTAL_FORMS"') def test_inline_primary(self): person = Person.objects.create(firstname='Imelda') item = OutfitItem.objects.create(name='Shoes') # Imelda likes shoes, but can't carry her own bags. data = { 'shoppingweakness_set-TOTAL_FORMS': 1, 'shoppingweakness_set-INITIAL_FORMS': 0, 'shoppingweakness_set-MAX_NUM_FORMS': 0, '_save': 'Save', 'person': person.id, 'max_weight': 0, 'shoppingweakness_set-0-item': item.id, } response = self.client.post(reverse('admin:admin_inlines_fashionista_add'), data) self.assertEqual(response.status_code, 302) self.assertEqual(len(Fashionista.objects.filter(person__firstname='Imelda')), 1) def test_tabular_non_field_errors(self): """ Ensure that non_field_errors are displayed correctly, including the right value for colspan. Refs #13510. """ data = { 'title_set-TOTAL_FORMS': 1, 'title_set-INITIAL_FORMS': 0, 'title_set-MAX_NUM_FORMS': 0, '_save': 'Save', 'title_set-0-title1': 'a title', 'title_set-0-title2': 'a different title', } response = self.client.post(reverse('admin:admin_inlines_titlecollection_add'), data) # Here colspan is "4": two fields (title1 and title2), one hidden field and the delete checkbox. self.assertContains(response, '
    • The two titles must be the same
    ') def test_no_parent_callable_lookup(self): """Admin inline `readonly_field` shouldn't invoke parent ModelAdmin callable""" # Identically named callable isn't present in the parent ModelAdmin, # rendering of the add view shouldn't explode response = self.client.get(reverse('admin:admin_inlines_novel_add')) self.assertEqual(response.status_code, 200) # View should have the child inlines section self.assertContains(response, '
    ') def test_callable_lookup(self): """Admin inline should invoke local callable when its name is listed in readonly_fields""" response = self.client.get(reverse('admin:admin_inlines_poll_add')) self.assertEqual(response.status_code, 200) # Add parent object view should have the child inlines section self.assertContains(response, '
    ') # The right callable should be used for the inline readonly_fields # column cells self.assertContains(response, '

    Callable in QuestionInline

    ') def test_help_text(self): """ Ensure that the inlines' model field help texts are displayed when using both the stacked and tabular layouts. Ref #8190. """ response = self.client.get(reverse('admin:admin_inlines_holder4_add')) self.assertContains(response, '

    Awesome stacked help text is awesome.

    ', 4) self.assertContains(response, '(Awesome tabular help text is awesome.)', 1) # ReadOnly fields response = self.client.get(reverse('admin:admin_inlines_capofamiglia_add')) self.assertContains(response, '(Help text for ReadOnlyInline)', 1) def test_inline_hidden_field_no_column(self): """#18263 -- Make sure hidden fields don't get a column in tabular inlines""" parent = SomeParentModel.objects.create(name='a') SomeChildModel.objects.create(name='b', position='0', parent=parent) SomeChildModel.objects.create(name='c', position='1', parent=parent) response = self.client.get(reverse('admin:admin_inlines_someparentmodel_change', args=(parent.pk,))) self.assertNotContains(response, '') self.assertContains(response, ( '')) def test_non_related_name_inline(self): """ Ensure that multiple inlines with related_name='+' have correct form prefixes. Bug #16838. """ response = self.client.get(reverse('admin:admin_inlines_capofamiglia_add')) self.assertContains(response, '', html=True) self.assertContains(response, '', html=True) self.assertContains(response, '', html=True) self.assertContains(response, '', html=True) self.assertContains(response, '', html=True) self.assertContains(response, '', html=True) @override_settings(USE_L10N=True, USE_THOUSAND_SEPARATOR=True) def test_localize_pk_shortcut(self): """ Ensure that the "View on Site" link is correct for locales that use thousand separators """ holder = Holder.objects.create(pk=123456789, dummy=42) inner = Inner.objects.create(pk=987654321, holder=holder, dummy=42, readonly='') response = self.client.get(reverse('admin:admin_inlines_holder_change', args=(holder.id,))) inner_shortcut = 'r/%s/%s/' % (ContentType.objects.get_for_model(inner).pk, inner.pk) self.assertContains(response, inner_shortcut) def test_custom_pk_shortcut(self): """ Ensure that the "View on Site" link is correct for models with a custom primary key field. Bug #18433. """ parent = ParentModelWithCustomPk.objects.create(my_own_pk="foo", name="Foo") child1 = ChildModel1.objects.create(my_own_pk="bar", name="Bar", parent=parent) child2 = ChildModel2.objects.create(my_own_pk="baz", name="Baz", parent=parent) response = self.client.get(reverse('admin:admin_inlines_parentmodelwithcustompk_change', args=('foo',))) child1_shortcut = 'r/%s/%s/' % (ContentType.objects.get_for_model(child1).pk, child1.pk) child2_shortcut = 'r/%s/%s/' % (ContentType.objects.get_for_model(child2).pk, child2.pk) self.assertContains(response, child1_shortcut) self.assertContains(response, child2_shortcut) def test_create_inlines_on_inherited_model(self): """ Ensure that an object can be created with inlines when it inherits another class. Bug #19524. """ data = { 'name': 'Martian', 'sighting_set-TOTAL_FORMS': 1, 'sighting_set-INITIAL_FORMS': 0, 'sighting_set-MAX_NUM_FORMS': 0, 'sighting_set-0-place': 'Zone 51', '_save': 'Save', } response = self.client.post(reverse('admin:admin_inlines_extraterrestrial_add'), data) self.assertEqual(response.status_code, 302) self.assertEqual(Sighting.objects.filter(et__name='Martian').count(), 1) def test_custom_get_extra_form(self): bt_head = BinaryTree.objects.create(name="Tree Head") BinaryTree.objects.create(name="First Child", parent=bt_head) # The maximum number of forms should respect 'get_max_num' on the # ModelAdmin max_forms_input = '' # The total number of forms will remain the same in either case total_forms_hidden = '' response = self.client.get(reverse('admin:admin_inlines_binarytree_add')) self.assertContains(response, max_forms_input % 3) self.assertContains(response, total_forms_hidden) response = self.client.get(reverse('admin:admin_inlines_binarytree_change', args=(bt_head.id,))) self.assertContains(response, max_forms_input % 2) self.assertContains(response, total_forms_hidden) def test_min_num(self): """ Ensure that min_num and extra determine number of forms. """ class MinNumInline(TabularInline): model = BinaryTree min_num = 2 extra = 3 modeladmin = ModelAdmin(BinaryTree, admin_site) modeladmin.inlines = [MinNumInline] min_forms = '' total_forms = '' request = self.factory.get(reverse('admin:admin_inlines_binarytree_add')) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request) self.assertContains(response, min_forms) self.assertContains(response, total_forms) def test_custom_min_num(self): """ Ensure that get_min_num is called and used correctly. """ bt_head = BinaryTree.objects.create(name="Tree Head") BinaryTree.objects.create(name="First Child", parent=bt_head) class MinNumInline(TabularInline): model = BinaryTree extra = 3 def get_min_num(self, request, obj=None, **kwargs): if obj: return 5 return 2 modeladmin = ModelAdmin(BinaryTree, admin_site) modeladmin.inlines = [MinNumInline] min_forms = '' total_forms = '' request = self.factory.get(reverse('admin:admin_inlines_binarytree_add')) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request) self.assertContains(response, min_forms % 2) self.assertContains(response, total_forms % 5) request = self.factory.get(reverse('admin:admin_inlines_binarytree_change', args=(bt_head.id,))) request.user = User(username='super', is_superuser=True) response = modeladmin.changeform_view(request, object_id=str(bt_head.id)) self.assertContains(response, min_forms % 5) self.assertContains(response, total_forms % 8) def test_inline_nonauto_noneditable_pk(self): response = self.client.get(reverse('admin:admin_inlines_author_add')) self.assertContains(response, '', html=True) self.assertContains(response, '', html=True) def test_inline_editable_pk(self): response = self.client.get(reverse('admin:admin_inlines_author_add')) self.assertContains(response, '', html=True, count=1) self.assertContains(response, '', html=True, count=1) def test_stacked_inline_edit_form_contains_has_original_class(self): holder = Holder.objects.create(dummy=1) holder.inner_set.create(dummy=1) response = self.client.get(reverse('admin:admin_inlines_holder_change', args=(holder.pk,))) self.assertContains( response, '