1. 程式人生 > >Django原始碼分析10:makemigrations命令概述

Django原始碼分析10:makemigrations命令概述

django原始碼分析

本文環境python3.5.2,django1.10.x系列

django原始碼分析-makemigrations命令概述

Django專案中的資料庫管理命令就是通過makemigrations來實現的,通過呼叫該命令可以對Django中的app的model表進行改動後生成相關連的migrations檔案,然後通過呼叫migrate命令執行migrations中對資料庫中操作。本次分析的過程基於上文的celery_django中的celery_app中的model檔案中的表結構,makemigrations的大概的思路是:根據現有的migrations檔案,來判斷哪些資料庫和資料庫對應的欄位已經執行過了,然後通過匯入每個app中的model的表結構,依次對比來判斷是否有表的更改或者欄位的變更或者新建表等操作,通過對比結果然後生成migrations檔案,此時通過migrate命令就可以達到更改資料庫的目的。

大致的流程如上所述,本文就舉其中最簡單的生成celery_app中的第一個migrations檔案為例。

makemigrations的基本執行流程

首先檢視對應命令的handle處理過程如下;

    def handle(self, *app_labels, **options):
        self.verbosity = options['verbosity']                               # 配置資訊
        self.interactive = options['interactive']                           # 是否需要使用者終端輸入確認
        self.dry_run = options['dry_run']   
        self.merge = options['merge']                                       # 是否是merge衝突
        self.empty = options['empty']                                       # 是否是生成empty的
        self.migration_name = options['name']                               # 生成的migration的檔名稱
        self.exit_code = options['exit_code']                               # 退出碼
        check_changes = options['check_changes']                            # 檢查變化

        if self.exit_code: 
            warnings.warn(
                "The --exit option is deprecated in favor of the --check option.",
                RemovedInDjango20Warning
            )

        # Make sure the app they asked for exists
        app_labels = set(app_labels)                                        # 獲取app的標籤
        bad_app_labels = set()                                              # bad app集合
        for app_label in app_labels:                                        # 遍歷app列表
            try:
                apps.get_app_config(app_label)                              # 嘗試獲取app配置
            except LookupError:
                bad_app_labels.add(app_label)                               # 如果獲取不到則新增到bad中
        if bad_app_labels:                                                  # 如果有Bad則打印出資訊後退出
            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)

        # Load the current graph state. Pass in None for the connection so
        # the loader doesn't try to resolve replaced migrations from DB.
        loader = MigrationLoader(None, ignore_no_migrations=True)           # 初始化一個loader

        # Raise an error if any migrations are applied before their dependencies.
        consistency_check_labels = set(config.label for config in apps.get_app_configs())   # 獲取所有需要檢查的app標籤
        # Non-default databases are only checked if database routers used.
        aliases_to_check = connections if settings.DATABASE_ROUTERS else [DEFAULT_DB_ALIAS]         # 獲取檢查的資料庫
        for alias in sorted(aliases_to_check):
            connection = connections[alias]
            if (connection.settings_dict['ENGINE'] != 'django.db.backends.dummy' and
                    # At least one app must be migrated to the database.
                    any(router.allow_migrate(connection.alias, label) for label in consistency_check_labels)):
                loader.check_consistent_history(connection)                         # 檢查資料庫中已經執行過的app的migrations

        # Before anything else, see if there's conflicting apps and drop out
        # hard if there are any and they don't want to merge
        conflicts = loader.detect_conflicts()                                       # 檢查是否有衝突

        # If app_labels is specified, filter out conflicting migrations for unspecified apps
        if app_labels:
            conflicts = {
                app_label: conflict for app_label, conflict in iteritems(conflicts)
                if app_label in app_labels
            }                                                                       # 查出衝突的app

        if conflicts and not self.merge:                                            # 如果有衝突並且沒有傳入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:                                            # 如果傳入merge引數但是沒有衝突則列印資訊返回
            self.stdout.write("No conflicts detected to merge.")
            return

        # If they want to merge and there is something to merge, then
        # divert into the merge code
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)                             # 傳入merge並有衝突則解決衝突後返回

        if self.interactive:                                                        # 是否需要使用者輸入確認資訊
            questioner = InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run)
        else:
            questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run)
        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            questioner,
        )                                                                           # 初始化生成migraitons類,傳入的引數就是從migraiotn載入的舊錶內容,並再傳入通過model重新載入的新表內容,通過對比生成migrations

        # If they want to make an empty migration, make one for each app
        if self.empty:                                                              # 是否生成空的內容
            if not app_labels:
                raise CommandError("You must supply at least one app label when using --empty.")
            # Make a fake changes() result we can pass to arrange_for_graph
            changes = {
                app: [Migration("custom", app)]
                for app in app_labels
            }
            changes = autodetector.arrange_for_graph(
                changes=changes,
                graph=loader.graph,
                migration_name=self.migration_name,
            )
            self.write_migration_files(changes)
            return

        # Detect changes
        changes = autodetector.changes(
            graph=loader.graph,
            trim_to_apps=app_labels or None,
            convert_apps=app_labels or None,
            migration_name=self.migration_name,
        )                                                                   # 通過比較新表和舊錶的欄位來獲取changs

        if not changes:                                                     # 沒有改變則列印相關資訊後退出
            # No changes? Tell them.
            if self.verbosity >= 1:
                if len(app_labels) == 1:
                    self.stdout.write("No changes detected in app '%s'" % app_labels.pop())
                elif len(app_labels) > 1:
                    self.stdout.write("No changes detected in apps '%s'" % ("', '".join(app_labels)))
                else:
                    self.stdout.write("No changes detected")

            if self.exit_code:
                sys.exit(1)
        else:
            self.write_migration_files(changes)                             # 將changes寫入到migraitons檔案中
            if check_changes:
                sys.exit(1)

