【问题标题】:Django multidb: write to multiple databasesDjango multidb:写入多个数据库
【发布时间】:2011-05-01 18:52:00
【问题描述】:

使用 Django multidb,编写运行主/从基础架构的router 相当容易。但是是否可以编写一个写入到多个数据库的路由器?我的用例是一组项目,它们都在同一个域上运行。为了避免用户在每个站点上注册/登录,我想同步 contrib.authcontrib.sessions 表。使用 Django multidb 是否可行,或者我应该研究数据库系统的复制功能(在我的例子中是 MySQL)?

【问题讨论】:

    标签: python django django-orm


    【解决方案1】:

    我认为你会更好地实施 SSO 或 OAuth 服务

    但是如果您想在两个数据库之间同步您的表用户,并且如果您使用自己的 UserModel,您可以这样做

    class MyUser(models.Model):
        name = models.CharField(max_length=100)
        user = models.ForeignKey(User, unique=True)
    
    
        def save(self, ...): # ALL the signature
            super(MyUser, self).save(using='database_1')
            super(MyUser, self).save(using='database_2')
    

    你也可以用这样的装饰器,这样你也可以用它来同步其他表:

    def save_multi_db(model_class):
    
        def save_wrapper(save_func): 
            def new_save(self, *args, **kws):
                super(model_class, self).save(using='database_1')
                super(model_class, self).save(using='database_1')
            return new_save
    
        func = getattr(model_class, 'save')
        setattr(model_class, 'save', save_wrapper(func)) 
    
        return save_wrapper
    
    # and use it like this:
    
    @save_multi_db
    class MyUser(models.Model):
          ....
    

    希望这会有所帮助:)

    【讨论】:

    • 我必须更改 Django 的源代码才能使其正常工作(正如我在问题中所说,我要复制的表来自 contrib.authcontrib.sessions)。如果可能的话,我想避免弄乱 Django 本身。
    【解决方案2】:

    我现在正在研究 Django 分片模式。

    我查看了 Django 路由器,但决定自己动手。

    对您的问题的一些想法:

    1. 一种想法是使用一个数据库,然后在保存后使用 Django 信号复制适当的数据。

    类似的东西——

    import settings.databases as dbs_list
    
    def post_save_function(UserModel):
         for db in dbs_list:
              UserModel.save(using=db,force_insert=True)
    

    保存用户对象(至少在单数据库模型上)似乎通过 django.contrib 中发生的各种魔法在后台保存会话、身份验证等数据,因此您可能不必进入并找出所有这些数据库表的名称和类型。

    为了支持这种工作的可能性,我发誓我最近在某个地方(可能在 Alex Gaynor 的一篇博客文章中)读到,如果一个对象有一个外键,Django 将尝试使用该对象所在的同一个数据库(对于根据 Django 通常的操作方式,有明显的原因)。

    1. 另一个想法:

    从您引用的 Django multiDB 页面上的示例中,我想知道以下内容是否可行:

    他们的示例代码:

    def allow_syncdb(self, db, model):
            "Explicitly put all models on all databases."
            return True
    

    可能的修改:

    def allow_syncdb(self, db, model):

        if isinstance(model,User):
             return True
    
        elif isinstance(model,Session):
             return True
    
        else:
             ''' something appropriate --
                 whatever your sharding schema is for other objects '''
    

    再看一遍,这段代码作为“db_for_write”函数可能会更有用。但你明白了。

    毫无疑问,您必须添加其他类型的模型才能完成这项工作(所有 auth 的东西,这是广泛的)。

    祝你好运!希望这对您有所帮助。

    我对你的发现和 cmets 很感兴趣!

    jb

    【讨论】:

    • 您好,感谢您的评论。我不再关注这个特定问题,因为我们选择了 LDAP/Kerberos 系统来提供集中式身份验证和单点登录。
    【解决方案3】:

    首先,我认为您需要的是一个 SSO 框架,例如 in this post

    我尝试了 mouad 的回答,但我无法让类装饰器工作......在我看来,这个解决方案不允许在模型中使用自定义 save()。

    更适合我的需要,我定义了一个自定义泛型类并简单地覆盖 save() 函数。

    class MultiDbModel(models.Model):
        class Meta:
            abstract = True
    
        def save(self, *args, **kwargs):
            for dbname in settings.DATABASES:
                super(MultiDbModel, self).save(using=dbname)
    

    然后:

    class MyObject(MultiDbModel):
        [...]
        def save(self, *args, **kwargs):
            [custom save]
            super(MyObject, self).save(args, kwargs)
    

    【讨论】:

    • too bad db_for_write in db router 无法返回多个 dbs 进行写入.. 会很酷。另一个问题是 filter().update() 不会触发 save() 所以你只能使用 save.
    • 这也不考虑为您保存的对象保存相关对象(FK),如果它不存在于第二个数据库中
    【解决方案4】:

    这是我的 multidb_model.py,它似乎也可以处理外键。如果有错误会更新它,但是一旦你按照 Stepahne 的回答从这个类继承,这将处理外键保存。

    import logging
    
    from django.db import models
    
    DEFAULT_DB = 'default'  # main postgres host
    MIRROR_COPY_DB = 'pg_mirror'  # a copy original db, i.e. if you want to move your data and keep it in sync
    
    
    class MultiPgDbModel(models.Model):
        class Meta:
            abstract = True
    
        def save(self, *args, **kwarg):
            super(MultiPgDbModel, self).save(using=DEFAULT_DB)
            try:
                fields = [field for field in self._meta.fields if field.get_internal_type() == 'ForeignKey']
    
                for field in fields:
                    getattr(self, field.name).save(using=MIRROR_COPY_DB)
                super(MultiPgDbModel, self).save(using=MIRROR_COPY_DB)
            except Exception as e:
                logging.exception(f"MultiPgDbModel.save unexpected error when saving to pg_mirror, object id "
                                  f"{self.pk} of model {self._meta.model}. {e}")
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-25
      • 2012-06-30
      • 1970-01-01
      • 1970-01-01
      • 2013-04-26
      • 2016-02-23
      • 1970-01-01
      • 2014-02-10
      相关资源
      最近更新 更多