def test_set_remove(self): """Testing ChangeMeta(unique_together) and setting indexes and removing one """ class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: unique_together = [('int_field1', 'char_field1')] self.set_base_model(ChangeMetaPlainBaseModel) self.perform_evolution_tests( DestModel, [ ChangeMeta('TestModel', 'unique_together', [('int_field1', 'char_field1'), ('int_field2', 'char_field2')]), ChangeMeta('TestModel', 'unique_together', [('int_field1', 'char_field1')]) ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'unique_together'," " [('int_field1', 'char_field1')])" ], 'set_remove')
def test_append_list(self): """Testing ChangeMeta(unique_together) and appending list""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: unique_together = [('int_field1', 'char_field1'), ('int_field2', 'char_field2')] self.set_base_model(ChangeMetaUniqueTogetherBaseModel) self.perform_evolution_tests( DestModel, [ ChangeMeta('TestModel', 'unique_together', [('int_field1', 'char_field1'), ('int_field2', 'char_field2')]), ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'unique_together'," " [('int_field1', 'char_field1')," " ('int_field2', 'char_field2')])" ], 'append_list')
def test_missing_indexes(self): """Testing ChangeMeta(unique_together) and old missing indexes""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: unique_together = [('char_field1', 'char_field2')] self.set_base_model(ChangeMetaUniqueTogetherBaseModel) # Remove the indexes from the database state, to simulate the indexes # not being found in the database. The evolution should still work. self.database_state.clear_indexes('tests_testmodel') self.perform_evolution_tests( DestModel, [ ChangeMeta('TestModel', 'unique_together', [('char_field1', 'char_field2')]) ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'unique_together'," " [('char_field1', 'char_field2')])" ], 'ignore_missing_indexes', rescan_indexes=False)
def test_upgrade_from_v1_sig_no_indexes(self): """Testing ChangeMeta(unique_together) and upgrade from v1 signature with no changes and no indexes in database""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: unique_together = [('int_field1', 'char_field1')] self.set_base_model(NoUniqueTogetherBaseModel) # Pretend this is an older signature with the same unique_together. meta = self.start_sig['tests']['TestModel']['meta'] del meta['__unique_together_applied'] meta['unique_together'] = DestModel._meta.unique_together self.perform_evolution_tests( DestModel, [ ChangeMeta('TestModel', 'unique_together', [('int_field1', 'char_field1')]) ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'unique_together'," " [('int_field1', 'char_field1')])" ], 'upgrade_from_v1_sig', rescan_indexes=False)
def test_replace_list(self): """Testing ChangeMeta(indexes) and replacing list""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: indexes = [ Index(fields=['int_field2']), ] self.set_base_model(ChangeMetaIndexesBaseModel) self.perform_evolution_tests( DestModel, [ ChangeMeta('TestModel', 'indexes', [{'fields': ['int_field2']}]) ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'indexes'," " [{'fields': ['int_field2']}])" ], 'replace_list')
def test_upgrade_from_v1_sig_with_indexes(self): """Testing ChangeMeta(unique_together) and upgrade from v1 signature with no changes and with indexes in database""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: unique_together = [('int_field1', 'char_field1')] self.set_base_model(ChangeMetaUniqueTogetherBaseModel) # Pretend this is an older signature with the same unique_together. model_sig = ( self.start_sig .get_app_sig('tests') .get_model_sig('TestModel') ) model_sig._unique_together_applied = False self.perform_evolution_tests( DestModel, [ ChangeMeta('TestModel', 'unique_together', [('int_field1', 'char_field1')]) ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'unique_together'," " [('int_field1', 'char_field1')])" ], None, rescan_indexes=False)
def test_removing(self): """Testing ChangeMeta(index_together) and removing property""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) self.set_base_model(IndexTogetherBaseModel) self.perform_evolution_tests( DestModel, [ChangeMeta('TestModel', 'index_together', [])], self.DIFF_TEXT, ["ChangeMeta('TestModel', 'index_together', [])"], 'removing')
def test_append_list(self): """Testing ChangeMeta(indexes) and appending list""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: indexes = [ Index(fields=['int_field1']), Index(fields=['char_field1', '-char_field2'], name='my_custom_index'), Index(fields=['int_field2']), ] self.set_base_model(ChangeMetaIndexesBaseModel) self.perform_evolution_tests( DestModel, [ ChangeMeta( 'TestModel', 'indexes', [ { 'fields': ['int_field1'], }, { 'fields': ['char_field1', '-char_field2'], 'name': 'my_custom_index', }, { 'fields': ['int_field2'], }, ]) ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'indexes'," " [{'fields': ['int_field1']}," " {'fields': ['char_field1', '-char_field2']," " 'name': 'my_custom_index'}," " {'fields': ['int_field2']}])" ], 'append_list')
def test_replace_list(self): """Testing ChangeMeta(index_together) and replacing list""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: index_together = [('int_field2', 'char_field2')] self.set_base_model(IndexTogetherBaseModel) self.perform_evolution_tests(DestModel, [ ChangeMeta('TestModel', 'index_together', [('int_field2', 'char_field2')]), ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'index_together'," " [('int_field2', 'char_field2')])" ], 'replace_list')
def test_setting_from_empty(self): """Testing ChangeMeta(index_together) and setting to valid list""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: index_together = [('int_field1', 'char_field1')] self.set_base_model(NoIndexTogetherBaseModel) self.perform_evolution_tests(DestModel, [ ChangeMeta('TestModel', 'index_together', [('int_field1', 'char_field1')]), ], self.DIFF_TEXT, [ "ChangeMeta('TestModel', 'index_together'," " [('int_field1', 'char_field1')])" ], 'setting_from_empty')
def test_keeping_empty(self): """Testing ChangeMeta(index_together) and keeping list empty""" class DestModel(models.Model): int_field1 = models.IntegerField() int_field2 = models.IntegerField() char_field1 = models.CharField(max_length=20) char_field2 = models.CharField(max_length=40) class Meta: index_together = [] self.set_base_model(NoIndexTogetherBaseModel) self.perform_evolution_tests(DestModel, [ ChangeMeta('TestModel', 'index_together', []), ], None, None, None, expect_noop=True)
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 __future__ import unicode_literals from django_evolution.mutations import AddField, ChangeMeta from django.db import models MUTATIONS = [ AddField('Repository', 'hooks_uuid', models.CharField, max_length=32, null=True), ChangeMeta('Repository', 'unique_together', (('name', 'local_site'), ('archived_timestamp', 'path', 'local_site'), ('hooks_uuid', 'local_site'))), ]
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(): model_sig = self.current_sig[app_label][model_name] for field_name in change.get('added', {}): field_sig = model_sig['fields'][field_name] field_type = field_sig['field_type'] add_params = [ (key, field_sig[key]) for key in field_sig.keys() if key in ATTRIBUTE_DEFAULTS.keys() ] add_params.append(('field_type', field_type)) if (field_type is not 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)) changed = change.get('changed', {}) for field_name, field_change in changed.iteritems(): changed_attrs = {} current_field_sig = model_sig['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 ('null' in changed_attrs 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, **changed_attrs)) meta_changed = change.get('meta_changed', []) for prop_name in ('unique_together', 'index_together'): if prop_name in meta_changed: mutations.setdefault(app_label, []).append( ChangeMeta(model_name, prop_name, model_sig['meta'][prop_name])) for model_name in app_changes.get('deleted', {}): mutations.setdefault(app_label, []).append( DeleteModel(model_name)) return mutations
from __future__ import unicode_literals from django_evolution.mutations import ChangeMeta MUTATIONS = [ ChangeMeta('Group', 'unique_together', (('name', 'local_site'), )), ChangeMeta('ReviewRequest', 'unique_together', (('commit_id', 'repository'), ('changenum', 'repository'), ('local_site', 'local_id'))), ]
from django_evolution.mutations import ChangeMeta MUTATIONS = [ ChangeMeta('ReviewRequestVisit', 'unique_together', [('user', 'review_request')]), ChangeMeta('LocalSiteProfile', 'unique_together', [('user', 'local_site'), ('profile', 'local_site')]), ]
from __future__ import unicode_literals from django_evolution.mutations import AddField, ChangeMeta from django.db import models MUTATIONS = [ AddField('ReviewRequestVisit', 'visibility', models.CharField, initial='V', max_length=1), ChangeMeta('ReviewRequestVisit', 'index_together', [('user', 'visibility')]) ]
from django_evolution.mutations import ChangeMeta MUTATIONS = [ ChangeMeta('ContentType', 'unique_together', [('app_label', 'model')]), ]
from __future__ import unicode_literals from django_evolution.mutations import ChangeMeta MUTATIONS = [ ChangeMeta('HostingServiceAccount', 'unique_together', []), ]
from __future__ import unicode_literals from django_evolution.mutations import ChangeMeta MUTATIONS = [ ChangeMeta('Permission', 'unique_together', [('content_type', 'codename')]), ]
from __future__ import unicode_literals from django_evolution.mutations import ChangeMeta MUTATIONS = [ ChangeMeta('Repository', 'unique_together', (('name', 'local_site'), ('path', 'local_site'))), ]
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