主要分析下,生成changes的過程即如下程式碼的執行過程;

    # Detect changes
    changes = autodetector.changes(
        graph=loader.graph,
        trim_to_apps=app_labels or None,
        convert_apps=app_labels or None,
        migration_name=self.migration_name,
    )

該方法如下;

def changes(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
    """
    Main entry point to produce a list of applicable changes.
    Takes a graph to base names on and an optional set of apps
    to try and restrict to (restriction is not guaranteed)
    """
    changes = self._detect_changes(convert_apps, graph)                     # 生成改變資訊
    changes = self.arrange_for_graph(changes, graph, migration_name)        
    if trim_to_apps:
        changes = self._trim_to_apps(changes, trim_to_apps)
    return changes

主要先呼叫了對比生成新的changes,然後再對changes進行依賴等資訊進行調整,首先檢視_detect_changes方法;

def _detect_changes(self, convert_apps=None, graph=None):
    """
    Returns a dict of migration plans which will achieve the
    change from from_state to to_state. The dict has app labels
    as keys and a list of migrations as values.

    The resulting migrations aren't specially named, but the names
    do matter for dependencies inside the set.

    convert_apps is the list of apps to convert to use migrations
    (i.e. to make initial migrations for, in the usual case)

    graph is an optional argument that, if provided, can help improve
    dependency generation and avoid potential circular dependencies.
    """

    # The first phase is generating all the operations for each app
    # and gathering them into a big per-app list.
    # We'll then go through that list later and order it and split
    # into migrations to resolve dependencies caused by M2Ms and FKs.
    self.generated_operations = {}

    # Prepare some old/new state and model lists, separating
    # proxy models and ignoring unmigrated apps.
    self.old_apps = self.from_state.concrete_apps                   # 獲取migrations中apps
    self.new_apps = self.to_state.apps                              # 獲取現在的apps
    self.old_model_keys = []                                        # 舊model的key
    self.old_proxy_keys = []                                        # 舊model代理的key
    self.old_unmanaged_keys = []                                    
    self.new_model_keys = []
    self.new_proxy_keys = []
    self.new_unmanaged_keys = []
    for al, mn in sorted(self.from_state.models.keys()):            # 遍歷舊models
        model = self.old_apps.get_model(al, mn)                     # 獲取該model
        if not model._meta.managed:                                 # 是否是managed預設為True
            self.old_unmanaged_keys.append((al, mn))
        elif al not in self.from_state.real_apps:                   # 判斷al是否在real_apps中
            if model._meta.proxy:                                   # 如果是代理
                self.old_proxy_keys.append((al, mn))                # 新增到代理中
            else:
                self.old_model_keys.append((al, mn))                # 新增到舊modelkey中

    for al, mn in sorted(self.to_state.models.keys()):              # 遍歷新Models
        model = self.new_apps.get_model(al, mn)
        if not model._meta.managed:
            self.new_unmanaged_keys.append((al, mn))
        elif (
            al not in self.from_state.real_apps or
            (convert_apps and al in convert_apps)
        ):                                                          # 如果不在real_appas中並且不在al在convert_apps中
            if model._meta.proxy:
                self.new_proxy_keys.append((al, mn))
            else:
                self.new_model_keys.append((al, mn))

    # Renames have to come first
    self.generate_renamed_models()                                  # 檢查是否有重新命名model

    # Prepare lists of fields and generate through model map
    self._prepare_field_lists()                                     # 先獲取所有Model的field_lists
    self._generate_through_model_map()                              # 檢查是否有通過through的model

    # Generate non-rename model operations
    self.generate_deleted_models()                                  # 檢查是否有刪除的model
    self.generate_created_models()                                  # 檢查是否有新建的model
    self.generate_deleted_proxies()                                 # 檢查是否有刪除的代理model
    self.generate_created_proxies()                                 # 檢查是否有新建的代理model
    self.generate_altered_options()                                 
    self.generate_altered_managers()                                # 檢查是否有更換manager的model

    # Generate field operations
    self.generate_renamed_fields()                                  # 檢查是否有重新命名的欄位
    self.generate_removed_fields()                                  # 檢查是否有要刪除的欄位
    self.generate_added_fields()                                    # 檢查是否有要新增的欄位
    self.generate_altered_fields()                                  # 檢查是否有更改的欄位
    self.generate_altered_unique_together()                         # 是否需要更改unique
    self.generate_altered_index_together()                          # 檢查是否更改index
    self.generate_altered_db_table()                                # 檢查是否有更改表明的
    self.generate_altered_order_with_respect_to()   

    self._sort_migrations()                                         # 排序
    self._build_migration_list(graph)                               # 通過依賴新增到graph中
    self._optimize_migrations()                                     # 優化

    return self.migrations                                          # 返回migraiotns

該方法就是對比所有變更的處理函式,其中獲取所有舊錶和新表的欄位處理函式如下;

def _prepare_field_lists(self):
    """
    Prepare field lists, and prepare a list of the fields that used
    through models in the old state so we can make dependencies
    from the through model deletion to the field that uses it.
    """
    self.kept_model_keys = set(self.old_model_keys).intersection(self.new_model_keys)           # 獲取old_model_keys和new_model_keys的交集
    self.kept_proxy_keys = set(self.old_proxy_keys).intersection(self.new_proxy_keys)           # 獲取old_proxy_keys和new_proxy_keys的交集
    self.kept_unmanaged_keys = set(self.old_unmanaged_keys).intersection(self.new_unmanaged_keys)
    self.through_users = {}
    self.old_field_keys = set()
    self.new_field_keys = set()
    for app_label, model_name in sorted(self.kept_model_keys):                                  # 遍歷
        old_model_name = self.renamed_models.get((app_label, model_name), model_name)           # 從更名Model中獲取舊的model名
        old_model_state = self.from_state.models[app_label, old_model_name]                     # 獲取舊的state
        new_model_state = self.to_state.models[app_label, model_name]                           # 獲取新的state
        self.old_field_keys.update((app_label, model_name, x) for x, y in old_model_state.fields)       # 遍歷舊錶的所有欄位 並新增到old_field_keys中 為後續比較做準備
        self.new_field_keys.update((app_label, model_name, x) for x, y in new_model_state.fields)       # 從新表中獲取所有的欄位 並新增到new_field_keys中

該函式就是將舊錶和新表的所有的欄位進行了新增到欄位中,方便後續比較是否有欄位新增或者變更等,當欄位載入完成後,舉例以新建Model為例;

def generate_created_models(self):
    """
    Find all new models (both managed and unmanaged) and make create
    operations for them as well as separate operations to create any
    foreign key or M2M relationships (we'll optimize these back in later
    if we can).

    We also defer any model options that refer to collections of fields
    that might be deferred (e.g. unique_together, index_together).
    """
    old_keys = set(self.old_model_keys).union(self.old_unmanaged_keys)          # 獲取所有的舊model的keys
    added_models = set(self.new_model_keys) - old_keys                          # 獲取需要新增的Models名稱
    added_unmanaged_models = set(self.new_unmanaged_keys) - old_keys            # 獲取需要新增的unmanaged的model的key
    all_added_models = chain(
        sorted(added_models, key=self.swappable_first_key, reverse=True),
        sorted(added_unmanaged_models, key=self.swappable_first_key, reverse=True)
    )                                                                           # 獲取排序後所有需要新增的models
    for app_label, model_name in all_added_models:                              # 依次遍歷所有的Models
        model_state = self.to_state.models[app_label, model_name]               # 獲取model_state例項
        model_opts = self.new_apps.get_model(app_label, model_name)._meta       # 獲取對應model例項的_meta屬性
        # Gather related fields
        related_fields = {}                                             
        primary_key_rel = None
        for field in model_opts.local_fields:                                   # 獲取所有的local_fields
            if field.remote_field:                                              # 是否是remote_field型別
                if field.remote_field.model:                                    # 獲取對應的model
                    if field.primary_key:                                       # 如果是主鍵
                        primary_key_rel = field.remote_field.model              # 設定主鍵
                    elif not field.remote_field.parent_link:                    # 如果不在parent_link中
                        related_fields[field.name] = field                      # 設定關聯欄位的欄位
                # through will be none on M2Ms on swapped-out models;
                # we can treat lack of through as auto_created=True, though.
                if (getattr(field.remote_field, "through", None) and      
                        not field.remote_field.through._meta.auto_created):     # 如果有through 或者自增
                    related_fields[field.name] = field                          # 新增到關聯欄位中
        for field in model_opts.local_many_to_many:                             # 遍歷多對多欄位
            if field.remote_field.model:                                        # 如果是remote_field
                related_fields[field.name] = field                              # 設定到關聯字典中
            if getattr(field.remote_field, "through", None) and not field.remote_field.through._meta.auto_created:
                related_fields[field.name] = field
        # Are there unique/index_together to defer?
        unique_together = model_state.options.pop('unique_together', None)      # 獲取unique_together屬性內容
        index_together = model_state.options.pop('index_together', None)        # 獲取index_together內容
        order_with_respect_to = model_state.options.pop('order_with_respect_to', None)
        # Depend on the deletion of any possible proxy version of us
        dependencies = [
            (app_label, model_name, None, False),                               # 建立依賴屬性值
        ]
        # Depend on all bases
        for base in model_state.bases:                                          # 依次訪問父類依賴
            if isinstance(base, six.string_types) and "." in base:              # 
                base_app_label, base_name = base.split(".", 1)
                dependencies.append((base_app_label, base_name, None, True))    # 依次新增父類依賴資訊
        # Depend on the other end of the primary key if it's a relation
        if primary_key_rel:
            dependencies.append((
                primary_key_rel._meta.app_label,
                primary_key_rel._meta.object_name,
                None,
                True
            ))
        # Generate creation operation
        self.add_operation(
            app_label,
            operations.CreateModel(
                name=model_state.name,
                fields=[d for d in model_state.fields if d[0] not in related_fields],
                options=model_state.options,
                bases=model_state.bases,
                managers=model_state.managers,
            ),
            dependencies=dependencies,
            beginning=True,
        )                                                                       # 新增生成資料表的操作,並新增依賴

        # Don't add operations which modify the database for unmanaged models
        if not model_opts.managed:                                              # 如果是managed為false則下一個
            continue

        # Generate operations for each related field
        for name, field in sorted(related_fields.items()):                      # 處理關聯欄位值
            dependencies = self._get_dependecies_for_foreign_key(field)         # 獲取依賴外來鍵
            # Depend on our own model being created
            dependencies.append((app_label, model_name, None, True))            # 新增到依賴中
            # Make operation
            self.add_operation(
                app_label,
                operations.AddField(
                    model_name=model_name,
                    name=name,
                    field=field,
                ),
                dependencies=list(set(dependencies)),
            )                                                                   # 新增依賴欄位值
        # Generate other opns
        related_dependencies = [
            (app_label, model_name, name, True)
            for name, field in sorted(related_fields.items())
        ]                                                                       
        related_dependencies.append((app_label, model_name, None, True))
        if unique_together:                                                     # 如果設定了unique_together
            self.add_operation(
                app_label,
                operations.AlterUniqueTogether(
                    name=model_name,
                    unique_together=unique_together,
                ),
                dependencies=related_dependencies
            )                                                                   # 新增unique_together欄位配置
        if index_together:                                                      # 如果有索引配置資訊則配置
            self.add_operation(
                app_label,
                operations.AlterIndexTogether(
                    name=model_name,
                    index_together=index_together,
                ),
                dependencies=related_dependencies
            )
        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),
                    (app_label, model_name, None, True),
                ]
            )

