Django和PostgreSQL模式

50

我已经尝试了一整周解决这个问题,非常感谢您的帮助。

我在Postgres数据库中有不同的模式,并且我想能够从同一或不同的Django应用程序中进行映射到它们。

其中一些模式是 :

samples

excavation

geophysics

...

我已经尝试了推荐的方法,但我无法从这些模式中显示任何数据,我只能连接到具有管理表的public模式。以下是来自settings.py文件的数据库连接信息。

DATABASES = {

'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'OPTIONS': {
            'options': '-c search_path=django,public'
        },
        'NAME': 'gygaia',
        'USER': 'appuser',
        'PASSWORD': 'secret',
},

'samples': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'OPTIONS': {
            'options': '-c search_path=samples,public'
        },
        'NAME': 'gygaia',
        'USER': 'appuser',
        'PASSWORD': 'secret',
},
}

来源:https://www.amvtek.com/blog/posts/2014/Jun/13/accessing-multiple-postgres-schemas-from-django/

在 model.py 中我添加了:

    from django.db import models

    # Create your models here.
    class Storage(models.Model):
        #id = models.IntegerField(default=0)
        storage_id = models.AutoField(primary_key=True)
        store_name = models.CharField(max_length=200, default='')
        address_1 = models.CharField(max_length=200, default='')
        address_2 = models.CharField(max_length=200, default='')
        region = models.CharField(max_length=200, default='')
        city = models.CharField(max_length=200, default='')
        zip = models.CharField(max_length=200, default='')
        country = models.CharField(max_length=200, default="Turkey")
        user = models.CharField(max_length=200, default="Gygaia")
        datestamp = models.DateTimeField(auto_now=True)

    class Meta():
        managed=False
        db_table = 'samples\".\"store'

我不想将模式限制在用户中,而且数据库是几年前创建的,所以我不能将其全部归于一个模式下。我知道stackoverflow和互联网的其他角落中发布了各种解决方案,我已经尝试过这些方法,但我无法使其正常工作。有任何解决方法吗?

4个回答

66

由于Django默认不支持Postgres数据库模式,因此为了使其工作,需要使用数据库路由器

我创建了一个测试数据库来尝试这个功能,以下是如何复制它:

使用psql创建一个测试数据库:

CREATE USER tester WITH PASSWORD 'lol so easy';
CREATE DATABASE multi_schema_db WITH OWNER tester;
CREATE SCHEMA samples AUTHORIZATION tester;
CREATE TABLE samples.my_samples (
  id          INTEGER   NOT NULL PRIMARY KEY,
  description CHAR(255) NOT NULL
);
将模式添加到设置中作为不同的数据库连接,记得添加 HOST 以避免“对等身份验证失败”错误。
DATABASES = {

'default': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'OPTIONS': {
        'options': '-c search_path=django,public'
    },
    'NAME': 'multi_schema_db',
    'USER': 'tester',
    'PASSWORD': 'lol so easy',
    'HOST': 'localhost'

},

'samples': {
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'OPTIONS': {
        'options': '-c search_path=samples,public'
    },
    'NAME': 'multi_schema_db',
    'USER': 'tester',
    'PASSWORD': 'lol so easy',
    'HOST': 'localhost'
},

}

接下来创建MySample模型:

from django.db import models

class MySample(models.Model):
    description = models.CharField(max_length=255, null=False)

    class Meta:
        managed = False
        db_table = 'my_samples'
创建一个数据库路由器将所有与样本相关的查询定向到样本数据库:
from database_test.models import MySample

ROUTED_MODELS = [MySample]


class MyDBRouter(object):

    def db_for_read(self, model, **hints):
        if model in ROUTED_MODELS:
            return 'samples'
        return None

    def db_for_write(self, model, **hints):
        if model in ROUTED_MODELS:
            return 'samples'
        return None

基本上,路由器将把在ROUTED_MODELS中指定的所有模型路由到数据库连接samples,并对所有其他模型返回None。这将把它们路由到默认的数据库连接default。

最后将路由器添加到您的settings.py中

DATABASE_ROUTERS = ('database_test.db_router.MyDBRouter',)

现在,当查询MySample模型时,它将从samples架构中获取数据。


38
今天我不得不实现一个数据库路由器,这样我就可以回到这里检查自己的答案。感谢过去圣诞节的我。 - tarikki
3
我想补充一点内容,你需要使用python manage.py migrate --database <db-config-name> <app-name>命令才能将迁移应用到正确的数据库中。我还必须为路由器类实现allow_migrateallow_relation方法,不确定是否适用于所有情况。欲了解更多信息,请参见https://docs.djangoproject.com/en/3.1/topics/db/multi-db/#topics-db-multi-db-routing。 - lukecampbell
没错,@lukecampbell,不幸的是这个答案只是文档中简单的部分,虽然它提供了一个有趣的想法来为不同的命名空间指定不同的连接。但是如何让allow_migrate调用基于模型路由变得困难。Django似乎没有为我的模型调用allow_migrate。正在进行中。 - NeilG

16

我也查阅了那个资料,但我不能像你一样解决它,但是通过进行测试,我实现了以下结果。

例如,如果我们有架构foo和bar,在Meta中写入:

class MySample1 (models.Model):
     description = models.CharField (max_length = 255, null = False)
     class Goal:
         managed = True
         db_table = 'fo\".\"my_samples1'

class MySample2 (models.Model):
     description = models.CharField (max_length = 255, null = False)
     class Goal:
         managed = True
         db_table = 'bar\".\"my_samples2'

如果我们将变量管理设置为True,那么我们就可以将每个模型重定向到我们想要的方案。限制在于我们必须自己命名表格。


确认这个能够完美运行。 - Ethan Chen

0

首先使用schemas在postgres中创建表,然后使用命令python manage.py inspectdb > models.py访问这些表,并迁移回去。


0

search_path在这里是关键。这是一个Postgres设置,决定了在尝试查找匹配的表时如何遍历数据库(source)。

除非您在多个模式中具有相同名称的表格(这应该尽量避免简化此过程),否则您不需要定义多个路由器或明确指定db_table或类似的内容。

因此,为了使您的PostgreSQL连接在访问数据库时始终遍历所有所需的模式,您只需要永久修改其search_pathsource):

ALTER DATABASE <database_name> SET search_path TO schema1,schema2;
-- After running this command, you will have to close your current connection and
-- reconnect for the changes to be reflected in your session

然后,您将能够像往常一样仅定义default Django DATABASES设置,并且它将浏览所有给定的模式以找到适当的表。
但是请注意,除了schema1之外,您可能仍然需要使用db_table来创建新的表。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接