def test_delete_many_to_many_field(self): """Testing DeleteField with ManyToManyField""" class DestModel(BaseTestModel): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) int_field = models.IntegerField() int_field2 = models.IntegerField(db_column='non-default_db_column') int_field3 = models.IntegerField(unique=True) fk_field1 = models.ForeignKey(DeleteAnchor1, on_delete=models.CASCADE) m2m_field2 = models.ManyToManyField( DeleteAnchor4, db_table='non-default_m2m_table') self.perform_evolution_tests( DestModel, [ DeleteField('TestModel', 'm2m_field1'), ], ("In model tests.TestModel:\n" " Field 'm2m_field1' has been deleted"), [ "DeleteField('TestModel', 'm2m_field1')", ], 'DefaultManyToManyModel')
def test_add_primary_key_with_delete_old_fails(self): """Testing AddField with primary key and deleting old key fails""" class DestModel(models.Model): my_primary_key = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) int_field = models.IntegerField() message = ( 'Cannot delete the field "id" on model "tests.TestModel". The ' 'field is a primary key and cannot be deleted.') with self.assertRaisesMessage(SimulationFailure, message): self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'my_primary_key', models.AutoField, initial=AddSequenceFieldInitial('AddPrimaryKeyModel'), primary_key=True), DeleteField('TestModel', 'id'), ], ("In model tests.TestModel:\n" " Field 'my_primary_key' has been added\n" " Field 'id' has been deleted"), [ "AddField('TestModel', 'my_primary_key', models.AutoField," " initial=<<USER VALUE REQUIRED>>, primary_key=True)", "DeleteField('TestModel', 'id')", ], None)
def test_delete_with_custom_column_name(self): """Testing DeleteField with custom column name""" class DestModel(BaseTestModel): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) int_field = models.IntegerField() int_field3 = models.IntegerField(unique=True) fk_field1 = models.ForeignKey(DeleteAnchor1, on_delete=models.CASCADE) m2m_field1 = models.ManyToManyField(DeleteAnchor3) m2m_field2 = models.ManyToManyField( DeleteAnchor4, db_table='non-default_m2m_table') self.perform_evolution_tests( DestModel, [ DeleteField('TestModel', 'int_field2'), ], ("In model tests.TestModel:\n" " Field 'int_field2' has been deleted"), [ "DeleteField('TestModel', 'int_field2')", ], 'NonDefaultNamedColumnModel')
def test_delete_column(self): """Testing generic relations and deleting column""" class DestModel(models.Model): int_field = models.IntegerField() # Plus a generic foreign key - the Generic itself should be ignored content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField(db_index=True) content_object = GenericForeignKey('content_type', 'object_id') # Plus a generic relation, which should be ignored generic = GenericRelation(GenericAnchor) self.perform_evolution_tests( DestModel, [ DeleteField('TestModel', 'char_field'), ], ("In model tests.TestModel:\n" " Field 'char_field' has been deleted"), [ "DeleteField('TestModel', 'char_field')", ], 'DeleteColumnModel')
def test_delete_column_from_custom_table(self): """Testing DeleteField with custom table name""" class DestModel(BaseTestModel): alt_value = models.CharField(max_length=20) class Meta(BaseTestModel.Meta): db_table = 'custom_table_name' def create_test_data(db_name): CustomDeleteTableModel.objects.create(value=100, alt_value='test') self.set_base_model(CustomDeleteTableModel, name='CustomTableModel') self.perform_evolution_tests( DestModel, [ DeleteField('CustomTableModel', 'value'), ], ("In model tests.CustomTableModel:\n" " Field 'value' has been deleted"), [ "DeleteField('CustomTableModel', 'value')", ], 'DeleteColumnCustomTableModel', model_name='CustomTableModel', create_test_data_func=create_test_data)
def evolution(self): "Generate an evolution that would neutralize the diff" mutations = {} for app_label, app_changes in self.changed.items(): for model_name, change in app_changes.get('changed', {}).items(): for field_name in change.get('added',{}): field_sig = self.current_sig[app_label][model_name]['fields'][field_name] add_params = [(key,field_sig[key]) for key in field_sig.keys() if key in ATTRIBUTE_DEFAULTS.keys()] add_params.append(('field_type', field_sig['field_type'])) if (field_sig['field_type'] != models.ManyToManyField and not field_sig.get('null', ATTRIBUTE_DEFAULTS['null'])): add_params.append( ('initial', get_initial_value(app_label, model_name, field_name))) if 'related_model' in field_sig: add_params.append(('related_model', '%s' % field_sig['related_model'])) mutations.setdefault(app_label,[]).append( AddField(model_name, field_name, **dict(add_params))) for field_name in change.get('deleted',[]): mutations.setdefault(app_label,[]).append( DeleteField(model_name, field_name)) for field_name,field_change in change.get('changed',{}).items(): changed_attrs = {} current_field_sig = self.current_sig[app_label][model_name]['fields'][field_name] for prop in field_change: if prop == 'related_model': changed_attrs[prop] = current_field_sig[prop] else: changed_attrs[prop] = \ current_field_sig.get(prop, ATTRIBUTE_DEFAULTS[prop]) if (changed_attrs.has_key('null') and current_field_sig['field_type'] != models.ManyToManyField and not current_field_sig.get('null', ATTRIBUTE_DEFAULTS['null'])): changed_attrs['initial'] = \ get_initial_value(app_label, model_name, field_name) mutations.setdefault(app_label,[]).append( ChangeField(model_name, field_name,current_field_sig, **changed_attrs)) #j----------add for model_name in app_changes.get('deleted',{}): mutations.setdefault(app_label,[]).append( DeleteModel(model_name)) return mutations
def test_rename_delete_field(self): """Testing pre-processing RenameField + DeleteField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) self.perform_evolution_tests(DestModel, [ RenameField('TestModel', 'char_field', 'renamed_field'), DeleteField('TestModel', 'renamed_field'), ], ("In model tests.TestModel:\n" " Field 'char_field' has been deleted"), [ "DeleteField('TestModel', 'char_field')", ], 'delete_char_field')
def test_with_bad_app(self): """Testing DeleteField with application not in signature""" mutation = DeleteField('TestModel', 'char_field1') message = ( 'Cannot delete 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_model(self): """Testing DeleteField with model not in signature""" mutation = DeleteField('TestModel', 'char_field1') project_sig = ProjectSignature() project_sig.add_app_sig(AppSignature(app_id='tests')) message = ('Cannot delete 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_add_delete_field(self): """Testing pre-processing AddField + DeleteField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='', max_length=20), DeleteField('TestModel', 'added_field'), ], '', [], 'noop', expect_noop=True)
def test_delete_with_unique(self): """Testing DeleteField with unique=True""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) int_field = models.IntegerField() int_field2 = models.IntegerField(db_column='non-default_db_column') fk_field1 = models.ForeignKey(DeleteAnchor1) m2m_field1 = models.ManyToManyField(DeleteAnchor3) m2m_field2 = models.ManyToManyField( DeleteAnchor4, db_table='non-default_m2m_table') self.perform_evolution_tests(DestModel, [ DeleteField('TestModel', 'int_field3'), ], ("In model tests.TestModel:\n" " Field 'int_field3' has been deleted"), [ "DeleteField('TestModel', 'int_field3')", ], 'ConstrainedColumnModel')
def test_delete_column_from_custom_table(self): """Testing DeleteField with custom table name""" class DestModel(models.Model): alt_value = models.CharField(max_length=20) class Meta: db_table = 'custom_table_name' self.set_base_model(CustomDeleteTableModel, name='CustomTableModel') self.perform_evolution_tests(DestModel, [ DeleteField('CustomTableModel', 'value'), ], ("In model tests.CustomTableModel:\n" " Field 'value' has been deleted"), [ "DeleteField('CustomTableModel', 'value')", ], 'DeleteColumnCustomTableModel', model_name='CustomTableModel')
def test_add_delete_add_field(self): """Testing pre-processing AddField + DeleteField + AddField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) added_field = models.IntegerField() self.perform_evolution_tests(DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='', max_length=20), DeleteField('TestModel', 'added_field'), AddField( 'TestModel', 'added_field', models.IntegerField, initial=42) ], ("In model tests.TestModel:\n" " Field 'added_field' has been added"), [ "AddField('TestModel', 'added_field', models.IntegerField," " initial=<<USER VALUE REQUIRED>>)", ], 'add_delete_add_field')
def test_add_sql_delete(self): """Testing pre-processing AddField + SQLMutation + DeleteField""" class DestModel(models.Model): my_id = models.AutoField(primary_key=True) char_field = models.CharField(max_length=20) self.perform_evolution_tests( DestModel, [ AddField('TestModel', 'added_field', models.CharField, initial='foo', max_length=20), SQLMutation('dummy-sql', ['-- Comment --'], lambda app_label, proj_sig: None), DeleteField('TestModel', 'added_field'), ], '', [ "DeleteField('TestModel', 'char_field')", ], 'add_sql_delete', expect_noop=True)
from __future__ import unicode_literals from django_evolution.mutations import AddField, DeleteField from djblets.db.fields import JSONField MUTATIONS = [ AddField('FileDiffData', 'extra_data', JSONField, null=True), DeleteField('FileDiffData', 'insert_count'), DeleteField('FileDiffData', 'delete_count'), ]
from django_evolution.mutations import DeleteField MUTATIONS = [ DeleteField('DiffSet', 'file_count'), DeleteField('DiffCommit', 'file_count'), ]
def evolution(self): """Return the mutations needed for resolving the diff. This will attempt to return a hinted evolution, consisting of a series of mutations for each affected application. These mutations will convert the database from the original to the target signatures. Returns: collections.OrderedDict: An ordered dictionary of mutations. Each key is an application label, and each value is a list of mutations for the application. """ if self._mutations is not None: return self._mutations mutations = OrderedDict() for app_label, app_changes in six.iteritems(self.changed): app_sig = self.target_project_sig.get_app_sig(app_label) model_changes = app_changes.get('changed', {}) app_mutations = [] for model_name, model_change in six.iteritems(model_changes): model_sig = app_sig.get_model_sig(model_name) # Process the list of added fields for the model. for field_name in model_change.get('added', {}): field_sig = model_sig.get_field_sig(field_name) field_type = field_sig.field_type add_params = field_sig.field_attrs.copy() add_params['field_type'] = field_type if (not issubclass(field_type, models.ManyToManyField) and not field_sig.get_attr_value('null')): # This field requires an initial value. Inject either # a suitable initial value or a placeholder that must # be filled in by the developer. add_params['initial'] = \ self._get_initial_value(app_label=app_label, model_name=model_name, field_name=field_name) if field_sig.related_model: add_params['related_model'] = field_sig.related_model app_mutations.append(AddField( model_name=model_name, field_name=field_name, **add_params)) # Process the list of deleted fields for the model. app_mutations += [ DeleteField(model_name=model_name, field_name=field_name) for field_name in model_change.get('deleted', []) ] # Process the list of changed fields for the model. field_changes = model_change.get('changed', {}) for field_name, field_change in six.iteritems(field_changes): field_sig = model_sig.get_field_sig(field_name) changed_attrs = OrderedDict() field_type_changed = 'field_type' in field_change if field_type_changed: # If the field type changes, we're doing a hard # reset on the attributes. We won't be showing the # difference between any other attributes on here. changed_attrs['field_type'] = field_sig.field_type changed_attrs.update(field_sig.field_attrs) else: changed_attrs.update( (attr, field_sig.get_attr_value(attr)) for attr in field_change ) if ('null' in field_change and not field_sig.get_attr_value('null') and not issubclass(field_sig.field_type, models.ManyToManyField)): # The field no longer allows null values, meaning an # initial value is required. Inject either a suitable # initial value or a placeholder that must be filled # in by the developer. changed_attrs['initial'] = \ self._get_initial_value(app_label=app_label, model_name=model_name, field_name=field_name) if 'related_model' in field_change: changed_attrs['related_model'] = \ field_sig.related_model app_mutations.append(ChangeField( model_name=model_name, field_name=field_name, **changed_attrs)) # Process the Meta attribute changes for the model. meta_changed = model_change.get('meta_changed', []) # Check if the Meta.constraints property has any changes. # They'll all be assembled into a single ChangeMeta. if 'constraints' in meta_changed: app_mutations.append(ChangeMeta( model_name=model_name, prop_name='constraints', new_value=[ dict({ 'type': constraint_sig.type, 'name': constraint_sig.name, }, **constraint_sig.attrs) for constraint_sig in model_sig.constraint_sigs ])) # Check if the Meta.indexes property has any changes. # They'll all be assembled into a single ChangeMeta. if 'indexes' in meta_changed: change_meta_indexes = [] for index_sig in model_sig.index_sigs: change_meta_index = index_sig.attrs.copy() if index_sig.expressions: change_meta_index['expressions'] = \ index_sig.expressions if index_sig.fields: change_meta_index['fields'] = index_sig.fields if index_sig.name: change_meta_index['name'] = index_sig.name change_meta_indexes.append(change_meta_index) app_mutations.append(ChangeMeta( model_name=model_name, prop_name='indexes', new_value=change_meta_indexes)) # Check Meta.index_together and Meta.unique_together. app_mutations += [ ChangeMeta(model_name=model_name, prop_name=prop_name, new_value=getattr(model_sig, prop_name) or []) for prop_name in ('index_together', 'unique_together') if prop_name in meta_changed ] # Process the list of deleted models for the application. app_mutations += [ DeleteModel(model_name=model_name) for model_name in app_changes.get('deleted', {}) ] # See if any important details about the app have changed. meta_changed = app_changes.get('meta_changed', {}) app_label_changed = meta_changed.get('app_id', {}) legacy_app_label_changed = meta_changed.get('legacy_app_label', {}) if app_label_changed or legacy_app_label_changed: app_mutations.append(RenameAppLabel( app_label_changed.get('old', app_sig.app_id), app_label_changed.get('new', app_sig.app_id), legacy_app_label=legacy_app_label_changed.get( 'new', app_sig.legacy_app_label))) if app_mutations: mutations[app_label] = app_mutations self._mutations = mutations return mutations
from django_evolution.mutations import DeleteField MUTATIONS = [DeleteField('Proyecto', 'telefono')]
def evolution(self): """Return the mutations needed for resolving the diff. This will attempt to return a hinted evolution, consisting of a series of mutations for each affected application. These mutations will convert the database from the original to the target signatures. Returns: collections.OrderedDict: An ordered dictionary of mutations. Each key is an application label, and each value is a list of mutations for the application. """ mutations = OrderedDict() for app_label, app_changes in six.iteritems(self.changed): app_sig = self.target_project_sig.get_app_sig(app_label) model_changes = app_changes.get('changed', {}) app_mutations = [] for model_name, model_change in six.iteritems(model_changes): model_sig = app_sig.get_model_sig(model_name) # Process the list of added fields for the model. for field_name in model_change.get('added', {}): field_sig = model_sig.get_field_sig(field_name) field_type = field_sig.field_type add_params = field_sig.field_attrs.copy() add_params['field_type'] = field_type if (not issubclass(field_type, models.ManyToManyField) and not field_sig.get_attr_value('null')): # This field requires an initial value. Inject either # a suitable initial value or a placeholder that must # be filled in by the developer. add_params['initial'] = \ self._get_initial_value(app_label=app_label, model_name=model_name, field_name=field_name) if field_sig.related_model: add_params['related_model'] = field_sig.related_model app_mutations.append( AddField(model_name=model_name, field_name=field_name, **add_params)) # Process the list of deleted fields for the model. app_mutations += [ DeleteField(model_name=model_name, field_name=field_name) for field_name in model_change.get('deleted', []) ] # Process the list of changed fields for the model. field_changes = model_change.get('changed', {}) for field_name, field_change in six.iteritems(field_changes): field_sig = model_sig.get_field_sig(field_name) changed_attrs = OrderedDict( (attr, field_sig.get_attr_value(attr)) for attr in field_change) if ('null' in changed_attrs and not field_sig.get_attr_value('null') and not issubclass(field_sig.field_type, models.ManyToManyField)): # The field no longer allows null values, meaning an # initial value is required. Inject either a suitable # initial value or a placeholder that must be filled # in by the developer. changed_attrs['initial'] = \ self._get_initial_value(app_label=app_label, model_name=model_name, field_name=field_name) if 'related_model' in field_change: changed_attrs['related_model'] = \ field_sig.related_model app_mutations.append( ChangeField(model_name=model_name, field_name=field_name, **changed_attrs)) # Process the Meta attribute changes for the model. meta_changed = model_change.get('meta_changed', []) # First, check if the Meta.indexes property has any changes. # They'll all be assembled into a single ChangeMeta. if 'indexes' in meta_changed: change_meta_indexes = [] for index_sig in model_sig.index_sigs: change_meta_index = { 'fields': index_sig.fields, } if index_sig.name: change_meta_index['name'] = index_sig.name change_meta_indexes.append(change_meta_index) app_mutations.append( ChangeMeta(model_name=model_name, prop_name='indexes', new_value=change_meta_indexes)) # Then check Meta.index_together and Meta.unique_together. app_mutations += [ ChangeMeta(model_name=model_name, prop_name=prop_name, new_value=getattr(model_sig, prop_name) or []) for prop_name in ('index_together', 'unique_together') if prop_name in meta_changed ] # Process the list of deleted models for the application. app_mutations += [ DeleteModel(model_name=model_name) for model_name in app_changes.get('deleted', {}) ] if app_mutations: mutations[app_label] = app_mutations return mutations