django1/django/db/migrations/autodetector.py

70 lines
2.8 KiB
Python
Raw Normal View History

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)