【问题标题】:Use multiple databases in Django with only one table "django_migrations"在 Django 中使用多个数据库,只有一张表“django_migrations”
【发布时间】:2020-02-24 17:53:55
【问题描述】:

对于 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

我像这样运行迁移:

python manage.py migrate my_app --database=remote

现在当我这样做时:

python manage.py runserver

我收到以下警告:

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

my_app 的表是在remote 数据库中创建的,而在remote 数据库内的django_migrations 中,迁移被标记为已应用。

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

如何在不同的数据库中应用迁移,以免引发警告?

【问题讨论】:

  • 对于不是“my_app”的其他应用,allow_migrate 返回 None。也许你想在那里再做一次检查?根据我对您的路由器的了解,“my_app”使用“远程”数据库,而所有其他应用程序将使用“默认”数据库?
  • @cezar 你要求的几乎是不可能的。为了共享django_migrations 表,需要区分defaultremote db 的迁移行。这在 django 内部非常深入。我什至冒着风险说这需要对迁移代码进行重大重写。
  • @KamilNiski 感谢您分享您的想法。我将改写问题。
  • This issue 可能是相关的。

标签: python django django-models


【解决方案1】:

感谢 cmets 关于我的问题,我做了一些研究并得出以下发现。

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

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

我们创建项目:

django-admin startproject foo

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

- foo
- manage.py

我有个习惯,把项目目录里面的所有应用都分组:

mkdir foo/bar
python manage.py bar foo/bar

在文件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'),
    }
}

现在我们运行迁移:

python manage.py migrate --database=default

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

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

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 认证 0002_alter_p 2019-11-13 16:51:04.906449 8 auth 0003_alter_u 2019-11-13 16:51:04.923902 9 认证 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 认证 0007_alter_v 2019-11-13 16:51:04.981532 13 认证 0008_alter_u 2019-11-13 16:51:05.004149 14 认证 0009_alter_u 2019-11-13 16:51:05.019705 15 身份验证 0010_alter_g 2019-11-13 16:51:05.037023 16 认证 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)

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

python manage.py makemigrations bar

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

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

    def db_for_write(自我,模型,**提示):
        如果 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':
            返回 db == '远程'
        如果 db == '远程':
            返回假
        返回无

并在foo/settings.py注册:

DATABASE_ROUTERS = ['foo.bar.routers.BarRouter']

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

python manage.py migrate bar --database=remote
要执行的操作:
  应用所有迁移:运行迁移:
  正在应用 bar.0001_initial... OK

迁移已应用于remote 数据库:

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

当我们运行时:

python manage.py runserver

将发出以下警告:

您有 1 个未应用的迁移。您的项目可能无法正常工作 直到您为应用应用迁移:bar。
运行“python manage.py migrate”以应用它们。

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

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

看起来像这样:

python manage.py migrate --database=default
python manage.py migrate --database=remote

在为 bar 创建迁移之后:

python manage.py migrate bar --database=default
python manage.py migrate bar --database=remote

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

这是一个很长的阅读,但我希望它能对这个问题有所了解,在我看来,官方documentation中没有彻底解释的问题。

【讨论】:

    猜你喜欢
    • 2011-03-14
    • 1970-01-01
    • 2020-07-26
    • 1970-01-01
    • 2021-04-07
    • 2014-05-13
    • 1970-01-01
    • 2016-08-22
    • 1970-01-01
    相关资源
    最近更新 更多