Django数据分片

Ric*_*ich 5 django

我已经使用基于模型的路由方案成功地使我的应用程序在多个数据库上运行.即模型A存在于DB A上,模型B存在于DB B上.我现在需要对数据进行分片.我正在查看文档并且无法解决如何执行此操作,因为同一模型需要存在于多个数据库服务器上.我想要一个标志,说新成员的数据库现在是数据库X,成员XY存在于数据库N等.

我怎么做?它是否使用**提示,这似乎没有充分记录给我.

Min*_*ark 6

hints参数旨在帮助您的数据库路由器决定应该在何处读取或写入其数据.它可能会随着python的未来版本而发展,但是现在只有一种可以由Django框架提供的提示,而这就是instance它的工作原理.

我写了这个非常简单的数据库路由器,看看Django做了什么:

# routers.py
import logging
logger = logging.getLogger("my_project")

class DebugRouter(object):
    """A debugging router"""

    def db_for_read(self, model, **hints):
        logger.debug("db_for_read %s" % repr((model, hints)))
        return None

    def db_for_write(self, model, **hints):
        logger.debug("db_for_write %s" % repr((model, hints)))
        return None

    def allow_relation(self, obj1, obj2, **hints):
        logger.debug("allow_relation %s" % repr((obj1, obj2, hints)))
        return None

    def allow_syncdb(self, db, model):
        logger.debug("allow_syncdb %s" % repr((db, model)))
        return None
Run Code Online (Sandbox Code Playgroud)

你在下面声明settings.py:

DATABASE_ROUTERS = ["my_project.routers.DebugRouter"]
Run Code Online (Sandbox Code Playgroud)

确保正确配置日志记录以输出调试输出(例如,输出到stderr):

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        [...some other handlers...] 
        'stderr': {
            'level': 'DEBUG',
            'class': 'logging.StreamHandler'
        }
    },
    'loggers': {
        [...some other loggers...]
        'my_project': {
            'handlers': ['stderr'],
            'level': 'DEBUG',
            'propagate': True,
        },
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以打开一个Django shell并测试一些请求,看看你的路由器给出了什么数据:

$ ./manage.py shell
[...]
>>> from my_project.my_app.models import User
>>> User.objects.get(pk = 1234)
db_for_read (<class 'my_project.my_app.models.User'>, {})
<User: User object>
>>> user = User.objects.create(name = "Arthur", title = "King")
db_for_write (<class 'my_project.my_app.models.User'>, {})
>>> user.name = "Kong"
>>> user.save()
db_for_write (<class 'my_project.my_app.models.User'>, {'instance':
              <User: User object>})
>>>
Run Code Online (Sandbox Code Playgroud)

如您所见,hints当没有可用的实例(在内存中)时,它总是为空的.因此,如果需要查询参数(例如对象的id),则无法使用路由器,以确定要查询的数据库.如果Django在hintsdict中提供查询或查询集对象,将来有可能.

所以为了回答你的问题,我想说现在你必须按照Aaron Merriam的建议创建一个自定义管理器.但是,仅仅覆盖该create方法是不够的,因为您还需要能够在适当的数据库中获取对象.这样的东西可能会起作用(尚未测试):

class CustomManager(models.Manager)
    def self.find_database_alias(self, pk):
        return #... implement the logic to determine the shard from the pk

    def self.new_object_database_alias(self):
        return #... database alias for a new object

    def get(self, *args, **kargs):
        pk = kargs.get("pk")
        if pk is None:
            raise Exception("Sharded table: you must provide the primary key")
        db_alias = self.find_database_alias(pk)
        qs = self.get_query_set().using(db_alias)
        return qs.get(*args, **kargs)

    def create(self, *args, **kwargs):
        db_alias = self.new_object_database_alias()
        qs = super(CustomManager, self).using(db_alias)
        return qs.create(*args, **kwargs)

class ModelA(models.Model):
    objects = CustomManager()
Run Code Online (Sandbox Code Playgroud)

干杯


Pip*_*iam 3

using应该允许您指定要使用的数据库。

子类化 create 方法可能会完成您想要做的事情。

class CustomManager(models.Manager)
    def get_query_set(self):
        return super(CustomManager, self).get_query_set()

    def create(self, *args, **kwargs):
        return super(CustomManager, self).using('OTHER_DB').create(*args, **kwargs)

class ModelA(models.Model):
    objects = CustomManager()
Run Code Online (Sandbox Code Playgroud)

我还没有测试过这个,所以我不知道你是否可以将“创建”添加到“使用”的末尾