70 lines
2.8 KiB
Python
70 lines
2.8 KiB
Python
|
from django.db.migrations import operations
|
||
|
from django.db.migrations.migration import Migration
|
||
|
|
||
|
|
||
|
class AutoDetector(object):
|
||
|
"""
|
||
|
Takes a pair of ProjectStates, and compares them to see what the
|
||
|
first would need doing to make it match the second (the second
|
||
|
usually being the project's current state).
|
||
|
|
||
|
Note that this naturally operates on entire projects at a time,
|
||
|
as it's likely that changes interact (for example, you can't
|
||
|
add a ForeignKey without having a migration to add the table it
|
||
|
depends on first). A user interface may offer single-app detection
|
||
|
if it wishes, with the caveat that it may not always be possible.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, from_state, to_state):
|
||
|
self.from_state = from_state
|
||
|
self.to_state = to_state
|
||
|
|
||
|
def changes(self):
|
||
|
"""
|
||
|
Returns a set of migration plans which will achieve the
|
||
|
change from from_state to to_state.
|
||
|
"""
|
||
|
# We'll store migrations as lists by app names for now
|
||
|
self.migrations = {}
|
||
|
# Stage one: Adding models.
|
||
|
added_models = set(self.to_state.keys()) - set(self.from_state.keys())
|
||
|
for app_label, model_name in added_models:
|
||
|
model_state = self.to_state[app_label, model_name]
|
||
|
self.add_to_migration(
|
||
|
app_label,
|
||
|
operations.CreateModel(
|
||
|
model_state.name,
|
||
|
model_state.fields,
|
||
|
model_state.options,
|
||
|
model_state.bases,
|
||
|
)
|
||
|
)
|
||
|
# Removing models
|
||
|
removed_models = set(self.from_state.keys()) - set(self.to_state.keys())
|
||
|
for app_label, model_name in removed_models:
|
||
|
model_state = self.from_state[app_label, model_name]
|
||
|
self.add_to_migration(
|
||
|
app_label,
|
||
|
operations.DeleteModel(
|
||
|
model_state.name,
|
||
|
)
|
||
|
)
|
||
|
# Alright, now sort out and return the migrations
|
||
|
for app_label, migrations in self.migrations.items():
|
||
|
for m1, m2 in zip(migrations, migrations[1:]):
|
||
|
m2.dependencies.append((app_label, m1.name))
|
||
|
# Flatten and return
|
||
|
result = set()
|
||
|
for app_label, migrations in self.migrations.items():
|
||
|
for migration in migrations:
|
||
|
subclass = type("Migration", (Migration,), migration)
|
||
|
instance = subclass(migration['name'], app_label)
|
||
|
result.append(instance)
|
||
|
return result
|
||
|
|
||
|
def add_to_migration(self, app_label, operation):
|
||
|
migrations = self.migrations.setdefault(app_label, [])
|
||
|
if not migrations:
|
||
|
migrations.append({"name": "temp-%i" % len(migrations) + 1, "operations": [], "dependencies": []})
|
||
|
migrations[-1].operations.append(operation)
|