123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- from __future__ import unicode_literals
- import datetime
- import os
- import re
- import tokenize
- import unittest
- import warnings
- from django.core.validators import RegexValidator, EmailValidator
- from django.db import models, migrations
- from django.db.migrations.writer import MigrationWriter, SettingsReference
- from django.test import TestCase
- from django.conf import settings
- from django.utils import datetime_safe, six
- from django.utils.deconstruct import deconstructible
- from django.utils.translation import ugettext_lazy as _
- from django.utils.timezone import get_default_timezone
- import custom_migration_operations.operations
- import custom_migration_operations.more_operations
- class TestModel1(object):
- def upload_to(self):
- return "somewhere dynamic"
- thing = models.FileField(upload_to=upload_to)
- 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(self):
- """
- Tests various different forms of the serializer.
- This does not care about formatting, just that the parsed result is
- correct, so we always exec() the result and check that.
- """
-
- self.assertSerializedEqual(1)
- self.assertSerializedEqual(None)
- 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'")
- self.assertSerializedEqual({1: 2})
- self.assertSerializedEqual(["a", 2, True, None])
- self.assertSerializedEqual({2, 3, "eighty"})
- self.assertSerializedEqual({"lalalala": ["yeah", "no", "maybe"]})
- self.assertSerializedEqual(_('Hello'))
-
- self.assertSerializedEqual([list, tuple, dict, set])
- string, imports = MigrationWriter.serialize([list, tuple, dict, set])
- self.assertEqual(string, "[list, tuple, dict, set]")
- self.assertEqual(imports, set())
-
- 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))
-
- 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())
- with self.assertRaises(ValueError):
- self.assertSerializedEqual(datetime.datetime(2012, 1, 1, 1, 1, tzinfo=get_default_timezone()))
- safe_date = datetime_safe.date(2014, 3, 31)
- string, imports = MigrationWriter.serialize(safe_date)
- self.assertEqual(string, repr(datetime.date(2014, 3, 31)))
- self.assertEqual(imports, {'import datetime'})
- safe_datetime = datetime_safe.datetime(2014, 3, 31, 16, 4, 31)
- string, imports = MigrationWriter.serialize(safe_datetime)
- self.assertEqual(string, repr(datetime.datetime(2014, 3, 31, 16, 4, 31)))
- self.assertEqual(imports, {'import datetime'})
-
- self.assertSerializedFieldEqual(models.CharField(max_length=255))
- self.assertSerializedFieldEqual(models.TextField(null=True, blank=True))
-
- 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)
-
- 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)
-
- 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)
-
- 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)
-
- 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_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()
-
- self.assertIsInstance(output, six.binary_type, "Migration as_string returned unicode")
-
-
- result = self.safe_exec(output)
- self.assertIn("Migration", result)
-
-
- 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()
- )
- )
- 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(__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)
-
-
-
- with warnings.catch_warnings():
- warnings.filterwarnings("ignore", category=ImportWarning)
- 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
- )
|