def test_add_fields_simulation_functions(self): """Testing SQLMutation and adding fields with simulation functions""" def update_first_two(app_label, proj_sig): app_sig = proj_sig[app_label] model_sig = app_sig['TestModel'] model_sig['fields']['added_field1'] = { 'field_type': models.IntegerField, 'null': True } model_sig['fields']['added_field2'] = { 'field_type': models.IntegerField, 'null': True } def update_third(app_label, proj_sig): app_sig = proj_sig[app_label] model_sig = app_sig['TestModel'] model_sig['fields']['added_field3'] = { 'field_type': models.IntegerField, 'null': True } self.perform_evolution_tests(AddFieldsModel, [ SQLMutation('first-two-fields', self.get_sql_mapping('AddFirstTwoFields').split('\n'), update_first_two), SQLMutation('third-field', self.get_sql_mapping('AddThirdField').split('\n'), update_third), ], ("In model tests.TestModel:\n" " Field 'added_field1' has been added\n" " Field 'added_field3' has been added\n" " Field 'added_field2' has been added"), sql_name='SQLMutationOutput')
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_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 get_mutations(app, evolution_labels): """ Obtain the list of mutations described by the named evolutions. """ # For each item in the evolution sequence. Check each item to see if it is # a python file or an sql file. try: app_name = '.'.join(app.__name__.split('.')[:-1]) evolution_module = __import__(app_name + '.evolutions', {}, {}, ['']) except ImportError: return [] mutations = [] for label in evolution_labels: directory_name = os.path.dirname(evolution_module.__file__) sql_file_name = os.path.join(directory_name, label + '.sql') if os.path.exists(sql_file_name): sql = [] sql_file = open(sql_file_name) for line in sql_file: sql.append(line) mutations.append(SQLMutation(label, sql)) else: try: module_name = [evolution_module.__name__, label] module = __import__('.'.join(module_name), {}, {}, [module_name]) mutations.extend(module.MUTATIONS) except ImportError, e: raise EvolutionException( 'Error: Failed to find an SQL or Python evolution named %s' % label)
def get_mutations(app, evolution_labels, database): """ Obtain the list of mutations described by the named evolutions. """ # For each item in the evolution sequence. Check each item to see if it is # a python file or an sql file. try: app_name = '.'.join(app.__name__.split('.')[:-1]) if app_name in BUILTIN_SEQUENCES: module_name = 'django_evolution.builtin_evolutions' else: module_name = '%s.evolutions' % app_name evolution_module = __import__(module_name, {}, {}, ['']) except ImportError: return [] mutations = [] for label in evolution_labels: directory_name = os.path.dirname(evolution_module.__file__) # The first element is used for compatibility purposes. filenames = [ os.path.join(directory_name, label + '.sql'), os.path.join(directory_name, "%s_%s.sql" % (database, label)), ] found = False for filename in filenames: if os.path.exists(filename): sql = [] sql_file = open(sql_file_name) for line in sql_file: sql.append(line) mutations.append(SQLMutation(label, sql)) found = True break if not found: try: module_name = [evolution_module.__name__, label] module = __import__('.'.join(module_name), {}, {}, [module_name]); mutations.extend(module.MUTATIONS) except ImportError: raise EvolutionException( 'Error: Failed to find an SQL or Python evolution named %s' % label) return mutations
def test_add_fields_cannot_simulate(self): """Testing SQLMutation and adding fields cannot be simulated""" self.assertRaisesMessage( CannotSimulate, 'Cannot simulate SQLMutations', lambda: self.perform_evolution_tests(AddFieldsModel, [ SQLMutation('first-two-fields', [ 'ALTER TABLE "tests_testmodel" ADD COLUMN' ' "added_field1" integer NULL;', 'ALTER TABLE "tests_testmodel" ADD COLUMN' ' "added_field2" integer NULL;' ]), SQLMutation('third-field', [ 'ALTER TABLE "tests_testmodel" ADD COLUMN' ' "added_field3" integer NULL;', ]) ], ("In model tests.TestModel:\n" " Field 'added_field1' has been added\n" " Field 'added_field3' has been added\n" " Field 'added_field2' has been added"), perform_mutations=False))
def test_add_fields_simulation_functions(self): """Testing SQLMutation and adding fields with simulation functions""" # Legacy simulation function. def update_first_two(app_label, proj_sig): app_sig = proj_sig[app_label] model_sig = app_sig['TestModel'] model_sig['fields']['added_field1'] = { 'field_type': models.IntegerField, 'null': True } model_sig['fields']['added_field2'] = { 'field_type': models.IntegerField, 'null': True } # Modern simulation function. def update_third(simulation): model_sig = simulation.get_model_sig('TestModel') model_sig.add_field_sig( FieldSignature(field_name='added_field3', field_type=models.IntegerField, field_attrs={ 'null': True, })) self.perform_evolution_tests(AddFieldsModel, [ SQLMutation('first-two-fields', self.get_sql_mapping('AddFirstTwoFields'), update_first_two), SQLMutation('third-field', self.get_sql_mapping('AddThirdField'), update_third), ], ("In model tests.TestModel:\n" " Field 'added_field1' has been added\n" " Field 'added_field2' has been added\n" " Field 'added_field3' has been added"), sql_name='SQLMutationOutput')
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)
AddField('ReviewRequest', 'description_rich_text', models.BooleanField, initial=False), AddField('ReviewRequest', 'testing_done_rich_text', models.BooleanField, initial=False), AddField('ReviewRequestDraft', 'description_rich_text', models.BooleanField, initial=False), AddField('ReviewRequestDraft', 'testing_done_rich_text', models.BooleanField, initial=False), AddField('Review', 'body_top_rich_text', models.BooleanField, initial=False), AddField('Review', 'body_bottom_rich_text', models.BooleanField, initial=False), SQLMutation('review_request_rich_text_defaults', [""" UPDATE reviews_reviewrequest SET description_rich_text = rich_text, testing_done_rich_text = rich_text; """]), SQLMutation('review_request_draft_rich_text_defaults', [""" UPDATE reviews_reviewrequestdraft SET description_rich_text = rich_text, testing_done_rich_text = rich_text; """]), SQLMutation('review_rich_text_defaults', [""" UPDATE reviews_review SET body_top_rich_text = rich_text, body_bottom_rich_text = rich_text; """]), ]
def get_mutations(app, evolution_labels, database): """ Obtain the list of mutations described by the named evolutions. """ # For each item in the evolution sequence. Check each item to see if it is # a python file or an sql file. try: app_name = '.'.join(app.__name__.split('.')[:-1]) if app_name in BUILTIN_SEQUENCES: module_name = 'django_evolution.builtin_evolutions' else: module_name = '%s.evolutions' % app_name evolution_module = __import__(module_name, {}, {}, ['']) except ImportError: return [] mutations = [] for label in evolution_labels: directory_name = os.path.dirname(evolution_module.__file__) # The first element is used for compatibility purposes. filenames = [ os.path.join(directory_name, label + '.sql'), os.path.join(directory_name, "%s_%s.sql" % (database, label)), ] found = False for filename in filenames: if os.path.exists(filename): sql = [] sql_file = open(sql_file_name) for line in sql_file: sql.append(line) mutations.append(SQLMutation(label, sql)) found = True break if not found: try: module_name = [evolution_module.__name__, label] module = __import__('.'.join(module_name), {}, {}, [module_name]) mutations.extend(module.MUTATIONS) except ImportError: raise EvolutionException( 'Error: Failed to find an SQL or Python evolution named %s' % label) if is_multi_db(): latest_version = Version.objects.using(database).latest('when') else: latest_version = Version.objects.latest('when') app_label = app.__name__.split('.')[-2] old_proj_sig = pickle.loads(str(latest_version.signature)) proj_sig = create_project_sig(database) if app_label in old_proj_sig and app_label in proj_sig: # We want to go through now and make sure we're only applying # evolutions for models where the signature is different between # what's stored and what's current. # # The reason for this is that we may have just installed a baseline, # which would have the up-to-date signature, and we might be trying # to apply evolutions on top of that (which would already be applied). # These would generate errors. So, try hard to prevent that. old_app_sig = old_proj_sig[app_label] app_sig = proj_sig[app_label] changed_models = set() # Find the list of models in the latest signature of this app # that aren't in the old signature. for model_name, model_sig in app_sig.iteritems(): if (model_name not in old_app_sig or old_app_sig[model_name] != model_sig): changed_models.add(model_name) # Now do the same for models in the old signature, in case the # model has been deleted. for model_name, model_sig in old_app_sig.iteritems(): if model_name not in app_sig: changed_models.add(model_name) # We should now have a full list of which models changed. Filter # the list of mutations appropriately. mutations = [ mutation for mutation in mutations if (not hasattr(mutation, 'model_name') or mutation.model_name in changed_models) ] return mutations
def get_app_mutations(app, evolution_labels=None, database=DEFAULT_DB_ALIAS): """Return the mutations on an app provided by the given evolution names. Args: app (module): The app the evolutions belong to. evolution_labels (list of unicode, optional): The labels of the evolutions to return mutations for. If ``None``, this will factor in all evolution labels for the app. database (unicode, optional): The name of the database the evolutions cover. Returns: list of django_evolution.mutations.BaseMutation: The list of mutations provided by the evolutions. Raises: django_evolution.errors.EvolutionException: One or more evolutions are missing. """ # Avoids a nasty circular import. Util modules should always be # importable, so we compensate here. from django_evolution.mutations import SQLMutation evolutions_module = get_evolutions_module(app) if evolutions_module is None: return [] mutations = [] directory_name = os.path.dirname(evolutions_module.__file__) if evolution_labels is None: evolution_labels = get_evolution_sequence(app) for label in evolution_labels: # The first element is used for compatibility purposes. filenames = [ os.path.join(directory_name, '%s.sql' % label), os.path.join(directory_name, '%s_%s.sql' % (database, label)), ] found = False for filename in filenames: if os.path.exists(filename): with open(filename, 'r') as fp: sql = fp.readlines() mutations.append(SQLMutation(label, sql)) found = True break if not found: try: module = get_evolution_module(app=app, evolution_label=label) mutations += getattr(module, 'MUTATIONS', []) except ImportError: raise EvolutionException( 'Error: Failed to find an SQL or Python evolution named %s' % label) return mutations
from __future__ import unicode_literals from django.db import models from django_evolution.mutations import AddField, SQLMutation MUTATIONS = [ AddField('ReviewRequest', 'shipit_count', models.IntegerField, initial=0, null=True), SQLMutation('populate_shipit_count', [ """ UPDATE reviews_reviewrequest SET shipit_count = ( SELECT COUNT(*) FROM reviews_review WHERE reviews_review.review_request_id = reviews_reviewrequest.id AND reviews_review.public AND reviews_review.ship_it AND reviews_review.base_reply_to_id is NULL) """ ]) ]
from __future__ import unicode_literals from django.conf import settings from django_evolution.mutations import ChangeField, SQLMutation if settings.DATABASES['default']['ENGINE'].endswith('mysql'): field_index_suffix = '(255)' else: field_index_suffix = '' index_sql = ( 'CREATE INDEX reviews_reviewrequest_summary ON reviews_reviewrequest' ' (summary%s);' % field_index_suffix) MUTATIONS = [ ChangeField('ReviewRequest', 'summary', initial=None, db_index=False), SQLMutation('reviewrequest_summary', [index_sql]), ]
from __future__ import unicode_literals from django.db import models from django_evolution.mutations import AddField, SQLMutation MUTATIONS = [ AddField('ReviewRequest', 'last_review_timestamp', models.DateTimeField, null=True), SQLMutation('populate_last_review_timestamp', [ """ UPDATE reviews_reviewrequest SET last_review_timestamp = ( SELECT reviews_review.timestamp FROM reviews_review WHERE reviews_review.review_request_id = reviews_reviewrequest.id AND reviews_review.public ORDER BY reviews_review.timestamp DESC LIMIT 1) """ ]) ]
from django_evolution.mutations import SQLMutation MUTATIONS = [ SQLMutation('mysql_fields_changed_longtext', [ """ ALTER TABLE changedescs_changedescription MODIFY fields_changed LONGTEXT; """ ]) ]