跳转到主要内容

使用每个数据库一个租户的方式为Django提供多租户支持。

项目描述

提供基于每个数据库只有一个租户概念的简单多租户解决方案。

此应用程序仍处于实验阶段,但作者已在生产中使用。欢迎贡献和讨论。

查看更改日志

背景

多租户是支持来自同一应用程序服务器的多个不同数据集的能力。每个数据集通常映射到一个客户(租户),并与其他租户数据部分或全部分区。

可能的方案包括

  • 隔离方法:每个租户一个单独的数据库。

  • 半隔离方法:共享数据库,每个租户具有单独的命名空间(PostgreSQL模式)或表名/前缀。

  • 共享方法:所有租户使用单个数据库。每个表都有一个列标识该行数据的租户。

此应用程序支持两个后端,MySQL和PostgreSQL

  • 使用MySQL,此应用程序实现了隔离方法的变体,每个租户都有自己的数据库,但它们的连接详情是共享的(如密码、数据库用户)。

  • 对于PostgreSQL,此应用程序实现了一种半隔离方法,每个租户都有自己的模式,并且连接详情通过公共模式共享。

django-db-multitenant使得将专为单个租户设计的Django应用程序与多个租户一起使用成为可能(甚至很容易)。

操作

主要技术如下

  1. 当第一个请求到达时,从请求对象中确定所需的租户,并将其保存到线程局部存储中。

  2. 在请求的后续过程中,当获取数据库游标时,对所需租户发出MySQL的USE <租户数据库名>或PostgreSQL的SET search_path TO <租户名> SQL命令。

第一步通过实现一个映射器类来完成。您的映射器接受一个请求对象,并返回一个数据库名称或租户名称,使用您喜欢的任何逻辑(翻译主机名、检查HTTP头等)。映射器结果保存在线程局部存储中以供以后使用。

第二步确定所需数据库或模式是否已经被选择,如果是,则跳过。这是通过使用一个薄的数据库后端包装器实现的,该包装器是针对MySQL和PostgreSQL的,必须设置在settings.DATABASES中作为后端。

使用方法

1. 安装

安装django-db-multitenant(或将它添加到您的setup.py中)。

$ pip install django-db-multitenant

2. 实现映射器

您必须实现一个db_multitenant.mapper的子类,它从请求中确定数据库名称和缓存前缀。

