另一个模式中的Django外键

时间:2018-09-18 12:48:20

标签: python mysql django django-models

我有一个带有2个shemas(A和B)的MySQL数据库。
我的django应用可以读写A。
它只能从B读取。

我的应用管理了A中的所有表。
B已经在表(b)中包含一些数据。

我想在A和B之间添加一个到一个字段。
像这样:

class SchemaBTableB(models.Model):
    class Meta:
        managed = False
        db_schema = 'B'
        db_table = 'b'

    [...]

class SchemaATableA(models.Model):
    class Meta:
        db_schema = 'A'
        db_table = 'a'

    id = models.OneToOneField(
        SchemaBTableB,
        on_delete=models.DO_NOTHING,
        primary_key=True
    )

    [...]

很遗憾,db_schema不存在。
有人知道解决方案吗?

2 个答案:

答案 0 :(得分:1)

我曾尝试使用两个数据库来模拟您的情况,并找出以下解决方案:

1。场景:

  1. 数据库schema1,由django(读写)管理
  2. 数据库schema2,它不是由Django管理的

2。步骤:

  1. 为模型创建迁移python manage.py makemigrations
  2. 为您的迁移生成SQL:python manage.py sqlmigrate app 0001。 (假设从步骤1生成的迁移文件名为0001_initial.py

此迁移的sql应该如下所示:

CREATE TABLE `user_info` (`id_id` integer NOT NULL PRIMARY KEY, `name` varchar(20) NOT NULL);
ALTER TABLE `user_info` ADD CONSTRAINT `user_info_id_id_e8dc4652_fk_schema2.user_extra_info_id` FOREIGN KEY (`id_id`) REFERENCES `user_extra_info` (`id`);
COMMIT;

如果直接运行上述sql,您将得到如下错误:

django.db.utils.OperationalError: (1824, "Failed to open the referenced table 'user_extra_info'")

这是因为django假定所有迁移步骤都在同一数据库中执行。因此无法在user_extra_info数据库中找到schema1

3。步骤如下:

  1. 为表schema2明确指定数据库user_extra_info

    ALTER TABLE `user_info` ADD CONSTRAINT `user_info_id_id_e8dc4652_fk_schema2.user_extra_info_id` FOREIGN KEY (`id_id`) REFERENCES schema2.user_extra_info (`id`);
    
  2. schema1数据库中手动运行修订的sql。

  3. 告诉Django我自己运行了迁移:python manage.py migrate --fake

  4. 完成!


源代码供您参考:

models.py

from django.db import models


class UserExtraInfo(models.Model):
    # table in schema2, not managed by django
    name = models.CharField('name', max_length=20)

    class Meta:
        managed = False
        db_table = 'user_extra_info'


class UserInfo(models.Model):
    # table in schema1, managed by django
    id = models.OneToOneField(
        UserExtraInfo,
        on_delete=models.CASCADE,
        primary_key=True
    )
    name = models.CharField('user name', max_length=20)

    class Meta:
        db_table = 'user_info'

settings.py

# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'schema1',
        'USER': 'USER',
        'PASSWORD': 'PASSWORD',
        'HOST': 'localhost',
        'PORT': 3306,
    },
    'extra': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'schema2',
        'USER': 'USER',
        'PASSWORD': 'PASSWORD',
        'HOST': 'localhost',
        'PORT': 3306,
    }
}

DATABASE_ROUTERS = ['two_schemas.router.DBRouter']

router.py

class DBRouter(object):
    """
    A router to control all database operations on models in the
    auth application.
    """
    def db_for_read(self, model, **hints):
        """
        Attempts to read auth models go to auth_db.
        """
        if model._meta.db_table == 'user_extra_info':
            # specify the db for `user_extra_info` table
            return 'extra'
        if model._meta.app_label == 'app':
            return 'default'
        return None

    def db_for_write(self, model, **hints):
        """
        Attempts to write auth models go to auth_db.
        """
        if model._meta.db_table == 'user_extra_info':
            # specify the db for `user_extra_info` table
            return 'extra'
        if model._meta.app_label == 'app':
            return 'default'
        return None

    def allow_relation(self, obj1, obj2, **hints):
        """
        Relations between objects are allowed if both objects are
        in the primary/replica pool.
        """
        db_list = ('default', 'extra')
        if obj1._state.db in db_list and obj2._state.db in db_list:
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        """
        Make sure the auth app only appears in the 'auth_db'
        database.
        """
        if app_label == 'app':
            return db == 'default'
        return None

答案 1 :(得分:0)

OneToOneForeignKey之间是有区别的:由于您是专门提到FK的-我假设您实际上想制作一个外键,方法是这样的:{{1 }}

ForeignKey(SomeModel, unique=True)

class ObjectTableA(models.Model): id = models.ForeignKey( ObjectTableB, on_delete=models.DO_NOTHING, unique=True, ) [...] class ObjectTableB(models.Model): class Meta: managed = False db_table = 'b' [...] OneToOne之间的区别如下:

关于ForeignKey,从概念上讲,这与具有unique = True的ForeignKey相似,但是关系的“反向”侧将直接返回单个对象。

与OneToOneField“反向”关系相反,ForeignKey“反向”关系返回QuerySet。

由于您的应用管理着OneToOne,因此请摆脱TableAtable_schema

在您的db_table模型中,您可能希望摆脱TableB

编辑:由于您确定要使用table_schema,因此应该可以执行以下操作:

OneToOne

由于您的id = models.OneToOneField( ObjectB, on_delete=models.DO_NOTHING, ) 由Django管理,因此只需将其ObjectA链接到您的OneToOne