makemigrationsに指定されたappがある場合はそれが存在するかのチェック app_labels = set(app_labels) bad_app_labels = set() for app_label in app_labels: try: apps.get_app_config(app_label) except LookupError: bad_app_labels.add(app_label) if bad_app_labels: for app_label in bad_app_labels: self.stderr.write("App '%s' could not be found. Is it in INSTALLED_APPS?" % app_label) sys.exit(2)
recorder = MigrationRecorder(connection) applied = recorder.applied_migrations() for migration in applied: # If the migration is unknown, skip it. if migration not in self.graph.nodes: continue for parent in self.graph.node_map[migration].parents: logger.debug('migration:{} parent:{} replacements:{}'.format(migration, parent, self.replacements)) if parent not in applied: : : raise InconsistentMigrationHistory( "Migration {}.{} is applied before its dependency " "{}.{} on database '{}'.".format( migration[0], migration[1], parent[0], parent[1], connection.alias, ) ) MigrationRecorder経由でdjango_migrationsを チェックしている
and not self.merge: name_str = "; ".join( "%s in %s" % (", ".join(names), app) for app, names in conflicts.items() ) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " "migration graph: (%s).\nTo fix them run " "'python manage.py makemigrations --merge'" % name_str ) # If they want to merge and there's nothing to merge, then politely exit if self.merge and not conflicts: self.stdout.write("No conflicts detected to merge.") return # @@ merge action # --mergeを指定していればconflict解決のためのMergeを実⾏する if self.merge and conflicts: return self.handle_merge(loader, conflicts) —merge の場合はここで`self.handle_merge`に流れる
name book date price bio app1 Book Author app2 Order Name name book date price bio author 適⽤すべき Migrationが 特定される 最新のコードからのState 既存のMigrationからのState author
: # StateからモデルやFieldを取得してマスターデータを作る for al, mn in self.from_state.models: model = self.old_apps.get_model(al, mn) if not model._meta.managed: self.old_unmanaged_keys.add((al, mn)) elif al not in self.from_state.real_apps: if model._meta.proxy: self.old_proxy_keys.add((al, mn)) else: self.old_model_keys.add((al, mn)) for al, mn in self.to_state.models: model = self.new_apps.get_model(al, mn) if not model._meta.managed: self.new_unmanaged_keys.add((al, mn)) elif ( al not in self.from_state.real_apps or (convert_apps and al in convert_apps) ): if model._meta.proxy: self.new_proxy_keys.add((al, mn)) else: self.new_model_keys.add((al, mn)) from/to のstateについてmodelやFieldを列挙して マスターデータを作成している
to come first self.generate_renamed_models() # Prepare lists of fields and generate through model map self._prepare_field_lists() self._generate_through_model_map() # Generate non-rename model operations self.generate_deleted_models() self.generate_created_models() self.generate_deleted_proxies() : self.generate_added_indexes() self.generate_altered_db_table() self.generate_altered_order_with_respect_to() self._sort_migrations() self._build_migration_list(graph) self._optimize_migrations() return self.migrations 変更内容ごとに個別に検知処理をしている
conflicting apps and drop out # hard if there are any # @@ マイグレーションファイルのコンフリクトチェック # 同じAppで複数のリーフが存在していないかを⾒ている conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join( "%s in %s" % (", ".join(names), app) for app, names in conflicts.items() ) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " "migration graph: (%s).\nTo fix them run " "'python manage.py makemigrations --merge'" % name_str ) makemigrationsでも実施していた マイグレーションファイルのチェック
""" Given a set of targets, return a list of (Migration instance, backwards?). """ # @@ # targets=[('admin', '0002_logentry_remove_auto_add'), ('app1', '0002_book_author'), ....] plan = [] if clean_start: applied = set() else: applied = set(self.loader.applied_migrations) for target in targets: : else: for migration in self.loader.graph.forwards_plan(target): if migration not in applied: plan.append((self.loader.graph.nodes[migration], False)) applied.add(migration) backwords=False ⾃ノードの親を辿っていき未適⽤のものを Migrate対象にしていく
plan=plan, state=pre_migrate_state.clone(), fake=fake, fake_initial=fake_initial, ) # django.db.migrations.executor.MigrationExecutor#migrate def migrate(self, targets, plan=None, state=None, fake=False, fake_initial=False): : : elif all_forwards: if state is None: # The resulting state should still include applied migrations. state = self._create_project_state(with_applied_migrations=True) # @@ migrate実⾏ state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial) 普通はこれ
fake, fake_initial): """ Take a list of 2-tuples of the form (migration instance, False) and apply them in the order they occur in the full_plan. """ migrations_to_run = {m[0] for m in plan} for migration, _ in full_plan: if not migrations_to_run: # process. break if migration in migrations_to_run: if 'apps' not in state.__dict__: if self.progress_callback: self.progress_callback("render_start") state.apps # Render all -- performance critical if self.progress_callback: self.progress_callback("render_success") # @@ 各Migrateの実⾏ state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial) migrations_to_run.remove(migration) return state さらにapply_migrationに流れる
Create a field on a model. Usually involves adding a column, but may involve adding a table instead (for M2M fields). """ # Special-case implicit M2M tables if field.many_to_many and field.remote_field.through._meta.auto_created: return self.create_model(field.remote_field.through) self._remake_table(model, create_field=field) SQLite3の場合は、DB側の制限のため実際は 列を増やしたテーブルを再作成している
27, in <module> execute_from_command_line(sys.argv) File "/Users/denzow/work/denzow/DjangoConSample/django/core/management/__init__.py", line 381, in execute_from_command_line utility.execute() self.check_related_objects(join_info.final_field, value, join_info.opts) File "/Users/denzow/work/denzow/DjangoConSample/django/db/models/sql/query.py", line 1063, in check_related_objects self.check_query_object_type(value, opts, field) File "/Users/denzow/work/denzow/DjangoConSample/django/db/models/sql/query.py", line 1046, in check_query_object_type (value, opts.object_name)) ValueError: Cannot query "<class '__fake__.Book'>": Must be "Book" instance.
apps.get_model('app1', 'Book') for book in book_model.objects.all(): # 他のサービスで使われいないBookを消す処理的なもの if not other_sv.get_by_book_list(book)): book.delete() class Migration(migrations.Migration): dependencies = [ ('app1', '0001_initial'), ] operations = [ migrations.RunPython(test, RunPython.noop), ] 他のモデルとの突き合わせをしながらのマイグレーションで死亡
apps.get_model('app1', 'Book') for book in book_model.objects.all(): # 他のサービスで使われいないBookを消す処理的なもの if not other_sv.get_by_book_list(book)): book.delete() 他のモデルとの突き合わせをしながらのマイグレーションで死亡