为了帮助您编写映射器,仓库中包含了映射器示例,这些示例从URL中提取主机名以确定租户名称(例如,在https://foo.example.com/bar/中,foo将是租户名称)

  • MySQL映射器,它直接使用主机名的一部分作为数据库名称。

  • PostgreSQL映射器,它使用主机名的一部分作为搜索路径(模式)。PostgreSQL允许复杂的设置,例如共享公共表(例如,公共账户),请参阅映射器中的注释以获取更多详细信息。

  • Redis映射器,它使用主机名查找租户,如果未识别则抛出404。

请随意复制一个示例映射器到您的项目中,然后调整它以满足您的需求。

3. 更新settings.py

通过指定您的实现的全限定路径来设置多租户映射器(在此示例中,mapper是文件mapper.py的名称)

MULTITENANT_MAPPER_CLASS = 'myapp.mapper.TenantMapper'

将多租户中间件作为列表中的第一个中间件(在Django 1.10之前,您必须使用MIDDLEWARE_CLASSES设置)

MIDDLEWARE = [
    'db_multitenant.middleware.MultiTenantMiddleware',
    ....
]

更改您的数据库后端为多租户包装器

DATABASES = {
    'default': {
        'ENGINE': 'db_multitenant.db.backends.mysql',
        'NAME': 'devnull',
    }
}

注意:对于MySQL,NAME是无用的,但由于当前的限制,必须存在命名的数据库。它可以空且只读。

或者对于PostgreSQL

DATABASES = {
    'default': {
        'ENGINE': 'db_multitenant.db.backends.postgresql',
        'NAME': 'mydb',
    }
}

可选地,将多租户辅助函数KEY_FUNCTION添加到您的缓存定义中,这将导致缓存键以mapper.get_cache_prefix的值作为前缀

CACHES = {
  'default' : {
        'LOCATION': '127.0.0.1:11211',
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'KEY_FUNCTION': 'db_multitenant.cache.helper.multitenant_key_func'
    }
}

4. 测试

如果您的应用程序的租户名称是从URL中提取的(如提供的映射器示例所示),您可以在/etc/hosts中添加一个主机,如foo.example.com,将其重定向到您的本地主机服务器。

您应该将 foo.example.com 添加到 Django 设置中的 ALLOWED_HOSTS 列表,并尝试通过浏览器使用 http://foo.example.com:8000 访问您的应用程序。

映射器的示例提供了有关创建租户区域的方法信息。

管理命令

为了使用正确的租户执行管理命令(如 migrate),请在 settings.py 文件的末尾注入以下小技巧:

from db_multitenant.utils import update_from_env
update_from_env(database_settings=DATABASES['default'],
    cache_settings=CACHES['default'])

如果您没有在设置中设置 CACHES,并且您不打算使用缓存系统,则无需将 cache_settings 参数传递给函数。

然后您可以在命令行上导出 TENANT_DATABASE_NAME(用于 MySQL)或 TENANT_NAME(用于 PostgreSQL)以及 TENANT_CACHE_PREFIX,例如

$ TENANT_DATABASE_NAME=example.com ./manage.py migrate

别忘了首先创建数据库(MySQL)或所需的模式(PostgreSQL)。

就是这样。因为 django-db-multitenant 没有定义任何模型,所以无需将其添加到 INSTALLED_APPS 中。

优点和局限性

对于像多租户这样的数据建模问题,并没有一种一刀切解决方案(请参阅“替代方案”)。

优点

  • 兼容性:您的 Django 应用程序不需要对多租户有意识。数据库级别的工具(如 mysqldumppgdump)只需正常工作。

  • 隔离:一个租户一个数据库,这意味着租户数据不会相互混合(除非您与 PostgreSQL 共享表)。

  • 简洁:您的应用程序模式不需要包含“租户”外键关系。

  • 应该与 Django 1.6 连接持久性和连接池很好地工作。

局限性

  • 非正统。Django 不期望这种动态数据库连接的篡改,可能存在意外的错误。

  • 隔离有限。由于所有租户都使用相同的 DB 凭证,映射器(或应用程序中的任何其他地方)中的错误可能导致数据损坏。

  • 仍然需要在 settings.DATABASE 中指定有效的数据库,以便在首次与 MySQL 建立连接时使用(这最终应该得到修复)。

  • 开销:请求可能增加一个额外的查询(MySQL 的 USE <db_name> 语句或 PostgreSQL 的 SET search_path TO <tenant_name>)。

替代方案和进一步阅读

  • django-tenant-schemas 使用 PostgreSQL 模式实现了一种半隔离的方法(并受到本项目以及上述“概述”部分的启发)。

致谢和许可

版权所有 2013 mike wakerly (opensource@hoho.com)

根据 Apache License 2.0 许可(“许可证”);除非您遵守许可证,否则不得使用此文件。您可以在以下位置获得许可证副本:

https://apache.ac.cn/licenses/LICENSE-2.0

除非适用法律要求或书面同意,否则根据许可证分发的软件按“原样”分发,不提供任何明示或暗示的保证。有关许可证的具体语言管辖权限和限制,请参阅许可证。

项目详情


下载文件

下载适用于您的平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源代码分发

django-db-multitenant-0.3.2.tar.gz (19.6 kB 查看哈希值)

上传于 源代码

构建分发

django_db_multitenant-0.3.2-py2.py3-none-any.whl (21.1 kB 查看哈希值)

上传于 Python 2 Python 3

由支持