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


11

对于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 migration”以应用它们。

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

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

如何将迁移应用到不同的数据库中,从而不会引发警告?


1
对于不是“ my_app”的其他应用,allow_migrate返回“无”。也许您想在那里再检查一次?据我从您的路由器了解,“ my_app”使用“远程”数据库,而所有其他应用将使用“默认”数据库吗?
马丁·塔列斯基

@cezar您要求几乎不可能。为了拥有共享django_migrations表,将需要在迁移了default和的行之间进行区分remote。这在Django内部非常深入。我什至冒风险指出,这将需要大量重写迁移代码。
卡米尔·尼斯基

@KamilNiski感谢您分享您的想法。我将重提这个问题。
cezar

Answers:


2

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

使用多个数据库会导致在使用django_migrations迁移时创建表。django_migrations正如Kamil Niski的评论所解释的,没有选项只能将迁移记录在一个表中。读取文件后,这一点很明显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将使用默认数据库。

要执行的操作:
  应用所有迁移:管理员,身份验证,内容类型,会话
 运行迁移:
  正在应用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 ...确定
  正在套用工作阶段。0001_initial...确定

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

1内容类型0001_initial 2019-11-13 16:51:04.767382
2验证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 contenttypes 0002_remove_ 2019-11-13 16:51:04.892220
7认证0002_alter_p 2019-11-13 16:51:04.906449
8验证0003_alter_u 2019-11-13 16:51:04.923902
9授权0004_alter_u 2019-11-13 16:51:04.941707
10验证0005_alter_u 2019-11-13 16:51:04.958371
11验证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)

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

python manage.py makemigrations bar

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

BarRouter(object)类:
    def db_for_read(self,model,** hints):
        如果model._meta.app_label =='bar':
            返回“远程”
        不返回

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

    def allow_relation(self,obj1,obj2,**提示):
        不返回

    def allow_migrate(self,db,app_label,model_name = None,**提示):
        如果app_label =='bar':
            return db =='远程'
        如果db =='remote':
            返回False
        不返回

并将其注册在foo/settings.py

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

现在,幼稚的方法是将迁移运行barremote数据库中:

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

迁移已应用于remote数据库:

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

当我们运行时:

python manage.py runserver

将发出以下警告:

您有1个未应用的迁移。您必须为应用程序应用迁移:bar,项目才能正常运行。
运行“ python manage.py migration”以应用它们。

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

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

它看起来像这样:

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。该表django_migrationsremote数据库将对这些迁移的记录了。

这是一本很长的书,但是我希望它能为我提供一些启发,使我认为这是官方文档中未详细解释的问题。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.