def test_signature_save(self): """Testing Version.signature field serializes JSON-encoded v2 signatures """ project_sig = ProjectSignature() project_sig.add_app_sig(AppSignature('app1')) project_sig.add_app_sig(AppSignature('app2')) version = Version.objects.create(signature=project_sig) raw_signature = (Version.objects.filter( pk=version.pk).values_list('signature'))[0][0] self.assertTrue(raw_signature.startswith('json!')) sig_data = json.loads(raw_signature[len('json!'):]) self.assertEqual( sig_data, { '__version__': 2, 'apps': { 'app1': { 'legacy_app_label': 'app1', 'models': {}, }, 'app2': { 'legacy_app_label': 'app2', 'models': {}, }, }, })
def test_current_version_with_dup_timestamps(self): """Testing Version.current_version() with two entries with same timestamps""" # Remove anything that may already exist. Version.objects.all().delete() timestamp = datetime(year=2015, month=12, day=10, hour=12, minute=13, second=14) Version.objects.create(signature=ProjectSignature(), when=timestamp) version = Version.objects.create(signature=ProjectSignature(), when=timestamp) latest_version = Version.objects.current_version() self.assertEqual(latest_version, version)
def test_with_bad_app(self): """Testing AddField with application not in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) message = ( 'Cannot add the field "char_field1" to model "badapp.TestModel". ' 'The application could not be found in the signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='badapp', project_sig=ProjectSignature(), database_state=None)
def test_add_fields_bad_update_func_signature(self): """Testing SQLMutation and bad update_func signature""" mutation = SQLMutation('test', '', update_func=lambda a, b, c: None) message = ('SQLMutations must provide an update_func(simulation) or ' 'legacy update_func(app_label, project_sig) parameter in ' 'order to be simulated.') with self.assertRaisesMessage(CannotSimulate, message): mutation.run_simulation(app_label='tests', project_sig=ProjectSignature(), database_state=None)
def test_with_bad_app(self): """Testing DeleteApplication with application not in signature""" mutation = DeleteApplication() message = ( 'Cannot delete the application "badapp". The application could ' 'not be found in the signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='badapp', project_sig=ProjectSignature(), database_state=None)
def test_with_bad_app(self): """Testing RenameField with application not in signature""" mutation = RenameField('TestModel', 'char_field1', 'char_field2') message = ( 'Cannot rename the field "char_field1" on model ' '"badapp.TestModel". The application could not be found in the ' 'signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='badapp', project_sig=ProjectSignature(), database_state=None)
def test_with_bad_app(self): """Testing RenameModel with application not in signature""" mutation = RenameModel('TestModel', 'DestModel', db_table='tests_destmodel') message = ( 'Cannot rename the model "badapp.TestModel". The application ' 'could not be found in the signature.' ) with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='badapp', project_sig=ProjectSignature(), database_state=None)
def test_with_bad_model(self): """Testing ChangeField with model not in signature""" mutation = ChangeField('TestModel', 'char_field1') project_sig = ProjectSignature() project_sig.add_app_sig(AppSignature(app_id='tests')) message = ('Cannot change the field "char_field1" on model ' '"tests.TestModel". The model could not be found in the ' 'signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def test_with_bad_model(self): """Testing DeleteModel with model not in signature""" mutation = DeleteModel('TestModel') project_sig = ProjectSignature() project_sig.add_app_sig(AppSignature(app_id='tests')) message = ( 'Cannot delete the model "tests.TestModel". The model could ' 'not be found in the signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def test_with_bad_field(self): """Testing RenameField with field not in signature""" mutation = RenameField('TestModel', 'char_field1', 'char_field2') model_sig = ModelSignature(model_name='TestModel', table_name='tests_testmodel') app_sig = AppSignature(app_id='tests') app_sig.add_model_sig(model_sig) project_sig = ProjectSignature() project_sig.add_app_sig(app_sig) message = ('Cannot rename the field "char_field1" on model ' '"tests.TestModel". The field could not be found in the ' 'signature.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def create_test_project_sig(models, app_label='tests', version=1): """Return a dummy project signature for the given models. Args: models (list of django.db.models.Model): The list of models for the project signature. app_label (unicode, optional): The application label that will contain the models. version (int, optional): The signature version to use for the project signature. Returns: dict: The new project signature. """ app_sig = AppSignature(app_id=app_label) project_sig = ProjectSignature() project_sig.add_app_sig(app_sig) for full_name, model in models: parts = full_name.split('.') if len(parts) == 1: app_sig.add_model_sig(ModelSignature.from_model(model)) else: model_app_label, model_name = parts model_app_sig = project_sig.get_app_sig(model_app_label) if model_app_sig is None: model_app_sig = AppSignature(app_id=model_app_label) project_sig.add_app_sig(model_app_sig) model_app_sig.add_model_sig(ModelSignature.from_model(model)) return project_sig
def to_python(self, value): """Return a ProjectSignature value from the field contents. Args: value (object): The current value assigned to the field. This might be serialized string content or a :py:class:`~django_evolution.signatures.ProjectSignature` instance. Returns: django_evolution.signatures.ProjectSignature: The project signature stored in the field. Raises: django.core.exceptions.ValidationError: The field contents are of an unexpected type. """ if not value: return ProjectSignature() elif isinstance(value, six.string_types): if value.startswith('json!'): loaded_value = json.loads(value[len('json!'):], object_pairs_hook=OrderedDict) else: loaded_value = pickle_loads(value) return ProjectSignature.deserialize(loaded_value) elif isinstance(value, ProjectSignature): return value else: raise ValidationError('Unsupported serialized signature type %s' % type(value), code='invalid', params={ 'value': value, })
def test_with_bad_field(self): """Testing AddField with field already in signature""" mutation = AddField('TestModel', 'char_field1', models.CharField) model_sig = ModelSignature(model_name='TestModel', table_name='tests_testmodel') model_sig.add_field_sig( FieldSignature(field_name='char_field1', field_type=models.CharField)) app_sig = AppSignature(app_id='tests') app_sig.add_model_sig(model_sig) project_sig = ProjectSignature() project_sig.add_app_sig(app_sig) message = ( 'Cannot add the field "char_field1" to model "tests.TestModel". ' 'A field with this name already exists.') with self.assertRaisesMessage(SimulationFailure, message): mutation.run_simulation(app_label='tests', project_sig=project_sig, database_state=None)
def __init__(self, hinted=False, verbosity=0, interactive=False, database_name=DEFAULT_DB_ALIAS): """Initialize the evolver. Args: hinted (bool, optional): Whether to operate against hinted evolutions. This may result in changes to the database without there being any accompanying evolution files backing those changes. verbosity (int, optional): The verbosity level for any output. This is passed along to signal emissions. interactive (bool, optional): Whether the evolution operations are being performed in a way that allows interactivity on the command line. This is passed along to signal emissions. database_name (unicode, optional): The name of the database to evolve. Raises: django_evolution.errors.EvolutionBaselineMissingError: An initial baseline for the project was not yet installed. This is due to ``syncdb``/``migrate`` not having been run. """ self.database_name = database_name self.hinted = hinted self.verbosity = verbosity self.interactive = interactive self.evolved = False self.initial_diff = None self.project_sig = None self.version = None self.installed_new_database = False self.connection = connections[database_name] if hasattr(self.connection, 'prepare_database'): # Django >= 1.8 self.connection.prepare_database() self.database_state = DatabaseState(self.database_name) self.target_project_sig = \ ProjectSignature.from_database(database_name) self._tasks_by_class = OrderedDict() self._tasks_by_id = OrderedDict() self._tasks_prepared = False latest_version = None if self.database_state.has_model(Version): try: latest_version = \ Version.objects.current_version(using=database_name) except Version.DoesNotExist: # We'll populate this next. pass if latest_version is None: # Either the models aren't yet synced to the database, or we # don't have a saved project signature, so let's set these up. self.installed_new_database = True self.project_sig = ProjectSignature() app = get_app('django_evolution') task = EvolveAppTask(evolver=self, app=app) task.prepare(hinted=False) with self.sql_executor() as sql_executor: task.execute(sql_executor=sql_executor, create_models_now=True) self.database_state.rescan_tables() app_sig = AppSignature.from_app(app=app, database=database_name) self.project_sig.add_app_sig(app_sig) # Let's make completely sure that we've only found the models # we expect. This is mostly for the benefit of unit tests. model_names = set(model_sig.model_name for model_sig in app_sig.model_sigs) expected_model_names = set(['Evolution', 'Version']) assert model_names == expected_model_names, ( 'Unexpected models found for django_evolution app: %s' % ', '.join(model_names - expected_model_names)) self._save_project_sig(new_evolutions=task.new_evolutions) latest_version = self.version self.project_sig = latest_version.signature self.initial_diff = Diff(self.project_sig, self.target_project_sig)