當生成的操作都是通過add_operation函式,新增到generated_operations列表中;

def add_operation(self, app_label, operation, dependencies=None, beginning=False):
    # Dependencies are (app_label, model_name, field_name, create/delete as True/False)
    operation._auto_deps = dependencies or []
    if beginning:
        self.generated_operations.setdefault(app_label, []).insert(0, operation)
    else:
        self.generated_operations.setdefault(app_label, []).append(operation)

當處理完成依賴關係後,通過makemigraiotns.py中的Command的write_migration_files方法將對應的更改內容寫入到檔案中;

def write_migration_files(self, changes):
    """
    Takes a changes dict and writes them out as migration files.
    """
    directory_created = {}
    for app_label, app_migrations in changes.items():                           # 獲取每一個改變
        if self.verbosity >= 1:                                                 # 如果輸出格式大於等於1則列印相關資訊
            self.stdout.write(self.style.MIGRATE_HEADING("Migrations for '%s':" % app_label) + "\n")
        for migration in app_migrations:                                        # 獲取所有的migration
            # Describe the migration
            writer = MigrationWriter(migration)                                 # 初始化一個writer
            if self.verbosity >= 1:                                             # 如果大於等於1則列印需要寫入檔案中的資訊
                # Display a relative path if it's below the current working
                # directory, or an absolute path otherwise.
                migration_string = os.path.relpath(writer.path)
                if migration_string.startswith('..'):
                    migration_string = writer.path
                self.stdout.write("  %s:\n" % (self.style.MIGRATE_LABEL(migration_string),))
                for operation in migration.operations:
                    self.stdout.write("    - %s\n" % operation.describe())
            if not self.dry_run:
                # Write the migrations file to the disk.
                migrations_directory = os.path.dirname(writer.path)             # 判斷資料夾名稱
                if not directory_created.get(app_label):                        # 檔案是否能夠獲取到
                    if not os.path.isdir(migrations_directory):                 # 如果不是資料夾則建立一個
                        os.mkdir(migrations_directory)
                    init_path = os.path.join(migrations_directory, "__init__.py")   # 新增__init__.py檔案
                    if not os.path.isfile(init_path):                               # 如果不是檔案
                        open(init_path, "w").close()                                # 開啟一下
                    # We just do this once per app
                    directory_created[app_label] = True                             # 是否建立了設定為True
                migration_string = writer.as_string()                               # 轉換成string
                with open(writer.path, "wb") as fh:                                 # 開啟對應的檔案
                    fh.write(migration_string)                                      # 寫入migraion_string
            elif self.verbosity == 3:
                # Alternatively, makemigrations --dry-run --verbosity 3
                # will output the migrations to stdout rather than saving
                # the file to the disk.
                self.stdout.write(self.style.MIGRATE_HEADING(
                    "Full migrations file '%s':" % writer.filename) + "\n"
                )
                self.stdout.write("%s\n" % writer.as_string())

