django_evolution-0.6.7/0000755000175000017500000000000011741741024015677 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/MANIFEST.in0000644000175000017500000000030011471453547017440 0ustar chipx86chipx8600000000000000recursive-include django_evolution *.py recursive-include docs *.txt recursive-include tests *.py *.exclude include AUTHORS include LICENSE include README include setup.py include ez_setup.py django_evolution-0.6.7/PKG-INFO0000644000175000017500000000123211741741024016772 0ustar chipx86chipx8600000000000000Metadata-Version: 1.0 Name: django_evolution Version: 0.6.7 Summary: A database schema evolution tool for the Django web framework. Home-page: http://code.google.com/p/django-evolution/ Author: Christian Hammond Author-email: chipx86@chipx86.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries :: Python Modules django_evolution-0.6.7/LICENSE0000644000175000017500000000277511471453547016731 0ustar chipx86chipx8600000000000000Copyright (c) 2007, Benjamin Khoo All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of Django Evolution nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.django_evolution-0.6.7/django_evolution/0000755000175000017500000000000011741741024021245 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/diff.py0000644000175000017500000002467411600341057022540 0ustar chipx86chipx8600000000000000from django.db import models from django.db.models.fields.related import * from django_evolution import EvolutionException from django_evolution.mutations import DeleteField, AddField, DeleteModel, ChangeField from django_evolution.signature import ATTRIBUTE_DEFAULTS try: set except ImportError: from sets import Set as set #Python 2.3 Fallback class NullFieldInitialCallback(object): def __init__(self, app, model, field): self.app = app self.model = model self.field = field def __repr__(self): return '<>' def __call__(self): raise EvolutionException( "Cannot use hinted evolution: AddField or ChangeField mutation " "for '%s.%s' in '%s' requires user-specified initial value." % (self.model, self.field, self.app)) def get_initial_value(app_label, model_name, field_name): """Derive an initial value for a field. If a default has been provided on the field definition or the field allows for an empty string, that value will be used. Otherwise, a placeholder callable will be used. This callable cannot actually be used in an evolution, but will indicate that user input is required. """ model = models.get_model(app_label, model_name) field = model._meta.get_field(field_name) if field and (field.has_default() or (field.empty_strings_allowed and field.blank)): return field.get_default() return NullFieldInitialCallback(app_label, model_name, field_name) class Diff(object): """ A diff between two model signatures. The resulting diff is contained in two attributes: self.changed = { app_label: { 'changed': { model_name : { 'added': [ list of added field names ] 'deleted': [ list of deleted field names ] 'changed': { field: [ list of modified property names ] } } 'deleted': [ list of deleted model names ] } } self.deleted = { app_label: [ list of models in deleted app ] } """ def __init__(self, original, current): self.original_sig = original self.current_sig = current self.changed = {} self.deleted = {} if self.original_sig.get('__version__', 1) != 1: raise EvolutionException( "Unknown version identifier in original signature: %s", self.original_sig['__version__']) if self.current_sig.get('__version__', 1) != 1: raise EvolutionException( "Unknown version identifier in target signature: %s", self.current_sig['__version__']) for app_name, old_app_sig in original.items(): if app_name == '__version__': # Ignore the __version__ tag continue new_app_sig = self.current_sig.get(app_name, None) if new_app_sig is None: # App has been deleted self.deleted[app_name] = old_app_sig.keys() continue for model_name, old_model_sig in old_app_sig.items(): new_model_sig = new_app_sig.get(model_name, None) if new_model_sig is None: # Model has been deleted self.changed.setdefault(app_name, {}).setdefault('deleted', []).append(model_name) continue old_fields = old_model_sig['fields'] new_fields = new_model_sig['fields'] # Look for deleted or modified fields for field_name, old_field_data in old_fields.items(): new_field_data = new_fields.get(field_name, None) if new_field_data is None: # Field has been deleted self.changed.setdefault(app_name, {}).setdefault('changed', {}).setdefault(model_name, {}).setdefault('deleted', []).append(field_name) continue properties = set(old_field_data.keys()) properties.update(new_field_data.keys()) for prop in properties: old_value = old_field_data.get(prop, ATTRIBUTE_DEFAULTS.get(prop, None)) new_value = new_field_data.get(prop, ATTRIBUTE_DEFAULTS.get(prop, None)) if old_value != new_value: try: if (prop == 'field_type' and (old_value().get_internal_type() == new_value().get_internal_type())): continue except TypeError: pass # Field has been changed self.changed.setdefault(app_name, {}).setdefault('changed', {}).setdefault(model_name, {}).setdefault('changed', {}).setdefault(field_name,[]).append(prop) # Look for added fields new_fields = new_model_sig['fields'] for field_name, new_field_data in new_fields.items(): old_field_data = old_fields.get(field_name, None) if old_field_data is None: self.changed.setdefault(app_name, {}).setdefault('changed', {}).setdefault(model_name, {}).setdefault('added', []).append(field_name) def is_empty(self, ignore_apps=True): """Is this an empty diff? i.e., is the source and target the same? Set 'ignore_apps=False' if you wish to ignore changes caused by deleted applications. This is used when you don't purge deleted applications during an evolve. """ if ignore_apps: return not self.changed else: return not self.deleted and not self.changed def __str__(self): "Output an application signature diff in a human-readable format" lines = [] for app_label in self.deleted: lines.append('The application %s has been deleted' % app_label) for app_label, app_changes in self.changed.items(): for model_name in app_changes.get('deleted', {}): lines.append('The model %s.%s has been deleted' % (app_label, model_name)) for model_name, change in app_changes.get('changed', {}).items(): lines.append('In model %s.%s:' % (app_label, model_name)) for field_name in change.get('added',[]): lines.append(" Field '%s' has been added" % field_name) for field_name in change.get('deleted',[]): lines.append(" Field '%s' has been deleted" % field_name) for field_name,field_change in change.get('changed',{}).items(): lines.append(" In field '%s':" % field_name) for prop in field_change: lines.append(" Property '%s' has changed" % prop) return '\n'.join(lines) 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, **changed_attrs)) for model_name in app_changes.get('deleted',{}): mutations.setdefault(app_label,[]).append( DeleteModel(model_name)) return mutations django_evolution-0.6.7/django_evolution/builtin_evolutions/0000755000175000017500000000000011741741024025202 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/builtin_evolutions/session_expire_date_db_index.py0000644000175000017500000000025211471453547033455 0ustar chipx86chipx8600000000000000from django.db import models from django_evolution.mutations import ChangeField MUTATIONS = [ ChangeField('Session', 'expire_date', initial=None, db_index=True) ] django_evolution-0.6.7/django_evolution/builtin_evolutions/auth_delete_message.py0000644000175000017500000000017711622376460031556 0ustar chipx86chipx8600000000000000from django.db import models from django_evolution.mutations import DeleteModel MUTATIONS = [ DeleteModel('Message') ] django_evolution-0.6.7/django_evolution/builtin_evolutions/__init__.py0000644000175000017500000000134311721666065027325 0ustar chipx86chipx8600000000000000from django.contrib.sessions.models import Session BUILTIN_SEQUENCES = { 'django.contrib.auth': [], 'django.contrib.sessions': [], } # Starting in Django 1.3 alpha, Session.expire_date has a db_index set. # This needs to be reflected in the evolutions. Rather than hard-coding # a specific version to check for, we check the actual value in the field. if Session._meta.get_field_by_name('expire_date')[0].db_index: BUILTIN_SEQUENCES['django.contrib.sessions'].append( 'session_expire_date_db_index') # Starting in Django 1.4 alpha, the Message model was deleted. try: from django.contrib.auth.models import Message except ImportError: BUILTIN_SEQUENCES['django.contrib.auth'].append('auth_delete_message') django_evolution-0.6.7/django_evolution/models.py0000644000175000017500000000165211736023656023116 0ustar chipx86chipx8600000000000000try: from django.utils.timezone import now except ImportError: from datetime import datetime now = datetime.now from django.db import models class Version(models.Model): signature = models.TextField() when = models.DateTimeField(default=now) class Meta: ordering = ('-when',) db_table = 'django_project_version' def __unicode__(self): if not self.evolutions.count(): return u'Hinted version, updated on %s' % self.when return u'Stored version, updated on %s' % self.when class Evolution(models.Model): version = models.ForeignKey(Version, related_name='evolutions') app_label = models.CharField(max_length=200) label = models.CharField(max_length=100) class Meta: db_table = 'django_evolution' def __unicode__(self): return u"Evolution %s, applied to %s" % (self.label, self.app_label) django_evolution-0.6.7/django_evolution/evolve.py0000644000175000017500000000545411614462564023137 0ustar chipx86chipx8600000000000000import os from django_evolution import EvolutionException, is_multi_db from django_evolution.builtin_evolutions import BUILTIN_SEQUENCES from django_evolution.models import Evolution from django_evolution.mutations import SQLMutation def get_evolution_sequence(app): "Obtain the full evolution sequence for an application" app_name = '.'.join(app.__name__.split('.')[:-1]) if app_name in BUILTIN_SEQUENCES: return BUILTIN_SEQUENCES[app_name] try: evolution_module = __import__(app_name + '.evolutions',{},{},['']) return evolution_module.SEQUENCE except: return [] def get_unapplied_evolutions(app, database): "Obtain the list of unapplied evolutions for an application" sequence = get_evolution_sequence(app) app_label = app.__name__.split('.')[-2] evolutions = Evolution.objects.filter(app_label=app_label) if is_multi_db(): evolutions = evolutions.using(database) applied = [evo.label for evo in evolutions] return [seq for seq in sequence if seq not in applied] 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 django_evolution-0.6.7/django_evolution/admin.py0000644000175000017500000000022511471453547022720 0ustar chipx86chipx8600000000000000from django.contrib import admin from django_evolution.models import Version, Evolution admin.site.register(Version) admin.site.register(Evolution) django_evolution-0.6.7/django_evolution/mutations.py0000644000175000017500000006360711736024701023656 0ustar chipx86chipx8600000000000000import copy from django.db.models.fields import * from django.db.models.fields.related import * from django.db import models from django.utils.datastructures import SortedDict from django.utils.functional import curry from django_evolution.signature import ATTRIBUTE_DEFAULTS from django_evolution import CannotSimulate, SimulationFailure, EvolutionNotImplementedError, is_multi_db from django_evolution.db import EvolutionOperationsMulti FK_INTEGER_TYPES = [ 'AutoField', 'PositiveIntegerField', 'PositiveSmallIntegerField' ] if is_multi_db(): from django.db import router def create_field(proj_sig, field_name, field_type, field_attrs, parent_model): """ Create an instance of a field from a field signature. This is useful for accessing all the database property mechanisms built into fields. """ # related_model isn't a valid field attribute, so it must be removed # prior to instantiating the field, but it must be restored # to keep the signature consistent. related_model = field_attrs.pop('related_model', None) if related_model: related_app_name, related_model_name = related_model.split('.') related_model_sig = proj_sig[related_app_name][related_model_name] to = MockModel(proj_sig, related_app_name, related_model_name, related_model_sig, stub=True) field = field_type(to, name=field_name, **field_attrs) field_attrs['related_model'] = related_model else: field = field_type(name=field_name, **field_attrs) if field_type == ManyToManyField and parent_model is not None: # Starting in Django 1.2, a ManyToManyField must have a through # model defined. This will be set internally to an auto-created # model if one isn't specified. We have to fake that model. through_model = field_attrs.get('through_model', None) through_model_sig = None if through_model: through_app_name, through_model_name = through_model.split('.') through_model_sig = proj_sig[through_app_name][through_model_name] elif hasattr(field, '_get_m2m_attr'): # Django >= 1.2 to = field.rel.to._meta.object_name.lower() if (field.rel.to == RECURSIVE_RELATIONSHIP_CONSTANT or to == parent_model._meta.object_name.lower()): from_ = 'from_%s' % to to = 'to_%s' % to else: from_ = parent_model._meta.object_name.lower() # This corresponds to the signature in # related.create_many_to_many_intermediary_model through_app_name = parent_model.app_name through_model_name = '%s_%s' % (parent_model._meta.object_name, field.name) through_model = '%s.%s' % (through_app_name, through_model_name) fields = SortedDict() fields['id'] = { 'field_type': AutoField, 'primary_key': True, } fields[from_] = { 'field_type': ForeignKey, 'related_model': '%s.%s' % (parent_model.app_name, parent_model._meta.object_name), 'related_name': '%s+' % through_model_name, } fields[to] = { 'field_type': ForeignKey, 'related_model': related_model, 'related_name': '%s+' % through_model_name, } through_model_sig = { 'meta': { 'db_table': field._get_m2m_db_table(parent_model._meta), 'managed': True, 'auto_created': True, 'app_label': through_app_name, 'unique_together': ((from_, to),), 'pk_column': 'id', }, 'fields': fields, } field.auto_created = True if through_model_sig: through = MockModel(proj_sig, through_app_name, through_model_name, through_model_sig) field.rel.through = through field.m2m_db_table = curry(field._get_m2m_db_table, parent_model._meta) field.set_attributes_from_rel() field.set_attributes_from_name(field_name) return field class MockMeta(object): """ A mockup of a models Options object, based on the model signature. The stub argument is used to circumvent recursive relationships. If 'stub' is provided, the constructed model will only be a stub - it will only have a primary key field. """ def __init__(self, proj_sig, app_name, model_name, model_sig): self.object_name = model_name self.app_label = app_name self.meta = { 'order_with_respect_to': None, 'has_auto_field': None, 'db_tablespace': None, } self.meta.update(model_sig['meta']) self._fields = SortedDict() self._many_to_many = SortedDict() self.abstract = False self.managed = True self.proxy = False self._model_sig = model_sig self._proj_sig = proj_sig def setup_fields(self, model, stub=False): for field_name, field_sig in self._model_sig['fields'].items(): if not stub or field_sig.get('primary_key', False): field_type = field_sig.pop('field_type') field = create_field(self._proj_sig, field_name, field_type, field_sig, model) if AutoField == type(field): self.meta['has_auto_field'] = True self.meta['auto_field'] = field field_sig['field_type'] = field_type if ManyToManyField == type(field): self._many_to_many[field.name] = field else: self._fields[field.name] = field field.set_attributes_from_name(field_name) if field_sig.get('primary_key', False): self.pk = field def __getattr__(self, name): return self.meta[name] def get_field(self, name): try: return self._fields[name] except KeyError: try: return self._many_to_many[name] except KeyError: raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, name)) def get_field_by_name(self, name): return (self.get_field(name), None, True, None) def get_fields(self): return self._fields.values() def get_many_to_many_fields(self): return self._many_to_many.values() fields = property(fget=get_fields) local_fields = property(fget=get_fields) local_many_to_many = property(fget=get_many_to_many_fields) class MockModel(object): """ A mockup of a model object, providing sufficient detail to derive database column and table names using the standard Django fields. """ def __init__(self, proj_sig, app_name, model_name, model_sig, stub=False): self.app_name = app_name self.model_name = model_name self._meta = MockMeta(proj_sig, app_name, model_name, model_sig) self._meta.setup_fields(self, stub) def __eq__(self, other): # For our purposes, we don't want to appear equal to "self". # Really, Django 1.2 should be checking if this is a string before # doing this comparison, return (isinstance(other, MockModel) and self.app_name == other.app_name and self.model_name == other.model_name) class MockRelated(object): """ A mockup of django.db.models.related.RelatedObject, providing sufficient detail to derive database column and table names using the standard Django fields. """ def __init__(self, related_model, model, field): self.parent_model = related_model self.model = model self.opts = model._meta self.field = field self.name = '%s:%s' % (model.app_name, model.model_name) self.var_name = model.model_name.lower() class BaseMutation: def __init__(self): pass def mutate(self, app_label, proj_sig, target_database=None): """ Performs the mutation on the database. Database changes will occur after this function is invoked. """ raise NotImplementedError() def simulate(self, app_label, proj_sig, target_database=None): """ Performs a simulation of the mutation to be performed. The purpose of the simulate function is to ensure that after all mutations have occured the database will emerge in a state consistent with the currently loaded models file. """ raise NotImplementedError() def is_mutable(self, app_label, proj_sig, database): """ test if the current mutation could be applied to the given database """ return False class MonoBaseMutation(BaseMutation): # introducting model_name at this stage will prevent subclasses to be # cross databases def __init__(self, model_name = None): BaseMutation.__init__(self) self.model_name = model_name def evolver(self, model, database=None): if is_multi_db() and database is None: db_name = router.db_for_write(model) else: db_name = database or 'default' return EvolutionOperationsMulti(db_name).get_evolver() def is_mutable(self, app_label, proj_sig, database): if is_multi_db(): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] model = MockModel(proj_sig, app_label, self.model_name, model_sig) db_name = router.db_for_write(model) return db_name and db_name == database else: return True class SQLMutation(BaseMutation): def __init__(self, tag, sql, update_func=None): self.tag = tag self.sql = sql self.update_func = update_func def __str__(self): return "SQLMutation('%s')" % self.tag def simulate(self, app_label, proj_sig, database=None): """SQL mutations cannot be simulated unless an update function is provided""" if callable(self.update_func): self.update_func(app_label, proj_sig) else: raise CannotSimulate('Cannot simulate SQLMutations') def mutate(self, app_label, proj_sig, database=None): "The mutation of an SQL mutation returns the raw SQL" return self.sql def is_mutable(self, app_label, proj_sig, database): return True class DeleteField(MonoBaseMutation): def __init__(self, model_name, field_name): MonoBaseMutation.__init__(self, model_name) self.field_name = field_name def __str__(self): return "DeleteField('%s', '%s')" % (self.model_name, self.field_name) def simulate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] # If the field was used in the unique_together attribute, update it. unique_together = model_sig['meta']['unique_together'] unique_together_list = [] for ut_index in range(0, len(unique_together), 1): ut = unique_together[ut_index] unique_together_fields = [] for field_name_index in range(0, len(ut), 1): field_name = ut[field_name_index] if not field_name == self.field_name: unique_together_fields.append(field_name) unique_together_list.append(tuple(unique_together_fields)) model_sig['meta']['unique_together'] = tuple(unique_together_list) if model_sig['fields'][self.field_name].get('primary_key',False): raise SimulationFailure('Cannot delete a primary key.') # Simulate the deletion of the field. try: model_sig['fields'].pop(self.field_name) except KeyError: raise SimulationFailure('Cannot find the field named "%s".' % self.field_name) def mutate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] field_sig = model_sig['fields'][self.field_name] model = MockModel(proj_sig, app_label, self.model_name, model_sig) # Temporarily remove field_type from the field signature # so that we can create a field field_type = field_sig.pop('field_type') field = create_field(proj_sig, self.field_name, field_type, field_sig, model) field_sig['field_type'] = field_type evolver = self.evolver(model, database) if field_type == models.ManyToManyField: sql_statements = \ evolver.delete_table(field._get_m2m_db_table(model._meta)) else: sql_statements = evolver.delete_column(model, field) return sql_statements class AddField(MonoBaseMutation): def __init__(self, model_name, field_name, field_type, initial=None, **kwargs): MonoBaseMutation.__init__(self, model_name) self.field_name = field_name self.field_type = field_type self.field_attrs = kwargs self.initial = initial def __str__(self): if self.field_type.__module__.startswith('django.db.models'): field_prefix = 'models.' else: field_prefix = '' str_output = ["'%(model_name)s', '%(field_name)s', %(field_type)s" % { 'model_name': self.model_name, 'field_name': self.field_name, 'field_type': field_prefix + self.field_type.__name__ }] if self.initial is not None: str_output.append('initial=%s' % repr(self.initial)) for key,value in self.field_attrs.items(): str_output.append("%s=%s" % (key,repr(value))) return 'AddField(' + ', '.join(str_output) + ')' def simulate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] if self.field_name in model_sig['fields']: raise SimulationFailure( "Model '%s.%s' already has a field named '%s'" % (app_label, self.model_name, self.field_name)) if (self.field_type != models.ManyToManyField and not self.field_attrs.get('null', ATTRIBUTE_DEFAULTS['null']) and self.initial is None): raise SimulationFailure( "Cannot create new column '%s' on '%s.%s' without a " "non-null initial value." % (self.field_name, app_label, self.model_name)) model_sig['fields'][self.field_name] = { 'field_type': self.field_type, } model_sig['fields'][self.field_name].update(self.field_attrs) def mutate(self, app_label, proj_sig, database=None): if self.field_type == models.ManyToManyField: return self.add_m2m_table(app_label, proj_sig, database) else: return self.add_column(app_label, proj_sig, database) def add_column(self, app_label, proj_sig, database): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] model = MockModel(proj_sig, app_label, self.model_name, model_sig) field = create_field(proj_sig, self.field_name, self.field_type, self.field_attrs, model) evolver = self.evolver(model, database) sql_statements = evolver.add_column(model, field, self.initial) # Create SQL index if necessary sql_statements.extend(evolver.create_index(model, field)) return sql_statements def add_m2m_table(self, app_label, proj_sig, database): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] model = MockModel(proj_sig, app_label, self.model_name, model_sig) field = create_field(proj_sig, self.field_name, self.field_type, self.field_attrs, model) related_app_label, related_model_name = \ self.field_attrs['related_model'].split('.') related_sig = proj_sig[related_app_label][related_model_name] related_model = MockModel(proj_sig, related_app_label, related_model_name, related_sig) related = MockRelated(related_model, model, field) if hasattr(field, '_get_m2m_column_name'): # Django < 1.2 field.m2m_column_name = curry(field._get_m2m_column_name, related) field.m2m_reverse_name = curry(field._get_m2m_reverse_name, related) else: # Django >= 1.2 field.m2m_column_name = curry(field._get_m2m_attr, related, 'column') field.m2m_reverse_name = curry(field._get_m2m_reverse_attr, related, 'column') evolver = self.evolver(model, database) sql_statements = evolver.add_m2m_table(model, field) return sql_statements class RenameField(MonoBaseMutation): def __init__(self, model_name, old_field_name, new_field_name, db_column=None, db_table=None): MonoBaseMutation.__init__(self, model_name) self.old_field_name = old_field_name self.new_field_name = new_field_name self.db_column = db_column self.db_table = db_table def __str__(self): params = "'%s', '%s', '%s'" % (self.model_name, self.old_field_name, self.new_field_name) if self.db_column: params = params + ", db_column='%s'" % (self.db_column) if self.db_table: params = params + ", db_table='%s'" % (self.db_table) return "RenameField(%s)" % params def simulate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] field_dict = model_sig['fields'] field_sig = field_dict[self.old_field_name] if models.ManyToManyField == field_sig['field_type']: if self.db_table: field_sig['db_table'] = self.db_table else: field_sig.pop('db_table',None) elif self.db_column: field_sig['db_column'] = self.db_column else: # db_column and db_table were not specified (or not specified for # the appropriate field types). Clear the old value if one was set. # This amounts to resetting the column or table name to the Django # default name field_sig.pop('db_column', None) field_dict[self.new_field_name] = field_dict.pop(self.old_field_name) def mutate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] old_field_sig = model_sig['fields'][self.old_field_name] # Temporarily remove the field type so that we can create mock field # instances field_type = old_field_sig.pop('field_type') # Duplicate the old field sig, and apply the table/column changes new_field_sig = copy.copy(old_field_sig) if models.ManyToManyField == field_type: if self.db_table: new_field_sig['db_table'] = self.db_table else: new_field_sig.pop('db_table', None) elif self.db_column: new_field_sig['db_column'] = self.db_column else: new_field_sig.pop('db_column', None) # Create the mock field instances. old_field = create_field(proj_sig, self.old_field_name, field_type, old_field_sig, None) new_field = create_field(proj_sig, self.new_field_name, field_type, new_field_sig, None) # Restore the field type to the signature old_field_sig['field_type'] = field_type model = MockModel(proj_sig, app_label, self.model_name, model_sig) evolver = self.evolver(model, database) if models.ManyToManyField == field_type: old_m2m_table = old_field._get_m2m_db_table(model._meta) new_m2m_table = new_field._get_m2m_db_table(model._meta) return evolver.rename_table(model, old_m2m_table, new_m2m_table) else: return evolver.rename_column(model._meta, old_field, new_field) class ChangeField(MonoBaseMutation): def __init__(self, model_name, field_name, initial=None, **kwargs): MonoBaseMutation.__init__(self, model_name) self.field_name = field_name self.field_attrs = kwargs self.initial = initial def __str__(self): params = (self.model_name, self.field_name) str_output = ["'%s', '%s'" % params] str_output.append('initial=%s' % repr(self.initial)) for attr_name, attr_value in self.field_attrs.items(): if str == type(attr_value): str_attr_value = "'%s'" % attr_value else: str_attr_value = str(attr_value) str_output.append('%s=%s' % (attr_name, str_attr_value,)) return 'ChangeField(' + ', '.join(str_output) + ')' def simulate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] field_sig = model_sig['fields'][self.field_name] # Catch for no-op changes. for field_attr, attr_value in self.field_attrs.items(): field_sig[field_attr] = attr_value if ('null' in self.field_attrs and field_sig['field_type'] != models.ManyToManyField and not self.field_attrs['null'] and self.initial is None): raise SimulationFailure( "Cannot change column '%s' on '%s.%s' without a " "non-null initial value." % (self.field_name, app_label, self.model_name)) def mutate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] old_field_sig = model_sig['fields'][self.field_name] model = MockModel(proj_sig, app_label, self.model_name, model_sig) sql_statements = [] for field_attr, attr_value in self.field_attrs.items(): old_field_attr = old_field_sig.get(field_attr, ATTRIBUTE_DEFAULTS[field_attr]) # Avoid useless SQL commands if nothing has changed. if not old_field_attr == attr_value: try: evolver_func = getattr(self.evolver(model, database), 'change_%s' % field_attr) if field_attr == 'null': sql_statements.extend( evolver_func(model, self.field_name, attr_value, self.initial)) elif field_attr == 'db_table': sql_statements.extend( evolver_func(model, old_field_attr, attr_value)) else: sql_statements.extend( evolver_func(model, self.field_name, attr_value)) except AttributeError: raise EvolutionNotImplementedError( "ChangeField does not support modifying the '%s' " "attribute on '%s.%s'." % (field_attr, self.model_name, self.field_name)) return sql_statements class DeleteModel(MonoBaseMutation): def __init__(self, model_name): MonoBaseMutation.__init__(self, model_name) def __str__(self): return "DeleteModel(%r)" % self.model_name def simulate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] # Simulate the deletion of the model. del app_sig[self.model_name] def mutate(self, app_label, proj_sig, database=None): app_sig = proj_sig[app_label] model_sig = app_sig[self.model_name] sql_statements = [] model = MockModel(proj_sig, app_label, self.model_name, model_sig) evolver = self.evolver(model, database) # Remove any many to many tables. for field_name, field_sig in model_sig['fields'].items(): if field_sig['field_type'] == models.ManyToManyField: field = model._meta.get_field(field_name) m2m_table = field._get_m2m_db_table(model._meta) sql_statements += evolver.delete_table(m2m_table) # Remove the table itself. sql_statements += evolver.delete_table(model._meta.db_table) return sql_statements class DeleteApplication(BaseMutation): def __str__(self): return 'DeleteApplication()' def simulate(self, app_label, proj_sig, database=None): if database: app_sig = proj_sig[app_label] # Simulate the deletion of the models. for model_name in app_sig.keys(): mutation = DeleteModel(model_name) if mutation.is_mutable(app_label, proj_sig, database): del app_sig[self.model_name] def mutate(self, app_label, proj_sig, database=None): sql_statements = [] # This test will introduce a regression, but we can't afford to remove # all models at a same time if they aren't owned by the same database if database: app_sig = proj_sig[app_label] for model_name in app_sig.keys(): mutation = DeleteModel(model_name) if mutation.is_mutable(app_label, proj_sig, database): sql_statements.extend(mutation.mutate(app_label, proj_sig)) return sql_statements def is_mutable(self, app_label, proj_sig, database): # the test is done in the mutate method above. We can return True return True django_evolution-0.6.7/django_evolution/db/0000755000175000017500000000000011741741024021632 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/db/postgresql_psycopg2.py0000644000175000017500000000010611471453547026244 0ustar chipx86chipx8600000000000000# Psycopg2 behaviour is identical to Psycopg1 from postgresql import *django_evolution-0.6.7/django_evolution/db/mysql_old.py0000644000175000017500000000010411471453547024214 0ustar chipx86chipx8600000000000000# MySQL_old behaviour is identical to mysql base from mysql import *django_evolution-0.6.7/django_evolution/db/sqlite3.py0000644000175000017500000002052711562106752023602 0ustar chipx86chipx8600000000000000from django.core.management import color from django.db import models from common import BaseEvolutionOperations TEMP_TABLE_NAME = 'TEMP_TABLE' class EvolutionOperations(BaseEvolutionOperations): def delete_column(self, model, f): output = [] field_list = [ field for field in model._meta.local_fields # Remove the field to be deleted if f.name != field.name # and any Generic fields and field.db_type(connection=self.connection) is not None ] table_name = model._meta.db_table output.extend(self.create_temp_table(field_list)) output.extend(self.copy_to_temp_table(table_name, field_list)) output.extend(self.delete_table(table_name)) output.extend(self.create_table(table_name, field_list)) output.extend(self.copy_from_temp_table(table_name, field_list)) output.extend(self.delete_table(TEMP_TABLE_NAME)) return output def copy_to_temp_table(self, source_table_name, original_field_list, new_field_list=None): qn = self.connection.ops.quote_name source_columns = self.column_names(original_field_list) if new_field_list: temp_columns = self.column_names(new_field_list) else: temp_columns = source_columns return ['INSERT INTO %s (%s) SELECT %s FROM %s;' % (qn(TEMP_TABLE_NAME), temp_columns, source_columns, qn(source_table_name))] def copy_from_temp_table(self, dest_table_name, field_list): qn = self.connection.ops.quote_name params = { 'dest_table_name': qn(dest_table_name), 'temp_table': qn(TEMP_TABLE_NAME), 'column_names': self.column_names(field_list), } return ['INSERT INTO %(dest_table_name)s (%(column_names)s) SELECT %(column_names)s FROM %(temp_table)s;' % params] def column_names(self, field_list): qn = self.connection.ops.quote_name columns = [] for field in field_list: if not isinstance(field, models.ManyToManyField): columns.append(qn(field.column)) return ', '.join(columns) def insert_to_temp_table(self, field, initial): qn = self.connection.ops.quote_name # At this point, initial can only be None if null=True, otherwise it is # a user callable or the default AddFieldInitialCallback which will shortly raise an exception. if initial is None: return [] params = { 'table_name': qn(TEMP_TABLE_NAME), 'column_name': qn(field.column), } if callable(initial): params['value'] = initial() return ["UPDATE %(table_name)s SET %(column_name)s = %(value)s;" % params] else: return [("UPDATE %(table_name)s SET %(column_name)s = %%s;" % params, (initial,))] def create_temp_table(self, field_list): return self.create_table(TEMP_TABLE_NAME, field_list, True, False) def create_indexes_for_table(self, table_name, field_list): class FakeMeta(object): def __init__(self, table_name, field_list): self.db_table = table_name self.local_fields = field_list self.fields = field_list # Required for Pre QS-RF support self.db_tablespace = None self.managed = True self.proxy = False class FakeModel(object): def __init__(self, table_name, field_list): self._meta = FakeMeta(table_name, field_list) style = color.no_style() return self.connection.creation.sql_indexes_for_model(FakeModel(table_name, field_list), style) def create_table(self, table_name, field_list, temporary=False, create_index=True): qn = self.connection.ops.quote_name output = [] create = ['CREATE'] if temporary: create.append('TEMPORARY') create.append('TABLE %s' % qn(table_name)) output = [' '.join(create)] output.append('(') columns = [] for field in field_list: if not models.ManyToManyField == field.__class__: column_name = qn(field.column) column_type = field.db_type(connection=self.connection) params = [column_name, column_type] # Always use null if this is a temporary table. It may be # used to create a new field (which will be null while data is # copied across from the old table). if temporary or field.null: params.append('NULL') else: params.append('NOT NULL') if field.unique: params.append('UNIQUE') if field.primary_key: params.append('PRIMARY KEY') columns.append(' '.join(params)) output.append(', '.join(columns)) output.append(');') output = [''.join(output)] if create_index: output.extend(self.create_indexes_for_table(table_name, field_list)) return output def rename_column(self, opts, old_field, new_field): if old_field.column == new_field.column: # No Operation return [] original_fields = opts.local_fields new_fields = [] for f in original_fields: # Ignore Generic Fields if f.db_type(connection=self.connection) is not None: if f.name == old_field.name: new_fields.append(new_field) else: new_fields.append(f) table_name = opts.db_table output = [] output.extend(self.create_temp_table(new_fields)) output.extend(self.copy_to_temp_table(table_name, original_fields, new_fields)) output.extend(self.delete_table(table_name)) output.extend(self.create_table(table_name, new_fields)) output.extend(self.copy_from_temp_table(table_name, new_fields)) output.extend(self.delete_table(TEMP_TABLE_NAME)) return output def add_column(self, model, f, initial): output = [] table_name = model._meta.db_table original_fields = [ field for field in model._meta.local_fields if field.db_type(connection=self.connection) is not None ] new_fields = list(original_fields) new_fields.append(f) output.extend(self.create_temp_table(new_fields)) output.extend(self.copy_to_temp_table(table_name, original_fields)) output.extend(self.insert_to_temp_table(f, initial)) output.extend(self.delete_table(table_name)) output.extend(self.create_table(table_name, new_fields, create_index=False)) output.extend(self.copy_from_temp_table(table_name, new_fields)) output.extend(self.delete_table(TEMP_TABLE_NAME)) return output def change_null(self, model, field_name, new_null_attr, initial=None): return self.change_attribute(model, field_name, 'null', new_null_attr, initial) def change_max_length(self, model, field_name, new_max_length, initial=None): return self.change_attribute(model, field_name, 'max_length', new_max_length, initial) def change_unique(self, model, field_name, new_unique_value, initial=None): return self.change_attribute(model, field_name, '_unique', new_unique_value, initial) def change_attribute(self, model, field_name, attr_name, new_attr_value, initial=None): output = [] opts = model._meta table_name = opts.db_table setattr(opts.get_field(field_name), attr_name, new_attr_value) fields = [ f for f in opts.local_fields if f.db_type(connection=self.connection) is not None ] output.extend(self.create_temp_table(fields)) output.extend(self.copy_to_temp_table(table_name, fields)) output.extend(self.insert_to_temp_table(opts.get_field(field_name), initial)) output.extend(self.delete_table(table_name)) output.extend(self.create_table(table_name, fields, create_index=False)) output.extend(self.copy_from_temp_table(table_name, fields)) output.extend(self.delete_table(TEMP_TABLE_NAME)) return output django_evolution-0.6.7/django_evolution/db/mysql.py0000644000175000017500000000761011562106617023361 0ustar chipx86chipx8600000000000000from django.core.management import color from common import BaseEvolutionOperations class EvolutionOperations(BaseEvolutionOperations): def rename_column(self, opts, old_field, f): if old_field.column == f.column: # No Operation return [] qn = self.connection.ops.quote_name style = color.no_style() ### col_type = f.db_type(connection=self.connection) tablespace = f.db_tablespace or opts.db_tablespace if col_type is None: # Skip ManyToManyFields, because they're not represented as # database columns in this table. return [] # Make the definition (e.g. 'foo VARCHAR(30)') for this field. field_output = [style.SQL_FIELD(qn(f.column)), style.SQL_COLTYPE(col_type)] field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) if f.primary_key: field_output.append(style.SQL_KEYWORD('PRIMARY KEY')) if f.unique: field_output.append(style.SQL_KEYWORD('UNIQUE')) if tablespace and self.connection.features.supports_tablespaces and (f.unique or f.primary_key) and self.connection.features.autoindexes_primary_keys: # We must specify the index tablespace inline, because we # won't be generating a CREATE INDEX statement for this field. field_output.append(self.connection.ops.tablespace_sql(tablespace, inline=True)) if f.rel: field_output.append(style.SQL_KEYWORD('REFERENCES') + ' ' + \ style.SQL_TABLE(qn(f.rel.to._meta.db_table)) + ' (' + \ style.SQL_FIELD(qn(f.rel.to._meta.get_field(f.rel.field_name).column)) + ')' + self.connection.ops.deferrable_sql() ) params = (qn(opts.db_table), qn(old_field.column), ' '.join(field_output)) return ['ALTER TABLE %s CHANGE COLUMN %s %s;' % params] def set_field_null(self, model, f, null): qn = self.connection.ops.quote_name params = (qn(model._meta.db_table), qn(f.column), f.db_type(connection=self.connection)) if null: return 'ALTER TABLE %s MODIFY COLUMN %s %s DEFAULT NULL;' % params else: return 'ALTER TABLE %s MODIFY COLUMN %s %s NOT NULL;' % params def change_max_length(self, model, field_name, new_max_length, initial=None): qn = self.connection.ops.quote_name opts = model._meta f = opts.get_field(field_name) f.max_length = new_max_length params = { 'table': qn(opts.db_table), 'column': qn(f.column), 'length': f.max_length, 'type': f.db_type(connection=self.connection) } return ['UPDATE %(table)s SET %(column)s=LEFT(%(column)s,%(length)d);' % params, 'ALTER TABLE %(table)s MODIFY COLUMN %(column)s %(type)s;' % params] def drop_index(self, model, f): qn = self.connection.ops.quote_name params = (qn(self.get_index_name(model, f)), qn(model._meta.db_table)) return ['DROP INDEX %s ON %s;' % params] def change_unique(self, model, field_name, new_unique_value, initial=None): qn = self.connection.ops.quote_name opts = model._meta f = opts.get_field(field_name) constraint_name = '%s' % (f.column,) if new_unique_value: params = (constraint_name, qn(opts.db_table), qn(f.column),) return ['CREATE UNIQUE INDEX %s ON %s(%s);' % params] else: params = (constraint_name, qn(opts.db_table)) return ['DROP INDEX %s ON %s;' % params] def rename_table(self, model, old_db_tablename, db_tablename): if old_db_tablename == db_tablename: return [] qn = self.connection.ops.quote_name params = (qn(old_db_tablename), qn(db_tablename)) return ['RENAME TABLE %s TO %s;' % params] django_evolution-0.6.7/django_evolution/db/postgresql.py0000644000175000017500000000450011562065466024420 0ustar chipx86chipx8600000000000000from django.core.management import color from django.db.backends.util import truncate_name from common import BaseEvolutionOperations class EvolutionOperations(BaseEvolutionOperations): def rename_column(self, opts, old_field, new_field): if old_field.column == new_field.column: # No Operation return [] style = color.no_style() qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() creation = self.connection.creation sql = [] refs = {} models = [] if old_field.primary_key: for field in opts.local_many_to_many: if field.rel and field.rel.through: through = field.rel.through for m2m_f in through._meta.local_fields: if (m2m_f.rel and m2m_f.rel.to._meta.db_table == opts.db_table and m2m_f.rel.field_name == old_field.column): models.append(m2m_f.rel.to) refs.setdefault(m2m_f.rel.to, []).append( (through, m2m_f)) remove_refs = refs.copy() for relto in models: sql.extend(creation.sql_remove_table_constraints( relto, remove_refs, style)) params = (qn(opts.db_table), truncate_name(qn(old_field.column), max_name_length), truncate_name(qn(new_field.column), max_name_length)) sql.append('ALTER TABLE %s RENAME COLUMN %s TO %s;' % params) if old_field.primary_key: for relto in models: for rel_class, f in refs[relto]: f.rel.field_name = new_field.column del relto._meta._fields[old_field.name] relto._meta._fields[new_field.name] = new_field sql.extend(creation.sql_for_pending_references( relto, style, refs)) return sql def get_index_name(self, model, f): # By default, Django 1.2 will use a digest hash for the column name. # The PostgreSQL support, however, uses the column name itself. return '%s_%s' % (model._meta.db_table, f.column) django_evolution-0.6.7/django_evolution/db/common.py0000644000175000017500000002475411562100444023504 0ustar chipx86chipx8600000000000000import django from django.core.management import color from django.db import connection as default_connection from django.db.backends.util import truncate_name import copy class BaseEvolutionOperations(object): connection = None def __init__(self, connection = default_connection): self.connection = connection def quote_sql_param(self, param): "Add protective quoting around an SQL string parameter" if isinstance(param, basestring): return u"'%s'" % unicode(param).replace(u"'",ur"\'") else: return param def rename_table(self, model, old_db_tablename, db_tablename): if old_db_tablename == db_tablename: # No Operation return [] style = color.no_style() qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() creation = self.connection.creation sql = [] refs = {} models = [] for field in model._meta.local_many_to_many: if (field.rel and field.rel.through and field.rel.through._meta.db_table == old_db_tablename): through = field.rel.through for m2m_field in through._meta.local_fields: if m2m_field.rel and m2m_field.rel.to == model: models.append(m2m_field.rel.to) refs.setdefault(m2m_field.rel.to, []).append( (through, m2m_field)) remove_refs = refs.copy() for relto in models: sql.extend(creation.sql_remove_table_constraints(relto, remove_refs, style)) params = (qn(old_db_tablename), qn(db_tablename)) sql.append('ALTER TABLE %s RENAME TO %s;' % params) for relto in models: for rel_class, f in refs[relto]: if rel_class._meta.db_table == old_db_tablename: rel_class._meta.db_table = db_tablename rel_class._meta.db_table = \ truncate_name(rel_class._meta.db_table, max_name_length) sql.extend(creation.sql_for_pending_references(relto, style, refs)) return sql def delete_column(self, model, f): qn = self.connection.ops.quote_name params = (qn(model._meta.db_table), qn(f.column)) return ['ALTER TABLE %s DROP COLUMN %s CASCADE;' % params] def delete_table(self, table_name): qn = self.connection.ops.quote_name return ['DROP TABLE %s;' % qn(table_name)] def add_m2m_table(self, model, f): style = color.no_style() creation = self.connection.creation if f.rel.through: references = {} pending_references = {} sql, references = creation.sql_create_model(f.rel.through, style) for refto, refs in references.items(): pending_references.setdefault(refto, []).extend(refs) sql.extend(creation.sql_for_pending_references( refto, style, pending_references)) sql.extend(creation.sql_for_pending_references( f.rel.through, style, pending_references)) else: sql = creation.sql_for_many_to_many_field(model, f, style) return sql def add_column(self, model, f, initial): qn = self.connection.ops.quote_name if f.rel: # it is a foreign key field # NOT NULL REFERENCES "django_evolution_addbasemodel" ("id") DEFERRABLE INITIALLY DEFERRED # ALTER TABLE ADD COLUMN NULL REFERENCES ("") DEFERRABLE INITIALLY DEFERRED related_model = f.rel.to related_table = related_model._meta.db_table related_pk_col = related_model._meta.pk.name constraints = ['%sNULL' % (not f.null and 'NOT ' or '')] if f.unique or f.primary_key: constraints.append('UNIQUE') params = (qn(model._meta.db_table), qn(f.column), f.db_type(connection=self.connection), ' '.join(constraints), qn(related_table), qn(related_pk_col), self.connection.ops.deferrable_sql()) output = ['ALTER TABLE %s ADD COLUMN %s %s %s REFERENCES %s (%s) %s;' % params] else: null_constraints = '%sNULL' % (not f.null and 'NOT ' or '') if f.unique or f.primary_key: unique_constraints = 'UNIQUE' else: unique_constraints = '' # At this point, initial can only be None if null=True, otherwise it is # a user callable or the default AddFieldInitialCallback which will shortly raise an exception. if initial is not None: if callable(initial): params = (qn(model._meta.db_table), qn(f.column), f.db_type(connection=self.connection), unique_constraints) output = ['ALTER TABLE %s ADD COLUMN %s %s %s;' % params] params = (qn(model._meta.db_table), qn(f.column), initial(), qn(f.column)) output.append('UPDATE %s SET %s = %s WHERE %s IS NULL;' % params) else: params = (qn(model._meta.db_table), qn(f.column), f.db_type(connection=self.connection), unique_constraints) output = [('ALTER TABLE %s ADD COLUMN %s %s %s DEFAULT %%s;' % params, (initial,))] # Django doesn't generate default columns, so now that we've # added one to get default values for existing tables, drop # that default. params = (qn(model._meta.db_table), qn(f.column)) output.append('ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT;' % params) if not f.null: # Only put this sql statement if the column cannot be null. output.append(self.set_field_null(model, f, f.null)) else: params = (qn(model._meta.db_table), qn(f.column), f.db_type(connection=self.connection), ' '.join([null_constraints, unique_constraints])) output = ['ALTER TABLE %s ADD COLUMN %s %s %s;' % params] return output def set_field_null(self, model, f, null): qn = self.connection.ops.quote_name params = (qn(model._meta.db_table), qn(f.column),) if null: return 'ALTER TABLE %s ALTER COLUMN %s DROP NOT NULL;' % params else: return 'ALTER TABLE %s ALTER COLUMN %s SET NOT NULL;' % params def create_index(self, model, f): "Returns the CREATE INDEX SQL statements." style = color.no_style() return self.connection.creation.sql_indexes_for_field(model, f, style) def drop_index(self, model, f): qn = self.connection.ops.quote_name index_name = self.get_index_name(model, f) max_length = self.connection.ops.max_name_length() return ['DROP INDEX %s;' % qn(truncate_name(index_name, max_length))] def get_index_name(self, model, f): if django.VERSION >= (1, 2): colname = self.connection.creation._digest(f.column) else: colname = f.column return '%s_%s' % (model._meta.db_table, colname) def change_null(self, model, field_name, new_null_attr, initial=None): qn = self.connection.ops.quote_name opts = model._meta f = opts.get_field(field_name) output = [] if new_null_attr: # Setting null to True opts = model._meta params = (qn(opts.db_table), qn(f.column),) output.append(self.set_field_null(model, f, new_null_attr)) else: if initial is not None: output = [] if callable(initial): params = (qn(opts.db_table), qn(f.column), initial(), qn(f.column)) output.append('UPDATE %s SET %s = %s WHERE %s IS NULL;' % params) else: params = (qn(opts.db_table), qn(f.column), qn(f.column)) output.append(('UPDATE %s SET %s = %%s WHERE %s IS NULL;' % params, (initial,))) output.append(self.set_field_null(model, f, new_null_attr)) return output def change_max_length(self, model, field_name, new_max_length, initial=None): qn = self.connection.ops.quote_name opts = model._meta f = opts.get_field(field_name) f.max_length = new_max_length db_type = f.db_type(connection=self.connection) params = (qn(opts.db_table), qn(f.column), db_type, qn(f.column), db_type) return ['ALTER TABLE %s ALTER COLUMN %s TYPE %s USING CAST(%s as %s);' % params] def change_db_column(self, model, field_name, new_db_column, initial=None): opts = model._meta old_field = opts.get_field(field_name) new_field = copy.copy(old_field) new_field.column = new_db_column return self.rename_column(opts, old_field, new_field) def change_db_table(self, model, old_db_tablename, new_db_tablename): return self.rename_table(model, old_db_tablename, new_db_tablename) def change_db_index(self, model, field_name, new_db_index, initial=None): f = model._meta.get_field(field_name) f.db_index = new_db_index if new_db_index: return self.create_index(model, f) else: return self.drop_index(model, f) def change_unique(self, model, field_name, new_unique_value, initial=None): qn = self.connection.ops.quote_name opts = model._meta f = opts.get_field(field_name) constraint_name = truncate_name('%s_%s_key' % (opts.db_table, f.column), self.connection.ops.max_name_length()) if new_unique_value: params = (qn(opts.db_table), constraint_name, qn(f.column),) return ['ALTER TABLE %s ADD CONSTRAINT %s UNIQUE(%s);' % params] else: params = (qn(opts.db_table), constraint_name,) return ['ALTER TABLE %s DROP CONSTRAINT %s;' % params] django_evolution-0.6.7/django_evolution/db/__init__.py0000644000175000017500000000153411562107324023746 0ustar chipx86chipx8600000000000000# Establish the common EvolutionOperations instance, called evolver. from django.conf import settings from django.db import connection class EvolutionOperationsMulti(object): def __init__(self, db_name): try: from django.db import connections, router engine = settings.DATABASES[db_name]['ENGINE'].split('.')[-1] connection = connections[db_name] module_name = ['django_evolution.db', engine] module = __import__('.'.join(module_name), {}, {}, ['']) self.evolver = module.EvolutionOperations(connection) except: module_name = ['django_evolution.db',settings.DATABASE_ENGINE] module = __import__('.'.join(module_name),{},{},['']) self.evolver = module.EvolutionOperations() def get_evolver(self): return self.evolver django_evolution-0.6.7/django_evolution/management/0000755000175000017500000000000011741741024023361 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/management/commands/0000755000175000017500000000000011741741024025162 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/management/commands/evolve.py0000644000175000017500000003215211736023656027047 0ustar chipx86chipx8600000000000000from optparse import make_option import sys try: import cPickle as pickle except ImportError: import pickle as pickle from django.conf import settings from django.core.exceptions import ImproperlyConfigured from django.core.management.base import BaseCommand, CommandError from django.db.models import get_apps, get_app from django.db import connection, transaction from django_evolution import CannotSimulate, EvolutionException, is_multi_db from django_evolution.diff import Diff from django_evolution.evolve import get_unapplied_evolutions, get_mutations from django_evolution.models import Version, Evolution from django_evolution.mutations import AddField, DeleteApplication from django_evolution.signature import create_project_sig from django_evolution.utils import write_sql, execute_sql class Command(BaseCommand): option_list = BaseCommand.option_list + ( make_option( '--noinput', action='store_false', dest='interactive', default=True, help='Tells Django to NOT prompt the user for input of any kind.'), make_option( '--hint', action='store_true', dest='hint', default=False, help='Generate an evolution script that would update the app.'), make_option( '--purge', action='store_true', dest='purge', default=False, help='Generate evolutions to delete stale applications.'), make_option( '--sql', action='store_true', dest='compile_sql', default=False, help='Compile a Django evolution script into SQL.'), make_option( '-x', '--execute', action='store_true', dest='execute', default=False, help='Apply the evolution to the database.'), make_option( '--database', action='store', dest='database', help='Nominates a database to synchronize.'), ) if '--verbosity' not in [opt.get_opt_string() for opt in BaseCommand.option_list]: option_list += make_option('-v', '--verbosity', action='store', dest='verbosity', default='1', type='choice', choices=['0', '1', '2'], help='Verbosity level; 0=minimal output, 1=normal output, ' '2=all output'), help = 'Evolve the models in a Django project.' args = '' requires_model_validation = False def handle(self, *app_labels, **options): self.evolve(*app_labels, **options) def evolve(self, *app_labels, **options): verbosity = int(options['verbosity']) interactive = options['interactive'] execute = options['execute'] compile_sql = options['compile_sql'] hint = options['hint'] purge = options['purge'] database = options['database'] if not database and is_multi_db(): from django.db.utils import DEFAULT_DB_ALIAS database = DEFAULT_DB_ALIAS using_args = {} if is_multi_db(): using_args['using'] = database # Use the list of all apps, unless app labels are specified. if app_labels: if execute: raise CommandError('Cannot specify an application name when ' 'executing evolutions.') try: app_list = [get_app(app_label) for app_label in app_labels] except (ImproperlyConfigured, ImportError), e: raise CommandError("%s. Are you sure your INSTALLED_APPS " "setting is correct?" % e) else: app_list = get_apps() # Iterate over all applications running the mutations evolution_required = False simulated = True sql = [] new_evolutions = [] current_proj_sig = create_project_sig(database) current_signature = pickle.dumps(current_proj_sig) try: if is_multi_db(): latest_version = Version.objects.using(database).latest('when') else: latest_version = Version.objects.latest('when') database_sig = pickle.loads(str(latest_version.signature)) diff = Diff(database_sig, current_proj_sig) except Evolution.DoesNotExist: raise CommandError("Can't evolve yet. Need to set an " "evolution baseline.") try: for app in app_list: app_label = app.__name__.split('.')[-2] if hint: evolutions = [] hinted_evolution = diff.evolution() temp_mutations = hinted_evolution.get(app_label, []) else: evolutions = get_unapplied_evolutions(app, database) temp_mutations = get_mutations(app, evolutions, database) mutations = [ mutation for mutation in temp_mutations if mutation.is_mutable(app_label, database_sig, database) ] if mutations: app_sql = ['-- Evolve application %s' % app_label] evolution_required = True for mutation in mutations: # Only compile SQL if we want to show it if compile_sql or execute: app_sql.extend( mutation.mutate(app_label, database_sig, database)) # Now run the simulation, which will modify the # signatures try: mutation.simulate(app_label, database_sig, database) except CannotSimulate: simulated = False new_evolutions.extend( Evolution(app_label=app_label, label=label) for label in evolutions) if not execute: if compile_sql: write_sql(app_sql, database) else: print self.generate_hint(app, app_label, mutations) sql.extend(app_sql) else: if verbosity > 1: print 'Application %s is up to date' % app_label # Process the purged applications if requested to do so. if purge: if diff.deleted: evolution_required = True delete_app = DeleteApplication() purge_sql = [] for app_label in diff.deleted: if delete_app.is_mutable(app_label, database_sig, database): if compile_sql or execute: purge_sql.append('-- Purge application %s' % app_label) purge_sql.extend( delete_app.mutate(app_label, database_sig, database)) delete_app.simulate(app_label, database_sig, database) if not execute: if compile_sql: write_sql(purge_sql, database) else: print 'The following application(s) can be purged:' for app_label in diff.deleted: print ' ', app_label print sql.extend(purge_sql) else: if verbosity > 1: print 'No applications need to be purged.' except EvolutionException, e: raise CommandError(str(e)) if simulated: diff = Diff(database_sig, current_proj_sig) if not diff.is_empty(not purge): if hint: print self.style.ERROR( 'Your models contain changes that Django Evolution ' 'cannot resolve automatically.') print 'This is probably due to a currently unimplemented ' \ 'mutation type.' print 'You will need to manually construct a mutation ' \ 'to resolve the remaining changes.' else: print self.style.ERROR( 'The stored evolutions do not completely resolve ' 'all model changes.') print 'Run `./manage.py evolve --hint` to see a ' \ 'suggestion for the changes required.' print print 'The following are the changes that could ' \ 'not be resolved:' print diff raise CommandError('Your models contain changes that Django ' 'Evolution cannot resolve automatically.') else: print self.style.NOTICE( 'Evolution could not be simulated, possibly due to raw ' 'SQL mutations') if evolution_required: if execute: # Now that we've worked out the mutations required, # and we know they simulate OK, run the evolutions if interactive: confirm = raw_input(""" You have requested a database evolution. This will alter tables and data currently in the %r database, and may result in IRREVERSABLE DATA LOSS. Evolutions should be *thoroughly* reviewed prior to execution. Are you sure you want to execute the evolutions? Type 'yes' to continue, or 'no' to cancel: """ % database) else: confirm = 'yes' if is_multi_db(): from django.db import connections if confirm.lower() == 'yes': # Begin Transaction transaction.enter_transaction_management(**using_args) transaction.managed(flag=True, **using_args) if is_multi_db(): cursor = connections[database].cursor() else: cursor = connection.cursor() try: # Perform the SQL execute_sql(cursor, sql) # Now update the evolution table version = Version(signature=current_signature) version.save(**using_args) for evolution in new_evolutions: evolution.version = version evolution.save(**using_args) transaction.commit(**using_args) except Exception, ex: transaction.rollback(**using_args) raise CommandError('Error applying evolution: %s' % str(ex)) transaction.leave_transaction_management(**using_args) if verbosity > 0: print 'Evolution successful.' else: print self.style.ERROR('Evolution cancelled.') elif not compile_sql: if verbosity > 0: if simulated: print "Trial evolution successful." print "Run './manage.py evolve %s--execute' to apply evolution." % (hint and '--hint ' or '') elif verbosity > 0: print 'No evolution required.' def generate_hint(self, app, app_label, mutations): imports = set() project_imports = set() mutation_types = set() app_prefix = app.__name__.split('.')[0] for m in mutations: mutation_types.add(m.__class__.__name__) if isinstance(m, AddField): field_module = m.field_type.__module__ if field_module.startswith('django.db.models'): imports.add('from django.db import models') else: import_str = 'from %s import %s' % \ (field_module, m.field_type.__name__) if field_module.startswith(app_prefix): project_imports.add(import_str) else: imports.add(import_str) lines = [ '#----- Evolution for %s' % app_label, 'from django_evolution.mutations import %s' % ', '.join(sorted(mutation_types)), ] lines += sorted(imports) if project_imports: lines += [''] + sorted(project_imports) lines += [ '', '', 'MUTATIONS = [', ' ' + ',\n '.join(unicode(m) for m in mutations), ']', '#----------------------', ] return '\n'.join(lines) django_evolution-0.6.7/django_evolution/management/commands/__init__.py0000644000175000017500000000000011471453547027273 0ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/management/__init__.py0000644000175000017500000001151011741712460025472 0ustar chipx86chipx8600000000000000try: import cPickle as pickle except ImportError: import pickle as pickle from django.core.management.color import color_style from django.db.models import signals, get_apps, get_app from django_evolution import is_multi_db, models as django_evolution from django_evolution.evolve import get_evolution_sequence, get_unapplied_evolutions from django_evolution.signature import create_project_sig from django_evolution.diff import Diff style = color_style() def install_baseline(app, latest_version, using_args, verbosity): app_label = app.__name__.split('.')[-2] sequence = get_evolution_sequence(app) if sequence: if verbosity > 0: print 'Evolutions in %s baseline:' % app_label, \ ', '.join(sequence) for evo_label in sequence: evolution = django_evolution.Evolution(app_label=app_label, label=evo_label, version=latest_version) evolution.save(**using_args) def evolution(app, created_models, verbosity=1, **kwargs): """ A hook into syncdb's post_syncdb signal, that is used to notify the user if a model evolution is necessary. """ default_db = None if is_multi_db(): from django.db.utils import DEFAULT_DB_ALIAS default_db = DEFAULT_DB_ALIAS db = kwargs.get('db', default_db) proj_sig = create_project_sig(db) signature = pickle.dumps(proj_sig) using_args = {} if is_multi_db(): using_args['using'] = db try: if is_multi_db(): latest_version = \ django_evolution.Version.objects.using(db).latest('when') else: latest_version = django_evolution.Version.objects.latest('when') except django_evolution.Version.DoesNotExist: # We need to create a baseline version. if verbosity > 0: print "Installing baseline version" latest_version = django_evolution.Version(signature=signature) latest_version.save(**using_args) for a in get_apps(): install_baseline(a, latest_version, using_args, verbosity) unapplied = get_unapplied_evolutions(app, db) if unapplied: print style.NOTICE('There are unapplied evolutions for %s.' % app.__name__.split('.')[-2]) # Evolutions are checked over the entire project, so we only need to check # once. We do this check when Django Evolutions itself is synchronized. if app == django_evolution: old_proj_sig = pickle.loads(str(latest_version.signature)) # If any models or apps have been added, a baseline must be set # for those new models changed = False new_apps = [] for app_name, new_app_sig in proj_sig.items(): if app_name == '__version__': # Ignore the __version__ tag continue old_app_sig = old_proj_sig.get(app_name, None) if old_app_sig is None: # App has been added old_proj_sig[app_name] = proj_sig[app_name] new_apps.append(app_name) changed = True else: for model_name, new_model_sig in new_app_sig.items(): old_model_sig = old_app_sig.get(model_name, None) if old_model_sig is None: # Model has been added old_proj_sig[app_name][model_name] = \ proj_sig[app_name][model_name] changed = True if changed: if verbosity > 0: print "Adding baseline version for new models" latest_version = \ django_evolution.Version(signature=pickle.dumps(old_proj_sig)) latest_version.save(**using_args) for app_name in new_apps: app = get_app(app_name, True) if app: install_baseline(app, latest_version, using_args, verbosity) # TODO: Model introspection step goes here. # # If the current database state doesn't match the last # # saved signature (as reported by latest_version), # # then we need to update the Evolution table. # actual_sig = introspect_project_sig() # acutal = pickle.dumps(actual_sig) # if actual != latest_version.signature: # nudge = Version(signature=actual) # nudge.save() # latest_version = nudge diff = Diff(old_proj_sig, proj_sig) if not diff.is_empty(): print style.NOTICE( 'Project signature has changed - an evolution is required') if verbosity > 1: old_proj_sig = pickle.loads(str(latest_version.signature)) print diff signals.post_syncdb.connect(evolution) django_evolution-0.6.7/django_evolution/__init__.py0000644000175000017500000000260111741740645023365 0ustar chipx86chipx8600000000000000# The version of Django Evolution # # This is in the format of: # # (Major, Minor, Micro, alpha/beta/rc/final, Release Number, Released) # VERSION = (0, 6, 7, 'final', 0, True) def get_version_string(): version = '%s.%s' % (VERSION[0], VERSION[1]) if VERSION[2]: version += ".%s" % VERSION[2] if VERSION[3] != 'final': if VERSION[3] == 'rc': version += ' RC%s' % VERSION[4] else: version += ' %s %s' % (VERSION[3], VERSION[4]) if not is_release(): version += " (dev)" return version def get_package_version(): version = '%s.%s' % (VERSION[0], VERSION[1]) if VERSION[2]: version += ".%s" % VERSION[2] if VERSION[3] != 'final': version += '%s%s' % (VERSION[3], VERSION[4]) return version def is_release(): return VERSION[5] __version_info__ = VERSION[:-1] __version__ = get_package_version() class EvolutionException(Exception): def __init__(self,msg): self.msg = msg def __str__(self): return str(self.msg) class CannotSimulate(EvolutionException): pass class SimulationFailure(EvolutionException): pass class EvolutionNotImplementedError(EvolutionException, NotImplementedError): pass try: from django.db import connections __is_multi_db = True except: __is_multi_db = False def is_multi_db(): return __is_multi_db django_evolution-0.6.7/django_evolution/signature.py0000644000175000017500000000637111623666430023635 0ustar chipx86chipx8600000000000000from django.db.models import get_apps, get_models from django.db.models.fields.related import * from django.conf import global_settings from django.contrib.contenttypes import generic from django.utils.datastructures import SortedDict from django_evolution import is_multi_db if is_multi_db(): from django.db import router ATTRIBUTE_DEFAULTS = { # Common to all fields 'primary_key': False, 'max_length' : None, 'unique' : False, 'null' : False, 'db_index' : False, 'db_column' : None, 'db_tablespace' : global_settings.DEFAULT_TABLESPACE, 'rel': None, # Decimal Field 'max_digits' : None, 'decimal_places' : None, # ManyToManyField 'db_table': None } # r7790 modified the unique attribute of the meta model to be # a property that combined an underlying _unique attribute with # the primary key attribute. We need the underlying property, # but we don't want to affect old signatures (plus the # underscore is ugly :-). ATTRIBUTE_ALIASES = { 'unique': '_unique' } def create_field_sig(field): field_sig = { 'field_type': field.__class__, } for attrib in ATTRIBUTE_DEFAULTS.keys(): alias = ATTRIBUTE_ALIASES.get(attrib, attrib) if hasattr(field,alias): value = getattr(field,alias) if isinstance(field, ForeignKey): if attrib == 'db_index': default = True else: default = ATTRIBUTE_DEFAULTS[attrib] else: default = ATTRIBUTE_DEFAULTS[attrib] # only store non-default values if default != value: field_sig[attrib] = value rel = field_sig.pop('rel', None) if rel: field_sig['related_model'] = '.'.join([rel.to._meta.app_label, rel.to._meta.object_name]) return field_sig def create_model_sig(model): model_sig = { 'meta': { 'unique_together': model._meta.unique_together, 'db_tablespace': model._meta.db_tablespace, 'db_table': model._meta.db_table, 'pk_column': model._meta.pk.column, }, 'fields': {}, } for field in model._meta.local_fields + model._meta.local_many_to_many: # Special case - don't generate a signature for generic relations if not isinstance(field, generic.GenericRelation): model_sig['fields'][field.name] = create_field_sig(field) return model_sig def create_app_sig(app, database): """ Creates a dictionary representation of the models in a given app. Only those attributes that are interesting from a schema-evolution perspective are included. """ app_sig = SortedDict() for model in get_models(app): # only include those who want to be syncdb if not is_multi_db() or router.allow_syncdb(database, model): app_sig[model._meta.object_name] = create_model_sig(model) return app_sig def create_project_sig(database): """ Create a dictionary representation of the apps in a given project. """ proj_sig = { '__version__': 1, } for app in get_apps(): proj_sig[app.__name__.split('.')[-2]] = create_app_sig(app, database) return proj_sig django_evolution-0.6.7/django_evolution/utils.py0000644000175000017500000000150711562054306022763 0ustar chipx86chipx8600000000000000from django_evolution.db import EvolutionOperationsMulti def write_sql(sql, database): "Output a list of SQL statements, unrolling parameters as required" qp = EvolutionOperationsMulti(database).get_evolver().quote_sql_param for statement in sql: if isinstance(statement, tuple): print unicode(statement[0] % tuple(qp(s) for s in statement[1])) else: print unicode(statement) def execute_sql(cursor, sql): """ Execute a list of SQL statements on the provided cursor, unrolling parameters as required """ for statement in sql: if isinstance(statement, tuple): if not statement[0].startswith('--'): cursor.execute(*statement) else: if not statement.startswith('--'): cursor.execute(statement) django_evolution-0.6.7/django_evolution/tests/0000755000175000017500000000000011741741024022407 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/tests/delete_app.py0000644000175000017500000000564011471453547025102 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from datetime import datetime >>> from pprint import PrettyPrinter >>> from django.db import models >>> from django_evolution.mutations import AddField, DeleteField, DeleteApplication >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> from django_evolution import signature >>> from django_evolution import models as test_app >>> import copy >>> class AppDeleteAnchor1(models.Model): ... value = models.IntegerField() >>> class AppDeleteAnchor2(models.Model): ... value = models.IntegerField() ... class Meta: ... db_table = 'app_delete_custom_add_anchor_table' >>> class AppDeleteBaseModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... anchor_fk = models.ForeignKey(AppDeleteAnchor1) ... anchor_m2m = models.ManyToManyField(AppDeleteAnchor2) >>> class AppDeleteCustomTableModel(models.Model): ... value = models.IntegerField() ... alt_value = models.CharField(max_length=20) ... class Meta: ... db_table = 'app_delete_custom_table_name' # Store the base signatures, and populate the app cache >>> anchors = [('AppDeleteAnchor1', AppDeleteAnchor1), ('AppDeleteAnchor2',AppDeleteAnchor2)] >>> test_model = [('TestModel', AppDeleteBaseModel)] >>> custom_model = [('CustomTestModel', AppDeleteCustomTableModel)] >>> all_models = [] >>> all_models.extend(anchors) >>> all_models.extend(test_model) >>> all_models.extend(custom_model) >>> start = register_models(*all_models) >>> start_sig = test_proj_sig(*all_models) # Copy the base signature, and delete the tests app. >>> deleted_app_sig = copy.deepcopy(start_sig) >>> deleted_app_sig = deleted_app_sig.pop('tests') >>> d = Diff(start_sig, deleted_app_sig) >>> print d.deleted {'tests': ['AppDeleteAnchor1', 'AppDeleteAnchor2', 'TestModel', 'CustomTestModel']} >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> delete_app = DeleteApplication() >>> for app_label in d.deleted.keys(): ... test_sql.append(delete_app.mutate(app_label, test_sig)) ... delete_app.simulate(app_label, test_sig) >>> Diff(test_sig, deleted_app_sig).is_empty(ignore_apps=True) True >>> for sql_list in test_sql: ... for sql in sql_list: ... print sql %(DeleteApplicationWithoutDatabase)s >>> test_sql = [] >>> delete_app = DeleteApplication() >>> for app_label in d.deleted.keys(): ... test_sql.append(delete_app.mutate(app_label, test_sig, 'default')) ... delete_app.simulate(app_label, test_sig) >>> Diff(test_sig, deleted_app_sig).is_empty(ignore_apps=True) True >>> for sql_list in test_sql: ... for sql in sql_list: ... print sql %(DeleteApplication)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('delete_application') django_evolution-0.6.7/django_evolution/tests/rename_field.py0000644000175000017500000004535011562117541025404 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" # Rename a database column (done) # RenameField with a specified db table for a field other than a M2MField is allowed (but will be ignored) (done) # Rename a primary key database column (done) # Rename a foreign key database column (done) # Rename a database column with a non-default name to a default name (done) # Rename a database column with a non-default name to a different non-default name (done) # RenameField with a specified db column and db table is allowed (but one will be ignored) (done) # Rename a database column in a non-default table (done) # Rename an indexed database column (Redundant, Not explicitly tested) # Rename a database column with null constraints (Redundant, Not explicitly tested) # Rename a M2M database table (done) # RenameField with a specified db column for a M2MField is allowed (but will be ignored) (done) # Rename a M2M non-default database table to a default name (done) >>> from django.db import models >>> from django_evolution.mutations import RenameField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> from django_evolution import signature >>> from django_evolution import models as test_app >>> import copy >>> class RenameAnchor1(models.Model): ... value = models.IntegerField() >>> class RenameAnchor2(models.Model): ... value = models.IntegerField() ... class Meta: ... db_table = 'custom_rename_anchor_table' >>> class RenameAnchor3(models.Model): ... value = models.IntegerField() >>> class RenameBaseModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> class CustomRenameTableModel(models.Model): ... value = models.IntegerField() ... alt_value = models.CharField(max_length=20) ... class Meta: ... db_table = 'custom_rename_table_name' # Store the base signatures >>> anchors = [ ... ('RenameAnchor1', RenameAnchor1), ... ('RenameAnchor2', RenameAnchor2), ... ('RenameAnchor3',RenameAnchor3) ... ] >>> test_model = ('TestModel', RenameBaseModel) >>> custom_model = ('CustomTableModel', CustomRenameTableModel) >>> custom = register_models(custom_model) >>> custom_table_sig = test_proj_sig(custom_model) >>> start = register_models(*anchors) >>> start.update(register_models(test_model)) >>> start_sig = test_proj_sig(test_model, *anchors) # Rename a database column >>> class RenameColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... renamed_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', RenameColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.IntegerField, initial=<>)", "DeleteField('TestModel', 'int_field')"] >>> evolution = [RenameField('TestModel', 'int_field', 'renamed_field')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameColumnModel %(RenameColumnModel)s # RenameField with a specified db table for a field other than a M2MField is allowed (but will be ignored) (done) >>> class RenameColumnWithTableNameModel(models.Model): ... char_field = models.CharField(max_length=20) ... renamed_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameColumnWithTableNameModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameColumnWithTableNameModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.IntegerField, initial=<>)", "DeleteField('TestModel', 'int_field')"] >>> evolution = [RenameField('TestModel', 'int_field', 'renamed_field', db_table='ignored_db-table')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameColumnWithTableNameModel %(RenameColumnWithTableNameModel)s # Rename a primary key database column >>> class RenamePrimaryKeyColumnModel(models.Model): ... my_pk_id = models.AutoField(primary_key=True) ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenamePrimaryKeyColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenamePrimaryKeyColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'my_pk_id', models.AutoField, initial=<>, primary_key=True)", "DeleteField('TestModel', 'id')"] >>> evolution = [RenameField('TestModel', 'id', 'my_pk_id')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenamePrimaryKeyColumnModel %(RenamePrimaryKeyColumnModel)s # Rename a foreign key database column >>> class RenameForeignKeyColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... renamed_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameForeignKeyColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameForeignKeyColumnModel), *anchors) >>> start_sig = copy.deepcopy(start_sig) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.ForeignKey, initial=<>, related_model='tests.RenameAnchor1')", "DeleteField('TestModel', 'fk_field')"] >>> evolution = [RenameField('TestModel', 'fk_field', 'renamed_field')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True # FIXME!! This test doesn't work on Postgres #>>> execute_test_sql(start, end, test_sql) #RenameForeignKeyColumnModel #%(RenameForeignKeyColumnModel)s # Rename a database column with a non-default name >>> class RenameNonDefaultColumnNameModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... renamed_field = models.IntegerField() ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameNonDefaultColumnNameModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameNonDefaultColumnNameModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.IntegerField, initial=<>)", "DeleteField('TestModel', 'int_field_named')"] >>> evolution = [RenameField('TestModel', 'int_field_named', 'renamed_field')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameNonDefaultColumnNameModel %(RenameNonDefaultColumnNameModel)s # Rename a database column with a non-default name to a different non-default name >>> class RenameNonDefaultColumnNameToNonDefaultNameModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... renamed_field = models.IntegerField(db_column='non-default_column_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameNonDefaultColumnNameToNonDefaultNameModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameNonDefaultColumnNameToNonDefaultNameModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.IntegerField, initial=<>, db_column='non-default_column_name')", "DeleteField('TestModel', 'int_field_named')"] >>> evolution = [RenameField('TestModel', 'int_field_named', 'renamed_field', db_column='non-default_column_name')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameNonDefaultColumnNameToNonDefaultNameModel %(RenameNonDefaultColumnNameToNonDefaultNameModel)s # RenameField with a specified db column and db table is allowed (but one will be ignored) >>> class RenameNonDefaultColumnNameToNonDefaultNameAndTableModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... renamed_field = models.IntegerField(db_column='non-default_column_name2') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameNonDefaultColumnNameToNonDefaultNameAndTableModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameNonDefaultColumnNameToNonDefaultNameAndTableModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.IntegerField, initial=<>, db_column='non-default_column_name2')", "DeleteField('TestModel', 'int_field_named')"] >>> evolution = [RenameField('TestModel', 'int_field_named', 'renamed_field', db_column='non-default_column_name2', db_table='custom_ignored_db-table')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameNonDefaultColumnNameToNonDefaultNameAndTableModel %(RenameNonDefaultColumnNameToNonDefaultNameAndTableModel)s # Rename a database column in a non-default table # Rename a database column >>> class RenameColumnCustomTableModel(models.Model): ... renamed_field = models.IntegerField() ... alt_value = models.CharField(max_length=20) ... class Meta: ... db_table = 'custom_rename_table_name' >>> end = register_models(('CustomTableModel', RenameColumnCustomTableModel)) >>> end_sig = test_proj_sig(('CustomTableModel',RenameColumnCustomTableModel)) >>> d = Diff(custom_table_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('CustomTableModel', 'renamed_field', models.IntegerField, initial=<>)", "DeleteField('CustomTableModel', 'value')"] >>> evolution = [RenameField('CustomTableModel', 'value', 'renamed_field')] >>> test_sig = copy.deepcopy(custom_table_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(custom, end, test_sql) #RenameColumnCustomTableModel %(RenameColumnCustomTableModel)s # Rename a M2M database table >>> class RenameM2MTableModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... renamed_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameM2MTableModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameM2MTableModel), *anchors) >>> start_sig = copy.deepcopy(start_sig) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.ManyToManyField, related_model='tests.RenameAnchor2')", "DeleteField('TestModel', 'm2m_field')"] >>> evolution = [RenameField('TestModel', 'm2m_field', 'renamed_field')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameManyToManyTableModel %(RenameManyToManyTableModel)s # RenameField with a specified db column for a M2MField is allowed (but will be ignored) >>> class RenameM2MTableWithColumnNameModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... renamed_field = models.ManyToManyField(RenameAnchor2) ... m2m_field_named = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameM2MTableWithColumnNameModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameM2MTableWithColumnNameModel), *anchors) >>> start_sig = copy.deepcopy(start_sig) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.ManyToManyField, related_model='tests.RenameAnchor2')", "DeleteField('TestModel', 'm2m_field')"] >>> evolution = [RenameField('TestModel', 'm2m_field', 'renamed_field', db_column='ignored_db-column')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #RenameManyToManyTableWithColumnNameModel %(RenameManyToManyTableWithColumnNameModel)s # Rename a M2M non-default database table to a default name >>> class RenameNonDefaultM2MTableModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... int_field_named = models.IntegerField(db_column='custom_db_col_name') ... int_field_named_indexed = models.IntegerField(db_column='custom_db_col_name_indexed', db_index=True) ... fk_field = models.ForeignKey(RenameAnchor1) ... m2m_field = models.ManyToManyField(RenameAnchor2) ... renamed_field = models.ManyToManyField(RenameAnchor3, db_table='non-default_db_table') >>> end = register_models(('TestModel', RenameNonDefaultM2MTableModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',RenameNonDefaultM2MTableModel), *anchors) >>> start_sig = copy.deepcopy(start_sig) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'renamed_field', models.ManyToManyField, db_table='non-default_db_table', related_model='tests.RenameAnchor3')", "DeleteField('TestModel', 'm2m_field_named')"] >>> evolution = [RenameField('TestModel', 'm2m_field_named', 'renamed_field')] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() False # FIXME!! This test fails under Postgres #>>> execute_test_sql(start, end, test_sql) #RenameNonDefaultManyToManyTableModel #%(RenameNonDefaultManyToManyTableModel)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('rename_field') django_evolution-0.6.7/django_evolution/tests/multi_db.py0000644000175000017500000007173311562112505024570 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from django.db import models >>> from django_evolution.mutations import ChangeField >>> from django_evolution.tests.utils import test_proj_sig_multi, execute_test_sql, register_models_multi, deregister_models >>> from django_evolution.diff import Diff >>> import copy # Use Cases: # Setting a null constraint # -- without an initial value # -- with a null initial value # -- with a good initial value (constant) # -- with a good initial value (callable) # Removing a null constraint # Invoking a no-op change field # Changing the max_length of a character field # -- increasing the max_length # -- decreasing the max_length # Renaming a column # Changing the db_table of a many to many relationship # Adding an index # Removing an index # Adding a unique constraint # Removing a unique constraint # Redundant attributes. (Some attribute have changed, while others haven't but are specified anyway.) # Changing more than one attribute at a time (on different fields) # Changing more than one attribute at a time (on one field) ### This one is a bit dubious because changing the primary key of a model will mean ### that all referenced foreign keys and M2M relationships need to be updated # Adding a primary key constraint # Removing a Primary Key (Changing the primary key column) # Options that apply to all fields: # DB related options # null # db_column # db_index # db_tablespace (Ignored) # primary_key # unique # db_table (only for many to many relationships) # -- CharField # max_length # Non-DB options # blank # core # default # editable # help_text # radio_admin # unique_for_date # unique_for_month # unique_for_year # validator_list # I don't know yet # choices >>> class ChangeSequenceFieldInitial(object): ... def __init__(self, suffix): ... self.suffix = suffix ... ... def __call__(self): ... from django.db import connections ... qn = connections['db_multi'].ops.quote_name ... return qn('char_field') # Now, a useful test model we can use for evaluating diffs >>> class ChangeAnchor1(models.Model): ... value = models.IntegerField() >>> class ChangeBaseModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') # Store the base signatures >>> anchors = [('ChangeAnchor1', ChangeAnchor1)] >>> test_model = ('TestModel', ChangeBaseModel) >>> start = register_models_multi('tests', 'db_multi', *anchors) >>> start.update(register_models_multi('tests', 'db_multi', test_model)) >>> start_sig = test_proj_sig_multi('tests', test_model, *anchors) # Setting a null constraint without an initial value >>> class SetNotNullChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=False) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', SetNotNullChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', SetNotNullChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field1': Property 'null' has changed >>> print [str(e) for e in d.evolution()['tests']] # SetNotNullChangeModel ["ChangeField('TestModel', 'char_field1', initial=<>, null=False)"] # Without an initial value >>> evolution = [ChangeField('TestModel', 'char_field1', null=False)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot change column 'char_field1' on 'tests.TestModel' without a non-null initial value. # With a null initial value >>> evolution = [ChangeField('TestModel', 'char_field1', null=False, initial=None)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot change column 'char_field1' on 'tests.TestModel' without a non-null initial value. # With a good initial value (constant) >>> evolution = [ChangeField('TestModel', 'char_field1', null=False, initial="abc's xyz")] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # SetNotNullChangedModelWithConstant %(SetNotNullChangeModelWithConstant)s # With a good initial value (callable) >>> evolution = [ChangeField('TestModel', 'char_field1', null=False, initial=ChangeSequenceFieldInitial('SetNotNullChangeModel'))] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # SetNotNullChangeModelWithCallable %(SetNotNullChangeModelWithCallable)s # Removing a null constraint >>> class SetNullChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', SetNullChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', SetNullChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field2': Property 'null' has changed >>> print [str(e) for e in d.evolution()['tests']] # SetNullChangeModel ["ChangeField('TestModel', 'char_field2', initial=None, null=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # SetNullChangeModel %(SetNullChangeModel)s # Removing a null constraint >>> class NoOpChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', NoOpChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', NoOpChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d >>> evolution = [ChangeField('TestModel', 'char_field1', null=True)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # NoOpChangeModel %(NoOpChangeModel)s # Increasing the max_length of a character field >>> class IncreasingMaxLengthChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=45) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', IncreasingMaxLengthChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', IncreasingMaxLengthChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field': Property 'max_length' has changed >>> print [str(e) for e in d.evolution()['tests']] # IncreasingMaxLengthChangeModel ["ChangeField('TestModel', 'char_field', initial=None, max_length=45)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # IncreasingMaxLengthChangeModel %(IncreasingMaxLengthChangeModel)s # Decreasing the max_length of a character field >>> class DecreasingMaxLengthChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=1) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', DecreasingMaxLengthChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', DecreasingMaxLengthChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field': Property 'max_length' has changed >>> print [str(e) for e in d.evolution()['tests']] # DecreasingMaxLengthChangeModel ["ChangeField('TestModel', 'char_field', initial=None, max_length=1)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # DecreasingMaxLengthChangeModel %(DecreasingMaxLengthChangeModel)s # Renaming a column >>> class DBColumnChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='customised_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', DBColumnChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', DBColumnChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field': Property 'db_column' has changed >>> print [str(e) for e in d.evolution()['tests']] # DBColumnChangeModel ["ChangeField('TestModel', 'int_field', initial=None, db_column='customised_db_column')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # DBColumnChangeModel %(DBColumnChangeModel)s # Changing the db_table of a many to many relationship >>> class M2MDBTableChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='custom_m2m_db_table_name') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', M2MDBTableChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', M2MDBTableChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'm2m_field1': Property 'db_table' has changed >>> print [str(e) for e in d.evolution()['tests']] # M2MDBTableChangeModel ["ChangeField('TestModel', 'm2m_field1', initial=None, db_table='custom_m2m_db_table_name')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # M2MDBTableChangeModel %(M2MDBTableChangeModel)s # Adding an index >>> class AddDBIndexChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=True) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', AddDBIndexChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', AddDBIndexChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field2': Property 'db_index' has changed >>> print [str(e) for e in d.evolution()['tests']] # AddDBIndexChangeModel ["ChangeField('TestModel', 'int_field2', initial=None, db_index=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # AddDBIndexChangeModel %(AddDBIndexChangeModel)s # Removing an index >>> class RemoveDBIndexChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=False) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', RemoveDBIndexChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', RemoveDBIndexChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field1': Property 'db_index' has changed >>> print [str(e) for e in d.evolution()['tests']] # RemoveDBIndexChangeModel ["ChangeField('TestModel', 'int_field1', initial=None, db_index=False)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # RemoveDBIndexChangeModel %(RemoveDBIndexChangeModel)s # Adding a unique constraint >>> class AddUniqueChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=True) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', AddUniqueChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', AddUniqueChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field4': Property 'unique' has changed >>> print [str(e) for e in d.evolution()['tests']] # AddUniqueChangeModel ["ChangeField('TestModel', 'int_field4', initial=None, unique=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # AddUniqueChangeModel %(AddUniqueChangeModel)s # Remove a unique constraint >>> class RemoveUniqueChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=False) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', RemoveUniqueChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', RemoveUniqueChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field3': Property 'unique' has changed >>> print [str(e) for e in d.evolution()['tests']] # RemoveUniqueChangeModel ["ChangeField('TestModel', 'int_field3', initial=None, unique=False)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # RemoveUniqueChangeModel %(RemoveUniqueChangeModel)s # Changing more than one attribute at a time (on different fields) >>> class MultiAttrChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column2') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=35) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', MultiAttrChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', MultiAttrChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field2': Property 'null' has changed In field 'int_field': Property 'db_column' has changed In field 'char_field': Property 'max_length' has changed >>> print [str(e) for e in d.evolution()['tests']] # MultiAttrChangeModel ["ChangeField('TestModel', 'char_field2', initial=None, null=True)", "ChangeField('TestModel', 'int_field', initial=None, db_column='custom_db_column2')", "ChangeField('TestModel', 'char_field', initial=None, max_length=35)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # MultiAttrChangeModel %(MultiAttrChangeModel)s # Changing more than one attribute at a time (on one fields) >>> class MultiAttrSingleFieldChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=35, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', MultiAttrSingleFieldChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', MultiAttrSingleFieldChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field2': Property 'max_length' has changed Property 'null' has changed >>> print [str(e) for e in d.evolution()['tests']] # MultiAttrSingleFieldChangeModel ["ChangeField('TestModel', 'char_field2', initial=None, max_length=35, null=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # MultiAttrSingleFieldChangeModel %(MultiAttrSingleFieldChangeModel)s # Redundant attributes. (Some attribute have changed, while others haven't but are specified anyway.) >>> class RedundantAttrsChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column3') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=35) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', RedundantAttrsChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', RedundantAttrsChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> evolutions = [ ... ChangeField("TestModel", "char_field2", initial=None, null=True, max_length=30), ... ChangeField("TestModel", "int_field", initial=None, db_column="custom_db_column3", primary_key=False, unique=False, db_index=False), ... ChangeField("TestModel", "char_field", initial=None, max_length=35), ... ] >>> for mutation in evolutions: ... test_sql.extend(mutation.mutate('tests', test_sig, database='db_multi')) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, database='db_multi', app_label='tests') # RedundantAttrsChangeModel %(RedundantAttrsChangeModel)s # Change field type to another type with same internal_type >>> class MyIntegerField(models.IntegerField): ... def get_internal_type(self): ... return 'IntegerField' >>> class MinorFieldTypeChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = MyIntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='multi_db_non-default_m2m_table') >>> end = register_models_multi('tests', 'db_multi', ('TestModel', MinorFieldTypeChangeModel), *anchors) >>> end_sig = test_proj_sig_multi('tests', ('TestModel', MinorFieldTypeChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> d.is_empty() True # Clean up after the applications that were installed >>> deregister_models('tests') """ % test_sql_mapping('multi_db', db_name='db_multi') django_evolution-0.6.7/django_evolution/tests/generics.py0000644000175000017500000000552011471453547024574 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from django.db import models >>> from django_evolution.mutations import DeleteField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> from django.contrib.contenttypes import generic >>> from django.contrib.contenttypes.models import ContentType >>> import copy # Now, a useful test model we can use for evaluating diffs >>> class GenericAnchor(models.Model): ... value = models.IntegerField() ... # Host a generic key here, too ... content_type = models.ForeignKey(ContentType) ... object_id = models.PositiveIntegerField(db_index=True) ... content_object = generic.GenericForeignKey('content_type','object_id') >>> class GenericBaseModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... # Plus a generic foreign key - the Generic itself should be ignored ... content_type = models.ForeignKey(ContentType) ... object_id = models.PositiveIntegerField(db_index=True) ... content_object = generic.GenericForeignKey('content_type','object_id') ... # Plus a generic relation, which should be ignored ... generic = generic.GenericRelation(GenericAnchor) # Store the base signatures >>> anchor = ('Anchor', GenericAnchor) >>> content_type = ('contenttypes.ContentType', ContentType) >>> test_model = ('TestModel', GenericBaseModel) >>> start = register_models(anchor) >>> start.update(register_models(test_model)) >>> start_sig = test_proj_sig(test_model, content_type, anchor) # Delete a column >>> class DeleteColumnModel(models.Model): ... int_field = models.IntegerField() ... # Plus a generic foreign key - the Generic itself should be ignored ... content_type = models.ForeignKey(ContentType) ... object_id = models.PositiveIntegerField(db_index=True) ... content_object = generic.GenericForeignKey('content_type','object_id') ... # Plus a generic relation, which should be ignored ... generic = generic.GenericRelation(GenericAnchor) >>> end = register_models(('TestModel', DeleteColumnModel), anchor) >>> end_sig = test_proj_sig(('TestModel', DeleteColumnModel), content_type, anchor) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'char_field')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #DeleteColumnModel %(DeleteColumnModel)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('generics') django_evolution-0.6.7/django_evolution/tests/models.py0000644000175000017500000000032111471453547024252 0ustar chipx86chipx8600000000000000# This module is used as a placeholder for the registration of test models. # It is intentionally empty; individual tests create and register models # that will appear to Django as if they are in this module. django_evolution-0.6.7/django_evolution/tests/inheritance.py0000644000175000017500000000540411471453547025267 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from datetime import datetime >>> from django.db import models >>> from django_evolution.mutations import AddField, DeleteField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> from django_evolution import signature >>> from django_evolution import models as test_app >>> import copy >>> class ParentModel(models.Model): ... parent_field = models.CharField(max_length=20) ... other_field = models.IntegerField() >>> class ChildModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() # Store the base signatures >>> parent_model = ('ParentModel', ParentModel) >>> parent = register_models(parent_model) >>> parent_table_sig = test_proj_sig(parent_model) >>> test_model = ('ChildModel', ChildModel) >>> start = register_models(test_model) >>> start_sig = test_proj_sig(test_model, parent_model) # Add field to child model >>> class AddToChildModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField(default=42) >>> end = register_models(('ChildModel', AddToChildModel), parent_model) >>> end_sig = test_proj_sig(('ChildModel',AddToChildModel), parent_model) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] # AddToChildModel ["AddField('ChildModel', 'added_field', models.IntegerField, initial=42)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # AddToChildModel %(AddToChildModel)s # Delete field from child model >>> class AddToChildModel(models.Model): ... char_field = models.CharField(max_length=20) >>> end = register_models(('ChildModel', AddToChildModel), parent_model) >>> end_sig = test_proj_sig(('ChildModel',AddToChildModel), parent_model) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] # DeleteFromChildModel ["DeleteField('ChildModel', 'int_field')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # DeleteFromChildModel %(DeleteFromChildModel)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('inheritance')django_evolution-0.6.7/django_evolution/tests/change_field.py0000644000175000017500000006651211471453547025375 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from django.db import models >>> from django_evolution.mutations import ChangeField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> import copy # Use Cases: # Setting a null constraint # -- without an initial value # -- with a null initial value # -- with a good initial value (constant) # -- with a good initial value (callable) # Removing a null constraint # Invoking a no-op change field # Changing the max_length of a character field # -- increasing the max_length # -- decreasing the max_length # Renaming a column # Changing the db_table of a many to many relationship # Adding an index # Removing an index # Adding a unique constraint # Removing a unique constraint # Redundant attributes. (Some attribute have changed, while others haven't but are specified anyway.) # Changing more than one attribute at a time (on different fields) # Changing more than one attribute at a time (on one field) ### This one is a bit dubious because changing the primary key of a model will mean ### that all referenced foreign keys and M2M relationships need to be updated # Adding a primary key constraint # Removing a Primary Key (Changing the primary key column) # Options that apply to all fields: # DB related options # null # db_column # db_index # db_tablespace (Ignored) # primary_key # unique # db_table (only for many to many relationships) # -- CharField # max_length # Non-DB options # blank # core # default # editable # help_text # radio_admin # unique_for_date # unique_for_month # unique_for_year # validator_list # I don't know yet # choices >>> class ChangeSequenceFieldInitial(object): ... def __init__(self, suffix): ... self.suffix = suffix ... ... def __call__(self): ... from django.db import connection ... qn = connection.ops.quote_name ... return qn('char_field') # Now, a useful test model we can use for evaluating diffs >>> class ChangeAnchor1(models.Model): ... value = models.IntegerField() >>> class ChangeBaseModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') # Store the base signatures >>> anchors = [('ChangeAnchor1', ChangeAnchor1)] >>> test_model = ('TestModel', ChangeBaseModel) >>> start = register_models(*anchors) >>> start.update(register_models(test_model)) >>> start_sig = test_proj_sig(test_model, *anchors) # Setting a null constraint without an initial value >>> class SetNotNullChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=False) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', SetNotNullChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', SetNotNullChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field1': Property 'null' has changed >>> print [str(e) for e in d.evolution()['tests']] # SetNotNullChangeModel ["ChangeField('TestModel', 'char_field1', initial=<>, null=False)"] # Without an initial value >>> evolution = [ChangeField('TestModel', 'char_field1', null=False)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot change column 'char_field1' on 'tests.TestModel' without a non-null initial value. # With a null initial value >>> evolution = [ChangeField('TestModel', 'char_field1', null=False, initial=None)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot change column 'char_field1' on 'tests.TestModel' without a non-null initial value. # With a good initial value (constant) >>> evolution = [ChangeField('TestModel', 'char_field1', null=False, initial="abc's xyz")] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # SetNotNullChangedModelWithConstant %(SetNotNullChangeModelWithConstant)s # With a good initial value (callable) >>> evolution = [ChangeField('TestModel', 'char_field1', null=False, initial=ChangeSequenceFieldInitial('SetNotNullChangeModel'))] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) %(SetNotNullChangeModelWithCallable)s # Removing a null constraint >>> class SetNullChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', SetNullChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', SetNullChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field2': Property 'null' has changed >>> print [str(e) for e in d.evolution()['tests']] # SetNullChangeModel ["ChangeField('TestModel', 'char_field2', initial=None, null=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # SetNullChangeModel %(SetNullChangeModel)s # Removing a null constraint >>> class NoOpChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', NoOpChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', NoOpChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d >>> evolution = [ChangeField('TestModel', 'char_field1', null=True)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # NoOpChangeModel %(NoOpChangeModel)s # Increasing the max_length of a character field >>> class IncreasingMaxLengthChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=45) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', IncreasingMaxLengthChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', IncreasingMaxLengthChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field': Property 'max_length' has changed >>> print [str(e) for e in d.evolution()['tests']] # IncreasingMaxLengthChangeModel ["ChangeField('TestModel', 'char_field', initial=None, max_length=45)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # IncreasingMaxLengthChangeModel %(IncreasingMaxLengthChangeModel)s # Decreasing the max_length of a character field >>> class DecreasingMaxLengthChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=1) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', DecreasingMaxLengthChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', DecreasingMaxLengthChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field': Property 'max_length' has changed >>> print [str(e) for e in d.evolution()['tests']] # DecreasingMaxLengthChangeModel ["ChangeField('TestModel', 'char_field', initial=None, max_length=1)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # DecreasingMaxLengthChangeModel %(DecreasingMaxLengthChangeModel)s # Renaming a column >>> class DBColumnChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='customised_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', DBColumnChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', DBColumnChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field': Property 'db_column' has changed >>> print [str(e) for e in d.evolution()['tests']] # DBColumnChangeModel ["ChangeField('TestModel', 'int_field', initial=None, db_column='customised_db_column')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # DBColumnChangeModel %(DBColumnChangeModel)s # Changing the db_table of a many to many relationship >>> class M2MDBTableChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='custom_m2m_db_table_name') >>> end = register_models(('TestModel', M2MDBTableChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', M2MDBTableChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'm2m_field1': Property 'db_table' has changed >>> print [str(e) for e in d.evolution()['tests']] # M2MDBTableChangeModel ["ChangeField('TestModel', 'm2m_field1', initial=None, db_table='custom_m2m_db_table_name')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # M2MDBTableChangeModel %(M2MDBTableChangeModel)s # Adding an index >>> class AddDBIndexChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=True) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', AddDBIndexChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', AddDBIndexChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field2': Property 'db_index' has changed >>> print [str(e) for e in d.evolution()['tests']] # AddDBIndexChangeModel ["ChangeField('TestModel', 'int_field2', initial=None, db_index=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # AddDBIndexChangeModel %(AddDBIndexChangeModel)s # Removing an index >>> class RemoveDBIndexChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=False) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', RemoveDBIndexChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', RemoveDBIndexChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field1': Property 'db_index' has changed >>> print [str(e) for e in d.evolution()['tests']] # RemoveDBIndexChangeModel ["ChangeField('TestModel', 'int_field1', initial=None, db_index=False)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # RemoveDBIndexChangeModel %(RemoveDBIndexChangeModel)s # Adding a unique constraint >>> class AddUniqueChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=True) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', AddUniqueChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', AddUniqueChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field4': Property 'unique' has changed >>> print [str(e) for e in d.evolution()['tests']] # AddUniqueChangeModel ["ChangeField('TestModel', 'int_field4', initial=None, unique=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # AddUniqueChangeModel %(AddUniqueChangeModel)s # Remove a unique constraint >>> class RemoveUniqueChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=False) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', RemoveUniqueChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', RemoveUniqueChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'int_field3': Property 'unique' has changed >>> print [str(e) for e in d.evolution()['tests']] # RemoveUniqueChangeModel ["ChangeField('TestModel', 'int_field3', initial=None, unique=False)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # RemoveUniqueChangeModel %(RemoveUniqueChangeModel)s # Changing more than one attribute at a time (on different fields) >>> class MultiAttrChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column2') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=35) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', MultiAttrChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', MultiAttrChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field2': Property 'null' has changed In field 'int_field': Property 'db_column' has changed In field 'char_field': Property 'max_length' has changed >>> print [str(e) for e in d.evolution()['tests']] # MultiAttrChangeModel ["ChangeField('TestModel', 'char_field2', initial=None, null=True)", "ChangeField('TestModel', 'int_field', initial=None, db_column='custom_db_column2')", "ChangeField('TestModel', 'char_field', initial=None, max_length=35)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # MultiAttrChangeModel %(MultiAttrChangeModel)s # Changing more than one attribute at a time (on one fields) >>> class MultiAttrSingleFieldChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=35, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', MultiAttrSingleFieldChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', MultiAttrSingleFieldChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print d In model tests.TestModel: In field 'char_field2': Property 'max_length' has changed Property 'null' has changed >>> print [str(e) for e in d.evolution()['tests']] # MultiAttrSingleFieldChangeModel ["ChangeField('TestModel', 'char_field2', initial=None, max_length=35, null=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # MultiAttrSingleFieldChangeModel %(MultiAttrSingleFieldChangeModel)s # Redundant attributes. (Some attribute have changed, while others haven't but are specified anyway.) >>> class RedundantAttrsChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column3') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = models.IntegerField(unique=False) ... char_field = models.CharField(max_length=35) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=True) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', RedundantAttrsChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', RedundantAttrsChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> evolutions = [ ... ChangeField("TestModel", "char_field2", initial=None, null=True, max_length=30), ... ChangeField("TestModel", "int_field", initial=None, db_column="custom_db_column3", primary_key=False, unique=False, db_index=False), ... ChangeField("TestModel", "char_field", initial=None, max_length=35), ... ] >>> for mutation in evolutions: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # RedundantAttrsChangeModel %(RedundantAttrsChangeModel)s # Change field type to another type with same internal_type >>> class MyIntegerField(models.IntegerField): ... def get_internal_type(self): ... return 'IntegerField' >>> class MinorFieldTypeChangeModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... alt_pk = models.IntegerField() ... int_field = models.IntegerField(db_column='custom_db_column') ... int_field1 = models.IntegerField(db_index=True) ... int_field2 = models.IntegerField(db_index=False) ... int_field3 = models.IntegerField(unique=True) ... int_field4 = MyIntegerField(unique=False) ... char_field = models.CharField(max_length=20) ... char_field1 = models.CharField(max_length=25, null=True) ... char_field2 = models.CharField(max_length=30, null=False) ... m2m_field1 = models.ManyToManyField(ChangeAnchor1, db_table='change_field_non-default_m2m_table') >>> end = register_models(('TestModel', MinorFieldTypeChangeModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', MinorFieldTypeChangeModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> d.is_empty() True # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('change_field') django_evolution-0.6.7/django_evolution/tests/sql_mutation.py0000644000175000017500000000611411471453547025514 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from django.db import models >>> from django_evolution.mutations import SQLMutation >>> from django.db import models >>> from django_evolution.mutations import AddField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> from django_evolution import signature >>> from django_evolution import models as test_app >>> import copy >>> class SQLBaseModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() # Store the base signatures >>> start = register_models(('TestModel', SQLBaseModel)) >>> start_sig = test_proj_sig(('TestModel', SQLBaseModel)) # Add 3 Fields resulting in new database columns. >>> class SQLMutationModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field1 = models.IntegerField(null=True) ... added_field2 = models.IntegerField(null=True) ... added_field3 = models.IntegerField(null=True) >>> end = register_models(('TestModel', SQLMutationModel)) >>> end_sig = test_proj_sig(('TestModel',SQLMutationModel)) >>> d = Diff(start_sig, end_sig) # Add the fields using SQLMutations >>> sequence = [ ... 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;', ... ])] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in sequence: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... CannotSimulate: Cannot simulate SQLMutations # Redefine the sequence with update 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 ... } >>> sequence = %(SQLMutationSequence)s >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in sequence: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #SQLMutationOutput %(SQLMutationOutput)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('sql_mutation') django_evolution-0.6.7/django_evolution/tests/db/0000755000175000017500000000000011741741024022774 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/tests/db/postgresql_psycopg2.py0000644000175000017500000000010611471453547027406 0ustar chipx86chipx8600000000000000# Psycopg2 behaviour is identical to Psycopg1 from postgresql import *django_evolution-0.6.7/django_evolution/tests/db/mysql_old.py0000644000175000017500000000010411471453547025356 0ustar chipx86chipx8600000000000000# MySQL_old behaviour is identical to mysql base from mysql import *django_evolution-0.6.7/django_evolution/tests/db/sqlite3.py0000644000175000017500000025721411471453547024757 0ustar chipx86chipx8600000000000000from django.db import connection from django.db.models.options import Options autocreate_through_tables = hasattr(Options({}), 'auto_created') # This is not a great check, but it's from the same version as auto-created # tables (Django 1.2), so we use it. digest_index_names = hasattr(Options({}), 'auto_created') def generate_index_name(table, column): if digest_index_names: column = connection.creation._digest(column) return '%s_%s' % (table, column) add_field = { 'AddNonNullNonCallableColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = 1;', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddNonNullCallableColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = "int_field";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddNullColumnWithInitialColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = 1;', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddStringColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" varchar(10) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = \'abc\\\'s xyz\';', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" varchar(10) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddDateColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" datetime NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = 2007-12-13 16:42:00;', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" datetime NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddDefaultColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = 42;', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddEmptyStringDefaultColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" varchar(20) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = \'\';', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" varchar(20) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddNullColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'NonDefaultColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "non-default_column" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "non-default_column" integer NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "non-default_column") SELECT "int_field", "id", "char_field", "non-default_column" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddColumnCustomTableModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("id" integer NULL UNIQUE PRIMARY KEY, "value" integer NULL, "alt_value" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("id", "value", "alt_value") SELECT "id", "value", "alt_value" FROM "custom_table_name";', 'DROP TABLE "custom_table_name";', 'CREATE TABLE "custom_table_name"("id" integer NOT NULL UNIQUE PRIMARY KEY, "value" integer NOT NULL, "alt_value" varchar(20) NOT NULL, "added_field" integer NULL);', 'INSERT INTO "custom_table_name" ("id", "value", "alt_value", "added_field") SELECT "id", "value", "alt_value", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddIndexedColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "add_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "add_field" integer NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "add_field") SELECT "int_field", "id", "char_field", "add_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE INDEX "%s" ON "tests_testmodel" ("add_field");' % generate_index_name('tests_testmodel', 'add_field'), ]), 'AddUniqueColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL UNIQUE);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NULL UNIQUE);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddUniqueIndexedModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL UNIQUE);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NULL UNIQUE);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'AddForeignKeyModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field_id" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field_id" integer NULL);', 'INSERT INTO "tests_testmodel" ("int_field", "id", "char_field", "added_field_id") SELECT "int_field", "id", "char_field", "added_field_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE INDEX "%s" ON "tests_testmodel" ("added_field_id");' % generate_index_name('tests_testmodel', 'added_field_id'), ]), } if autocreate_through_tables: add_field.update({ 'AddManyToManyDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" integer NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL,', ' "addanchor1_id" integer NOT NULL,', ' UNIQUE ("testmodel_id", "addanchor1_id")', ')', ';', ]), 'AddManyToManyNonDefaultDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" integer NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL,', ' "addanchor2_id" integer NOT NULL,', ' UNIQUE ("testmodel_id", "addanchor2_id")', ')', ';', ]), 'AddManyToManySelf': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" integer NOT NULL PRIMARY KEY,', ' "from_testmodel_id" integer NOT NULL,', ' "to_testmodel_id" integer NOT NULL,', ' UNIQUE ("from_testmodel_id", "to_testmodel_id")', ')', ';', ]), }) else: add_field.update({ 'AddManyToManyDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" integer NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id"),', ' "addanchor1_id" integer NOT NULL REFERENCES "tests_addanchor1" ("id"),', ' UNIQUE ("testmodel_id", "addanchor1_id")', ')', ';', ]), 'AddManyToManyNonDefaultDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" integer NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id"),', ' "addanchor2_id" integer NOT NULL REFERENCES "custom_add_anchor_table" ("id"),', ' UNIQUE ("testmodel_id", "addanchor2_id")', ')', ';', ]), 'AddManyToManySelf': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" integer NOT NULL PRIMARY KEY,', ' "from_testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id"),', ' "to_testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id"),', ' UNIQUE ("from_testmodel_id", "to_testmodel_id")', ')', ';', ]), }) delete_field = { 'DefaultNamedColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("non-default_db_column" integer NULL, "int_field3" integer NULL UNIQUE, "fk_field1_id" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("non-default_db_column", "int_field3", "fk_field1_id", "char_field", "my_id") SELECT "non-default_db_column", "int_field3", "fk_field1_id", "char_field", "my_id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("non-default_db_column" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "fk_field1_id" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field1_id");' % generate_index_name('tests_testmodel', 'fk_field1_id'), 'INSERT INTO "tests_testmodel" ("non-default_db_column", "int_field3", "fk_field1_id", "char_field", "my_id") SELECT "non-default_db_column", "int_field3", "fk_field1_id", "char_field", "my_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'NonDefaultNamedColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "int_field3" integer NULL UNIQUE, "fk_field1_id" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("int_field", "int_field3", "fk_field1_id", "char_field", "my_id") SELECT "int_field", "int_field3", "fk_field1_id", "char_field", "my_id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "fk_field1_id" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field1_id");' % generate_index_name('tests_testmodel', 'fk_field1_id'), 'INSERT INTO "tests_testmodel" ("int_field", "int_field3", "fk_field1_id", "char_field", "my_id") SELECT "int_field", "int_field3", "fk_field1_id", "char_field", "my_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'ConstrainedColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "non-default_db_column" integer NULL, "fk_field1_id" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("int_field", "non-default_db_column", "fk_field1_id", "char_field", "my_id") SELECT "int_field", "non-default_db_column", "fk_field1_id", "char_field", "my_id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "non-default_db_column" integer NOT NULL, "fk_field1_id" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field1_id");' % generate_index_name('tests_testmodel', 'fk_field1_id'), 'INSERT INTO "tests_testmodel" ("int_field", "non-default_db_column", "fk_field1_id", "char_field", "my_id") SELECT "int_field", "non-default_db_column", "fk_field1_id", "char_field", "my_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'DefaultManyToManyModel': 'DROP TABLE "tests_testmodel_m2m_field1";', 'NonDefaultManyToManyModel': 'DROP TABLE "non-default_m2m_table";', 'DeleteForeignKeyModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "non-default_db_column" integer NULL, "int_field3" integer NULL UNIQUE, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("int_field", "non-default_db_column", "int_field3", "char_field", "my_id") SELECT "int_field", "non-default_db_column", "int_field3", "char_field", "my_id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "non-default_db_column" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "tests_testmodel" ("int_field", "non-default_db_column", "int_field3", "char_field", "my_id") SELECT "int_field", "non-default_db_column", "int_field3", "char_field", "my_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'DeleteColumnCustomTableModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("id" integer NULL UNIQUE PRIMARY KEY, "alt_value" varchar(20) NULL);', 'INSERT INTO "TEMP_TABLE" ("id", "alt_value") SELECT "id", "alt_value" FROM "custom_table_name";', 'DROP TABLE "custom_table_name";', 'CREATE TABLE "custom_table_name"("id" integer NOT NULL UNIQUE PRIMARY KEY, "alt_value" varchar(20) NOT NULL);', 'INSERT INTO "custom_table_name" ("id", "alt_value") SELECT "id", "alt_value" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), } change_field = { "SetNotNullChangeModelWithConstant": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "char_field1" = \'abc\\\'s xyz\';', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NOT NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "SetNotNullChangeModelWithCallable": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "char_field1" = "char_field";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NOT NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "SetNullChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "NoOpChangeModel": '', "IncreasingMaxLengthChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(45) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(45) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "DecreasingMaxLengthChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(1) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(1) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "DBColumnChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "customised_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "customised_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "customised_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field1");' % generate_index_name('tests_testmodel', 'int_field1'), 'INSERT INTO "tests_testmodel" ("int_field4", "customised_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "customised_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "M2MDBTableChangeModel": 'ALTER TABLE "change_field_non-default_m2m_table" RENAME TO "custom_m2m_db_table_name";', "AddDBIndexChangeModel": 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field2");' % generate_index_name('tests_testmodel', 'int_field2'), "RemoveDBIndexChangeModel": 'DROP INDEX "%s";' % generate_index_name('tests_testmodel', 'int_field1'), "AddUniqueChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL UNIQUE, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL UNIQUE, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "RemoveUniqueChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "MultiAttrChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column2" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column2" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field1");' % generate_index_name('tests_testmodel', 'int_field1'), 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column2" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(35) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column2" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(35) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "MultiAttrSingleFieldChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "RedundantAttrsChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column3" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column3" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field1");' % generate_index_name('tests_testmodel', 'int_field1'), 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column3" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(35) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column3" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(35) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), } multi_db = { "SetNotNullChangeModelWithConstant": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "char_field1" = \'abc\\\'s xyz\';', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NOT NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "SetNotNullChangeModelWithCallable": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'UPDATE "TEMP_TABLE" SET "char_field1" = "char_field";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NOT NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "SetNullChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "NoOpChangeModel": '', "IncreasingMaxLengthChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(45) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(45) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "DecreasingMaxLengthChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(1) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(1) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "DBColumnChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "customised_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "customised_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "customised_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field1");' % generate_index_name('tests_testmodel', 'int_field1'), 'INSERT INTO "tests_testmodel" ("int_field4", "customised_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "customised_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "M2MDBTableChangeModel": 'ALTER TABLE "multi_db_non-default_m2m_table" RENAME TO "custom_m2m_db_table_name";', "AddDBIndexChangeModel": 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field2");' % generate_index_name('tests_testmodel', 'int_field2'), "RemoveDBIndexChangeModel": 'DROP INDEX "%s";' % generate_index_name('tests_testmodel', 'int_field1'), "AddUniqueChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL UNIQUE, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL UNIQUE, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "RemoveUniqueChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "MultiAttrChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column2" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column2" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field1");' % generate_index_name('tests_testmodel', 'int_field1'), 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column2" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(35) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column2" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(35) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column2", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "MultiAttrSingleFieldChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NOT NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(35) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), "RedundantAttrsChangeModel": '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column3" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(20) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column3" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(20) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("int_field1");' % generate_index_name('tests_testmodel', 'int_field1'), 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field4" integer NULL, "custom_db_column3" integer NULL, "int_field1" integer NULL, "int_field2" integer NULL, "int_field3" integer NULL UNIQUE, "alt_pk" integer NULL, "char_field" varchar(35) NULL, "my_id" integer NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field4" integer NOT NULL, "custom_db_column3" integer NOT NULL, "int_field1" integer NOT NULL, "int_field2" integer NOT NULL, "int_field3" integer NOT NULL UNIQUE, "alt_pk" integer NOT NULL, "char_field" varchar(35) NOT NULL, "my_id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field1" varchar(25) NULL, "char_field2" varchar(30) NULL);', 'INSERT INTO "tests_testmodel" ("int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2") SELECT "int_field4", "custom_db_column3", "int_field1", "int_field2", "int_field3", "alt_pk", "char_field", "my_id", "char_field1", "char_field2" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), } delete_model = { 'BasicModel': 'DROP TABLE "tests_basicmodel";', 'BasicWithM2MModel': '\n'.join([ 'DROP TABLE "tests_basicwithm2mmodel_m2m";', 'DROP TABLE "tests_basicwithm2mmodel";' ]), 'CustomTableModel': 'DROP TABLE "custom_table_name";', 'CustomTableWithM2MModel': '\n'.join([ 'DROP TABLE "another_custom_table_name_m2m";', 'DROP TABLE "another_custom_table_name";' ]), } delete_application = { 'DeleteApplication': '\n'.join([ 'DROP TABLE "tests_appdeleteanchor1";', 'DROP TABLE "app_delete_custom_add_anchor_table";', 'DROP TABLE "tests_testmodel_anchor_m2m";', 'DROP TABLE "tests_testmodel";', 'DROP TABLE "app_delete_custom_table_name";', ]), 'DeleteApplicationWithoutDatabase': "", } rename_field = { 'RenameColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("renamed_field" integer NULL, "char_field" varchar(20) NULL, "custom_db_col_name" integer NULL, "custom_db_col_name_indexed" integer NULL, "fk_field_id" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("renamed_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("renamed_field" integer NOT NULL, "char_field" varchar(20) NOT NULL, "custom_db_col_name" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "fk_field_id" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field_id");' % generate_index_name('tests_testmodel', 'fk_field_id'), 'INSERT INTO "tests_testmodel" ("renamed_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "renamed_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameColumnWithTableNameModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("renamed_field" integer NULL, "char_field" varchar(20) NULL, "custom_db_col_name" integer NULL, "custom_db_col_name_indexed" integer NULL, "fk_field_id" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("renamed_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("renamed_field" integer NOT NULL, "char_field" varchar(20) NOT NULL, "custom_db_col_name" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "fk_field_id" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field_id");' % generate_index_name('tests_testmodel', 'fk_field_id'), 'INSERT INTO "tests_testmodel" ("renamed_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "renamed_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenamePrimaryKeyColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "char_field" varchar(20) NULL, "custom_db_col_name" integer NULL, "custom_db_col_name_indexed" integer NULL, "fk_field_id" integer NULL, "my_pk_id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "my_pk_id") SELECT "int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "char_field" varchar(20) NOT NULL, "custom_db_col_name" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "fk_field_id" integer NOT NULL, "my_pk_id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field_id");' % generate_index_name('tests_testmodel', 'fk_field_id'), 'INSERT INTO "tests_testmodel" ("int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "my_pk_id") SELECT "int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "fk_field_id", "my_pk_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameForeignKeyColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "char_field" varchar(20) NULL, "custom_db_col_name" integer NULL, "custom_db_col_name_indexed" integer NULL, "renamed_field_id" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("renamed_field", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "custom_db_col_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("int_field" integer NOT NULL, "char_field" varchar(20) NOT NULL, "custom_db_col_name" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "renamed_field_id" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("renamed_field_id");' % generate_index_name('tests_testmodel', 'renamed_field_id'), 'INSERT INTO "tests_testmodel" ("int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "renamed_field_id", "id") SELECT "int_field", "char_field", "custom_db_col_name", "custom_db_col_name_indexed", "renamed_field_id", "id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameNonDefaultColumnNameModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("renamed_field" integer NULL, "char_field" varchar(20) NULL, "int_field" integer NULL, "custom_db_col_name_indexed" integer NULL, "fk_field_id" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("renamed_field", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "custom_db_col_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("renamed_field" integer NOT NULL, "char_field" varchar(20) NOT NULL, "int_field" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "fk_field_id" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field_id");' % generate_index_name('tests_testmodel', 'fk_field_id'), 'INSERT INTO "tests_testmodel" ("renamed_field", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "renamed_field", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameNonDefaultColumnNameToNonDefaultNameModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("non-default_column_name" integer NULL, "char_field" varchar(20) NULL, "int_field" integer NULL, "custom_db_col_name_indexed" integer NULL, "fk_field_id" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("non-default_column_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "custom_db_col_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("non-default_column_name" integer NOT NULL, "char_field" varchar(20) NOT NULL, "int_field" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "fk_field_id" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field_id");' % generate_index_name('tests_testmodel', 'fk_field_id'), 'INSERT INTO "tests_testmodel" ("non-default_column_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "non-default_column_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameNonDefaultColumnNameToNonDefaultNameAndTableModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("non-default_column_name2" integer NULL, "char_field" varchar(20) NULL, "int_field" integer NULL, "custom_db_col_name_indexed" integer NULL, "fk_field_id" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY);', 'INSERT INTO "TEMP_TABLE" ("non-default_column_name2", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "custom_db_col_name", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("non-default_column_name2" integer NOT NULL, "char_field" varchar(20) NOT NULL, "int_field" integer NOT NULL, "custom_db_col_name_indexed" integer NOT NULL, "fk_field_id" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY);', 'CREATE INDEX "%s" ON "tests_testmodel" ("custom_db_col_name_indexed");' % generate_index_name('tests_testmodel', 'custom_db_col_name_indexed'), 'CREATE INDEX "%s" ON "tests_testmodel" ("fk_field_id");' % generate_index_name('tests_testmodel', 'fk_field_id'), 'INSERT INTO "tests_testmodel" ("non-default_column_name2", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id") SELECT "non-default_column_name2", "char_field", "int_field", "custom_db_col_name_indexed", "fk_field_id", "id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameColumnCustomTableModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("id" integer NULL UNIQUE PRIMARY KEY, "renamed_field" integer NULL, "alt_value" varchar(20) NULL);', 'INSERT INTO "TEMP_TABLE" ("id", "renamed_field", "alt_value") SELECT "id", "value", "alt_value" FROM "custom_rename_table_name";', 'DROP TABLE "custom_rename_table_name";', 'CREATE TABLE "custom_rename_table_name"("id" integer NOT NULL UNIQUE PRIMARY KEY, "renamed_field" integer NOT NULL, "alt_value" varchar(20) NOT NULL);', 'INSERT INTO "custom_rename_table_name" ("id", "renamed_field", "alt_value") SELECT "id", "renamed_field", "alt_value" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'RenameManyToManyTableModel': 'ALTER TABLE "tests_testmodel_m2m_field" RENAME TO "tests_testmodel_renamed_field";', 'RenameManyToManyTableWithColumnNameModel': 'ALTER TABLE "tests_testmodel_m2m_field" RENAME TO "tests_testmodel_renamed_field";', 'RenameNonDefaultManyToManyTableModel': 'ALTER TABLE "non-default_db_table" RENAME TO "tests_testmodel_renamed_field";', } sql_mutation = { 'SQLMutationSequence': """[ ... SQLMutation('first-two-fields', [ ... 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field1" integer NULL;', ... 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field2" integer NULL;' ... ], update_first_two), ... SQLMutation('third-field', [ ... 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field3" integer NULL;', ... ], update_third)] """, 'SQLMutationOutput': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field1" integer NULL;', 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field2" integer NULL;', 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field3" integer NULL;', ]), } generics = { 'DeleteColumnModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("object_id" integer unsigned NULL, "int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "content_type_id" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("object_id", "int_field", "id", "content_type_id") SELECT "object_id", "int_field", "id", "content_type_id" FROM "tests_testmodel";', 'DROP TABLE "tests_testmodel";', 'CREATE TABLE "tests_testmodel"("object_id" integer unsigned NOT NULL, "int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "content_type_id" integer NOT NULL);', 'CREATE INDEX "%s" ON "tests_testmodel" ("object_id");' % generate_index_name('tests_testmodel', 'object_id'), 'CREATE INDEX "%s" ON "tests_testmodel" ("content_type_id");' % generate_index_name('tests_testmodel', 'content_type_id'), 'INSERT INTO "tests_testmodel" ("object_id", "int_field", "id", "content_type_id") SELECT "object_id", "int_field", "id", "content_type_id" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]) } inheritance = { 'AddToChildModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("int_field" integer NULL, "id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL, "added_field" integer NULL);', 'INSERT INTO "TEMP_TABLE" ("int_field", "id", "char_field") SELECT "int_field", "id", "char_field" FROM "tests_childmodel";', 'UPDATE "TEMP_TABLE" SET "added_field" = 42;', 'DROP TABLE "tests_childmodel";', 'CREATE TABLE "tests_childmodel"("int_field" integer NOT NULL, "id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL, "added_field" integer NOT NULL);', 'INSERT INTO "tests_childmodel" ("int_field", "id", "char_field", "added_field") SELECT "int_field", "id", "char_field", "added_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";', ]), 'DeleteFromChildModel': '\n'.join([ 'CREATE TEMPORARY TABLE "TEMP_TABLE"("id" integer NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NULL);', 'INSERT INTO "TEMP_TABLE" ("id", "char_field") SELECT "id", "char_field" FROM "tests_childmodel";', 'DROP TABLE "tests_childmodel";', 'CREATE TABLE "tests_childmodel"("id" integer NOT NULL UNIQUE PRIMARY KEY, "char_field" varchar(20) NOT NULL);', 'INSERT INTO "tests_childmodel" ("id", "char_field") SELECT "id", "char_field" FROM "TEMP_TABLE";', 'DROP TABLE "TEMP_TABLE";' ]) } django_evolution-0.6.7/django_evolution/tests/db/mysql.py0000644000175000017500000003276011562106503024521 0ustar chipx86chipx8600000000000000from django.db import connection from django.db.models.options import Options # This is not a great check, but it's from the same version as auto-created # tables (Django 1.2), so we use it. digest_index_names = hasattr(Options({}), 'auto_created') def generate_index_name(table, column): if digest_index_names: column = connection.creation._digest(column) return '%s_%s' % (table, column) add_field = { 'AddNonNullNonCallableColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer DEFAULT 1;', 'ALTER TABLE `tests_testmodel` ALTER COLUMN `added_field` DROP DEFAULT;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `added_field` integer NOT NULL;', ]), 'AddNonNullCallableColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer ;', 'UPDATE `tests_testmodel` SET `added_field` = `int_field` WHERE `added_field` IS NULL;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `added_field` integer NOT NULL;', ]), 'AddNullColumnWithInitialColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer DEFAULT 1;', 'ALTER TABLE `tests_testmodel` ALTER COLUMN `added_field` DROP DEFAULT;', ]), 'AddStringColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` varchar(10) DEFAULT \'abc\\\'s xyz\';', 'ALTER TABLE `tests_testmodel` ALTER COLUMN `added_field` DROP DEFAULT;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `added_field` varchar(10) NOT NULL;', ]), 'AddDateColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` datetime DEFAULT 2007-12-13 16:42:00;', 'ALTER TABLE `tests_testmodel` ALTER COLUMN `added_field` DROP DEFAULT;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `added_field` datetime NOT NULL;', ]), 'AddDefaultColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer DEFAULT 42;', 'ALTER TABLE `tests_testmodel` ALTER COLUMN `added_field` DROP DEFAULT;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `added_field` integer NOT NULL;', ]), 'AddEmptyStringDefaultColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` varchar(20) DEFAULT \'\';', 'ALTER TABLE `tests_testmodel` ALTER COLUMN `added_field` DROP DEFAULT;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `added_field` varchar(20) NOT NULL;', ]), 'AddNullColumnModel': 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer NULL ;', 'NonDefaultColumnModel': 'ALTER TABLE `tests_testmodel` ADD COLUMN `non-default_column` integer NULL ;', 'AddColumnCustomTableModel': 'ALTER TABLE `custom_table_name` ADD COLUMN `added_field` integer NULL ;', 'AddIndexedColumnModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `add_field` integer NULL ;', 'CREATE INDEX `%s` ON `tests_testmodel` (`add_field`);' % generate_index_name('tests_testmodel', 'add_field') ]), 'AddUniqueColumnModel': 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer NULL UNIQUE;', 'AddUniqueIndexedModel': 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field` integer NULL UNIQUE;', 'AddForeignKeyModel': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field_id` integer NULL REFERENCES `tests_addanchor1` (`id`) ;', 'CREATE INDEX `%s` ON `tests_testmodel` (`added_field_id`);' % generate_index_name('tests_testmodel', 'added_field_id') ]), 'AddManyToManyDatabaseTableModel': '\n'.join([ 'CREATE TABLE `tests_testmodel_added_field` (', ' `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,', ' `testmodel_id` integer NOT NULL,', ' `addanchor1_id` integer NOT NULL,', ' UNIQUE (`testmodel_id`, `addanchor1_id`)', ')', ';', 'ALTER TABLE `tests_testmodel_added_field` ADD CONSTRAINT `testmodel_id_refs_id_ed159e33` FOREIGN KEY (`testmodel_id`) REFERENCES `tests_testmodel` (`id`);', 'ALTER TABLE `tests_testmodel_added_field` ADD CONSTRAINT `addanchor1_id_refs_id_7efbb240` FOREIGN KEY (`addanchor1_id`) REFERENCES `tests_addanchor1` (`id`);' ]), 'AddManyToManyNonDefaultDatabaseTableModel': '\n'.join([ 'CREATE TABLE `tests_testmodel_added_field` (', ' `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,', ' `testmodel_id` integer NOT NULL,', ' `addanchor2_id` integer NOT NULL,', ' UNIQUE (`testmodel_id`, `addanchor2_id`)', ')', ';', 'ALTER TABLE `tests_testmodel_added_field` ADD CONSTRAINT `testmodel_id_refs_id_ed159e33` FOREIGN KEY (`testmodel_id`) REFERENCES `tests_testmodel` (`id`);', 'ALTER TABLE `tests_testmodel_added_field` ADD CONSTRAINT `addanchor2_id_refs_id_ec3e2588` FOREIGN KEY (`addanchor2_id`) REFERENCES `custom_add_anchor_table` (`id`);' ]), 'AddManyToManySelf': '\n'.join([ 'CREATE TABLE `tests_testmodel_added_field` (', ' `id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,', ' `from_testmodel_id` integer NOT NULL,', ' `to_testmodel_id` integer NOT NULL,', ' UNIQUE (`from_testmodel_id`, `to_testmodel_id`)', ')', ';', 'ALTER TABLE `tests_testmodel_added_field` ADD CONSTRAINT `from_testmodel_id_refs_id_ed159e33` FOREIGN KEY (`from_testmodel_id`) REFERENCES `tests_testmodel` (`id`);', 'ALTER TABLE `tests_testmodel_added_field` ADD CONSTRAINT `to_testmodel_id_refs_id_ed159e33` FOREIGN KEY (`to_testmodel_id`) REFERENCES `tests_testmodel` (`id`);' ]), } delete_field = { 'DefaultNamedColumnModel': 'ALTER TABLE `tests_testmodel` DROP COLUMN `int_field` CASCADE;', 'NonDefaultNamedColumnModel': 'ALTER TABLE `tests_testmodel` DROP COLUMN `non-default_db_column` CASCADE;', 'ConstrainedColumnModel': 'ALTER TABLE `tests_testmodel` DROP COLUMN `int_field3` CASCADE;', 'DefaultManyToManyModel': 'DROP TABLE `tests_testmodel_m2m_field1`;', 'NonDefaultManyToManyModel': 'DROP TABLE `non-default_m2m_table`;', 'DeleteForeignKeyModel': 'ALTER TABLE `tests_testmodel` DROP COLUMN `fk_field1_id` CASCADE;', 'DeleteColumnCustomTableModel': 'ALTER TABLE `custom_table_name` DROP COLUMN `value` CASCADE;', } change_field = { "SetNotNullChangeModelWithConstant": '\n'.join([ 'UPDATE `tests_testmodel` SET `char_field1` = \'abc\\\'s xyz\' WHERE `char_field1` IS NULL;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field1` varchar(25) NOT NULL;', ]), "SetNotNullChangeModelWithCallable": '\n'.join([ 'UPDATE `tests_testmodel` SET `char_field1` = `char_field` WHERE `char_field1` IS NULL;', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field1` varchar(25) NOT NULL;', ]), "SetNullChangeModel": 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field2` varchar(30) DEFAULT NULL;', "NoOpChangeModel": '', 'IncreasingMaxLengthChangeModel': '\n'.join([ 'UPDATE `tests_testmodel` SET `char_field`=LEFT(`char_field`,45);', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field` varchar(45);', ]), 'DecreasingMaxLengthChangeModel': '\n'.join([ 'UPDATE `tests_testmodel` SET `char_field`=LEFT(`char_field`,1);', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field` varchar(1);', ]), "DBColumnChangeModel": 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `custom_db_column` `customised_db_column` integer NOT NULL;', "M2MDBTableChangeModel": 'RENAME TABLE `change_field_non-default_m2m_table` TO `custom_m2m_db_table_name`;', "AddDBIndexChangeModel": 'CREATE INDEX `%s` ON `tests_testmodel` (`int_field2`);' % generate_index_name('tests_testmodel', 'int_field2'), "RemoveDBIndexChangeModel": 'DROP INDEX `%s` ON `tests_testmodel`;' % generate_index_name('tests_testmodel', 'int_field1'), "AddUniqueChangeModel": 'CREATE UNIQUE INDEX int_field4 ON `tests_testmodel`(`int_field4`);', "RemoveUniqueChangeModel": 'DROP INDEX int_field3 ON `tests_testmodel`;', "MultiAttrChangeModel": '\n'.join([ 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field2` varchar(30) DEFAULT NULL;', 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `custom_db_column` `custom_db_column2` integer NOT NULL;', 'UPDATE `tests_testmodel` SET `char_field`=LEFT(`char_field`,35);', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field` varchar(35);', ]), "MultiAttrSingleFieldChangeModel": '\n'.join([ 'UPDATE `tests_testmodel` SET `char_field2`=LEFT(`char_field2`,35);', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field2` varchar(35);', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field2` varchar(35) DEFAULT NULL;', ]), "RedundantAttrsChangeModel": '\n'.join([ 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field2` varchar(30) DEFAULT NULL;', 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `custom_db_column` `custom_db_column3` integer NOT NULL;', 'UPDATE `tests_testmodel` SET `char_field`=LEFT(`char_field`,35);', 'ALTER TABLE `tests_testmodel` MODIFY COLUMN `char_field` varchar(35);', ]), } delete_model = { 'BasicModel': 'DROP TABLE `tests_basicmodel`;', 'BasicWithM2MModel': '\n'.join([ 'DROP TABLE `tests_basicwithm2mmodel_m2m`;', 'DROP TABLE `tests_basicwithm2mmodel`;' ]), 'CustomTableModel': 'DROP TABLE `custom_table_name`;', 'CustomTableWithM2MModel': '\n'.join([ 'DROP TABLE `another_custom_table_name_m2m`;', 'DROP TABLE `another_custom_table_name`;' ]), } delete_application = { 'DeleteApplication': '\n'.join([ 'DROP TABLE `tests_appdeleteanchor1`;', 'DROP TABLE `app_delete_custom_add_anchor_table`;', 'DROP TABLE `tests_testmodel_anchor_m2m`;', 'DROP TABLE `tests_testmodel`;', 'DROP TABLE `app_delete_custom_table_name`;', ]), 'DeleteApplicationWithoutDatabase': "", } rename_field = { 'RenameColumnModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `int_field` `renamed_field` integer NOT NULL;', 'RenameColumnWithTableNameModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `int_field` `renamed_field` integer NOT NULL;', 'RenamePrimaryKeyColumnModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `id` `my_pk_id`;', 'RenameForeignKeyColumnModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `fk_field_id` `renamed_field_id` integer NOT NULL;', 'RenameNonDefaultColumnNameModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `custom_db_col_name` `renamed_field` integer NOT NULL;', 'RenameNonDefaultColumnNameToNonDefaultNameModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `custom_db_col_name` `non-default_column_name` integer NOT NULL;', 'RenameNonDefaultColumnNameToNonDefaultNameAndTableModel': 'ALTER TABLE `tests_testmodel` CHANGE COLUMN `custom_db_col_name` `non-default_column_name2` integer NOT NULL;', 'RenameColumnCustomTableModel': 'ALTER TABLE `custom_rename_table_name` CHANGE COLUMN `value` `renamed_field` integer NOT NULL;', 'RenameManyToManyTableModel': 'RENAME TABLE `tests_testmodel_m2m_field` TO `tests_testmodel_renamed_field`;', 'RenameManyToManyTableWithColumnNameModel': 'RENAME TABLE `tests_testmodel_m2m_field` TO `tests_testmodel_renamed_field`;', 'RenameNonDefaultManyToManyTableModel': 'RENAME TABLE `non-default_db_table` TO `tests_testmodel_renamed_field`;', } sql_mutation = { 'SQLMutationSequence': """[ ... SQLMutation('first-two-fields', [ ... 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field1` integer NULL;', ... 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field2` integer NULL;' ... ], update_first_two), ... SQLMutation('third-field', [ ... 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field3` integer NULL;', ... ], update_third)] """, 'SQLMutationOutput': '\n'.join([ 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field1` integer NULL;', 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field2` integer NULL;', 'ALTER TABLE `tests_testmodel` ADD COLUMN `added_field3` integer NULL;', ]), } generics = { 'DeleteColumnModel': "ALTER TABLE `tests_testmodel` DROP COLUMN `char_field` CASCADE;" } inheritance = { 'AddToChildModel': '\n'.join([ 'ALTER TABLE `tests_childmodel` ADD COLUMN `added_field` integer DEFAULT 42;', 'ALTER TABLE `tests_childmodel` ALTER COLUMN `added_field` DROP DEFAULT;', 'ALTER TABLE `tests_childmodel` MODIFY COLUMN `added_field` integer NOT NULL;', ]), 'DeleteFromChildModel': 'ALTER TABLE `tests_childmodel` DROP COLUMN `int_field` CASCADE;', } django_evolution-0.6.7/django_evolution/tests/db/postgresql.py0000644000175000017500000004214311562077342025563 0ustar chipx86chipx8600000000000000from django.db.models.options import Options autocreate_through_tables = hasattr(Options({}), 'auto_created') add_field = { 'AddNonNullNonCallableColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer DEFAULT 1;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" DROP DEFAULT;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'AddNonNullCallableColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer ;', 'UPDATE "tests_testmodel" SET "added_field" = "int_field" WHERE "added_field" IS NULL;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'AddNullColumnWithInitialColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer DEFAULT 1;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" DROP DEFAULT;', ]), 'AddStringColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" varchar(10) DEFAULT \'abc\\\'s xyz\';', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" DROP DEFAULT;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'AddDateColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" timestamp with time zone DEFAULT 2007-12-13 16:42:00;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" DROP DEFAULT;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'AddDefaultColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer DEFAULT 42;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" DROP DEFAULT;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'AddEmptyStringDefaultColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" varchar(20) DEFAULT \'\';', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" DROP DEFAULT;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'AddNullColumnModel': 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer NULL ;', 'NonDefaultColumnModel': 'ALTER TABLE "tests_testmodel" ADD COLUMN "non-default_column" integer NULL ;', 'AddColumnCustomTableModel': 'ALTER TABLE "custom_table_name" ADD COLUMN "added_field" integer NULL ;', 'AddIndexedColumnModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "add_field" integer NULL ;', 'CREATE INDEX "tests_testmodel_add_field" ON "tests_testmodel" ("add_field");' ]), 'AddUniqueColumnModel': 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer NULL UNIQUE;', 'AddUniqueIndexedModel': 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field" integer NULL UNIQUE;', 'AddForeignKeyModel': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field_id" integer NULL REFERENCES "tests_addanchor1" ("id") DEFERRABLE INITIALLY DEFERRED;', 'CREATE INDEX "tests_testmodel_added_field_id" ON "tests_testmodel" ("added_field_id");' ]), } if autocreate_through_tables: add_field.update({ 'AddManyToManyDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" serial NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL,', ' "addanchor1_id" integer NOT NULL,', ' UNIQUE ("testmodel_id", "addanchor1_id")', ')', ';', 'ALTER TABLE "tests_testmodel_added_field" ADD CONSTRAINT "testmodel_id_refs_id_ed159e33" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED;', 'ALTER TABLE "tests_testmodel_added_field" ADD CONSTRAINT "addanchor1_id_refs_id_7efbb240" FOREIGN KEY ("addanchor1_id") REFERENCES "tests_addanchor1" ("id") DEFERRABLE INITIALLY DEFERRED;', ]), 'AddManyToManyNonDefaultDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" serial NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL,', ' "addanchor2_id" integer NOT NULL,', ' UNIQUE ("testmodel_id", "addanchor2_id")', ')', ';', 'ALTER TABLE "tests_testmodel_added_field" ADD CONSTRAINT "testmodel_id_refs_id_ed159e33" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED;', 'ALTER TABLE "tests_testmodel_added_field" ADD CONSTRAINT "addanchor2_id_refs_id_ec3e2588" FOREIGN KEY ("addanchor2_id") REFERENCES "custom_add_anchor_table" ("id") DEFERRABLE INITIALLY DEFERRED;', ]), 'AddManyToManySelf': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" serial NOT NULL PRIMARY KEY,', ' "from_testmodel_id" integer NOT NULL,', ' "to_testmodel_id" integer NOT NULL,', ' UNIQUE ("from_testmodel_id", "to_testmodel_id")', ')', ';', 'ALTER TABLE "tests_testmodel_added_field" ADD CONSTRAINT "from_testmodel_id_refs_id_ed159e33" FOREIGN KEY ("from_testmodel_id") REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED;', 'ALTER TABLE "tests_testmodel_added_field" ADD CONSTRAINT "to_testmodel_id_refs_id_ed159e33" FOREIGN KEY ("to_testmodel_id") REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED;', ]), }) else: add_field.update({ 'AddManyToManyDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" serial NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED,', ' "addanchor1_id" integer NOT NULL REFERENCES "tests_addanchor1" ("id") DEFERRABLE INITIALLY DEFERRED,', ' UNIQUE ("testmodel_id", "addanchor1_id")', ')', ';' ]), 'AddManyToManyNonDefaultDatabaseTableModel': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" serial NOT NULL PRIMARY KEY,', ' "testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED,', ' "addanchor2_id" integer NOT NULL REFERENCES "custom_add_anchor_table" ("id") DEFERRABLE INITIALLY DEFERRED,', ' UNIQUE ("testmodel_id", "addanchor2_id")', ')', ';' ]), 'AddManyToManySelf': '\n'.join([ 'CREATE TABLE "tests_testmodel_added_field" (', ' "id" serial NOT NULL PRIMARY KEY,', ' "from_testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED,', ' "to_testmodel_id" integer NOT NULL REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED,', ' UNIQUE ("from_testmodel_id", "to_testmodel_id")', ')', ';' ]), }) delete_field = { 'DefaultNamedColumnModel': 'ALTER TABLE "tests_testmodel" DROP COLUMN "int_field" CASCADE;', 'NonDefaultNamedColumnModel': 'ALTER TABLE "tests_testmodel" DROP COLUMN "non-default_db_column" CASCADE;', 'ConstrainedColumnModel': 'ALTER TABLE "tests_testmodel" DROP COLUMN "int_field3" CASCADE;', 'DefaultManyToManyModel': 'DROP TABLE "tests_testmodel_m2m_field1";', 'NonDefaultManyToManyModel': 'DROP TABLE "non-default_m2m_table";', 'DeleteForeignKeyModel': 'ALTER TABLE "tests_testmodel" DROP COLUMN "fk_field1_id" CASCADE;', 'DeleteColumnCustomTableModel': 'ALTER TABLE "custom_table_name" DROP COLUMN "value" CASCADE;', } change_field = { "SetNotNullChangeModelWithConstant": '\n'.join([ 'UPDATE "tests_testmodel" SET "char_field1" = \'abc\\\'s xyz\' WHERE "char_field1" IS NULL;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field1" SET NOT NULL;', ]), "SetNotNullChangeModelWithCallable": '\n'.join([ 'UPDATE "tests_testmodel" SET "char_field1" = "char_field" WHERE "char_field1" IS NULL;', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field1" SET NOT NULL;', ]), "SetNullChangeModel": 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field2" DROP NOT NULL;', "NoOpChangeModel": '', "IncreasingMaxLengthChangeModel": 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field" TYPE varchar(45) USING CAST("char_field" as varchar(45));', "DecreasingMaxLengthChangeModel": 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field" TYPE varchar(1) USING CAST("char_field" as varchar(1));', "DBColumnChangeModel": 'ALTER TABLE "tests_testmodel" RENAME COLUMN "custom_db_column" TO "customised_db_column";', "AddDBIndexChangeModel": 'CREATE INDEX "tests_testmodel_int_field2" ON "tests_testmodel" ("int_field2");', "RemoveDBIndexChangeModel": 'DROP INDEX "tests_testmodel_int_field1";', "AddUniqueChangeModel": 'ALTER TABLE "tests_testmodel" ADD CONSTRAINT tests_testmodel_int_field4_key UNIQUE("int_field4");', "RemoveUniqueChangeModel": 'ALTER TABLE "tests_testmodel" DROP CONSTRAINT tests_testmodel_int_field3_key;', "MultiAttrChangeModel": '\n'.join([ 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field2" DROP NOT NULL;', 'ALTER TABLE "tests_testmodel" RENAME COLUMN "custom_db_column" TO "custom_db_column2";', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field" TYPE varchar(35) USING CAST("char_field" as varchar(35));', ]), "MultiAttrSingleFieldChangeModel": '\n'.join([ 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field2" TYPE varchar(35) USING CAST("char_field2" as varchar(35));', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field2" DROP NOT NULL;', ]), "RedundantAttrsChangeModel": '\n'.join([ 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field2" DROP NOT NULL;', 'ALTER TABLE "tests_testmodel" RENAME COLUMN "custom_db_column" TO "custom_db_column3";', 'ALTER TABLE "tests_testmodel" ALTER COLUMN "char_field" TYPE varchar(35) USING CAST("char_field" as varchar(35));', ]), } if autocreate_through_tables: change_field.update({ "M2MDBTableChangeModel": '\n'.join([ 'ALTER TABLE "change_field_non-default_m2m_table" DROP CONSTRAINT "testmodel_id_refs_my_id_5d3392f8";', 'ALTER TABLE "change_field_non-default_m2m_table" RENAME TO "custom_m2m_db_table_name";', 'ALTER TABLE "custom_m2m_db_table_name" ADD CONSTRAINT "testmodel_id_refs_my_id_a31f0c6f" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("my_id") DEFERRABLE INITIALLY DEFERRED;', ]), }) else: change_field.update({ "M2MDBTableChangeModel": 'ALTER TABLE "change_field_non-default_m2m_table" RENAME TO "custom_m2m_db_table_name";', }) delete_model = { 'BasicModel': 'DROP TABLE "tests_basicmodel";', 'BasicWithM2MModel': '\n'.join([ 'DROP TABLE "tests_basicwithm2mmodel_m2m";', 'DROP TABLE "tests_basicwithm2mmodel";' ]), 'CustomTableModel': 'DROP TABLE "custom_table_name";', 'CustomTableWithM2MModel': '\n'.join([ 'DROP TABLE "another_custom_table_name_m2m";', 'DROP TABLE "another_custom_table_name";' ]), } delete_application = { 'DeleteApplication': '\n'.join([ 'DROP TABLE "tests_appdeleteanchor1";', 'DROP TABLE "app_delete_custom_add_anchor_table";', 'DROP TABLE "tests_testmodel_anchor_m2m";', 'DROP TABLE "tests_testmodel";', 'DROP TABLE "app_delete_custom_table_name";', ]), 'DeleteApplicationWithoutDatabase': "", } rename_field = { 'RenameColumnModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "int_field" TO "renamed_field";', 'RenameColumnWithTableNameModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "int_field" TO "renamed_field";', 'RenameForeignKeyColumnModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "fk_field_id" TO "renamed_field_id";', 'RenameNonDefaultColumnNameModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "custom_db_col_name" TO "renamed_field";', 'RenameNonDefaultColumnNameToNonDefaultNameModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "custom_db_col_name" TO "non-default_column_name";', 'RenameNonDefaultColumnNameToNonDefaultNameAndTableModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "custom_db_col_name" TO "non-default_column_name2";', 'RenameColumnCustomTableModel': 'ALTER TABLE "custom_rename_table_name" RENAME COLUMN "value" TO "renamed_field";', 'RenameNonDefaultManyToManyTableModel': 'ALTER TABLE "non-default_db_table" RENAME TO "tests_testmodel_renamed_field";', } sql_mutation = { 'SQLMutationSequence': """[ ... SQLMutation('first-two-fields', [ ... 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field1" integer NULL;', ... 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field2" integer NULL;' ... ], update_first_two), ... SQLMutation('third-field', [ ... 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field3" integer NULL;', ... ], update_third)] """, 'SQLMutationOutput': '\n'.join([ 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field1" integer NULL;', 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field2" integer NULL;', 'ALTER TABLE "tests_testmodel" ADD COLUMN "added_field3" integer NULL;', ]), } if autocreate_through_tables: rename_field.update({ 'RenamePrimaryKeyColumnModel': '\n'.join([ 'ALTER TABLE "non-default_db_table" DROP CONSTRAINT "testmodel_id_refs_id_eeae318e";', 'ALTER TABLE "tests_testmodel_m2m_field" DROP CONSTRAINT "testmodel_id_refs_id_ba77d38d";', 'ALTER TABLE "tests_testmodel" RENAME COLUMN "id" TO "my_pk_id";', 'ALTER TABLE "non-default_db_table" ADD CONSTRAINT "testmodel_id_refs_my_pk_id_eeae318e" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("my_pk_id") DEFERRABLE INITIALLY DEFERRED;', 'ALTER TABLE "tests_testmodel_m2m_field" ADD CONSTRAINT "testmodel_id_refs_my_pk_id_ba77d38d" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("my_pk_id") DEFERRABLE INITIALLY DEFERRED;', ]), 'RenameManyToManyTableModel': '\n'.join([ 'ALTER TABLE "tests_testmodel_m2m_field" DROP CONSTRAINT "testmodel_id_refs_id_ba77d38d";', 'ALTER TABLE "tests_testmodel_m2m_field" RENAME TO "tests_testmodel_renamed_field";', 'ALTER TABLE "tests_testmodel_renamed_field" ADD CONSTRAINT "testmodel_id_refs_id_f50a5e5d" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED;', ]), 'RenameManyToManyTableWithColumnNameModel': '\n'.join([ 'ALTER TABLE "tests_testmodel_m2m_field" DROP CONSTRAINT "testmodel_id_refs_id_ba77d38d";', 'ALTER TABLE "tests_testmodel_m2m_field" RENAME TO "tests_testmodel_renamed_field";', 'ALTER TABLE "tests_testmodel_renamed_field" ADD CONSTRAINT "testmodel_id_refs_id_f50a5e5d" FOREIGN KEY ("testmodel_id") REFERENCES "tests_testmodel" ("id") DEFERRABLE INITIALLY DEFERRED;', ]), }) else: rename_field.update({ 'RenamePrimaryKeyColumnModel': 'ALTER TABLE "tests_testmodel" RENAME COLUMN "id" TO "my_pk_id";', 'RenameManyToManyTableModel': 'ALTER TABLE "tests_testmodel_m2m_field" RENAME TO "tests_testmodel_renamed_field";', 'RenameManyToManyTableWithColumnNameModel': 'ALTER TABLE "tests_testmodel_m2m_field" RENAME TO "tests_testmodel_renamed_field";', }) generics = { 'DeleteColumnModel': 'ALTER TABLE "tests_testmodel" DROP COLUMN "char_field" CASCADE;' } inheritance = { 'AddToChildModel': '\n'.join([ 'ALTER TABLE "tests_childmodel" ADD COLUMN "added_field" integer DEFAULT 42;', 'ALTER TABLE "tests_childmodel" ALTER COLUMN "added_field" DROP DEFAULT;', 'ALTER TABLE "tests_childmodel" ALTER COLUMN "added_field" SET NOT NULL;', ]), 'DeleteFromChildModel': 'ALTER TABLE "tests_childmodel" DROP COLUMN "int_field" CASCADE;', } django_evolution-0.6.7/django_evolution/tests/db/__init__.py0000644000175000017500000000000011471453547025105 0ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution/tests/delete_model.py0000644000175000017500000000777411471453547025434 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from django.db import models >>> from django_evolution.mutations import DeleteModel >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> import copy # Now, a useful test model we can use for evaluating diffs >>> class DeleteModelAnchor(models.Model): ... value = models.IntegerField() >>> class BasicModel(models.Model): ... value = models.IntegerField() >>> class BasicWithM2MModel(models.Model): ... value = models.IntegerField() ... m2m = models.ManyToManyField(DeleteModelAnchor) >>> class CustomTableModel(models.Model): ... value = models.IntegerField() ... class Meta: ... db_table = 'custom_table_name' >>> class CustomTableWithM2MModel(models.Model): ... value = models.IntegerField() ... m2m = models.ManyToManyField(DeleteModelAnchor) ... class Meta: ... db_table = 'another_custom_table_name' # Store the base signature >>> base_models = ( ... ('DeleteModelAnchor', DeleteModelAnchor), ... ('BasicModel', BasicModel), ... ('BasicWithM2MModel', BasicWithM2MModel), ... ('CustomTableModel', CustomTableModel), ... ('CustomTableWithM2MModel', CustomTableWithM2MModel), ... ) >>> start = register_models(*base_models) >>> start_sig = test_proj_sig(*base_models) # Delete a Model >>> end_sig = copy.deepcopy(start_sig) >>> _ = end_sig['tests'].pop('BasicModel') >>> end = copy.deepcopy(start) >>> _ = end.pop('basicmodel') >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteModel('BasicModel')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #BasicModel %(BasicModel)s # Delete a model with an m2m field >>> end_sig = copy.deepcopy(start_sig) >>> _ = end_sig['tests'].pop('BasicWithM2MModel') >>> end = copy.deepcopy(start) >>> _ = end.pop('basicwithm2mmodel') >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteModel('BasicWithM2MModel')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) # BasicWithM2MModels %(BasicWithM2MModel)s # Delete a model with a custom table name >>> end_sig = copy.deepcopy(start_sig) >>> _ = end_sig['tests'].pop('CustomTableModel') >>> end = copy.deepcopy(start) >>> _ = end.pop('customtablemodel') >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteModel('CustomTableModel')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #CustomTableModel %(CustomTableModel)s # Delete a model with a custom table name and an m2m field >>> end_sig = copy.deepcopy(start_sig) >>> _ = end_sig['tests'].pop('CustomTableWithM2MModel') >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteModel('CustomTableWithM2MModel')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #CustomTableWithM2MModel %(CustomTableWithM2MModel)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('delete_model') django_evolution-0.6.7/django_evolution/tests/add_field.py0000644000175000017500000005271611471453547024701 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" # The AddField tests will aim to test the following usecases: # Field resulting in a new database column. # Field resulting in a new database column with a non-default name. # Field resulting in a new database column in a table with a non-default name. # Primary key field. # Indexed field # Unique field. # Null field # # Foreign Key field. # M2M field between models with default table names. # M2M field between models with non-default table names. # M2M field between self >>> from datetime import datetime >>> from django.db import models >>> from django_evolution.mutations import AddField, DeleteField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> from django_evolution import signature >>> from django_evolution import models as test_app >>> import copy >>> class AddSequenceFieldInitial(object): ... def __init__(self, suffix): ... self.suffix = suffix ... ... def __call__(self): ... from django.db import connection ... qn = connection.ops.quote_name ... return qn('int_field') >>> class AddAnchor1(models.Model): ... value = models.IntegerField() >>> class AddAnchor2(models.Model): ... value = models.IntegerField() ... class Meta: ... db_table = 'custom_add_anchor_table' >>> class AddBaseModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() >>> class CustomTableModel(models.Model): ... value = models.IntegerField() ... alt_value = models.CharField(max_length=20) ... class Meta: ... db_table = 'custom_table_name' # Store the base signatures >>> anchors = ( ... ('AddAnchor1', AddAnchor1), ... ('AddAnchor2', AddAnchor2) ... ) >>> custom_model = ('CustomTableModel', CustomTableModel) >>> custom = register_models(custom_model) >>> custom_table_sig = test_proj_sig(custom_model) >>> test_model = ('TestModel', AddBaseModel) >>> start = register_models(*anchors) >>> start.update(register_models(test_model)) >>> start_sig = test_proj_sig(test_model, *anchors) # Add non-null field with non-callable initial value >>> class AddNonNullColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField() >>> end = register_models(('TestModel', AddNonNullColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddNonNullColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] #AddNonNullColumnModel ["AddField('TestModel', 'added_field', models.IntegerField, initial=<>)"] # Evolution won't run as-is >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... EvolutionException: Cannot use hinted evolution: AddField or ChangeField mutation for 'TestModel.added_field' in 'tests' requires user-specified initial value. # First try without an initial value. This will fail >>> evolution = [AddField('TestModel', 'added_field', models.IntegerField)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot create new column 'added_field' on 'tests.TestModel' without a non-null initial value. # Now try with an explicitly null initial value. This will also fail >>> evolution = [AddField('TestModel', 'added_field', models.IntegerField, initial=None)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot create new column 'added_field' on 'tests.TestModel' without a non-null initial value. # Now try with a good initial value >>> evolution = [AddField('TestModel', 'added_field', models.IntegerField, initial=1)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddNonNullNonCallableColumnModel %(AddNonNullNonCallableColumnModel)s # Now try with a good callable initial value >>> evolution = [AddField('TestModel', 'added_field', models.IntegerField, initial=AddSequenceFieldInitial('AddNonNullCallableColumnModel'))] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddNonNullCallableColumnModel %(AddNonNullCallableColumnModel)s # Add nullable column with initial data >>> class AddNullColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField(null=True) >>> end = register_models(('TestModel',AddNullColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddNullColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] #AddNullColumnModel ["AddField('TestModel', 'added_field', models.IntegerField, null=True)"] >>> evolution = [AddField('TestModel', 'added_field', models.IntegerField, initial=1, null=True)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddNullColumnWithInitialColumnModel %(AddNullColumnWithInitialColumnModel)s # Add a field that requires string-form initial data >>> class AddStringColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.CharField(max_length=10) >>> end = register_models(('TestModel',AddStringColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddStringColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] # AddStringColumnModel ["AddField('TestModel', 'added_field', models.CharField, initial=<>, max_length=10)"] >>> evolution = [AddField('TestModel', 'added_field', models.CharField, initial="abc's xyz", max_length=10)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddStringColumnModel %(AddStringColumnModel)s # Add a string field that allows empty strings as initial values >>> class AddBlankStringColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.CharField(max_length=10, blank=True) >>> end = register_models(('TestModel',AddBlankStringColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddBlankStringColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] # AddBlankStringColumnModel ["AddField('TestModel', 'added_field', models.CharField, initial='', max_length=10)"] # Add a field that requires date-form initial data >>> class AddDateColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.DateTimeField() >>> end = register_models(('TestModel',AddDateColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddDateColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] # AddDateColumnModel ["AddField('TestModel', 'added_field', models.DateTimeField, initial=<>)"] >>> new_date = datetime(2007,12,13,16,42,0) >>> evolution = [AddField('TestModel', 'added_field', models.DateTimeField, initial=new_date)] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in evolution: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddDateColumnModel %(AddDateColumnModel)s # Add column with default value >>> class AddDefaultColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField(default=42) >>> end = register_models(('TestModel',AddDefaultColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddDefaultColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] #AddDefaultColumnModel ["AddField('TestModel', 'added_field', models.IntegerField, initial=42)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddDefaultColumnModel %(AddDefaultColumnModel)s # Add column with an empty string as the default value >>> class AddEmptyStringDefaultColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.CharField(max_length=20, default='') >>> end = register_models(('TestModel',AddEmptyStringDefaultColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddEmptyStringDefaultColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] #AddEmptyStringDefaultColumnModel ["AddField('TestModel', 'added_field', models.CharField, initial=u'', max_length=20)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddEmptyStringDefaultColumnModel %(AddEmptyStringDefaultColumnModel)s # Null field >>> class AddNullColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField(null=True) >>> end = register_models(('TestModel', AddNullColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', AddNullColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] #AddNullColumnModel ["AddField('TestModel', 'added_field', models.IntegerField, null=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddNullColumnModel %(AddNullColumnModel)s # Field resulting in a new database column with a non-default name. >>> class NonDefaultColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... add_field = models.IntegerField(db_column='non-default_column', null=True) >>> end = register_models(('TestModel',NonDefaultColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',NonDefaultColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'add_field', models.IntegerField, null=True, db_column='non-default_column')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #NonDefaultColumnModel %(NonDefaultColumnModel)s # Field resulting in a new database column in a table with a non-default name. >>> class AddColumnCustomTableModel(models.Model): ... value = models.IntegerField() ... alt_value = models.CharField(max_length=20) ... added_field = models.IntegerField(null=True) ... class Meta: ... db_table = 'custom_table_name' >>> end = register_models(('CustomTableModel',AddColumnCustomTableModel)) >>> end_sig = test_proj_sig(('CustomTableModel',AddColumnCustomTableModel)) >>> d = Diff(custom_table_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('CustomTableModel', 'added_field', models.IntegerField, null=True)"] >>> test_sig = copy.deepcopy(custom_table_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(custom, end, test_sql) #AddColumnCustomTableModel %(AddColumnCustomTableModel)s # Add Primary key field. # Delete of old Primary Key is prohibited. >>> class AddPrimaryKeyModel(models.Model): ... my_primary_key = models.AutoField(primary_key=True) ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() >>> end = register_models(('TestModel', AddPrimaryKeyModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddPrimaryKeyModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'my_primary_key', models.AutoField, initial=<>, primary_key=True)", "DeleteField('TestModel', 'id')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in [AddField('TestModel', 'my_primary_key', models.AutoField, initial=AddSequenceFieldInitial('AddPrimaryKeyModel'), primary_key=True), DeleteField('TestModel', 'id')]: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) Traceback (most recent call last): ... SimulationFailure: Cannot delete a primary key. # Indexed field >>> class AddIndexedColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... add_field = models.IntegerField(db_index=True, null=True) >>> end = register_models(('TestModel',AddIndexedColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddIndexedColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'add_field', models.IntegerField, null=True, db_index=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql, debug=False) #AddIndexedColumnModel %(AddIndexedColumnModel)s # Unique field. >>> class AddUniqueColumnModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField(unique=True, null=True) >>> end = register_models(('TestModel',AddUniqueColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddUniqueColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'added_field', models.IntegerField, unique=True, null=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddUniqueColumnModel %(AddUniqueColumnModel)s # Unique indexed field. >>> class AddUniqueIndexedModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.IntegerField(unique=True, db_index=True, null=True) >>> end = register_models(('TestModel',AddUniqueIndexedModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddUniqueIndexedModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'added_field', models.IntegerField, unique=True, null=True, db_index=True)"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddUniqueIndexedModel %(AddUniqueIndexedModel)s Foreign Key field. >>> class AddForeignKeyModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.ForeignKey(AddAnchor1, null=True) >>> end = register_models(('TestModel',AddForeignKeyModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddForeignKeyModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'added_field', models.ForeignKey, null=True, related_model='tests.AddAnchor1')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddForeignKeyModel %(AddForeignKeyModel)s # M2M field between models with default table names. >>> class AddM2MDatabaseTableModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.ManyToManyField(AddAnchor1) >>> end = register_models(('TestModel',AddM2MDatabaseTableModel), *anchors) >>> end_sig = test_proj_sig(('TestModel',AddM2MDatabaseTableModel), *anchors) >>> end_sig['tests'][AddAnchor1.__name__] = signature.create_model_sig(AddAnchor1) >>> anchor_sig = copy.deepcopy(start_sig) >>> anchor_sig['tests'][AddAnchor1.__name__] = signature.create_model_sig(AddAnchor1) >>> d = Diff(anchor_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'added_field', models.ManyToManyField, related_model='tests.AddAnchor1')"] >>> test_sig = copy.deepcopy(anchor_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddManyToManyDatabaseTableModel %(AddManyToManyDatabaseTableModel)s # M2M field between models with non-default table names. >>> class AddM2MNonDefaultDatabaseTableModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.ManyToManyField(AddAnchor2) >>> end = register_models(('TestModel', AddM2MNonDefaultDatabaseTableModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', AddM2MNonDefaultDatabaseTableModel), *anchors) >>> end_sig['tests'][AddAnchor2.__name__] = signature.create_model_sig(AddAnchor2) >>> anchor_sig = copy.deepcopy(start_sig) >>> anchor_sig['tests'][AddAnchor2.__name__] = signature.create_model_sig(AddAnchor2) >>> d = Diff(anchor_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'added_field', models.ManyToManyField, related_model='tests.AddAnchor2')"] >>> test_sig = copy.deepcopy(anchor_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddManyToManyNonDefaultDatabaseTableModel %(AddManyToManyNonDefaultDatabaseTableModel)s # M2M field between self # Need to find a better way to do this. >>> class AddM2MSelfDatabaseTableModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... added_field = models.ManyToManyField('AddM2MSelfDatabaseTableModel') >>> end = register_models(('TestModel', AddM2MSelfDatabaseTableModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', AddM2MSelfDatabaseTableModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["AddField('TestModel', 'added_field', models.ManyToManyField, related_model='tests.TestModel')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #AddManyToManySelf %(AddManyToManySelf)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('add_field') django_evolution-0.6.7/django_evolution/tests/delete_field.py0000644000175000017500000002350111471453547025401 0ustar chipx86chipx8600000000000000from django_evolution.tests.utils import test_sql_mapping tests = r""" >>> from django.db import models >>> from django_evolution.mutations import DeleteField >>> from django_evolution.tests.utils import test_proj_sig, execute_test_sql, register_models, deregister_models >>> from django_evolution.diff import Diff >>> import copy # All Fields # db index (ignored for now) # db tablespace (ignored for now) # db column # primary key # unique # M2M Fields # to field # db table # Model Meta # db table # db tablespace (ignored for now) # unique together (ignored for now) # Now, a useful test model we can use for evaluating diffs >>> class DeleteAnchor1(models.Model): ... value = models.IntegerField() >>> class DeleteAnchor2(models.Model): ... value = models.IntegerField() >>> class DeleteAnchor3(models.Model): ... value = models.IntegerField() >>> class DeleteAnchor4(models.Model): ... value = models.IntegerField() >>> class DeleteBaseModel(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') ... int_field3 = models.IntegerField(unique=True) ... fk_field1 = models.ForeignKey(DeleteAnchor1) ... m2m_field1 = models.ManyToManyField(DeleteAnchor3) ... m2m_field2 = models.ManyToManyField(DeleteAnchor4, db_table='non-default_m2m_table') >>> class CustomTableModel(models.Model): ... value = models.IntegerField() ... alt_value = models.CharField(max_length=20) ... class Meta: ... db_table = 'custom_table_name' # Store the base signatures >>> anchors = ( ... ('DeleteAnchor1', DeleteAnchor1), ... ('DeleteAnchor2', DeleteAnchor2), ... ('DeleteAnchor3', DeleteAnchor3), ... ('DeleteAnchor4', DeleteAnchor4), ... ) >>> custom_model = ('CustomTableModel', CustomTableModel) >>> custom = register_models(custom_model) >>> custom_sig = test_proj_sig(custom_model) >>> test_model = ('TestModel', DeleteBaseModel) >>> start = register_models(*anchors) >>> start.update(register_models(test_model)) >>> start_sig = test_proj_sig(test_model, *anchors) # Deleting a default named column >>> class DefaultNamedColumnModel(models.Model): ... my_id = models.AutoField(primary_key=True) ... char_field = models.CharField(max_length=20) ... int_field2 = models.IntegerField(db_column='non-default_db_column') ... int_field3 = models.IntegerField(unique=True) ... fk_field1 = models.ForeignKey(DeleteAnchor1) ... m2m_field1 = models.ManyToManyField(DeleteAnchor3) ... m2m_field2 = models.ManyToManyField(DeleteAnchor4, db_table='non-default_m2m_table') >>> end = register_models(('TestModel', DefaultNamedColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', DefaultNamedColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'int_field')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #DefaultNamedColumnModel %(DefaultNamedColumnModel)s # Deleting a non-default named column >>> class NonDefaultNamedColumnModel(models.Model): ... 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) ... m2m_field1 = models.ManyToManyField(DeleteAnchor3) ... m2m_field2 = models.ManyToManyField(DeleteAnchor4, db_table='non-default_m2m_table') >>> end = register_models(('TestModel', NonDefaultNamedColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', NonDefaultNamedColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'int_field2')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #NonDefaultNamedColumnModel %(NonDefaultNamedColumnModel)s # Deleting a column with database constraints (unique) # TODO: Verify that the produced SQL is actually correct # -- BK >>> class ConstrainedColumnModel(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') >>> end = register_models(('TestModel', ConstrainedColumnModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', ConstrainedColumnModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'int_field3')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #ConstrainedColumnModel %(ConstrainedColumnModel)s # Deleting a default m2m >>> class DefaultM2MModel(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') ... int_field3 = models.IntegerField(unique=True) ... fk_field1 = models.ForeignKey(DeleteAnchor1) ... m2m_field2 = models.ManyToManyField(DeleteAnchor4, db_table='non-default_m2m_table') >>> end = register_models(('TestModel', DefaultM2MModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', DefaultM2MModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'm2m_field1')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #DefaultManyToManyModel %(DefaultManyToManyModel)s # Deleting a m2m stored in a non-default table >>> class NonDefaultM2MModel(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') ... int_field3 = models.IntegerField(unique=True) ... fk_field1 = models.ForeignKey(DeleteAnchor1) ... m2m_field1 = models.ManyToManyField(DeleteAnchor3) >>> end = register_models(('TestModel', NonDefaultM2MModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', NonDefaultM2MModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'm2m_field2')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #NonDefaultManyToManyModel %(NonDefaultManyToManyModel)s # Delete a foreign key >>> class DeleteForeignKeyModel(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') ... int_field3 = models.IntegerField(unique=True) ... m2m_field1 = models.ManyToManyField(DeleteAnchor3) ... m2m_field2 = models.ManyToManyField(DeleteAnchor4, db_table='non-default_m2m_table') >>> end = register_models(('TestModel', DeleteForeignKeyModel), *anchors) >>> end_sig = test_proj_sig(('TestModel', DeleteForeignKeyModel), *anchors) >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'fk_field1')"] >>> test_sig = copy.deepcopy(start_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(start, end, test_sql) #DeleteForeignKeyModel %(DeleteForeignKeyModel)s # Deleting a column from a non-default table >>> class DeleteColumnCustomTableModel(models.Model): ... alt_value = models.CharField(max_length=20) ... class Meta: ... db_table = 'custom_table_name' >>> end = register_models(('CustomTableModel', DeleteColumnCustomTableModel)) >>> end_sig = test_proj_sig(('CustomTableModel', DeleteColumnCustomTableModel)) >>> d = Diff(custom_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('CustomTableModel', 'value')"] >>> test_sig = copy.deepcopy(custom_sig) >>> test_sql = [] >>> for mutation in d.evolution()['tests']: ... test_sql.extend(mutation.mutate('tests', test_sig)) ... mutation.simulate('tests', test_sig) >>> Diff(test_sig, end_sig).is_empty() True >>> execute_test_sql(custom, end, test_sql) #DeleteColumnCustomTableModel %(DeleteColumnCustomTableModel)s # Clean up after the applications that were installed >>> deregister_models() """ % test_sql_mapping('delete_field') django_evolution-0.6.7/django_evolution/tests/__init__.py0000644000175000017500000000216211471453547024533 0ustar chipx86chipx8600000000000000from signature import tests as signature_tests from add_field import tests as add_field_tests from delete_field import tests as delete_field_tests from delete_model import tests as delete_model_tests from delete_app import tests as delete_app_tests from rename_field import tests as rename_field_tests from change_field import tests as change_field_tests from sql_mutation import tests as sql_mutation_tests from ordering import tests as ordering_tests from generics import tests as generics_tests from inheritance import tests as inheritance_tests from django_evolution import is_multi_db # Define doctests __test__ = { 'signature': signature_tests, 'add_field': add_field_tests, 'delete_field': delete_field_tests, 'delete_model': delete_model_tests, 'delete_app': delete_app_tests, 'rename_field': rename_field_tests, 'change_field': change_field_tests, 'sql_mutation': sql_mutation_tests, 'ordering': ordering_tests, 'generics': generics_tests, 'inheritance': inheritance_tests } if is_multi_db(): from multi_db import tests as multi_db_tests __test__['multi_db'] = multi_db_tests django_evolution-0.6.7/django_evolution/tests/ordering.py0000644000175000017500000000263611471453547024613 0ustar chipx86chipx8600000000000000tests = r""" >>> from django.db import models >>> from django_evolution.tests.utils import test_proj_sig, register_models, deregister_models >>> from django_evolution.diff import Diff >>> import copy >>> class Case41Anchor(models.Model): ... value = models.IntegerField() >>> class Case41Model(models.Model): ... value = models.IntegerField() ... ref = models.ForeignKey(Case41Anchor) # Store the base signatures >>> anchors = ( ... ('Case41Anchor', Case41Anchor), ... ) >>> test_model = ('TestModel', Case41Model) >>> start = register_models(*anchors) >>> start.update(register_models(test_model)) >>> start_sig = test_proj_sig(test_model, *anchors) # Regression case 41: If deleteing a model and a foreign key to that model, # The key deletion needs to happen before the model deletion. # Delete the foreign key... >>> class UpdatedCase41Model(models.Model): ... value = models.IntegerField() >>> end = register_models(('TestModel', UpdatedCase41Model), *anchors) >>> end_sig = test_proj_sig(('TestModel',UpdatedCase41Model), *anchors) # ... And also delete the model that was being referenced >>> _ = end_sig['tests'].pop('Case41Anchor') # The evolution sequence needs >>> d = Diff(start_sig, end_sig) >>> print [str(e) for e in d.evolution()['tests']] ["DeleteField('TestModel', 'ref')", "DeleteModel('Case41Anchor')"] # Clean up after the applications that were installed >>> deregister_models() """ django_evolution-0.6.7/django_evolution/tests/signature.py0000644000175000017500000002506511471453547025004 0ustar chipx86chipx8600000000000000 tests = r""" >>> from django.db import models >>> from django_evolution import signature >>> from django_evolution.diff import Diff >>> from django_evolution.tests.utils import test_proj_sig, register_models, deregister_models >>> from pprint import pprint >>> from django.contrib.contenttypes import generic >>> from django.contrib.contenttypes.models import ContentType # First, a model that has one of everything so we can validate all cases for a signature >>> class Anchor1(models.Model): ... value = models.IntegerField() >>> class Anchor2(models.Model): ... value = models.IntegerField() >>> class Anchor3(models.Model): ... value = models.IntegerField() ... # Host a generic key here, too ... content_type = models.ForeignKey(ContentType) ... object_id = models.PositiveIntegerField(db_index=True) ... content_object = generic.GenericForeignKey('content_type','object_id') >>> anchors = [('Anchor1', Anchor1),('Anchor2', Anchor2),('Anchor3', Anchor3)] >>> class SigModel(models.Model): ... char_field = models.CharField(max_length=20) ... int_field = models.IntegerField() ... null_field = models.IntegerField(null=True, db_column='size_column') ... id_card = models.IntegerField(unique=True, db_index=True) ... dec_field = models.DecimalField(max_digits=10, decimal_places=4) ... ref1 = models.ForeignKey(Anchor1) ... ref2 = models.ForeignKey(Anchor1, related_name='other_sigmodel') ... ref3 = models.ForeignKey(Anchor2, db_column='value', db_index=True) ... ref4 = models.ForeignKey('self') ... ref5 = models.ManyToManyField(Anchor3) ... ref6 = models.ManyToManyField(Anchor3, related_name='other_sigmodel') ... ref7 = models.ManyToManyField('self') ... # Plus a generic foreign key - the Generic itself should be ignored ... content_type = models.ForeignKey(ContentType) ... object_id = models.PositiveIntegerField(db_index=True) ... content_object = generic.GenericForeignKey('content_type','object_id') ... # Plus a generic relation, which should be ignored ... generic = generic.GenericRelation(Anchor3) >>> class ParentModel(models.Model): ... parent_field = models.CharField(max_length=20) >>> class ChildModel(ParentModel): ... child_field = models.CharField(max_length=20) # Store the base signatures >>> base_cache = register_models(('Anchor1', Anchor1), ('Anchor2', Anchor2), ('Anchor3', Anchor3), ('TestModel', SigModel), ('ParentModel',ParentModel), ('ChildModel',ChildModel)) # You can create a model signature for a model >>> pprint(signature.create_model_sig(SigModel)) {'fields': {'char_field': {'field_type': , 'max_length': 20}, 'content_type': {'field_type': , 'related_model': 'contenttypes.ContentType'}, 'dec_field': {'decimal_places': 4, 'field_type': , 'max_digits': 10}, 'id': {'field_type': , 'primary_key': True}, 'id_card': {'db_index': True, 'field_type': , 'unique': True}, 'int_field': {'field_type': }, 'null_field': {'db_column': 'size_column', 'field_type': , 'null': True}, 'object_id': {'db_index': True, 'field_type': }, 'ref1': {'field_type': , 'related_model': 'tests.Anchor1'}, 'ref2': {'field_type': , 'related_model': 'tests.Anchor1'}, 'ref3': {'db_column': 'value', 'field_type': , 'related_model': 'tests.Anchor2'}, 'ref4': {'field_type': , 'related_model': 'tests.TestModel'}, 'ref5': {'field_type': , 'related_model': 'tests.Anchor3'}, 'ref6': {'field_type': , 'related_model': 'tests.Anchor3'}, 'ref7': {'field_type': , 'related_model': 'tests.TestModel'}}, 'meta': {'db_table': 'tests_testmodel', 'db_tablespace': '', 'pk_column': 'id', 'unique_together': []}} >>> pprint(signature.create_model_sig(ChildModel)) {'fields': {'child_field': {'field_type': , 'max_length': 20}, 'parentmodel_ptr': {'field_type': , 'primary_key': True, 'related_model': 'tests.ParentModel', 'unique': True}}, 'meta': {'db_table': 'tests_childmodel', 'db_tablespace': '', 'pk_column': 'parentmodel_ptr_id', 'unique_together': []}} # Now, a useful test model we can use for evaluating diffs >>> class BaseModel(models.Model): ... name = models.CharField(max_length=20) ... age = models.IntegerField() ... ref = models.ForeignKey(Anchor1) >>> start = register_models(('TestModel', BaseModel), *anchors) >>> start_sig = test_proj_sig(('TestModel', BaseModel), *anchors) # An identical model gives an empty Diff >>> class TestModel(models.Model): ... name = models.CharField(max_length=20) ... age = models.IntegerField() ... ref = models.ForeignKey(Anchor1) >>> end = register_models(('TestModel', TestModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',TestModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() True >>> d.evolution() {} # Adding a field gives a non-empty diff >>> class AddFieldModel(models.Model): ... name = models.CharField(max_length=20) ... age = models.IntegerField() ... ref = models.ForeignKey(Anchor1) ... date_of_birth = models.DateField() >>> end = register_models(('TestModel', AddFieldModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',AddFieldModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() False >>> print [str(e) for e in d.evolution()['tests']] # Add Field ["AddField('TestModel', 'date_of_birth', models.DateField, initial=<>)"] # Deleting a field gives a non-empty diff >>> class DeleteFieldModel(models.Model): ... name = models.CharField(max_length=20) ... ref = models.ForeignKey(Anchor1) >>> end = register_models(('TestModel', DeleteFieldModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',DeleteFieldModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() False >>> print [str(e) for e in d.evolution()['tests']] # Delete Field ["DeleteField('TestModel', 'age')"] # Renaming a field is caught as 2 diffs # (For the moment - long term, this should hint as a Rename) >>> class RenameFieldModel(models.Model): ... full_name = models.CharField(max_length=20) ... age = models.IntegerField() ... ref = models.ForeignKey(Anchor1) >>> end = register_models(('TestModel', RenameFieldModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',RenameFieldModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() False >>> print [str(e) for e in d.evolution()['tests']] # Rename Field ["AddField('TestModel', 'full_name', models.CharField, initial=<>, max_length=20)", "DeleteField('TestModel', 'name')"] # Adding a property to a field which was not present in the original Model >>> class AddPropertyModel(models.Model): ... name = models.CharField(max_length=20) ... age = models.IntegerField(null=True) ... ref = models.ForeignKey(Anchor1) >>> end = register_models(('TestModel', AddPropertyModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',AddPropertyModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() False >>> print [str(e) for e in d.evolution()['tests']] # Change Field - add property ["ChangeField('TestModel', 'age', initial=None, null=True)"] # Since we can't check the evolutions, check the diff instead >>> print d In model tests.TestModel: In field 'age': Property 'null' has changed # Adding a property of a field which was not present in the original Model, but # is now set to the default for that property. >>> class AddDefaultPropertyModel(models.Model): ... name = models.CharField(max_length=20) ... age = models.IntegerField(null=False) ... ref = models.ForeignKey(Anchor1) >>> end = register_models(('TestModel', AddDefaultPropertyModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',AddDefaultPropertyModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() True >>> print d.evolution() {} # Changing a property of a field >>> class ChangePropertyModel(models.Model): ... name = models.CharField(max_length=30) ... age = models.IntegerField() ... ref = models.ForeignKey(Anchor1) >>> end = register_models(('TestModel', ChangePropertyModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',ChangePropertyModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() False >>> print [str(e) for e in d.evolution()['tests']] # Change Field - change property ["ChangeField('TestModel', 'name', initial=None, max_length=30)"] # Since we can't check the evolutions, check the diff instead >>> print d In model tests.TestModel: In field 'name': Property 'max_length' has changed # Changing the model that a ForeignKey references >>> class ChangeFKModel(models.Model): ... name = models.CharField(max_length=20) ... age = models.IntegerField() ... ref = models.ForeignKey(Anchor2) >>> end = register_models(('TestModel', ChangeFKModel), *anchors) >>> test_sig = test_proj_sig(('TestModel',ChangeFKModel), *anchors) >>> d = Diff(start_sig, test_sig) >>> d.is_empty() False >>> print [str(e) for e in d.evolution()['tests']] # Change Field - change property ["ChangeField('TestModel', 'ref', initial=None, related_model='tests.Anchor2')"] # Clean up after the applications that were installed >>> deregister_models() """ django_evolution-0.6.7/django_evolution/tests/utils.py0000644000175000017500000003115311600341247024121 0ustar chipx86chipx8600000000000000from datetime import datetime from django.core.management import sql from django.core.management.color import no_style from django.db import connection, transaction, settings, models from django.db.backends.util import truncate_name from django.db.models.loading import cache from django.utils.datastructures import SortedDict from django.utils.functional import curry from django_evolution import signature, is_multi_db from django_evolution.tests import models as evo_test from django_evolution.utils import write_sql, execute_sql import copy if is_multi_db(): from django.db import connections from django.db.utils import DEFAULT_DB_ALIAS DEFAULT_TEST_ATTRIBUTE_VALUES = { models.CharField: 'TestCharField', models.IntegerField: '123', models.AutoField: None, models.DateTimeField: datetime.now(), models.PositiveIntegerField: '42' } def wrap_sql_func(func, evo_test, style, db_name=None): if is_multi_db(): return func(evo_test, style, connections[db_name or DEFAULT_DB_ALIAS]) else: return func(evo_test, style) # Wrap the sql.* functions to work with the multi-db support sql_create = curry(wrap_sql_func, sql.sql_create) sql_indexes = curry(wrap_sql_func, sql.sql_indexes) sql_delete = curry(wrap_sql_func, sql.sql_delete) def _register_models(app_label='tests', db_name='default', *models): app_cache = SortedDict() my_connection = connection if is_multi_db(): my_connection = connections[db_name or DEFAULT_DB_ALIAS] max_name_length = my_connection.ops.max_name_length() for name, model in reversed(models): if model._meta.module_name in cache.app_models['django_evolution']: del cache.app_models['django_evolution'][model._meta.module_name] orig_db_table = model._meta.db_table orig_object_name = model._meta.object_name orig_module_name = model._meta.module_name generated_db_table = truncate_name( '%s_%s' % (model._meta.app_label, model._meta.module_name), max_name_length) if orig_db_table.startswith(generated_db_table): model._meta.db_table = '%s_%s' % (app_label, name.lower()) model._meta.db_table = truncate_name(model._meta.db_table, max_name_length) model._meta.app_label = app_label model._meta.object_name = name model._meta.module_name = name.lower() add_app_test_model(model, app_label=app_label) for field in model._meta.local_many_to_many: if not field.rel.through: continue through = field.rel.through generated_db_table = truncate_name( '%s_%s' % (orig_db_table, field.name), max_name_length) if through._meta.db_table == generated_db_table: through._meta.app_label = app_label # Transform the 'through' table information only # if we've transformed the parent db_table. if model._meta.db_table != orig_db_table: through._meta.db_table = \ '%s_%s' % (model._meta.db_table, field.name) through._meta.object_name = \ through._meta.object_name.replace( orig_object_name, model._meta.object_name) through._meta.module_name = \ through._meta.module_name.replace( orig_module_name, model._meta.module_name) through._meta.db_table = \ truncate_name(through._meta.db_table, max_name_length) for field in through._meta.local_fields: if field.rel and field.rel.to: column = field.column if (column.startswith(orig_module_name) or column.startswith('to_%s' % orig_module_name) or column.startswith('from_%s' % orig_module_name)): field.column = column.replace( orig_module_name, model._meta.module_name) if (through._meta.module_name in cache.app_models['django_evolution']): del cache.app_models['django_evolution'][ through._meta.module_name] app_cache[through._meta.module_name] = through add_app_test_model(through, app_label=app_label) app_cache[model._meta.module_name] = model if evo_test not in cache.app_store: cache.app_store[evo_test] = len(cache.app_store) if hasattr(cache, 'app_labels'): cache.app_labels[app_label] = evo_test return app_cache def register_models(*models): return _register_models('tests', 'default', *models) def register_models_multi(app_label, db_name, *models): return _register_models(app_label, db_name, *models) def _test_proj_sig(app_label, *models, **kwargs): "Generate a dummy project signature based around a single model" version = kwargs.get('version', 1) proj_sig = { app_label: SortedDict(), '__version__': version, } # Compute the project siguature for full_name, model in models: parts = full_name.split('.') if len(parts) == 1: name = parts[0] app = app_label else: app, name = parts proj_sig.setdefault(app, SortedDict())[name] = \ signature.create_model_sig(model) return proj_sig def test_proj_sig(*models, **kwargs): return _test_proj_sig('tests', *models, **kwargs) def test_proj_sig_multi(app_label, *models, **kwargs): return _test_proj_sig(app_label, *models, **kwargs) def execute_transaction(sql, output=False, database='default'): "A transaction wrapper for executing a list of SQL statements" my_connection = connection using_args = {} if is_multi_db(): if not database: database = DEFAULT_DB_ALIAS my_connection = connections[database] using_args['using'] = database try: # Begin Transaction transaction.enter_transaction_management(**using_args) transaction.managed(True, **using_args) cursor = my_connection.cursor() # Perform the SQL if output: write_sql(sql, database) execute_sql(cursor, sql) transaction.commit(**using_args) transaction.leave_transaction_management(**using_args) except Exception: transaction.rollback(**using_args) raise def execute_test_sql(start, end, sql, debug=False, app_label='tests', database='default'): """ Execute a test SQL sequence. This method also creates and destroys the database tables required by the models registered against the test application. start and end are the start- and end-point states of the application cache. sql is the list of sql statements to execute. cleanup is a list of extra sql statements required to clean up. This is primarily for any extra m2m tables that were added during a test that won't be cleaned up by Django's sql_delete() implementation. debug is a helper flag. It displays the ALL the SQL that would be executed, (including setup and teardown SQL), and executes the Django-derived setup/teardown SQL. """ # Set up the initial state of the app cache set_app_test_models(copy.deepcopy(start), app_label=app_label) # Install the initial tables and indicies style = no_style() execute_transaction(sql_create(evo_test, style, database), output=debug, database=database) execute_transaction(sql_indexes(evo_test, style, database), output=debug, database=database) create_test_data(models.get_models(evo_test), database) # Set the app cache to the end state set_app_test_models(copy.deepcopy(end), app_label=app_label) try: # Execute the test sql if debug: write_sql(sql, database) else: execute_transaction(sql, output=True, database=database) finally: # Cleanup the apps. if debug: print sql_delete(evo_test, style, database) else: execute_transaction(sql_delete(evo_test, style, database), output=debug, database=database) def create_test_data(app_models, database): deferred_models = [] deferred_fields = {} using_args = {} if is_multi_db(): using_args['using'] = database for model in app_models: params = {} deferred = False for field in model._meta.fields: if not deferred: if type(field) in (models.ForeignKey, models.ManyToManyField): related_model = field.rel.to related_q = related_model.objects.all() if is_multi_db(): related_q = related_q.using(database) if related_q.count(): related_instance = related_q[0] else: if field.null == False: # Field cannot be null yet the related object # hasn't been created yet Defer the creation of # this model deferred = True deferred_models.append(model) else: # Field cannot be set yet but null is acceptable # for the moment deferred_fields[type(model)] = \ deferred_fields.get(type(model), []).append(field) related_instance = None if not deferred: if type(field) == models.ForeignKey: params[field.name] = related_instance else: params[field.name] = [related_instance] else: params[field.name] = \ DEFAULT_TEST_ATTRIBUTE_VALUES[type(field)] if not deferred: model(**params).save(**using_args) # Create all deferred models. if deferred_models: create_test_data(deferred_models, database) # All models should be created (Not all deferred fields have been populated # yet) Populate deferred fields that we know about. Here lies untested # code! if deferred_fields: for model, field_list in deferred_fields.items(): for field in field_list: related_model = field.rel.to related_instance = related_model.objects.using(database)[0] if type(field) == models.ForeignKey: setattr(model, field.name, related_instance) else: getattr(model, field.name).add(related_instance, **using_args) model.save(**using_args) def test_sql_mapping(test_field_name, db_name='default'): if is_multi_db(): engine = settings.DATABASES[db_name]['ENGINE'].split('.')[-1] else: engine = settings.DATABASE_ENGINE sql_for_engine = __import__('django_evolution.tests.db.%s' % (engine), {}, {}, ['']) return getattr(sql_for_engine, test_field_name) def deregister_models(app_label='tests'): "Clear the test section of the app cache" del cache.app_models[app_label] clear_models_cache() def clear_models_cache(): """Clears the Django models cache. This cache is used in Django >= 1.2 to quickly return results from cache.get_models(). It needs to be cleared when modifying the model registry. """ if hasattr(cache, '_get_models_cache'): # On Django 1.2, we need to clear this cache when unregistering models. cache._get_models_cache.clear() def set_app_test_models(models, app_label): """Sets the list of models in the Django test models registry.""" cache.app_models[app_label] = models clear_models_cache() def add_app_test_model(model, app_label): """Adds a model to the Django test models registry.""" key = model._meta.object_name.lower() cache.app_models.setdefault(app_label, SortedDict())[key] = model clear_models_cache() django_evolution-0.6.7/ez_setup.py0000644000175000017500000002231311471453547020122 0ustar chipx86chipx8600000000000000#!python """Bootstrap setuptools installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from ez_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import sys DEFAULT_VERSION = "0.6c8" DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] md5_data = { 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', } import sys, os def _validate_md5(egg_name, data): if egg_name in md5_data: from md5 import md5 digest = md5(data).hexdigest() if digest != md5_data[egg_name]: print >>sys.stderr, ( "md5 validation of %s failed! (Possible download problem?)" % egg_name ) sys.exit(2) return data def use_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15 ): """Automatically find/download setuptools and make it available on sys.path `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where setuptools will be downloaded, if it is not already available. If `download_delay` is specified, it should be the number of seconds that will be paused before initiating a download, should one be required. If an older version of setuptools is installed, this routine will print a message to ``sys.stderr`` and raise SystemExit in an attempt to abort the calling script. """ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules def do_download(): egg = download_setuptools(version, download_base, to_dir, download_delay) sys.path.insert(0, egg) import setuptools; setuptools.bootstrap_install_from = egg try: import pkg_resources except ImportError: return do_download() try: pkg_resources.require("setuptools>="+version); return except pkg_resources.VersionConflict, e: if was_imported: print >>sys.stderr, ( "The required version of setuptools (>=%s) is not available, and\n" "can't be installed while this script is running. Please install\n" " a more recent version first, using 'easy_install -U setuptools'." "\n\n(Currently using %r)" ) % (version, e.args[0]) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return do_download() except pkg_resources.DistributionNotFound: return do_download() def download_setuptools( version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay = 15 ): """Download setuptools from a specified location and return its filename `version` should be a valid setuptools version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ import urllib2, shutil egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) url = download_base + egg_name saveto = os.path.join(to_dir, egg_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: from distutils import log if delay: log.warn(""" --------------------------------------------------------------------------- This script requires setuptools version %s to run (even to display help). I will attempt to download it for you (from %s), but you may need to enable firewall access for this script first. I will start the download in %d seconds. (Note: if this machine does not have network access, please obtain the file %s and place it in this directory before rerunning this script.) ---------------------------------------------------------------------------""", version, download_base, delay, url ); from time import sleep; sleep(delay) log.warn("Downloading %s", url) src = urllib2.urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = _validate_md5(egg_name, src.read()) dst = open(saveto,"wb"); dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" try: import setuptools except ImportError: egg = None try: egg = download_setuptools(version, delay=0) sys.path.insert(0,egg) from setuptools.command.easy_install import main return main(list(argv)+[egg]) # we're done here finally: if egg and os.path.exists(egg): os.unlink(egg) else: if setuptools.__version__ == '0.0.1': print >>sys.stderr, ( "You have an obsolete version of setuptools installed. Please\n" "remove it from your system entirely before rerunning this script." ) sys.exit(2) req = "setuptools>="+version import pkg_resources try: pkg_resources.require(req) except pkg_resources.VersionConflict: try: from setuptools.command.easy_install import main except ImportError: from easy_install import main main(list(argv)+[download_setuptools(delay=0)]) sys.exit(0) # try to force an exit else: if argv: from setuptools.command.easy_install import main main(argv) else: print "Setuptools version",version,"or greater has been installed." print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' def update_md5(filenames): """Update our built-in md5 registry""" import re from md5 import md5 for name in filenames: base = os.path.basename(name) f = open(name,'rb') md5_data[base] = md5(f.read()).hexdigest() f.close() data = [" %r: %r,\n" % it for it in md5_data.items()] data.sort() repl = "".join(data) import inspect srcfile = inspect.getsourcefile(sys.modules[__name__]) f = open(srcfile, 'rb'); src = f.read(); f.close() match = re.search("\nmd5_data = {\n([^}]+)}", src) if not match: print >>sys.stderr, "Internal error!" sys.exit(2) src = src[:match.start(1)] + repl + src[match.end(1):] f = open(srcfile,'w') f.write(src) f.close() if __name__=='__main__': if len(sys.argv)>2 and sys.argv[1]=='--md5update': update_md5(sys.argv[2:]) else: main(sys.argv[1:]) django_evolution-0.6.7/django_evolution.egg-info/0000755000175000017500000000000011741741024022737 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/django_evolution.egg-info/dependency_links.txt0000644000175000017500000000000111741741024027005 0ustar chipx86chipx8600000000000000 django_evolution-0.6.7/django_evolution.egg-info/top_level.txt0000644000175000017500000000002711741741024025470 0ustar chipx86chipx8600000000000000tests django_evolution django_evolution-0.6.7/django_evolution.egg-info/PKG-INFO0000644000175000017500000000123211741741024024032 0ustar chipx86chipx8600000000000000Metadata-Version: 1.0 Name: django-evolution Version: 0.6.7 Summary: A database schema evolution tool for the Django web framework. Home-page: http://code.google.com/p/django-evolution/ Author: Christian Hammond Author-email: chipx86@chipx86.com License: UNKNOWN Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Framework :: Django Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries :: Python Modules django_evolution-0.6.7/django_evolution.egg-info/requires.txt0000644000175000017500000000001511741741024025333 0ustar chipx86chipx8600000000000000Django>=1.1.1django_evolution-0.6.7/django_evolution.egg-info/SOURCES.txt0000644000175000017500000000361511741741024024630 0ustar chipx86chipx8600000000000000AUTHORS LICENSE MANIFEST.in README ez_setup.py setup.py django_evolution/__init__.py django_evolution/admin.py django_evolution/diff.py django_evolution/evolve.py django_evolution/models.py django_evolution/mutations.py django_evolution/signature.py django_evolution/utils.py django_evolution.egg-info/PKG-INFO django_evolution.egg-info/SOURCES.txt django_evolution.egg-info/dependency_links.txt django_evolution.egg-info/requires.txt django_evolution.egg-info/top_level.txt django_evolution/builtin_evolutions/__init__.py django_evolution/builtin_evolutions/auth_delete_message.py django_evolution/builtin_evolutions/session_expire_date_db_index.py django_evolution/db/__init__.py django_evolution/db/common.py django_evolution/db/mysql.py django_evolution/db/mysql_old.py django_evolution/db/postgresql.py django_evolution/db/postgresql_psycopg2.py django_evolution/db/sqlite3.py django_evolution/management/__init__.py django_evolution/management/commands/__init__.py django_evolution/management/commands/evolve.py django_evolution/tests/__init__.py django_evolution/tests/add_field.py django_evolution/tests/change_field.py django_evolution/tests/delete_app.py django_evolution/tests/delete_field.py django_evolution/tests/delete_model.py django_evolution/tests/generics.py django_evolution/tests/inheritance.py django_evolution/tests/models.py django_evolution/tests/multi_db.py django_evolution/tests/ordering.py django_evolution/tests/rename_field.py django_evolution/tests/signature.py django_evolution/tests/sql_mutation.py django_evolution/tests/utils.py django_evolution/tests/db/__init__.py django_evolution/tests/db/mysql.py django_evolution/tests/db/mysql_old.py django_evolution/tests/db/postgresql.py django_evolution/tests/db/postgresql_psycopg2.py django_evolution/tests/db/sqlite3.py docs/evolution.txt docs/faq.txt tests/__init__.py tests/pyflakes.exclude tests/run-pyflakes.py tests/runtests.py tests/settings.pydjango_evolution-0.6.7/AUTHORS0000644000175000017500000000151311741740416016753 0ustar chipx86chipx8600000000000000 The PRIMARY AUTHORS of Django Evolution are: Ben Khoo Russell Keith-Magee Christian Hammond And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- people who have submitted patches, reported bugs, and generally made Django Evolution that much better: Chris Beaven (SmileyChris) Eric Florenzano (floguy) Jacob Kaplan-Moss Yann Malet Carl J Meyer Todd O'Bryan Christopher Petrilli Massimiliano Ravelli Michael Trier Mike Conley Marc Bee django_evolution-0.6.7/setup.cfg0000644000175000017500000000007311741741024017520 0ustar chipx86chipx8600000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 django_evolution-0.6.7/setup.py0000755000175000017500000000232411471453547017427 0ustar chipx86chipx8600000000000000#!/usr/bin/env python # # Setup script for Django Evolution from ez_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages from setuptools.command.test import test import django_evolution def run_tests(*args): import os os.system('tests/runtests.py') test.run_tests = run_tests PACKAGE_NAME = 'django_evolution' # Build the package setup( name=PACKAGE_NAME, version=django_evolution.__version__, description='A database schema evolution tool for the Django web framework.', url='http://code.google.com/p/django-evolution/', author='Ben Khoo', author_email='khoobks@westnet.com.au', maintainer='Christian Hammond', maintainer_email='chipx86@chipx86.com', packages=find_packages(), install_requires=[ 'Django>=1.1.1', ], include_package_data=True, classifiers=[ 'Development Status :: 4 - Beta', 'Framework :: Django', 'Intended Audience :: Developers', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Software Development', 'Topic :: Software Development :: Libraries :: Python Modules', ] ) django_evolution-0.6.7/tests/0000755000175000017500000000000011741741024017041 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/tests/run-pyflakes.py0000755000175000017500000000216011471453547022047 0ustar chipx86chipx8600000000000000#!/usr/bin/env python # # Utility script to run pyflakes with the modules we care about and # exclude errors we know to be fine. import os import re import subprocess import sys def main(): cur_dir = os.path.dirname(__file__) os.chdir(os.path.join(cur_dir, "..")) modules = sys.argv[1:] if not modules: modules = ['django_evolution'] p = subprocess.Popen(['pyflakes'] + modules, stderr=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) contents = p.stdout.readlines() # Read in the exclusions file exclusions = {} fp = open(os.path.join(cur_dir, "pyflakes.exclude"), "r") for line in fp.readlines(): if not line.startswith("#"): exclusions[line.rstrip()] = 1 fp.close() # Now filter thin for line in contents: line = line.rstrip() test_line = re.sub(r':[0-9]+:', r':*:', line, 1) test_line = re.sub(r'line [0-9]+', r'line *', test_line) if test_line not in exclusions: print line if __name__ == "__main__": main() django_evolution-0.6.7/tests/runtests.py0000755000175000017500000000344011471453547021320 0ustar chipx86chipx8600000000000000#!/usr/bin/env python import nose import os import sys def run_tests(verbosity=1, interactive=False): from django.conf import settings from django.core import management from django.test.utils import setup_test_environment, \ teardown_test_environment from django_evolution import is_multi_db setup_test_environment() settings.DEBUG = False old_db_names = [] if is_multi_db(): from django.db import connections for alias in connections: connection = connections[alias] old_db_names.append((connection, connection.settings_dict['NAME'])) connection.creation.create_test_db(verbosity, autoclobber=not interactive) else: from django.db import connection old_db_names.append((connection, settings.DATABASE_NAME)) connection.creation.create_test_db(verbosity, autoclobber=not interactive) management.call_command('syncdb', verbosity=verbosity, interactive=interactive) nose_argv = ['runtests.py', '-v', '--with-coverage', '--with-doctest', '--doctest-extension=.txt', '--cover-package=django_evolution', '--match=tests[\/]*.py'] if len(sys.argv) > 2: nose_argv += sys.argv[2:] nose.run(argv=nose_argv) for connection, name in old_db_names: connection.creation.destroy_test_db(name, verbosity=0) teardown_test_environment() if __name__ == "__main__": os.chdir(os.path.join(os.path.dirname(__file__), "..")) sys.path.insert(0, os.getcwd()) os.environ['DJANGO_SETTINGS_MODULE'] = "tests.settings" run_tests() django_evolution-0.6.7/tests/settings.py0000644000175000017500000000526111562143704021261 0ustar chipx86chipx8600000000000000import os DEBUG = True TEMPLATE_DEBUG = DEBUG ADMINS = ( # ('Your Name', 'your_email@domain.com'), ) MANAGERS = ADMINS DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'django_evolution_test.db', }, 'db_multi': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'django_evolution_test_multi.db', }, } # For testing compatibility on Django 1.1. DATABASE_ENGINE = 'sqlite3' DATABASE_NAME = 'django_evolution_test.db' # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. TIME_ZONE = 'America/Chicago' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'en-us' SITE_ID = 1 # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. USE_I18N = True # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" MEDIA_ROOT = '' # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" MEDIA_URL = '' # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. # Examples: "http://foo.com/media/", "/media/". ADMIN_MEDIA_PREFIX = '/media/' # Make this unique, and don't share it with anybody. SECRET_KEY = 'af=y9ydd51a0g#bevy0+p#(7ime@m#k)$4$9imoz*!rl97w0j0' # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', 'django.template.loaders.app_directories.load_template_source', # 'django.template.loaders.eggs.load_template_source', ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.middleware.doc.XViewMiddleware', ) ROOT_URLCONF = 'urls' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". # Always use forward slashes, even on Windows. # Don't forget to use absolute paths, not relative paths. ) INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django_evolution', ] django_evolution-0.6.7/tests/pyflakes.exclude0000644000175000017500000000206711471453547022251 0ustar chipx86chipx8600000000000000django_evolution/diff.py:*: 'from django.db.models.fields.related import *' used; unable to detect undefined names django_evolution/mutations.py:*: 'from django.db.models.fields import *' used; unable to detect undefined names django_evolution/mutations.py:*: 'from django.db.models.fields.related import *' used; unable to detect undefined names django_evolution/signature.py:*: 'from django.db.models.fields.related import *' used; unable to detect undefined names django_evolution/tests/db/mysql_old.py:*: 'from mysql import *' used; unable to detect undefined names django_evolution/tests/db/postgresql_psycopg2.py:*: 'from postgresql import *' used; unable to detect undefined names django_evolution/management/__init__.py:*: redefinition of unused 'pickle' from line * django_evolution/management/commands/evolve.py:*: redefinition of unused 'pickle' from line * django_evolution/db/mysql_old.py:*: 'from mysql import *' used; unable to detect undefined names django_evolution/db/postgresql_psycopg2.py:*: 'from postgresql import *' used; unable to detect undefined names django_evolution-0.6.7/tests/__init__.py0000644000175000017500000000000011471453547021152 0ustar chipx86chipx8600000000000000django_evolution-0.6.7/README0000644000175000017500000000100711471453547016567 0ustar chipx86chipx8600000000000000================ Django Evolution ================ An implementation of schema evolution for the Django web framework. For details on how to use Django Evolution, read the tutorial/instructions contained in docs/evolution.txt. Django Evolution is a work in progress; the interfaces are not guaranteed to be stable, and the SQL that is generated may sometimes be in error. In short, don't deploy this on a production site. If you do, and it breaks, you get to keep every single one of the shiny little pieces. :-) django_evolution-0.6.7/docs/0000755000175000017500000000000011741741024016627 5ustar chipx86chipx8600000000000000django_evolution-0.6.7/docs/evolution.txt0000644000175000017500000005561211471453547021437 0ustar chipx86chipx8600000000000000================ Django Evolution ================ When you run ``./manage.py syncdb``, Django will look for any new models that have been defined, and add a database table to represent those new models. However, if you make a change to an existing model, ``./manage.py syncdb`` will not make any changes to the database. This is where **Django Evolution** fits in. Django Evolution is an extension to Django that allows you to track changes in your models over time, and to update the database to reflect those changes. .. admonition:: Work in Progress Django Evolution is a work in progress. The interface and usage described in this document is subject to change as we finesse the details. The SQL that is automatically generated may occasionally be wrong. There are some significant limitations to the capabilities of the system. In short, Django Evolution is not yet ready for a production rollout. An example ========== To demonstrate how Django Evolution works, let's work through the lifecycle of a Django application - a new super-cool blogging application called ``Blogette``. After some initial design work, we come up with the following models:: class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() date_of_birth = models.DateField() def __unicode__(self): return self.name class Entry(models.Model): headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateTimeField() author = models.ForeignKey(Author) def __unicode__(self): return self.headline We create a test project, create our ``blogette`` application directory, put the model definitions in the models.py file, add ``blogette`` to ``settings.INSTALLED_APPS``, and run ``./manage.py syncdb``. This installs the application, creating the Entry and Author tables. The first changes ================= But why do we want to track the date of birth of the Author? In retrospect, this field was a mistake, so let's delete that field from the model. However, if we just delete the field and run ``syncdb`` again, Django won't do anything to respond to the change. We need to use a slightly different process. Before we start making changes to ``models.py``, we need to set up the project to allow evolutions. To do this, we add ``django_evolution`` to the ``settings.INSTALLED_APPS`` and run ``./manage.py syncdb``. This sets up the tables for tracking model changes:: $ ./manage.py syncdb Creating table django_evolution Installing baseline version for testproject.blogette Loading 'initial_data' fixtures... No fixtures found. Now we can go into ``models.py`` remove the ``date_of_birth`` field. After removing the field, ``./manage.py syncdb`` will provide a warning that changes have been detected:: $ ./manage.py syncdb Project signature has changed - an evolution is required Loading 'initial_data' fixtures... No fixtures found. The evolution process itself is controlled using a new management command -- **evolve**. This command is made available when you install the ``django_evolution`` application in your project. If you want to know what has changed, you can use ask Django Evolution to provide a hint:: $ ./manage.py evolve --hint #----- Evolution for blogette from django_evolution.mutations import * from django.db import models MUTATIONS = [ DeleteField('Author', 'date_of_birth') ] #---------------------- Trial evolution successful. Run './manage.py evolve --execute' to apply evolution. The output of the hint is a Django Evolution. An Evolution is python code that defines a list of mutations that need to be made to update a model - in this case, the fact that we need to delete the ``date_of_birth`` field from the model ``Author``. If you want to see what the SQL would look like for this evolution, you can use the ``--sql`` option:: $ ./manage.py evolve --hint --sql ;; Compiled evolution SQL for blogette ALTER TABLE "blogette_person" DROP COLUMN "date_of_birth" CASCADE; .. note:: The SQL output shown here is what would be produced by the Postgres backend; if you are using a different backend, the SQL produced by this step will be slightly different. If we wanted to, we could pipe this SQL output directly into our database. however, we don't need to - we can get Django Evolution to do this for us, by passing the execute option to ``evolve``. When you do this, you will be given a stern warning that you may destroy data:: $ ./manage.py evolve --hint --execute You have requested a database evolution. This will alter tables and data currently in the 'blogette' database, and may result in IRREVERSABLE DATA LOSS. Evolutions should be *thoroughly* reviewed prior to execution. Are you sure you want to execute the evolutions? Type 'yes' to continue, or 'no' to cancel: Assuming you answer ``yes``, Django Evolution should respond:: Evolution successful. Now if we re-run syncdb, we find that there are no evolution warnings:: $ ./manage.py syncdb Loading 'initial_data' fixtures... No fixtures found. If we were to inspect the database itself, we will find that the ``date_of_birth`` field has been deleted. Stored evolutions ================= At this point, we are happy with the model definitions. Once we have developed the views and templates for Blogette, we can deploy Version 1 of Blogette on our production server. We copy our source code to the production server, run syncdb, and the production server will have tables that represent the latest version of our models. Now we can start work on Blogette Version 2. For this release, we decide to add a 'location' field to Authors, and a 'summary' field to entries. This means we now will need to make changes to models.py. However, we now have a production server with the Version 1 models deployed. This means that we need to keep track of any changes we make during the development process so that we can duplicate those changes on our production server. To do this, we make use of stored evolutions - a mechanism for defining and reusing collections of evolutions for later use. Mutation definitions -------------------- Let's start with adding the ``location`` field to ``Authors``. First, we edit ``models.py`` to add a new CharField definition, with a length of 100. We will also allow null values, since we have existing data that won't have an author location. Add the following field definition to authors:: location = models.CharField(max_length=100, null=True) After making this change to the Author model, we know an evolution will be required; if we run ``evolve --hint`` we will get the exact change required:: $ ./manage.py evolve --hint #----- Evolution for blogette from django_evolution.mutations import * from django.db import models MUTATIONS = [ AddField('Author', 'location', models.CharField, max_length=100, null=True) ] #---------------------- Trial evolution successful. Run './manage.py evolve --execute' to apply evolution. At this point, we *could* just run ``evolve --hint --execute`` to update the development server. However, we want to remember this change, so we need to store it in a way that Django Evolution can use it. To do this, we need to create an evolution store. In the blogette directory, we create an ``evolutions`` module. In this module, we create two files - ``__init__.py``, and ``add_location.py``. The ``blogette`` application directory should now look something like this:: /blogette /evolutions __init__.py add_location.py models.py views.py ``add_location.py`` is a file used to describe the evolution we want to perform. The contents of this file is exactly the same as the ``evolve --hint`` command produced -- just copy the content between the lines marked ``----`` into ``add_location.py``. We then need to define an evolution sequence. This sequence defines the order in which evolutions need to be applied. Since we only have one evolution, the definition looks like this:: SEQUENCE = ['add_location'] Put this statement in ``__init__.py``, and you've have defined your first stored evolution. Now we can apply the evolution. We don't need a hint this time - we have a stored evolution, so we can just run ``evolve``:: $ ./manage.py evolve #----- Evolution for blogette from django_evolution.mutations import * from django.db import models MUTATIONS = [ AddField('Author', 'location', models.CharField, max_length=100, null=True) ] #---------------------- Trial evolution successful. Run './manage.py evolve --execute' to apply evolution. This shows us the sequence of mutations described by our stored evolution sequence. It also tells us that the trial evolution was successful - every time you run ``evolve``, Django Evolution will simulate the changes to make sure that the mutations that are defined will reconcile the differences between the models.py file and the state of the database. Since the simulation was successful, we can apply the evolution using the ``--execute`` flag:: $ ./manage.py evolve --execute ... Evolution successful. .. note:: The warning and prompt for confirmation will be displayed every time you evolve your database. It is omitted for clarity in this and later examples. If you don't want to be prompted every time, use the ``--noinput`` option. SQL mutations ------------- Now we need to add the ``summary`` field to ``Entry``. We could follow the same procedure - however, we going to do something a little different. Rather than define a Python Evolution file, we're going to define our mutation in raw SQL. The process of adding a stored SQL evolution is very similar to adding a stored Python evolution. In this case, we're going to call our mutation ``add_summary``, so we create a file called ``add_summary.sql`` to the evolutions directory. Into this file, we put the SQL that will make the change we require. In our case, this means:: ALTER TABLE blogette_entry ADD COLUMN summary varchar(100) NULL; Then, we add the new evolution to the evolution sequence in ``evolutions/__init__.py``. The sequence should now look like this:: SEQUENCE = ['add_location', 'add_summary'] We have now defined our SQL mutation, and how it should be executed, so we can trial the evolution:: $ ./manage.py evolve #----- Evolution for blogette from django_evolution.mutations import * from django.db import models MUTATIONS = [ SQLMutation('add_summary') ] #---------------------- Evolution could not be simulated, possibly due to raw SQL mutations. Unfortunately, Django Evolution can't simulate SQL mutations, so we can't be sure that the mutation is correct. However, we can inspect the SQL that will be used using the --sql option:: $ ./manage.py evolve --sql ;; Compiled evolution SQL for blogette ALTER TABLE blogette_entry ADD COLUMN summary varchar(100) NULL; If we are satisfied that this SQL is correct, we can execute it:: $ ./manage.py evolve --execute ... Evolution successful. Meanwhile, on the production site... ------------------------------------ Now that we have finished Blogette Version 2, we can update our existing production server. We copy the Version 2 source code, including the evolutions, to the production server. We then run ./manage.py syncdb, which reports that evolutions are required:: $ ./manage.py syncdb Project signature has changed - an evolution is required There are unapplied evolutions for blogette. Loading 'initial_data' fixtures... No fixtures found. If we run evolve, we can see the full sequence of mutations that will be applied:: $ ./manage.py evolve #----- Evolution for blogette from django_evolution.mutations import * from django.db import models MUTATIONS = [ AddField('Author', 'location', models.CharField, max_length=100, null=True), SQLMutation('add_summary') ] #---------------------- Evolution could not be simulated, possibly due to raw SQL mutations. Again, since there is a raw SQL migration involved, we will need to validate the migration ourselves using the ``--sql`` option:: $ ./manage.py evolve --sql ;; Compiled evolution SQL for testproject.blogette ALTER TABLE blogette_author ADD COLUMN location varchar(100) NULL; ALTER TABLE blogette_entry ADD COLUMN summary varchar(100) NULL; If we are happy with this sequence, we can apply it:: $ ./manage.py evolve --execute ... Evolution successful. Our production site now has a database that mirrors the changes made on the development server. Reference ========= The Contract ------------ Django Evolution imposes one important restriction on your development process: If you intend to make a change to your database, you **must** do it through Django Evolution. If you modify the database outside of Django and the Django Evolution framework, you're on your own. The operation of Django Evolution is entirely based upon the observing and storing changes to the models.py file. As a result, any changes to the database will not be observed by Django Evolution, and won't be used in evaluating hints or establishing if evolution simulations have been successful. .. admonition:: Room for improvement This is one area of Django Evolution that could be significantly improved. Adding database introspection to allow for external database modifications is on the plan for the future, but for the moment, stick to the contract. Usage of the ``evolve`` command ------------------------------- ``./manage.py evolve`` does not require any arguments or options. When run without any options, ``./manage.py evolve`` will list the evolutions that have been stored and are available, but have not yet been applied to the database. You may optionally provide the name of one or more applications as an argument to evolve. This will restrict the output of the command to those applications that are named. You cannot specify application names if you use the ``--execute`` option. Evolutions cannot be executed on a per-application basis. They must be applied across the entire project, or not at all. The following options may also be used to alter the behavior of the ``evolve`` command. --hint ~~~~~~ Provide a hinted list of mutations for migrating. If no application labels are provided, hints for the entire project will be generated. If one or more application names are provided, the hints provided will be restricted to those applications. May be combined with ``--sql`` to generate hinted mutations in SQL format. May be combined with ``--execute`` to apply the changes to the database. --sql ~~~~~ Convert an evolution from Python syntax to SQL syntax. If ``--hint`` is specified, the hinted list of mutations will be converted. If ``--hint`` is not specified, the generated SQL will be for any stored evolutions that have not been applied. --execute (-x) ~~~~~~~~~~~~~~ Apply evolutions to the database. If ``--hint`` is specified, the hinted list of mutations will be applied. If ``--hint`` is not specified, this command will apply any unapplied stored evolutions. .. note:: You cannot specify an application name if you are trying to execute an evolution. Evolutions must be applied across the entire database. --purge ~~~~~~~ Remove any stale applications from the database. If you remove an application from the ``INSTALLED_APPS`` list, the database tables for that application also need to be removed. Django Evolution will only remove these tables if you specify ``--purge`` as a command line argument. --noinput ~~~~~~~~~ Use the ``--noinput`` option to suppress all user prompting, such as "Are you sure?" confirmation messages. This is useful if ``django-admin.py`` is being executed as an unattended, automated script. --verbosity ~~~~~~~~~~~ Use ``--verbosity`` to specify the amount of notification and debug information that the evolve command should print to the console. * ``0`` means no input. * ``1`` means normal input (default). * ``2`` means verbose input. Built-in Mutations ------------------ Django Evolution comes with a number of mutations built-in. These mutations cover the simple cases for model evolutions. AddField(model_name, field_name, initial=None, \*\*kwargs) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Add the field 'field_name' to the model, using the provided initial value to populate the new column. The extra keyword arguments are the arguments used to construct the new field. If you are adding a column to a existing table, you will need to specify either ``null=True`` on the field definition, or provide an initial value for the new field. This is because after adding the field, every item in the database must be able to provide a value for the new field. Since existing rows will not have a value for the new field, you must either explicitly allow a value of NULL in the model definition, or you must provide an initial value. The initial value can be a literal (e.g., ``initial=3`` for an `IntegerField`), or a callable that takes no arguments and returns a literal. If you use a literal, Django Evolution will apply any quoting required to protect the literal value. If you use a callable, the value returned by the callable will be inserted directly into the SQL ``INSERT`` statement that populates the new column, so it should be in a format appropriate for database insertion. If your newly added field definition provides a default, the default will be used as the initial value. Example:: # Add a nickname field, allowing NULL values. AddField('Author', 'nickname', models.CharField, max_length=100, null=True) # Add a nickname field, specifying the nickname for all existing Authors # to be 'That Guy' AddField('Author', 'nickname', models.CharField, max_length=100, initial='That Guy') # Add a note field, using a callable for the initial value. # Note that the value returned by the callable has been wrapped in # quotes, so it is ready for database insertion. def initial_note_value(): return "'Registered %s'" % datetime.now() AddField('Author', 'note', models.CharField, max_length=100, initial=initial_note_value) ChangeField(model_name, field_name, initial=None, \*\*kwargs) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Change database affecting attributes of the field 'field_name' to the model. Changes to the field are specified using keyword arguments such as ''null=True'' and ''max_length=20''. Like the AddField, if the change to the field is to set ''null=False'', an initial value must be provided as either a literal or a callable. The ChangeField will accept both changed attributes and unchanged attributes of a field. This means that if a CharField has a modified ''max_length'', its ''null'' attribute may be re-specified in the ChangeField. The ChangeField will detect that the ''null'' attribute was specified but not changed and will silently ignore the re-specification. Example:: # Adding a null constraint to the birthday field. ChangeField('Author', 'date_of_birth', null=True) # Increasing the size of the author's name. ChangeField('Author', 'name', max_length=100) .. note:: The ChangeField only supports the null, max_length, unique, db_column, db_index and db_table (for many to many fields) attribute at this time. DeleteField(model_name, field_name) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Remove the field 'field_name' from the model. Example:: # Remove the author's name from the Author model DeleteField('Author', 'name') RenameField(model_name, old_field_name, new_field_name, db_column=None, db_table=None) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Rename the field 'old_name' to 'new_name'. ``db_column`` and ``db_table`` are optional arguments; if provided, they specify the new column name (in the case of a value or ForeignKey field) or the new table name (in the case of a ManyToManyField). If you attempt to specify a table name for a normal or ForeignKey rename, an Exception will be raised; similarly, and exception will be raised if you attempt to specify a column name for a ManyToManyField rename. Example:: # Rename the field 'name' as 'full_name' RenameField('Author', 'name', 'full_name') DeleteModel(model_name) ~~~~~~~~~~~~~~~~~~~~~~~ Remove the model 'model_name' from the application. Example:: # Remove the Author model DeleteModel('Author') SQLMutation(tag, sql, update_func=None) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Run a list of SQL statements as a mutation. This can be used as an alternative to defining an mutation as an SQL file. By default, SQLMutations cannot be simulated - therefore, you should carefully audit the SQL for any evolution sequence including an SQLMutation before you execute that SQL on a database. ``tag`` is a text description of the changes. It will usually be the evolution name. ``update_func`` is a optional function that will be invoked to update the project signature when the mutation is simulated. This allows SQLMutations to be included as part of a simulated evolution sequence. An update function takes the application label and current project signature, and mutates that signature until it reflects the changes implemented by the SQL. For example, a simple mutation function to remove an 'Author' table from an application might look something like:: def my_update_func(app_label, project_signature): del project_signature[app_label]['Author'] If you provide an update function, it will be invoked as part of the simulation sequence. Any discrepancies between the simulated and expected project signatures will be reported as an error during the evolution process. Example:: # Add a new column to the Author table SQLMutation('add_location', ['ALTER TABLE blogette_author ADD COLUMN location varchar(100);']) Defining your own mutations --------------------------- If you have a special set of mutation requirements, you can define your own mutation. All you need to do is subclass ``django_evolution.mutations.BaseMutation``, and include the mutation in a stored evolution. More to come ============ Django Evolution isn't complete - yet. There is still lots to do, and we'd appreciate any help or suggestions you may have. The following are some examples of features that we would like to add: 1. Support for more mutation types, including: a. ChangeModel - to handle model constraints like ``unique_together``, b. ChangeData - to handle data changes without a corresponding field definition change. 2. Improving support for the MySQL database backend 3. Support for other database backends (Oracle...) 4. Support for database introspection - the ability to look at the actual database structure to determine if evolutions are required. django_evolution-0.6.7/docs/faq.txt0000644000175000017500000003644611471453547020166 0ustar chipx86chipx8600000000000000========================== Frequently Asked Questions ========================== General Questions ================= What is Django Evolution? ------------------------- When you run ``./manage.py syncdb``, Django will look for any new models that have been defined, and add a database table to represent those new models. However, if you make a change to an existing model, ``./manage.py syncdb`` will not make any changes to the database. This is where **Django Evolution** fits in. Django Evolution is an extension to Django that allows you to track changes in your models over time, and to update the database to reflect those changes. When will Django Evolution be merged into Django trunk? ------------------------------------------------------- Possibly never, and certainly not before it's ready. Django Evolution is still in the early stages of development; considering plans for merging to the Django trunk would be premature. Installation ============ How do I install Django Evolution? ---------------------------------- There have not been any official releases of Django Evolution. If you want to use Django Evolution, you will need to use a subversion checkout. Check out the Django Evolution sources, and put the checkout directory into your Python Path. Then add 'django_evolution' to the list of installed applications for the project in which you want to use Django Evolution. What version of Django does Django Evolution support? ----------------------------------------------------- Django Evolution requires that you are using Django v1.0. You should also be able to use any SVN checkout from revision 8296 or higher. You cannot use Django Evolution with a 0.96 (or earlier) release of Django. Management ========== Who is behind Django Evolution? ------------------------------- Django Evolution was originally developed as a part time project by two guys based in Perth, Western Australia: `Ben Khoo`_ and `Russell Keith-Magee`_. Russell is also a core developer of Django itself. Russell is now an emeritus contributor. the project is maintained by `Christian Hammond`_. .. _`Ben Khoo`: mailto:khoobks@westnet.com.au .. _`Russell Keith-Magee`: mailto:russell@keith-magee.com .. _`Christian Hammond`: mailto:chipx86@chipx86.com Where should I ask questions about Django Evolution? ---------------------------------------------------- Django Evolution has a `Google Groups mailing list`_ for discussing the usage and development of the project. .. _`Google Groups mailing list`: http://groups.google.com/group/django-evolution There is also a read-only mailing list if you wish to be notified whenever code is checked into the Subversion repository, or whenever a ticket is added or modified. .. _`read-only mailing list`: http://groups.google.com/group/django-evolution-changes I think I've found a bug! What should I do? ------------------------------------------- Django Evolution has a `bug tracker`_. Check the list of bugs that are already known; if your bug doesn't appear to be logged, please add it to the list. When you lodge your bug, *please* remember to provide clear instructions on how to reproduce your problem. This means providing sample models, sample migrations, and any error logs that appeared during the evolution process. .. _`bug tracker`: http://code.google.com/p/django-evolution/issues/list Status ====== What works? ----------- The general framework should be stable. Any difficulties installing Django Evolution into your project, running the evolve command, or identifying changes in your models should be reported as a bug. The following is a list of the model changes that we believe to be working: * Addition of simple fields (e.g., a new CharField). * Addition of Many To Many Fields. * Deletion of most fields (of any type). * Deletion of models. * Deletion of entire applications. * Renaming of a field. Renames won't ever appear in a hinted evolution, but they can be manually added as a replacement for the Add+Delete pair that a hinted evolution will provide. Primary development for Django Evolution has used the Psycopg1 interface to PostgreSQL. As a result, this is the most stable database interface. A Psycopg2 PostgreSQL backend also exists, and should work without difficulty. The SQLite backend is also fairly reliable. The MySQL backend works for most mutations, with some exceptions (details below). What doesn't work? ------------------ Django Evolution is a work in progress, and as a result, there are things that don't work, or don't work as expected. The following model changes are known to cause difficulty: * Addition of ForeignKey fields. A field will be added, but the foreign key constraints are not correctly constructed. * Deletion of primary keys (including adding a manual primary key as a replacement for an automatically installed primary key) * Deletion of fields referenced by other database constraints. Dependencies between applications can also cause difficulties. If you have changes in one application that are dependent on changes in another application, you may experience difficulties in applying evolutions. Django Evolution is known to work well for PostgreSQL and SQLite. This is because the core developers both use PostgreSQL and SQLite on a daily basis. Support for MySQL is less complete. The following features are known to be buggy or broken under MySQL: * Renaming of primary key fields and foreign key fields. Improving MySQL support (as well as adding support for other database backends) is one big area where contributions would be most welcome. Usage ===== How do I remove an entire application from my project? ------------------------------------------------------ If you remove an application from the ``INSTALLED_APPS`` list, Django Evolution will not attempt to remove the tables for this application. If you want to remove any tables associated with a deleted application, you must specify the ``--purge`` option to evolve. Why does Django Evolution generate an error when hinting an evolution? ---------------------------------------------------------------------- Django Evolution is a work in progress. The infrastructure necessary to identify model changes is in place and is (to the best of our knowledge) complete. However, the definitions required to handle every possible type of model change have not yet been developed. As a result, there are some types of change that you can make to a model for which Django Evolution cannot produce a corresponding SQL mutation. We hope to eventually support these changes with a hinted mutation. However, in the interim, the workaround is to manually define an SQL mutation and include it as a stored evolution in your project. My OneToOneField has changed as a result of the QuerySet Refactor merge! What should I do? ------------------------------------------------------------------------------------------ First - some background. `Django revision 7477`_ saw the merging of the `Queryset Refactor branch`_ back into the Django trunk. This change introduced a lot of behind-the-scenes improvements to the way queries are composed. However, this also introduced some subtle `backwards-incompatible changes`_ that can affect your database definitions. In turn, these changes have an effect on the way Django Evolution treats your database. For example, consider the case of the following model defining a user profile:: class UserProfile(models.Model): user = models.OneToOneField() foobar = models.CharField(max_length=40) If you were to run syncdb on this model prior to Django revision 7477, you would get a database definition that looks something like:: CREATE TABLE "test_app_userprofile" ( "user_id" integer NOT NULL PRIMARY KEY REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "foobar" varchar(40) NOT NULL ); However, if you were to run syncdb using a version of Django after revision 7477, you would get the following:: CREATE TABLE "test_app_userprofile" ( "id" serial NOT NULL PRIMARY KEY, "user_id" integer NOT NULL UNIQUE REFERENCES "auth_user" ("id") DEFERRABLE INITIALLY DEFERRED, "foobar" varchar(40) NOT NULL ); The suggested response to this change is to add "primary_key=True" to your OneToOneField defintion. While this will result in the same functionality at runtime, it will be identified by Django Evolution as a change in model signature - even though no database change is required. What is required is a way to ignore the change of signature. This can be achieved using an empty SQL mutation that provides a signature update function. To update the previous example, put the following into your mutation sequence definition:: def qsrf_update(app_label, proj_sig): app_sig = proj_sig[app_label] model_sig = app_sig['UserProfile'] model_sig['fields']['user'] = { 'primary_key': True } SEQUENCE = [ ... SQLMutation('qsrf-update', [], qsrf_update) ... ] This defines a signature update function called "qsrf_update()" that puts a "primary_key" definition into the signature for the OneToOneField. This function is used by an SQL mutation called 'qsrf-update' that contains no actual SQL statements. So, when this mutation is applied, the signature will be updated, but no changes will be made to the database. .. _`Django revision 7477`: http://code.djangoproject.com/changeset/7477 .. _`Queryset Refactor branch`: http://code.djangoproject.com/wiki/QuerysetRefactorBranch .. _`backwards-incompatible changes`: http://code.djangoproject.com/wiki/QuerysetRefactorBranch#Backwardsincompatiblechanges Design ====== What are the design goals for Django Evolution? ----------------------------------------------- Django Evolution has the following design goals: 1. Provide a way for simple changes to be applied to a database without significant user intervention. 2. Provide an entry point for using raw SQL as a migration tactic. 3. Provide a way to 'hint and tweak' - i.e., use an automatic method to generate a first pass at a migration, but provide a way for a developer to fine tune the migration before application. 4. Provide a way to be self-documenting and self-auditing - i.e., keep a permanent archive of the changes that have been made, which ones have been applied to which database, etc. 5. Provide some manner of validation that a migration, when applied, will be correct. 6. Provide an easy way for end users to integrate their own custom migration needs into the framework. Isn't this just ActiveRecord::Migration in Python? -------------------------------------------------- Superficially, yes, but not really. There is a degree of similarity - the Django Evolution syntax is a DSL for describing the changes that have been made to a model. However, there are also some significant differences. In addition to ActiveRecord::Migration, Django Evolution provides: 1. An audit trail - a permanent archive in the database of the changes that have been applied, and when. 2. An automated hinting scheme. If you don't want to write the migrations yourself, you don't have to. 3. Validation that an evolution script will leave the database in a state consistent with the current model definition. These differences are largely afforded by the model-centric design of Django itself. Whereas a Ruby on Rails model is a description of a database that has been created by hand, Django uses the Python model to creates the database. As a result, the model definition is canonical - not the database. This means that audit, hinting, and verification schemes can use the Django model as a point of reference. A Django Evolution script is much more than just an alternate syntax for SQL ALTER statements - it is an evolution scheme that is bound to the canonical model definition. .. admonition:: Ultracrepidation We're not Rails developers, so when we talk about Ruby on Rails and ActiveRecord::Migration, it's not from extensive personal experience. If we have misrepresented or misunderstood Ruby on Rails or ActiveRecord::Migration, feel free to correct us. Why do you generate signatures based on the model, rather than introspecting the database? ------------------------------------------------------------------------------------------ There are two arguments for using model-based signatures rather than database introspection: One is practical; the other is philosophical. The practical argument is that introspection is _much_ harder to do properly. To prove the point: ``inspectdb`` has been in Django since the beginning, and it still doesn't work right 100% of the time. Introspection of fields with custom column names or custom field types will always be very difficult to resolve. By using model-based signatures, we always have access to all the properties that Django considers significant, in a format directly relevant to Django - as a result, it was possible to get model-based signatures working with pretty much 100% accuracy in relatively short order. The philosophical argument is that by making the model the canonical reference point (rather than the database) it puts the emphasis on Django as the centre of the process, and discourages external modification. This has the side effect of encouraging users to store (which is to say *document*) all the changes they make to the database, rather than just poking the schema as they see fit. This also has the advantage of being consistent with general Django philosophy - for example, in default usage, you write a Django model, and Django creates the tables. You *can* manually create the tables if you want, and you *can* manually tweak the tables if you need to - but if you do, Django doesn't guarantee that that anything will work as expected. Django provides initial SQL hooks that implicitly encourage you to store/document any external modifications. Django doesn't go out of its way to make life difficult in this regard, but the core design of Django isn't going to change fundamentally in order to accommodate externalities. Contributing code ================= How can I get started contributing code to Django Evolution? ------------------------------------------------------------ Thanks for asking! If you're interested in contributing to Django Evolution, please send a private email to Christian. However, be advised that if you you want your request to be taken seriously, you need to have a track record of producing high quality patches. A good place to start would be to pick a non-trivial missing feature from the ticket database, and implement a fix. I submitted a bug fix in the ticket system several weeks ago. Why are you ignoring my patch? -------------------------------------------------------------------------------------------- Don't worry: We're not ignoring you! It's important to understand there is a difference between "a ticket is being ignored" and "a ticket has not been attended to yet." Django Evolution is a volunteer project, and the core developers may sometimes get distracted by other things; if this happens, a ticket may sit unattended for a while. Don't worry - we'll get to your issue eventually. Besides, if your feature request stands no chance of inclusion in Django Evolution, we won't ignore it -- we'll just close the ticket. So if your ticket is still open, it doesn't mean we're ignoring you; it just means we haven't had time to look at it yet.