Ajoutez des fichiers projet.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
from .fields import AddField, AlterField, RemoveField, RenameField
|
||||
from .models import (
|
||||
AddConstraint, AddIndex, AlterIndexTogether, AlterModelManagers,
|
||||
AlterModelOptions, AlterModelTable, AlterOrderWithRespectTo,
|
||||
AlterUniqueTogether, CreateModel, DeleteModel, RemoveConstraint,
|
||||
RemoveIndex, RenameModel,
|
||||
)
|
||||
from .special import RunPython, RunSQL, SeparateDatabaseAndState
|
||||
|
||||
__all__ = [
|
||||
'CreateModel', 'DeleteModel', 'AlterModelTable', 'AlterUniqueTogether',
|
||||
'RenameModel', 'AlterIndexTogether', 'AlterModelOptions', 'AddIndex',
|
||||
'RemoveIndex', 'AddField', 'RemoveField', 'AlterField', 'RenameField',
|
||||
'AddConstraint', 'RemoveConstraint',
|
||||
'SeparateDatabaseAndState', 'RunSQL', 'RunPython',
|
||||
'AlterOrderWithRespectTo', 'AlterModelManagers',
|
||||
]
|
140
venv/Lib/site-packages/django/db/migrations/operations/base.py
Normal file
140
venv/Lib/site-packages/django/db/migrations/operations/base.py
Normal file
@@ -0,0 +1,140 @@
|
||||
from django.db import router
|
||||
|
||||
|
||||
class Operation:
|
||||
"""
|
||||
Base class for migration operations.
|
||||
|
||||
It's responsible for both mutating the in-memory model state
|
||||
(see db/migrations/state.py) to represent what it performs, as well
|
||||
as actually performing it against a live database.
|
||||
|
||||
Note that some operations won't modify memory state at all (e.g. data
|
||||
copying operations), and some will need their modifications to be
|
||||
optionally specified by the user (e.g. custom Python code snippets)
|
||||
|
||||
Due to the way this class deals with deconstruction, it should be
|
||||
considered immutable.
|
||||
"""
|
||||
|
||||
# If this migration can be run in reverse.
|
||||
# Some operations are impossible to reverse, like deleting data.
|
||||
reversible = True
|
||||
|
||||
# Can this migration be represented as SQL? (things like RunPython cannot)
|
||||
reduces_to_sql = True
|
||||
|
||||
# Should this operation be forced as atomic even on backends with no
|
||||
# DDL transaction support (i.e., does it have no DDL, like RunPython)
|
||||
atomic = False
|
||||
|
||||
# Should this operation be considered safe to elide and optimize across?
|
||||
elidable = False
|
||||
|
||||
serialization_expand_args = []
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# We capture the arguments to make returning them trivial
|
||||
self = object.__new__(cls)
|
||||
self._constructor_args = (args, kwargs)
|
||||
return self
|
||||
|
||||
def deconstruct(self):
|
||||
"""
|
||||
Return a 3-tuple of class import path (or just name if it lives
|
||||
under django.db.migrations), positional arguments, and keyword
|
||||
arguments.
|
||||
"""
|
||||
return (
|
||||
self.__class__.__name__,
|
||||
self._constructor_args[0],
|
||||
self._constructor_args[1],
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
"""
|
||||
Take the state from the previous migration, and mutate it
|
||||
so that it matches what this migration would perform.
|
||||
"""
|
||||
raise NotImplementedError('subclasses of Operation must provide a state_forwards() method')
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
"""
|
||||
Perform the mutation on the database schema in the normal
|
||||
(forwards) direction.
|
||||
"""
|
||||
raise NotImplementedError('subclasses of Operation must provide a database_forwards() method')
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
"""
|
||||
Perform the mutation on the database schema in the reverse
|
||||
direction - e.g. if this were CreateModel, it would in fact
|
||||
drop the model's table.
|
||||
"""
|
||||
raise NotImplementedError('subclasses of Operation must provide a database_backwards() method')
|
||||
|
||||
def describe(self):
|
||||
"""
|
||||
Output a brief summary of what the action does.
|
||||
"""
|
||||
return "%s: %s" % (self.__class__.__name__, self._constructor_args)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
"""
|
||||
A filename part suitable for automatically naming a migration
|
||||
containing this operation, or None if not applicable.
|
||||
"""
|
||||
return None
|
||||
|
||||
def references_model(self, name, app_label):
|
||||
"""
|
||||
Return True if there is a chance this operation references the given
|
||||
model name (as a string), with an app label for accuracy.
|
||||
|
||||
Used for optimization. If in doubt, return True;
|
||||
returning a false positive will merely make the optimizer a little
|
||||
less efficient, while returning a false negative may result in an
|
||||
unusable optimized migration.
|
||||
"""
|
||||
return True
|
||||
|
||||
def references_field(self, model_name, name, app_label):
|
||||
"""
|
||||
Return True if there is a chance this operation references the given
|
||||
field name, with an app label for accuracy.
|
||||
|
||||
Used for optimization. If in doubt, return True.
|
||||
"""
|
||||
return self.references_model(model_name, app_label)
|
||||
|
||||
def allow_migrate_model(self, connection_alias, model):
|
||||
"""
|
||||
Return whether or not a model may be migrated.
|
||||
|
||||
This is a thin wrapper around router.allow_migrate_model() that
|
||||
preemptively rejects any proxy, swapped out, or unmanaged model.
|
||||
"""
|
||||
if not model._meta.can_migrate(connection_alias):
|
||||
return False
|
||||
|
||||
return router.allow_migrate_model(connection_alias, model)
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
"""
|
||||
Return either a list of operations the actual operation should be
|
||||
replaced with or a boolean that indicates whether or not the specified
|
||||
operation can be optimized across.
|
||||
"""
|
||||
if self.elidable:
|
||||
return [operation]
|
||||
elif operation.elidable:
|
||||
return [self]
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s %s%s>" % (
|
||||
self.__class__.__name__,
|
||||
", ".join(map(repr, self._constructor_args[0])),
|
||||
",".join(" %s=%r" % x for x in self._constructor_args[1].items()),
|
||||
)
|
341
venv/Lib/site-packages/django/db/migrations/operations/fields.py
Normal file
341
venv/Lib/site-packages/django/db/migrations/operations/fields.py
Normal file
@@ -0,0 +1,341 @@
|
||||
from django.db.migrations.utils import field_references
|
||||
from django.db.models import NOT_PROVIDED
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from .base import Operation
|
||||
|
||||
|
||||
class FieldOperation(Operation):
|
||||
def __init__(self, model_name, name, field=None):
|
||||
self.model_name = model_name
|
||||
self.name = name
|
||||
self.field = field
|
||||
|
||||
@cached_property
|
||||
def model_name_lower(self):
|
||||
return self.model_name.lower()
|
||||
|
||||
@cached_property
|
||||
def name_lower(self):
|
||||
return self.name.lower()
|
||||
|
||||
def is_same_model_operation(self, operation):
|
||||
return self.model_name_lower == operation.model_name_lower
|
||||
|
||||
def is_same_field_operation(self, operation):
|
||||
return self.is_same_model_operation(operation) and self.name_lower == operation.name_lower
|
||||
|
||||
def references_model(self, name, app_label):
|
||||
name_lower = name.lower()
|
||||
if name_lower == self.model_name_lower:
|
||||
return True
|
||||
if self.field:
|
||||
return bool(field_references(
|
||||
(app_label, self.model_name_lower), self.field, (app_label, name_lower)
|
||||
))
|
||||
return False
|
||||
|
||||
def references_field(self, model_name, name, app_label):
|
||||
model_name_lower = model_name.lower()
|
||||
# Check if this operation locally references the field.
|
||||
if model_name_lower == self.model_name_lower:
|
||||
if name == self.name:
|
||||
return True
|
||||
elif self.field and hasattr(self.field, 'from_fields') and name in self.field.from_fields:
|
||||
return True
|
||||
# Check if this operation remotely references the field.
|
||||
if self.field is None:
|
||||
return False
|
||||
return bool(field_references(
|
||||
(app_label, self.model_name_lower),
|
||||
self.field,
|
||||
(app_label, model_name_lower),
|
||||
name,
|
||||
))
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
return (
|
||||
super().reduce(operation, app_label) or
|
||||
not operation.references_field(self.model_name, self.name, app_label)
|
||||
)
|
||||
|
||||
|
||||
class AddField(FieldOperation):
|
||||
"""Add a field to a model."""
|
||||
|
||||
def __init__(self, model_name, name, field, preserve_default=True):
|
||||
self.preserve_default = preserve_default
|
||||
super().__init__(model_name, name, field)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'model_name': self.model_name,
|
||||
'name': self.name,
|
||||
'field': self.field,
|
||||
}
|
||||
if self.preserve_default is not True:
|
||||
kwargs['preserve_default'] = self.preserve_default
|
||||
return (
|
||||
self.__class__.__name__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.add_field(
|
||||
app_label,
|
||||
self.model_name_lower,
|
||||
self.name,
|
||||
self.field,
|
||||
self.preserve_default,
|
||||
)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, to_model):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
field = to_model._meta.get_field(self.name)
|
||||
if not self.preserve_default:
|
||||
field.default = self.field.default
|
||||
schema_editor.add_field(
|
||||
from_model,
|
||||
field,
|
||||
)
|
||||
if not self.preserve_default:
|
||||
field.default = NOT_PROVIDED
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, from_model):
|
||||
schema_editor.remove_field(from_model, from_model._meta.get_field(self.name))
|
||||
|
||||
def describe(self):
|
||||
return "Add field %s to %s" % (self.name, self.model_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return '%s_%s' % (self.model_name_lower, self.name_lower)
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
if isinstance(operation, FieldOperation) and self.is_same_field_operation(operation):
|
||||
if isinstance(operation, AlterField):
|
||||
return [
|
||||
AddField(
|
||||
model_name=self.model_name,
|
||||
name=operation.name,
|
||||
field=operation.field,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, RemoveField):
|
||||
return []
|
||||
elif isinstance(operation, RenameField):
|
||||
return [
|
||||
AddField(
|
||||
model_name=self.model_name,
|
||||
name=operation.new_name,
|
||||
field=self.field,
|
||||
),
|
||||
]
|
||||
return super().reduce(operation, app_label)
|
||||
|
||||
|
||||
class RemoveField(FieldOperation):
|
||||
"""Remove a field from a model."""
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'model_name': self.model_name,
|
||||
'name': self.name,
|
||||
}
|
||||
return (
|
||||
self.__class__.__name__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.remove_field(app_label, self.model_name_lower, self.name)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, from_model):
|
||||
schema_editor.remove_field(from_model, from_model._meta.get_field(self.name))
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, to_model):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
schema_editor.add_field(from_model, to_model._meta.get_field(self.name))
|
||||
|
||||
def describe(self):
|
||||
return "Remove field %s from %s" % (self.name, self.model_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'remove_%s_%s' % (self.model_name_lower, self.name_lower)
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
from .models import DeleteModel
|
||||
if isinstance(operation, DeleteModel) and operation.name_lower == self.model_name_lower:
|
||||
return [operation]
|
||||
return super().reduce(operation, app_label)
|
||||
|
||||
|
||||
class AlterField(FieldOperation):
|
||||
"""
|
||||
Alter a field's database column (e.g. null, max_length) to the provided
|
||||
new field.
|
||||
"""
|
||||
|
||||
def __init__(self, model_name, name, field, preserve_default=True):
|
||||
self.preserve_default = preserve_default
|
||||
super().__init__(model_name, name, field)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'model_name': self.model_name,
|
||||
'name': self.name,
|
||||
'field': self.field,
|
||||
}
|
||||
if self.preserve_default is not True:
|
||||
kwargs['preserve_default'] = self.preserve_default
|
||||
return (
|
||||
self.__class__.__name__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.alter_field(
|
||||
app_label,
|
||||
self.model_name_lower,
|
||||
self.name,
|
||||
self.field,
|
||||
self.preserve_default,
|
||||
)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, to_model):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
from_field = from_model._meta.get_field(self.name)
|
||||
to_field = to_model._meta.get_field(self.name)
|
||||
if not self.preserve_default:
|
||||
to_field.default = self.field.default
|
||||
schema_editor.alter_field(from_model, from_field, to_field)
|
||||
if not self.preserve_default:
|
||||
to_field.default = NOT_PROVIDED
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
self.database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
|
||||
def describe(self):
|
||||
return "Alter field %s on %s" % (self.name, self.model_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'alter_%s_%s' % (self.model_name_lower, self.name_lower)
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
if isinstance(operation, RemoveField) and self.is_same_field_operation(operation):
|
||||
return [operation]
|
||||
elif isinstance(operation, RenameField) and self.is_same_field_operation(operation):
|
||||
return [
|
||||
operation,
|
||||
AlterField(
|
||||
model_name=self.model_name,
|
||||
name=operation.new_name,
|
||||
field=self.field,
|
||||
),
|
||||
]
|
||||
return super().reduce(operation, app_label)
|
||||
|
||||
|
||||
class RenameField(FieldOperation):
|
||||
"""Rename a field on the model. Might affect db_column too."""
|
||||
|
||||
def __init__(self, model_name, old_name, new_name):
|
||||
self.old_name = old_name
|
||||
self.new_name = new_name
|
||||
super().__init__(model_name, old_name)
|
||||
|
||||
@cached_property
|
||||
def old_name_lower(self):
|
||||
return self.old_name.lower()
|
||||
|
||||
@cached_property
|
||||
def new_name_lower(self):
|
||||
return self.new_name.lower()
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'model_name': self.model_name,
|
||||
'old_name': self.old_name,
|
||||
'new_name': self.new_name,
|
||||
}
|
||||
return (
|
||||
self.__class__.__name__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.rename_field(app_label, self.model_name_lower, self.old_name, self.new_name)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, to_model):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
schema_editor.alter_field(
|
||||
from_model,
|
||||
from_model._meta.get_field(self.old_name),
|
||||
to_model._meta.get_field(self.new_name),
|
||||
)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, to_model):
|
||||
from_model = from_state.apps.get_model(app_label, self.model_name)
|
||||
schema_editor.alter_field(
|
||||
from_model,
|
||||
from_model._meta.get_field(self.new_name),
|
||||
to_model._meta.get_field(self.old_name),
|
||||
)
|
||||
|
||||
def describe(self):
|
||||
return "Rename field %s on %s to %s" % (self.old_name, self.model_name, self.new_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'rename_%s_%s_%s' % (
|
||||
self.old_name_lower,
|
||||
self.model_name_lower,
|
||||
self.new_name_lower,
|
||||
)
|
||||
|
||||
def references_field(self, model_name, name, app_label):
|
||||
return self.references_model(model_name, app_label) and (
|
||||
name.lower() == self.old_name_lower or
|
||||
name.lower() == self.new_name_lower
|
||||
)
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
if (isinstance(operation, RenameField) and
|
||||
self.is_same_model_operation(operation) and
|
||||
self.new_name_lower == operation.old_name_lower):
|
||||
return [
|
||||
RenameField(
|
||||
self.model_name,
|
||||
self.old_name,
|
||||
operation.new_name,
|
||||
),
|
||||
]
|
||||
# Skip `FieldOperation.reduce` as we want to run `references_field`
|
||||
# against self.old_name and self.new_name.
|
||||
return (
|
||||
super(FieldOperation, self).reduce(operation, app_label) or
|
||||
not (
|
||||
operation.references_field(self.model_name, self.old_name, app_label) or
|
||||
operation.references_field(self.model_name, self.new_name, app_label)
|
||||
)
|
||||
)
|
884
venv/Lib/site-packages/django/db/migrations/operations/models.py
Normal file
884
venv/Lib/site-packages/django/db/migrations/operations/models.py
Normal file
@@ -0,0 +1,884 @@
|
||||
from django.db import models
|
||||
from django.db.migrations.operations.base import Operation
|
||||
from django.db.migrations.state import ModelState
|
||||
from django.db.migrations.utils import field_references, resolve_relation
|
||||
from django.db.models.options import normalize_together
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from .fields import (
|
||||
AddField, AlterField, FieldOperation, RemoveField, RenameField,
|
||||
)
|
||||
|
||||
|
||||
def _check_for_duplicates(arg_name, objs):
|
||||
used_vals = set()
|
||||
for val in objs:
|
||||
if val in used_vals:
|
||||
raise ValueError(
|
||||
"Found duplicate value %s in CreateModel %s argument." % (val, arg_name)
|
||||
)
|
||||
used_vals.add(val)
|
||||
|
||||
|
||||
class ModelOperation(Operation):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
@cached_property
|
||||
def name_lower(self):
|
||||
return self.name.lower()
|
||||
|
||||
def references_model(self, name, app_label):
|
||||
return name.lower() == self.name_lower
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
return (
|
||||
super().reduce(operation, app_label) or
|
||||
not operation.references_model(self.name, app_label)
|
||||
)
|
||||
|
||||
|
||||
class CreateModel(ModelOperation):
|
||||
"""Create a model's table."""
|
||||
|
||||
serialization_expand_args = ['fields', 'options', 'managers']
|
||||
|
||||
def __init__(self, name, fields, options=None, bases=None, managers=None):
|
||||
self.fields = fields
|
||||
self.options = options or {}
|
||||
self.bases = bases or (models.Model,)
|
||||
self.managers = managers or []
|
||||
super().__init__(name)
|
||||
# Sanity-check that there are no duplicated field names, bases, or
|
||||
# manager names
|
||||
_check_for_duplicates('fields', (name for name, _ in self.fields))
|
||||
_check_for_duplicates('bases', (
|
||||
base._meta.label_lower if hasattr(base, '_meta') else
|
||||
base.lower() if isinstance(base, str) else base
|
||||
for base in self.bases
|
||||
))
|
||||
_check_for_duplicates('managers', (name for name, _ in self.managers))
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'name': self.name,
|
||||
'fields': self.fields,
|
||||
}
|
||||
if self.options:
|
||||
kwargs['options'] = self.options
|
||||
if self.bases and self.bases != (models.Model,):
|
||||
kwargs['bases'] = self.bases
|
||||
if self.managers and self.managers != [('objects', models.Manager())]:
|
||||
kwargs['managers'] = self.managers
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.add_model(ModelState(
|
||||
app_label,
|
||||
self.name,
|
||||
list(self.fields),
|
||||
dict(self.options),
|
||||
tuple(self.bases),
|
||||
list(self.managers),
|
||||
))
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.create_model(model)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = from_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.delete_model(model)
|
||||
|
||||
def describe(self):
|
||||
return "Create %smodel %s" % ("proxy " if self.options.get("proxy", False) else "", self.name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return self.name_lower
|
||||
|
||||
def references_model(self, name, app_label):
|
||||
name_lower = name.lower()
|
||||
if name_lower == self.name_lower:
|
||||
return True
|
||||
|
||||
# Check we didn't inherit from the model
|
||||
reference_model_tuple = (app_label, name_lower)
|
||||
for base in self.bases:
|
||||
if (base is not models.Model and isinstance(base, (models.base.ModelBase, str)) and
|
||||
resolve_relation(base, app_label) == reference_model_tuple):
|
||||
return True
|
||||
|
||||
# Check we have no FKs/M2Ms with it
|
||||
for _name, field in self.fields:
|
||||
if field_references((app_label, self.name_lower), field, reference_model_tuple):
|
||||
return True
|
||||
return False
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
if (isinstance(operation, DeleteModel) and
|
||||
self.name_lower == operation.name_lower and
|
||||
not self.options.get("proxy", False)):
|
||||
return []
|
||||
elif isinstance(operation, RenameModel) and self.name_lower == operation.old_name_lower:
|
||||
return [
|
||||
CreateModel(
|
||||
operation.new_name,
|
||||
fields=self.fields,
|
||||
options=self.options,
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, AlterModelOptions) and self.name_lower == operation.name_lower:
|
||||
options = {**self.options, **operation.options}
|
||||
for key in operation.ALTER_OPTION_KEYS:
|
||||
if key not in operation.options:
|
||||
options.pop(key, None)
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=self.fields,
|
||||
options=options,
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, AlterTogetherOptionOperation) and self.name_lower == operation.name_lower:
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=self.fields,
|
||||
options={**self.options, **{operation.option_name: operation.option_value}},
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, AlterOrderWithRespectTo) and self.name_lower == operation.name_lower:
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=self.fields,
|
||||
options={**self.options, 'order_with_respect_to': operation.order_with_respect_to},
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, FieldOperation) and self.name_lower == operation.model_name_lower:
|
||||
if isinstance(operation, AddField):
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=self.fields + [(operation.name, operation.field)],
|
||||
options=self.options,
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, AlterField):
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=[
|
||||
(n, operation.field if n == operation.name else v)
|
||||
for n, v in self.fields
|
||||
],
|
||||
options=self.options,
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, RemoveField):
|
||||
options = self.options.copy()
|
||||
for option_name in ('unique_together', 'index_together'):
|
||||
option = options.pop(option_name, None)
|
||||
if option:
|
||||
option = set(filter(bool, (
|
||||
tuple(f for f in fields if f != operation.name_lower) for fields in option
|
||||
)))
|
||||
if option:
|
||||
options[option_name] = option
|
||||
order_with_respect_to = options.get('order_with_respect_to')
|
||||
if order_with_respect_to == operation.name_lower:
|
||||
del options['order_with_respect_to']
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=[
|
||||
(n, v)
|
||||
for n, v in self.fields
|
||||
if n.lower() != operation.name_lower
|
||||
],
|
||||
options=options,
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
elif isinstance(operation, RenameField):
|
||||
options = self.options.copy()
|
||||
for option_name in ('unique_together', 'index_together'):
|
||||
option = options.get(option_name)
|
||||
if option:
|
||||
options[option_name] = {
|
||||
tuple(operation.new_name if f == operation.old_name else f for f in fields)
|
||||
for fields in option
|
||||
}
|
||||
order_with_respect_to = options.get('order_with_respect_to')
|
||||
if order_with_respect_to == operation.old_name:
|
||||
options['order_with_respect_to'] = operation.new_name
|
||||
return [
|
||||
CreateModel(
|
||||
self.name,
|
||||
fields=[
|
||||
(operation.new_name if n == operation.old_name else n, v)
|
||||
for n, v in self.fields
|
||||
],
|
||||
options=options,
|
||||
bases=self.bases,
|
||||
managers=self.managers,
|
||||
),
|
||||
]
|
||||
return super().reduce(operation, app_label)
|
||||
|
||||
|
||||
class DeleteModel(ModelOperation):
|
||||
"""Drop a model's table."""
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'name': self.name,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.remove_model(app_label, self.name_lower)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = from_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.delete_model(model)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.create_model(model)
|
||||
|
||||
def references_model(self, name, app_label):
|
||||
# The deleted model could be referencing the specified model through
|
||||
# related fields.
|
||||
return True
|
||||
|
||||
def describe(self):
|
||||
return "Delete model %s" % self.name
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'delete_%s' % self.name_lower
|
||||
|
||||
|
||||
class RenameModel(ModelOperation):
|
||||
"""Rename a model."""
|
||||
|
||||
def __init__(self, old_name, new_name):
|
||||
self.old_name = old_name
|
||||
self.new_name = new_name
|
||||
super().__init__(old_name)
|
||||
|
||||
@cached_property
|
||||
def old_name_lower(self):
|
||||
return self.old_name.lower()
|
||||
|
||||
@cached_property
|
||||
def new_name_lower(self):
|
||||
return self.new_name.lower()
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'old_name': self.old_name,
|
||||
'new_name': self.new_name,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.rename_model(app_label, self.old_name, self.new_name)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.new_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, new_model):
|
||||
old_model = from_state.apps.get_model(app_label, self.old_name)
|
||||
# Move the main table
|
||||
schema_editor.alter_db_table(
|
||||
new_model,
|
||||
old_model._meta.db_table,
|
||||
new_model._meta.db_table,
|
||||
)
|
||||
# Alter the fields pointing to us
|
||||
for related_object in old_model._meta.related_objects:
|
||||
if related_object.related_model == old_model:
|
||||
model = new_model
|
||||
related_key = (app_label, self.new_name_lower)
|
||||
else:
|
||||
model = related_object.related_model
|
||||
related_key = (
|
||||
related_object.related_model._meta.app_label,
|
||||
related_object.related_model._meta.model_name,
|
||||
)
|
||||
to_field = to_state.apps.get_model(
|
||||
*related_key
|
||||
)._meta.get_field(related_object.field.name)
|
||||
schema_editor.alter_field(
|
||||
model,
|
||||
related_object.field,
|
||||
to_field,
|
||||
)
|
||||
# Rename M2M fields whose name is based on this model's name.
|
||||
fields = zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many)
|
||||
for (old_field, new_field) in fields:
|
||||
# Skip self-referential fields as these are renamed above.
|
||||
if new_field.model == new_field.related_model or not new_field.remote_field.through._meta.auto_created:
|
||||
continue
|
||||
# Rename the M2M table that's based on this model's name.
|
||||
old_m2m_model = old_field.remote_field.through
|
||||
new_m2m_model = new_field.remote_field.through
|
||||
schema_editor.alter_db_table(
|
||||
new_m2m_model,
|
||||
old_m2m_model._meta.db_table,
|
||||
new_m2m_model._meta.db_table,
|
||||
)
|
||||
# Rename the column in the M2M table that's based on this
|
||||
# model's name.
|
||||
schema_editor.alter_field(
|
||||
new_m2m_model,
|
||||
old_m2m_model._meta.get_field(old_model._meta.model_name),
|
||||
new_m2m_model._meta.get_field(new_model._meta.model_name),
|
||||
)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
self.new_name_lower, self.old_name_lower = self.old_name_lower, self.new_name_lower
|
||||
self.new_name, self.old_name = self.old_name, self.new_name
|
||||
|
||||
self.database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
|
||||
self.new_name_lower, self.old_name_lower = self.old_name_lower, self.new_name_lower
|
||||
self.new_name, self.old_name = self.old_name, self.new_name
|
||||
|
||||
def references_model(self, name, app_label):
|
||||
return (
|
||||
name.lower() == self.old_name_lower or
|
||||
name.lower() == self.new_name_lower
|
||||
)
|
||||
|
||||
def describe(self):
|
||||
return "Rename model %s to %s" % (self.old_name, self.new_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'rename_%s_%s' % (self.old_name_lower, self.new_name_lower)
|
||||
|
||||
def reduce(self, operation, app_label):
|
||||
if (isinstance(operation, RenameModel) and
|
||||
self.new_name_lower == operation.old_name_lower):
|
||||
return [
|
||||
RenameModel(
|
||||
self.old_name,
|
||||
operation.new_name,
|
||||
),
|
||||
]
|
||||
# Skip `ModelOperation.reduce` as we want to run `references_model`
|
||||
# against self.new_name.
|
||||
return (
|
||||
super(ModelOperation, self).reduce(operation, app_label) or
|
||||
not operation.references_model(self.new_name, app_label)
|
||||
)
|
||||
|
||||
|
||||
class ModelOptionOperation(ModelOperation):
|
||||
def reduce(self, operation, app_label):
|
||||
if isinstance(operation, (self.__class__, DeleteModel)) and self.name_lower == operation.name_lower:
|
||||
return [operation]
|
||||
return super().reduce(operation, app_label)
|
||||
|
||||
|
||||
class AlterModelTable(ModelOptionOperation):
|
||||
"""Rename a model's table."""
|
||||
|
||||
def __init__(self, name, table):
|
||||
self.table = table
|
||||
super().__init__(name)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'name': self.name,
|
||||
'table': self.table,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.alter_model_options(app_label, self.name_lower, {'db_table': self.table})
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, new_model):
|
||||
old_model = from_state.apps.get_model(app_label, self.name)
|
||||
schema_editor.alter_db_table(
|
||||
new_model,
|
||||
old_model._meta.db_table,
|
||||
new_model._meta.db_table,
|
||||
)
|
||||
# Rename M2M fields whose name is based on this model's db_table
|
||||
for (old_field, new_field) in zip(old_model._meta.local_many_to_many, new_model._meta.local_many_to_many):
|
||||
if new_field.remote_field.through._meta.auto_created:
|
||||
schema_editor.alter_db_table(
|
||||
new_field.remote_field.through,
|
||||
old_field.remote_field.through._meta.db_table,
|
||||
new_field.remote_field.through._meta.db_table,
|
||||
)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
return self.database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
|
||||
def describe(self):
|
||||
return "Rename table for %s to %s" % (
|
||||
self.name,
|
||||
self.table if self.table is not None else "(default)"
|
||||
)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'alter_%s_table' % self.name_lower
|
||||
|
||||
|
||||
class AlterTogetherOptionOperation(ModelOptionOperation):
|
||||
option_name = None
|
||||
|
||||
def __init__(self, name, option_value):
|
||||
if option_value:
|
||||
option_value = set(normalize_together(option_value))
|
||||
setattr(self, self.option_name, option_value)
|
||||
super().__init__(name)
|
||||
|
||||
@cached_property
|
||||
def option_value(self):
|
||||
return getattr(self, self.option_name)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'name': self.name,
|
||||
self.option_name: self.option_value,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.alter_model_options(
|
||||
app_label,
|
||||
self.name_lower,
|
||||
{self.option_name: self.option_value},
|
||||
)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
new_model = to_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, new_model):
|
||||
old_model = from_state.apps.get_model(app_label, self.name)
|
||||
alter_together = getattr(schema_editor, 'alter_%s' % self.option_name)
|
||||
alter_together(
|
||||
new_model,
|
||||
getattr(old_model._meta, self.option_name, set()),
|
||||
getattr(new_model._meta, self.option_name, set()),
|
||||
)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
return self.database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
|
||||
def references_field(self, model_name, name, app_label):
|
||||
return (
|
||||
self.references_model(model_name, app_label) and
|
||||
(
|
||||
not self.option_value or
|
||||
any((name in fields) for fields in self.option_value)
|
||||
)
|
||||
)
|
||||
|
||||
def describe(self):
|
||||
return "Alter %s for %s (%s constraint(s))" % (self.option_name, self.name, len(self.option_value or ''))
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'alter_%s_%s' % (self.name_lower, self.option_name)
|
||||
|
||||
|
||||
class AlterUniqueTogether(AlterTogetherOptionOperation):
|
||||
"""
|
||||
Change the value of unique_together to the target one.
|
||||
Input value of unique_together must be a set of tuples.
|
||||
"""
|
||||
option_name = 'unique_together'
|
||||
|
||||
def __init__(self, name, unique_together):
|
||||
super().__init__(name, unique_together)
|
||||
|
||||
|
||||
class AlterIndexTogether(AlterTogetherOptionOperation):
|
||||
"""
|
||||
Change the value of index_together to the target one.
|
||||
Input value of index_together must be a set of tuples.
|
||||
"""
|
||||
option_name = "index_together"
|
||||
|
||||
def __init__(self, name, index_together):
|
||||
super().__init__(name, index_together)
|
||||
|
||||
|
||||
class AlterOrderWithRespectTo(ModelOptionOperation):
|
||||
"""Represent a change with the order_with_respect_to option."""
|
||||
|
||||
option_name = 'order_with_respect_to'
|
||||
|
||||
def __init__(self, name, order_with_respect_to):
|
||||
self.order_with_respect_to = order_with_respect_to
|
||||
super().__init__(name)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'name': self.name,
|
||||
'order_with_respect_to': self.order_with_respect_to,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.alter_model_options(
|
||||
app_label,
|
||||
self.name_lower,
|
||||
{self.option_name: self.order_with_respect_to},
|
||||
)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
to_model = to_state.apps.get_model(app_label, self.name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, to_model):
|
||||
from_model = from_state.apps.get_model(app_label, self.name)
|
||||
# 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("_order"))
|
||||
# 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("_order")
|
||||
if not field.has_default():
|
||||
field.default = 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_field(self, model_name, name, app_label):
|
||||
return (
|
||||
self.references_model(model_name, app_label) and
|
||||
(
|
||||
self.order_with_respect_to is None or
|
||||
name == self.order_with_respect_to
|
||||
)
|
||||
)
|
||||
|
||||
def describe(self):
|
||||
return "Set order_with_respect_to on %s to %s" % (self.name, self.order_with_respect_to)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'alter_%s_order_with_respect_to' % self.name_lower
|
||||
|
||||
|
||||
class AlterModelOptions(ModelOptionOperation):
|
||||
"""
|
||||
Set new model options that don't directly affect the database schema
|
||||
(like verbose_name, permissions, ordering). Python code in migrations
|
||||
may still need them.
|
||||
"""
|
||||
|
||||
# Model options we want to compare and preserve in an AlterModelOptions op
|
||||
ALTER_OPTION_KEYS = [
|
||||
"base_manager_name",
|
||||
"default_manager_name",
|
||||
"default_related_name",
|
||||
"get_latest_by",
|
||||
"managed",
|
||||
"ordering",
|
||||
"permissions",
|
||||
"default_permissions",
|
||||
"select_on_save",
|
||||
"verbose_name",
|
||||
"verbose_name_plural",
|
||||
]
|
||||
|
||||
def __init__(self, name, options):
|
||||
self.options = options
|
||||
super().__init__(name)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'name': self.name,
|
||||
'options': self.options,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.alter_model_options(
|
||||
app_label,
|
||||
self.name_lower,
|
||||
self.options,
|
||||
self.ALTER_OPTION_KEYS,
|
||||
)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
|
||||
def describe(self):
|
||||
return "Change Meta options on %s" % self.name
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'alter_%s_options' % self.name_lower
|
||||
|
||||
|
||||
class AlterModelManagers(ModelOptionOperation):
|
||||
"""Alter the model's managers."""
|
||||
|
||||
serialization_expand_args = ['managers']
|
||||
|
||||
def __init__(self, name, managers):
|
||||
self.managers = managers
|
||||
super().__init__(name)
|
||||
|
||||
def deconstruct(self):
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[self.name, self.managers],
|
||||
{}
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.alter_model_managers(app_label, self.name_lower, self.managers)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
|
||||
def describe(self):
|
||||
return "Change managers on %s" % self.name
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'alter_%s_managers' % self.name_lower
|
||||
|
||||
|
||||
class IndexOperation(Operation):
|
||||
option_name = 'indexes'
|
||||
|
||||
@cached_property
|
||||
def model_name_lower(self):
|
||||
return self.model_name.lower()
|
||||
|
||||
|
||||
class AddIndex(IndexOperation):
|
||||
"""Add an index on a model."""
|
||||
|
||||
def __init__(self, model_name, index):
|
||||
self.model_name = model_name
|
||||
if not index.name:
|
||||
raise ValueError(
|
||||
"Indexes passed to AddIndex operations require a name "
|
||||
"argument. %r doesn't have one." % index
|
||||
)
|
||||
self.index = index
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.add_index(app_label, self.model_name_lower, self.index)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.add_index(model, self.index)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = from_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.remove_index(model, self.index)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'model_name': self.model_name,
|
||||
'index': self.index,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs,
|
||||
)
|
||||
|
||||
def describe(self):
|
||||
if self.index.expressions:
|
||||
return 'Create index %s on %s on model %s' % (
|
||||
self.index.name,
|
||||
', '.join([str(expression) for expression in self.index.expressions]),
|
||||
self.model_name,
|
||||
)
|
||||
return 'Create index %s on field(s) %s of model %s' % (
|
||||
self.index.name,
|
||||
', '.join(self.index.fields),
|
||||
self.model_name,
|
||||
)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return '%s_%s' % (self.model_name_lower, self.index.name.lower())
|
||||
|
||||
|
||||
class RemoveIndex(IndexOperation):
|
||||
"""Remove an index from a model."""
|
||||
|
||||
def __init__(self, model_name, name):
|
||||
self.model_name = model_name
|
||||
self.name = name
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.remove_index(app_label, self.model_name_lower, self.name)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = from_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
from_model_state = from_state.models[app_label, self.model_name_lower]
|
||||
index = from_model_state.get_index_by_name(self.name)
|
||||
schema_editor.remove_index(model, index)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
to_model_state = to_state.models[app_label, self.model_name_lower]
|
||||
index = to_model_state.get_index_by_name(self.name)
|
||||
schema_editor.add_index(model, index)
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'model_name': self.model_name,
|
||||
'name': self.name,
|
||||
}
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs,
|
||||
)
|
||||
|
||||
def describe(self):
|
||||
return 'Remove index %s from %s' % (self.name, self.model_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'remove_%s_%s' % (self.model_name_lower, self.name.lower())
|
||||
|
||||
|
||||
class AddConstraint(IndexOperation):
|
||||
option_name = 'constraints'
|
||||
|
||||
def __init__(self, model_name, constraint):
|
||||
self.model_name = model_name
|
||||
self.constraint = constraint
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.add_constraint(app_label, self.model_name_lower, self.constraint)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.add_constraint(model, self.constraint)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
schema_editor.remove_constraint(model, self.constraint)
|
||||
|
||||
def deconstruct(self):
|
||||
return self.__class__.__name__, [], {
|
||||
'model_name': self.model_name,
|
||||
'constraint': self.constraint,
|
||||
}
|
||||
|
||||
def describe(self):
|
||||
return 'Create constraint %s on model %s' % (self.constraint.name, self.model_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return '%s_%s' % (self.model_name_lower, self.constraint.name.lower())
|
||||
|
||||
|
||||
class RemoveConstraint(IndexOperation):
|
||||
option_name = 'constraints'
|
||||
|
||||
def __init__(self, model_name, name):
|
||||
self.model_name = model_name
|
||||
self.name = name
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
state.remove_constraint(app_label, self.model_name_lower, self.name)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
from_model_state = from_state.models[app_label, self.model_name_lower]
|
||||
constraint = from_model_state.get_constraint_by_name(self.name)
|
||||
schema_editor.remove_constraint(model, constraint)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.model_name)
|
||||
if self.allow_migrate_model(schema_editor.connection.alias, model):
|
||||
to_model_state = to_state.models[app_label, self.model_name_lower]
|
||||
constraint = to_model_state.get_constraint_by_name(self.name)
|
||||
schema_editor.add_constraint(model, constraint)
|
||||
|
||||
def deconstruct(self):
|
||||
return self.__class__.__name__, [], {
|
||||
'model_name': self.model_name,
|
||||
'name': self.name,
|
||||
}
|
||||
|
||||
def describe(self):
|
||||
return 'Remove constraint %s from model %s' % (self.name, self.model_name)
|
||||
|
||||
@property
|
||||
def migration_name_fragment(self):
|
||||
return 'remove_%s_%s' % (self.model_name_lower, self.name.lower())
|
@@ -0,0 +1,203 @@
|
||||
from django.db import router
|
||||
|
||||
from .base import Operation
|
||||
|
||||
|
||||
class SeparateDatabaseAndState(Operation):
|
||||
"""
|
||||
Take two lists of operations - ones that will be used for the database,
|
||||
and ones that will be used for the state change. This allows operations
|
||||
that don't support state change to have it applied, or have operations
|
||||
that affect the state or not the database, or so on.
|
||||
"""
|
||||
|
||||
serialization_expand_args = ['database_operations', 'state_operations']
|
||||
|
||||
def __init__(self, database_operations=None, state_operations=None):
|
||||
self.database_operations = database_operations or []
|
||||
self.state_operations = state_operations or []
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {}
|
||||
if self.database_operations:
|
||||
kwargs['database_operations'] = self.database_operations
|
||||
if self.state_operations:
|
||||
kwargs['state_operations'] = self.state_operations
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
for state_operation in self.state_operations:
|
||||
state_operation.state_forwards(app_label, state)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
# We calculate state separately in here since our state functions aren't useful
|
||||
for database_operation in self.database_operations:
|
||||
to_state = from_state.clone()
|
||||
database_operation.state_forwards(app_label, to_state)
|
||||
database_operation.database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
from_state = to_state
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
# We calculate state separately in here since our state functions aren't useful
|
||||
to_states = {}
|
||||
for dbop in self.database_operations:
|
||||
to_states[dbop] = to_state
|
||||
to_state = to_state.clone()
|
||||
dbop.state_forwards(app_label, to_state)
|
||||
# to_state now has the states of all the database_operations applied
|
||||
# which is the from_state for the backwards migration of the last
|
||||
# operation.
|
||||
for database_operation in reversed(self.database_operations):
|
||||
from_state = to_state
|
||||
to_state = to_states[database_operation]
|
||||
database_operation.database_backwards(app_label, schema_editor, from_state, to_state)
|
||||
|
||||
def describe(self):
|
||||
return "Custom state/database change combination"
|
||||
|
||||
|
||||
class RunSQL(Operation):
|
||||
"""
|
||||
Run some raw SQL. A reverse SQL statement may be provided.
|
||||
|
||||
Also accept a list of operations that represent the state change effected
|
||||
by this SQL change, in case it's custom column/table creation/deletion.
|
||||
"""
|
||||
noop = ''
|
||||
|
||||
def __init__(self, sql, reverse_sql=None, state_operations=None, hints=None, elidable=False):
|
||||
self.sql = sql
|
||||
self.reverse_sql = reverse_sql
|
||||
self.state_operations = state_operations or []
|
||||
self.hints = hints or {}
|
||||
self.elidable = elidable
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'sql': self.sql,
|
||||
}
|
||||
if self.reverse_sql is not None:
|
||||
kwargs['reverse_sql'] = self.reverse_sql
|
||||
if self.state_operations:
|
||||
kwargs['state_operations'] = self.state_operations
|
||||
if self.hints:
|
||||
kwargs['hints'] = self.hints
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
@property
|
||||
def reversible(self):
|
||||
return self.reverse_sql is not None
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
for state_operation in self.state_operations:
|
||||
state_operation.state_forwards(app_label, state)
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints):
|
||||
self._run_sql(schema_editor, self.sql)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
if self.reverse_sql is None:
|
||||
raise NotImplementedError("You cannot reverse this operation")
|
||||
if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints):
|
||||
self._run_sql(schema_editor, self.reverse_sql)
|
||||
|
||||
def describe(self):
|
||||
return "Raw SQL operation"
|
||||
|
||||
def _run_sql(self, schema_editor, sqls):
|
||||
if isinstance(sqls, (list, tuple)):
|
||||
for sql in sqls:
|
||||
params = None
|
||||
if isinstance(sql, (list, tuple)):
|
||||
elements = len(sql)
|
||||
if elements == 2:
|
||||
sql, params = sql
|
||||
else:
|
||||
raise ValueError("Expected a 2-tuple but got %d" % elements)
|
||||
schema_editor.execute(sql, params=params)
|
||||
elif sqls != RunSQL.noop:
|
||||
statements = schema_editor.connection.ops.prepare_sql_script(sqls)
|
||||
for statement in statements:
|
||||
schema_editor.execute(statement, params=None)
|
||||
|
||||
|
||||
class RunPython(Operation):
|
||||
"""
|
||||
Run Python code in a context suitable for doing versioned ORM operations.
|
||||
"""
|
||||
|
||||
reduces_to_sql = False
|
||||
|
||||
def __init__(self, code, reverse_code=None, atomic=None, hints=None, elidable=False):
|
||||
self.atomic = atomic
|
||||
# Forwards code
|
||||
if not callable(code):
|
||||
raise ValueError("RunPython must be supplied with a callable")
|
||||
self.code = code
|
||||
# Reverse code
|
||||
if reverse_code is None:
|
||||
self.reverse_code = None
|
||||
else:
|
||||
if not callable(reverse_code):
|
||||
raise ValueError("RunPython must be supplied with callable arguments")
|
||||
self.reverse_code = reverse_code
|
||||
self.hints = hints or {}
|
||||
self.elidable = elidable
|
||||
|
||||
def deconstruct(self):
|
||||
kwargs = {
|
||||
'code': self.code,
|
||||
}
|
||||
if self.reverse_code is not None:
|
||||
kwargs['reverse_code'] = self.reverse_code
|
||||
if self.atomic is not None:
|
||||
kwargs['atomic'] = self.atomic
|
||||
if self.hints:
|
||||
kwargs['hints'] = self.hints
|
||||
return (
|
||||
self.__class__.__qualname__,
|
||||
[],
|
||||
kwargs
|
||||
)
|
||||
|
||||
@property
|
||||
def reversible(self):
|
||||
return self.reverse_code is not None
|
||||
|
||||
def state_forwards(self, app_label, state):
|
||||
# RunPython objects have no state effect. To add some, combine this
|
||||
# with SeparateDatabaseAndState.
|
||||
pass
|
||||
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
# RunPython has access to all models. Ensure that all models are
|
||||
# reloaded in case any are delayed.
|
||||
from_state.clear_delayed_apps_cache()
|
||||
if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints):
|
||||
# We now execute the Python code in a context that contains a 'models'
|
||||
# object, representing the versioned models as an app registry.
|
||||
# We could try to override the global cache, but then people will still
|
||||
# use direct imports, so we go with a documentation approach instead.
|
||||
self.code(from_state.apps, schema_editor)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
if self.reverse_code is None:
|
||||
raise NotImplementedError("You cannot reverse this operation")
|
||||
if router.allow_migrate(schema_editor.connection.alias, app_label, **self.hints):
|
||||
self.reverse_code(from_state.apps, schema_editor)
|
||||
|
||||
def describe(self):
|
||||
return "Raw Python operation"
|
||||
|
||||
@staticmethod
|
||||
def noop(apps, schema_editor):
|
||||
return None
|
Reference in New Issue
Block a user