其中主要是將需要寫入的migrations例項化了一個MigrationWriter,

並最後呼叫as_string方法生成渲染的字元資料;

def as_string(self):
    """
    Returns a string of the file contents.
    """
    items = {
        "replaces_str": "",
        "initial_str": "",
    }

    imports = set()

    # Deconstruct operations
    operations = []
    for operation in self.migration.operations:
        operation_string, operation_imports = OperationWriter(operation).serialize()            # 序列化
        imports.update(operation_imports)                                                       # 更新對應的內容
        operations.append(operation_string)                                                     # 新增到operations中
    items["operations"] = "\n".join(operations) + "\n" if operations else ""                    # 新增資訊

    # Format dependencies and write out swappable dependencies right
    dependencies = []
    for dependency in self.migration.dependencies:                                              # 獲取依賴資訊
        if dependency[0] == "__setting__":                                                      # 如果第一個為__setting__
            dependencies.append("        migrations.swappable_dependency(settings.%s)," % dependency[1])    # 新增依賴資訊
            imports.add("from django.conf import settings")                                     # 新增匯入資訊
        else:
            # No need to output bytestrings for dependencies
            dependency = tuple(force_text(s) for s in dependency)
            dependencies.append("        %s," % self.serialize(dependency)[0])
    items["dependencies"] = "\n".join(dependencies) + "\n" if dependencies else ""              # 新增依賴資訊

    # Format imports nicely, swapping imports of functions from migration files
    # for comments
    migration_imports = set()
    for line in list(imports):                                                                  # 獲取所有的匯入資訊
        if re.match("^import (.*)\.\d+[^\s]*$", line):                                             # 正則匹配
            migration_imports.add(line.split("import")[1].strip())                              # 新增到migration_imports中
            imports.remove(line)                                                                # 從imports中移除
            self.needs_manual_porting = True

    # django.db.migrations is always used, but models import may not be.
    # If models import exists, merge it with migrations import.
    if "from django.db import models" in imports:
        imports.discard("from django.db import models")                                         # 移除該內容
        imports.add("from django.db import migrations, models")                                 # 新增該內容
    else:
        imports.add("from django.db import migrations")

    # Sort imports by the package / module to be imported (the part after
    # "from" in "from ... import ..." or after "import" in "import ...").
    sorted_imports = sorted(imports, key=lambda i: i.split()[1])                                # 匯入排序
    items["imports"] = "\n".join(sorted_imports) + "\n" if imports else ""                      # 安全匯入
    if migration_imports:
        items["imports"] += (
            "\n\n# Functions from the following migrations need manual "
            "copying.\n# Move them and any dependencies into this file, "
            "then update the\n# RunPython operations to refer to the local "
            "versions:\n# %s"
        ) % "\n# ".join(sorted(migration_imports))                                              # 新增匯入資訊
    # If there's a replaces, make a string for it
    if self.migration.replaces:
        items['replaces_str'] = "\n    replaces = %s\n" % self.serialize(self.migration.replaces)[0]
    # Hinting that goes into comment
    items.update(
        version=get_version(),
        timestamp=now().strftime("%Y-%m-%d %H:%M"),
    )                                                                                           # 更新版本資訊

    if self.migration.initial:
        items['initial_str'] = "\n    initial = True\n"                                         # 設定initial

    return (MIGRATION_TEMPLATE % items).encode("utf8")                                          # 通過MIGRATION_TEMPLATE序列化

其中MIGRATION_TEMPLATE內容如下;

MIGRATION_TEMPLATE = """\
# -*- coding: utf-8 -*-
# Generated by Django %(version)s on %(timestamp)s
from __future__ import unicode_literals

%(imports)s

class Migration(migrations.Migration):
%(replaces_str)s%(initial_str)s
    dependencies = [
%(dependencies)s\
    ]

    operations = [
%(operations)s\
    ]
"""

至此一個新建Model的大致流程分析完成,其中沒有詳細分析依賴的解決等細節內容,大家有興趣可自行檢視。

總結

本文只是簡單的介紹了一下新建model的migrations檔案的生成過程,並未討論到更復雜的應用場景,如複雜的應用場景可根據上文的執行流程進行詳細的分析,由於本人水平有限,並沒有使用到資料庫應用很複雜的場景,所以對資料庫中一些的處理也缺乏認識,如有疏漏請批評指正。