在Django中仅使用一个表“ django_migrations”使用多个数据库

cez*_*zar 11 python django django-models

对于Django中的项目,我必须使用两个数据库:defaultremote。我创建了routers.py,一切正常。

要求在远程数据库上创建一个表,然后我创建了迁移,然后运行它并django_migrations创建了表。我想django_migrations在默认数据库中只有一个表。

相关部分在routers.py这里:

class MyRouter(object):
     # ...
     def allow_migrate(self, db, app_label, model_name=None, **hints):
         if app_label == 'my_app':
             return db == 'remote'
         return None
Run Code Online (Sandbox Code Playgroud)

我这样运行迁移:

python manage.py migrate my_app --database=remote
Run Code Online (Sandbox Code Playgroud)

现在,当我这样做时:

python manage.py runserver
Run Code Online (Sandbox Code Playgroud)

我收到以下警告:

您有1个未应用的迁移。在为应用程序my_app应用迁移之前,您的项目可能无法正常工作。
运行“ python manage.py migration”以应用它们。

my_appremote数据库中创建的表,并在数据库django_migrations内部remote将迁移标记为已应用。

编辑:
如何强制Django只使用一个表django_migrations,但仍将迁移应用到不同的数据库?

如何在不同的数据库中应用迁移,从而不发出警告?

cez*_*zar 6

由于对我的问题的评论,我做了一些研究并得出了以下发现。

使用多个数据库会导致在使用django_migrations迁移时创建一个表。django_migrations正如Kamil Niski的评论所解释的那样,没有选项只在一张表中记录迁移。读完文件就明白了django/db/migrations/recorder.py

我将用一个项目foo和一个bar项目内的应用程序来说明一个例子。该应用程序bar只有一个模型Baz

我们创建项目:

django-admin startproject foo
Run Code Online (Sandbox Code Playgroud)

现在我们在主项目目录中有这些内容:

- foo
- manage.py
Run Code Online (Sandbox Code Playgroud)

我习惯将项目目录内的所有应用程序分组:

mkdir foo/bar
python manage.py bar foo/bar
Run Code Online (Sandbox Code Playgroud)

在文件中,foo/settings.py我们调整设置以使用两个不同的数据库,出于本示例的目的,我们使用sqlite3

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
    },
    'remote': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们运行迁移:

python manage.py migrate --database=default
Run Code Online (Sandbox Code Playgroud)

这将运行所有迁移,该部分--database=default是可选的,因为如果未指定,Django 将使用默认数据库。

要执行的操作:
  应用所有迁移: admin、auth、contenttypes、sessions
运行迁移:
  正在应用 contenttypes.0001_initial... 好的
  正在应用 auth.0001_initial... 好的
  正在应用 admin.0001_initial... 好的
  正在应用 admin.0002_logentry_remove_auto_add... 好的
  正在应用 admin.0003_logentry_add_action_flag_choices... 好的
  正在应用 contenttypes.0002_remove_content_type_name... 好的
  正在应用 auth.0002_alter_permission_name_max_length... 好的
  正在应用 auth.0003_alter_user_email_max_length... 好的
  正在应用 auth.0004_alter_user_username_opts... 好的
  正在应用 auth.0005_alter_user_last_login_null... 好的
  正在应用 auth.0006_require_contenttypes_0002... 好的
  正在应用 auth.0007_alter_validators_add_error_messages... 好的
  正在应用 auth.0008_alter_user_username_max_length... 好的
  正在应用 auth.0009_alter_user_last_name_max_length... 好的
  正在应用 auth.0010_alter_group_name_max_length... 好的
  正在应用 auth.0011_update_proxy_permissions... 好的
  正在应用 session.0001_initial... 好的

Django 已将所有迁移应用到默认数据库:

1 内容类型 0001_initial 2019-11-13 16:51:04.767382
2 auth 0001_initial 2019-11-13 16:51:04.792245
3 管理员 0001_initial 2019-11-13 16:51:04.827454
4 管理员 0002_logentr 2019-11-13 16:51:04.846627
5 管理员 0003_logentr 2019-11-13 16:51:04.864458
6 内容类型 0002_remove_ 2019-11-13 16:51:04.892220
7 auth 0002_alter_p 2019-11-13 16:51:04.906449
8 auth 0003_alter_u 2019-11-13 16:51:04.923902
9 auth 0004_alter_u 2019-11-13 16:51:04.941707
10 auth 0005_alter_u 2019-11-13 16:51:04.958371
11 auth 0006_require 2019-11-13 16:51:04.965527
12 auth 0007_alter_v 2019-11-13 16:51:04.981532
13 auth 0008_alter_u 2019-11-13 16:51:05.004149
14 auth 0009_alter_u 2019-11-13 16:51:05.019705
15 auth 0010_alter_g 2019-11-13 16:51:05.037023
16 auth 0011_update_ 2019-11-13 16:51:05.054449
17 节 0001_initial 2019-11-13 16:51:05.063868

现在我们创建模型Baz

models.py

from django.db import models

class Baz(models.Model):
    name = models.CharField(max_length=255, unique=True)
Run Code Online (Sandbox Code Playgroud)

将应用程序注册barINSTALLED_APPS( foo/settings.py) 并创建迁移:

python manage.py makemigrations bar
Run Code Online (Sandbox Code Playgroud)

在我们运行迁移之前,我们routers.pybar应用程序中创建:

类 BarRouter(对象):
    def db_for_read(自我,模型,**提示):
        如果 model._meta.app_label == 'bar':
            返回“远程”
        返回无

    def db_for_write(self, model, **hints):
        如果 model._meta.app_label == 'bar':
            返回“远程”
        返回无

    def allow_relation(self, obj1, obj2, **hints):
        返回无

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        如果 app_label == 'bar':
            返回数据库 == '远程'
        如果数据库 == '远程':
            返回错误
        返回无

并将其注册在foo/settings.py

DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']
Run Code Online (Sandbox Code Playgroud)

现在最简单的方法是将迁移运行barremote数据库中:

python manage.py migrate bar --database=remote
Run Code Online (Sandbox Code Playgroud)
要执行的操作:
  应用所有迁移: bar
运行迁移:
  正在应用 bar.0001_initial... 好的

迁移已应用于remote数据库:

1 条 0001_initial 2019-11-13 17:32:39.701784

当我们运行时:

python manage.py runserver
Run Code Online (Sandbox Code Playgroud)

将发出以下警告:

您有 1 个未应用的迁移。在您为 app(s) 应用迁移之前,您的项目可能无法正常工作:bar。
运行“python manage.py migrate”以应用它们。

不过,一切似乎都运行良好。但是,收到此警告并不令人满意。

正确的方法是按照本答案中的建议为每个数据库运行所有迁移。

它看起来像这样:

python manage.py migrate --database=default
python manage.py migrate --database=remote
Run Code Online (Sandbox Code Playgroud)

在为以下创建迁移之后bar

python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote
Run Code Online (Sandbox Code Playgroud)

路由器会注意该表bar_baz仅在remote数据库中创建,但 Django 会将迁移标记为在两个数据库中都应用。此外,用于authadminsessions等的表将仅在default数据库中创建,如 中所指定routers.py。该表django_migrationsremote数据库将对这些迁移的记录了。

这是一篇很长的文章,但我希望它能对此有所了解,在我看来,官方文档中没有彻底解释的问题。