Loading django/db/migrations/autodetector.py +56 −2 Original line number Diff line number Diff line Loading @@ -163,6 +163,7 @@ class MigrationAutodetector(object): self.generate_altered_fields() self.generate_altered_unique_together() self.generate_altered_index_together() self.generate_altered_order_with_respect_to() # Now, reordering to make things possible. The order we have already # isn't bad, but we need to pull a few things around so FKs work nicely Loading Loading @@ -305,9 +306,16 @@ class MigrationAutodetector(object): operation.model_name.lower() == dependency[1].lower() and operation.name.lower() == dependency[2].lower() ) # order_with_respect_to being unset for a field elif dependency[2] is not None and dependency[3] == "order_wrt_unset": return ( isinstance(operation, operations.AlterOrderWithRespectTo) and operation.name.lower() == dependency[1].lower() and (operation.order_with_respect_to or "").lower() != dependency[2].lower() ) # Unknown dependency. Raise an error. else: raise ValueError("Can't handle dependency %r" % dependency) raise ValueError("Can't handle dependency %r" % (dependency, )) def add_operation(self, app_label, operation, dependencies=None): # Dependencies are (app_label, model_name, field_name, create/delete as True/False) Loading Loading @@ -375,6 +383,7 @@ class MigrationAutodetector(object): # Are there unique/index_together to defer? unique_together = model_state.options.pop('unique_together', None) index_together = model_state.options.pop('index_together', None) order_with_respect_to = model_state.options.pop('order_with_respect_to', None) # Generate creation operatoin self.add_operation( app_label, Loading Loading @@ -438,6 +447,17 @@ class MigrationAutodetector(object): for name, field in sorted(related_fields.items()) ] ) if order_with_respect_to: self.add_operation( app_label, operations.AlterOrderWithRespectTo( name=model_name, order_with_respect_to=order_with_respect_to, ), dependencies=[ (app_label, model_name, order_with_respect_to, True), ] ) def generate_deleted_models(self): """ Loading Loading @@ -595,7 +615,10 @@ class MigrationAutodetector(object): operations.RemoveField( model_name=model_name, name=field_name, ) ), # We might need to depend on the removal of an order_with_respect_to; # this is safely ignored if there isn't one dependencies=[(app_label, model_name, field_name, "order_wrt_unset")], ) def generate_altered_fields(self): Loading Loading @@ -659,6 +682,11 @@ class MigrationAutodetector(object): ) def generate_altered_options(self): """ Works out if any non-schema-affecting options have changed and makes an operation to represent them in state changes (in case Python code in migrations needs them) """ for app_label, model_name in sorted(self.kept_model_keys): old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] Loading @@ -680,6 +708,32 @@ class MigrationAutodetector(object): ) ) def generate_altered_order_with_respect_to(self): for app_label, model_name in sorted(self.kept_model_keys): old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] if old_model_state.options.get("order_with_respect_to", None) != new_model_state.options.get("order_with_respect_to", None): # Make sure it comes second if we're adding # (removal dependency is part of RemoveField) dependencies = [] if new_model_state.options.get("order_with_respect_to", None): dependencies.append(( app_label, model_name, new_model_state.options["order_with_respect_to"], True, )) # Actually generate the operation self.add_operation( app_label, operations.AlterOrderWithRespectTo( name=model_name, order_with_respect_to=new_model_state.options.get('order_with_respect_to', None), ), dependencies = dependencies, ) def arrange_for_graph(self, changes, graph): """ Takes in a result from changes() and a MigrationGraph, Loading django/db/migrations/operations/__init__.py +3 −1 Original line number Diff line number Diff line from .models import (CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether, AlterIndexTogether, RenameModel, AlterModelOptions) AlterUniqueTogether, AlterIndexTogether, RenameModel, AlterModelOptions, AlterOrderWithRespectTo) from .fields import AddField, RemoveField, AlterField, RenameField from .special import SeparateDatabaseAndState, RunSQL, RunPython Loading @@ -8,4 +9,5 @@ __all__ = [ 'RenameModel', 'AlterIndexTogether', 'AlterModelOptions', 'AddField', 'RemoveField', 'AlterField', 'RenameField', 'SeparateDatabaseAndState', 'RunSQL', 'RunPython', 'AlterOrderWithRespectTo', ] django/db/migrations/operations/models.py +39 −0 Original line number Diff line number Diff line Loading @@ -285,6 +285,45 @@ class AlterIndexTogether(Operation): return "Alter index_together for %s (%s constraints)" % (self.name, len(self.index_together)) class AlterOrderWithRespectTo(Operation): """ Represents a change with the order_with_respect_to option. """ def __init__(self, name, order_with_respect_to): self.name = name self.order_with_respect_to = order_with_respect_to def state_forwards(self, app_label, state): model_state = state.models[app_label, self.name.lower()] model_state.options['order_with_respect_to'] = self.order_with_respect_to def database_forwards(self, app_label, schema_editor, from_state, to_state): from_model = from_state.render().get_model(app_label, self.name) to_model = to_state.render().get_model(app_label, self.name) if router.allow_migrate(schema_editor.connection.alias, to_model): # Remove a field if we need to if from_model._meta.order_with_respect_to and not to_model._meta.order_with_respect_to: schema_editor.remove_field(from_model, from_model._meta.get_field_by_name("_order")[0]) # Add a field if we need to (altering the column is untouched as # it's likely a rename) elif to_model._meta.order_with_respect_to and not from_model._meta.order_with_respect_to: field = to_model._meta.get_field_by_name("_order")[0] schema_editor.add_field( from_model, field, ) def database_backwards(self, app_label, schema_editor, from_state, to_state): self.database_forwards(app_label, schema_editor, from_state, to_state) def references_model(self, name, app_label=None): return name.lower() == self.name.lower() def describe(self): return "Set order_with_respect_to on %s to %s" % (self.name, self.order_with_respect_to) class AlterModelOptions(Operation): """ Sets new model options that don't directly affect the database schema Loading django/db/migrations/state.py +3 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ from django.apps.registry import Apps, apps as global_apps from django.db import models from django.db.models.options import DEFAULT_NAMES, normalize_together from django.db.models.fields.related import do_pending_lookups from django.db.models.fields.proxy import OrderWrt from django.conf import settings from django.utils import six from django.utils.encoding import force_text Loading Loading @@ -166,6 +167,8 @@ class ModelState(object): for field in model._meta.local_fields: if getattr(field, "rel", None) and exclude_rels: continue if isinstance(field, OrderWrt): continue name, path, args, kwargs = field.deconstruct() field_class = import_string(path) try: Loading docs/ref/migration-operations.txt +18 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,24 @@ Changes the model's set of custom indexes (the :attr:`~django.db.models.Options.index_together` option on the ``Meta`` subclass). AlterOrderWithRespectTo ----------------------- .. class:: AlterIndexTogether(name, order_with_respect_to) Makes or deletes the ``_order`` column needed for the :attr:`~django.db.models.Options.order_with_respect_to` option on the ``Meta`` subclass. AlterModelOptions ----------------- .. class:: AlterIndexTogether(name, options) Stores changes to miscellaneous model options (settings on a model's ``Meta``) like ``permissions`` and ``verbose_name``. Does not affect the database, but persists these changes for :class:`RunPython` instances to use. AddField -------- Loading Loading
django/db/migrations/autodetector.py +56 −2 Original line number Diff line number Diff line Loading @@ -163,6 +163,7 @@ class MigrationAutodetector(object): self.generate_altered_fields() self.generate_altered_unique_together() self.generate_altered_index_together() self.generate_altered_order_with_respect_to() # Now, reordering to make things possible. The order we have already # isn't bad, but we need to pull a few things around so FKs work nicely Loading Loading @@ -305,9 +306,16 @@ class MigrationAutodetector(object): operation.model_name.lower() == dependency[1].lower() and operation.name.lower() == dependency[2].lower() ) # order_with_respect_to being unset for a field elif dependency[2] is not None and dependency[3] == "order_wrt_unset": return ( isinstance(operation, operations.AlterOrderWithRespectTo) and operation.name.lower() == dependency[1].lower() and (operation.order_with_respect_to or "").lower() != dependency[2].lower() ) # Unknown dependency. Raise an error. else: raise ValueError("Can't handle dependency %r" % dependency) raise ValueError("Can't handle dependency %r" % (dependency, )) def add_operation(self, app_label, operation, dependencies=None): # Dependencies are (app_label, model_name, field_name, create/delete as True/False) Loading Loading @@ -375,6 +383,7 @@ class MigrationAutodetector(object): # Are there unique/index_together to defer? unique_together = model_state.options.pop('unique_together', None) index_together = model_state.options.pop('index_together', None) order_with_respect_to = model_state.options.pop('order_with_respect_to', None) # Generate creation operatoin self.add_operation( app_label, Loading Loading @@ -438,6 +447,17 @@ class MigrationAutodetector(object): for name, field in sorted(related_fields.items()) ] ) if order_with_respect_to: self.add_operation( app_label, operations.AlterOrderWithRespectTo( name=model_name, order_with_respect_to=order_with_respect_to, ), dependencies=[ (app_label, model_name, order_with_respect_to, True), ] ) def generate_deleted_models(self): """ Loading Loading @@ -595,7 +615,10 @@ class MigrationAutodetector(object): operations.RemoveField( model_name=model_name, name=field_name, ) ), # We might need to depend on the removal of an order_with_respect_to; # this is safely ignored if there isn't one dependencies=[(app_label, model_name, field_name, "order_wrt_unset")], ) def generate_altered_fields(self): Loading Loading @@ -659,6 +682,11 @@ class MigrationAutodetector(object): ) def generate_altered_options(self): """ Works out if any non-schema-affecting options have changed and makes an operation to represent them in state changes (in case Python code in migrations needs them) """ for app_label, model_name in sorted(self.kept_model_keys): old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] Loading @@ -680,6 +708,32 @@ class MigrationAutodetector(object): ) ) def generate_altered_order_with_respect_to(self): for app_label, model_name in sorted(self.kept_model_keys): old_model_name = self.renamed_models.get((app_label, model_name), model_name) old_model_state = self.from_state.models[app_label, old_model_name] new_model_state = self.to_state.models[app_label, model_name] if old_model_state.options.get("order_with_respect_to", None) != new_model_state.options.get("order_with_respect_to", None): # Make sure it comes second if we're adding # (removal dependency is part of RemoveField) dependencies = [] if new_model_state.options.get("order_with_respect_to", None): dependencies.append(( app_label, model_name, new_model_state.options["order_with_respect_to"], True, )) # Actually generate the operation self.add_operation( app_label, operations.AlterOrderWithRespectTo( name=model_name, order_with_respect_to=new_model_state.options.get('order_with_respect_to', None), ), dependencies = dependencies, ) def arrange_for_graph(self, changes, graph): """ Takes in a result from changes() and a MigrationGraph, Loading
django/db/migrations/operations/__init__.py +3 −1 Original line number Diff line number Diff line from .models import (CreateModel, DeleteModel, AlterModelTable, AlterUniqueTogether, AlterIndexTogether, RenameModel, AlterModelOptions) AlterUniqueTogether, AlterIndexTogether, RenameModel, AlterModelOptions, AlterOrderWithRespectTo) from .fields import AddField, RemoveField, AlterField, RenameField from .special import SeparateDatabaseAndState, RunSQL, RunPython Loading @@ -8,4 +9,5 @@ __all__ = [ 'RenameModel', 'AlterIndexTogether', 'AlterModelOptions', 'AddField', 'RemoveField', 'AlterField', 'RenameField', 'SeparateDatabaseAndState', 'RunSQL', 'RunPython', 'AlterOrderWithRespectTo', ]
django/db/migrations/operations/models.py +39 −0 Original line number Diff line number Diff line Loading @@ -285,6 +285,45 @@ class AlterIndexTogether(Operation): return "Alter index_together for %s (%s constraints)" % (self.name, len(self.index_together)) class AlterOrderWithRespectTo(Operation): """ Represents a change with the order_with_respect_to option. """ def __init__(self, name, order_with_respect_to): self.name = name self.order_with_respect_to = order_with_respect_to def state_forwards(self, app_label, state): model_state = state.models[app_label, self.name.lower()] model_state.options['order_with_respect_to'] = self.order_with_respect_to def database_forwards(self, app_label, schema_editor, from_state, to_state): from_model = from_state.render().get_model(app_label, self.name) to_model = to_state.render().get_model(app_label, self.name) if router.allow_migrate(schema_editor.connection.alias, to_model): # Remove a field if we need to if from_model._meta.order_with_respect_to and not to_model._meta.order_with_respect_to: schema_editor.remove_field(from_model, from_model._meta.get_field_by_name("_order")[0]) # Add a field if we need to (altering the column is untouched as # it's likely a rename) elif to_model._meta.order_with_respect_to and not from_model._meta.order_with_respect_to: field = to_model._meta.get_field_by_name("_order")[0] schema_editor.add_field( from_model, field, ) def database_backwards(self, app_label, schema_editor, from_state, to_state): self.database_forwards(app_label, schema_editor, from_state, to_state) def references_model(self, name, app_label=None): return name.lower() == self.name.lower() def describe(self): return "Set order_with_respect_to on %s to %s" % (self.name, self.order_with_respect_to) class AlterModelOptions(Operation): """ Sets new model options that don't directly affect the database schema Loading
django/db/migrations/state.py +3 −0 Original line number Diff line number Diff line Loading @@ -5,6 +5,7 @@ from django.apps.registry import Apps, apps as global_apps from django.db import models from django.db.models.options import DEFAULT_NAMES, normalize_together from django.db.models.fields.related import do_pending_lookups from django.db.models.fields.proxy import OrderWrt from django.conf import settings from django.utils import six from django.utils.encoding import force_text Loading Loading @@ -166,6 +167,8 @@ class ModelState(object): for field in model._meta.local_fields: if getattr(field, "rel", None) and exclude_rels: continue if isinstance(field, OrderWrt): continue name, path, args, kwargs = field.deconstruct() field_class = import_string(path) try: Loading
docs/ref/migration-operations.txt +18 −0 Original line number Diff line number Diff line Loading @@ -99,6 +99,24 @@ Changes the model's set of custom indexes (the :attr:`~django.db.models.Options.index_together` option on the ``Meta`` subclass). AlterOrderWithRespectTo ----------------------- .. class:: AlterIndexTogether(name, order_with_respect_to) Makes or deletes the ``_order`` column needed for the :attr:`~django.db.models.Options.order_with_respect_to` option on the ``Meta`` subclass. AlterModelOptions ----------------- .. class:: AlterIndexTogether(name, options) Stores changes to miscellaneous model options (settings on a model's ``Meta``) like ``permissions`` and ``verbose_name``. Does not affect the database, but persists these changes for :class:`RunPython` instances to use. AddField -------- Loading