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

2024-04-11

对于 Django 中的项目,我必须使用两个数据库:default and remote。我创造了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数据库,并在django_migrations在 - 的里面remote数据库迁移被标记为已应用。

EDIT:
如何强制 Django 仅使用一张表django_migrations,但仍将迁移应用到不同的数据库中?

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


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

使用多个数据库会导致创建一个表django_migrations当使用迁移时。没有选项可以仅在一张表中记录迁移django_migrations,正如来自的评论卡米尔·尼斯基 https://stackoverflow.com/users/9820085/kamil-niski解释说。读完文件就清楚了django/db/migrations/recorder.py https://github.com/django/django/blob/master/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 将使用默认数据库。



Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK
  

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



1           contenttypes  0001_initial  2019-11-13 16:51:04.767382
2           auth          0001_initial  2019-11-13 16:51:04.792245
3           admin         0001_initial  2019-11-13 16:51:04.827454
4           admin         0002_logentr  2019-11-13 16:51:04.846627
5           admin         0003_logentr  2019-11-13 16:51:04.864458
6           contenttypes  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          sessions      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 into INSTALLED_APPS (foo/settings.py)并创建迁移:

python manage.py makemigrations bar

在运行我们创建的迁移之前routers.py在 - 的里面bar app:


class BarRouter(object):
    def db_for_read(self, model, **hints):
        if model._meta.app_label == 'bar':
            return 'remote'
        return None

    def db_for_write(self, model, **hints):
        if model._meta.app_label == 'bar':
            return 'remote'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        if app_label == 'bar':
            return db == 'remote'
        if db == 'remote':
            return False
        return None
  

并将其注册到foo/settings.py:

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

现在天真的方法是运行迁移bar进入remote数据库:

python manage.py migrate bar --database=remote

Operations to perform:
  Apply all migrations: bar
Running migrations:
  Applying bar.0001_initial... OK
  

迁移已应用于remote数据库:


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

当我们运行时:

python manage.py runserver

将发出以下警告:

您有 1 项未应用的迁移。您的项目可能无法正常运行 直到您应用应用程序的迁移:栏。
运行“python manage.py migrate”来应用它们。

不过一切似乎都运行良好。然而,这个警告并不能令人满意。

正确的方法是按照本中的建议为每个数据库运行所有迁移answer https://stackoverflow.com/a/35614973/3848833.

它看起来像这样:

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 会将迁移标记为在两个数据库中应用。还有表格auth, admin, sessions等将仅在default数据库,如指定routers.py。桌子django_migrations in the remote数据库也会有这些迁移的记录。

这是一篇很长的阅读,但我希望它能对这个问题有所启发,在我看来,官方没有彻底解释这个问题文档 https://docs.djangoproject.com/en/2.2/topics/db/multi-db/#synchronizing-your-databases.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Django 中使用多个数据库,仅使用一张表“django_migrations” 的相关文章

随机推荐