基于当前用户登录的 Django 数据库路由

Posted

技术标签:

【中文标题】基于当前用户登录的 Django 数据库路由【英文标题】:Django Database routing based on current user logged in 【发布时间】:2017-01-14 05:57:34 【问题描述】:

view 类中,您可以调用self.request.user 并根据它执行操作。就我而言,我希望能够根据当前登录的用户切换数据库。无论如何,是否可以将self.request 调用注入到db_for_read()db_for_write() 方法中,如下所示?

class DataBaseRouter(object):

    def db_for_read(self, model, **hints):
        user = self.request.user
        if user.id == 1:
            return "master"
        return "default"

    def db_for_write(self, model, **hints):
        user = self.request.user
        if user.id == 1:
            return "master"
        return "default"

【问题讨论】:

【参考方案1】:

您可以使用RouterMiddlewear 检查是否登录的用户,然后将所有queries 重定向到您选择的特定数据库,如果您使用view based 执行查询,这将很有帮助.

class RouterMiddleware (object):

    def process_view( self, request, view_func, args, kwargs ):
        # Check the user logged in
        user = self.request.user
        # Call your functions to set the database by passing the user.id



    def process_response( self, request, response ):
        # Make the database to default here if you wish to use it no longer

        return response


class DataBaseRouter(object):

    def db_for_read(self, model, user_id=None, **hints):
       if user.id == 1:
            return "master"
        return "default"

    def db_for_write(self, model, user_id=None, **hints):
        if user.id == 1:
            return "master"
        return "default"

这是我根据您的要求修改的link。

确保将RouterMiddleware 添加到您的MIDDLEWARE_CLASSES

【讨论】:

如果您可以灵活地在项目中安装软件包,@kartheek 的答案将是一个更简单的解决方案。如果您不喜欢检查和更改每个视图函数的数据库,可以使用中间件处理器。 嘿,谢谢@kt14 的回答!我最终接受了您的回答,因为我发现在 kartheek 上实现起来要容易得多。不过我确实有一个担心。是否有可能多个请求大致同时调用会搞砸? IE。 Request1 > user.id =1 Request2 > user.id = 2 Response1 > gets user.id = 2 导致错误 我觉得应该没问题,因为django会为用户传递的每个请求创建一个新的请求对象,不管两个不同的用户同时查询,查询通过context_processor 并且将选择该特定数据库,因此据我所知不会有交叉交互。 @kt14 嗨,我真的不明白我们如何将 user.id 注入路由器类。在链接的 sn-p 中,我们使用 threading.local() 来分享它,但我不能让它在 django 2.1.4 上工作。也许这有一些变化? 好吧抱歉,这是因为使用了 Django 版本的 MIDDLEWARE。添加了 initcall,现在一切正常。【参考方案2】:

试试这个django dynamic db router package。它非常简单。如下安装并配置使用。

settings.py

DATABASES = 
'default': 
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': 'my_local_database',
    'USER': 'postgres',
    'PASSWORD': 'my-pass',
    'HOST': '127.0.0.1',
    'PORT': '5432',
,
'master': 
    'ENGINE': 'django.db.backends.postgresql_psycopg2',
    'NAME': 'my_master_database',
    'USER': 'postgres',
    'PASSWORD': 'my-pass',
    'HOST': 'example.com',
    'PORT': '5432',
,

DATABASE_ROUTERS = ['dynamic_db_router.DynamicDbRouter']

my_app/views.py

from dynamic_db_router import in_database

from my_app.models import MyModel

def index(request):
     #Picking the DB based on logged in user or you can do this in middile ware as well.
     use_db = "default"
     user = self.request.user
     if user.id == 1:
        use_db = "master"
     # Fetching data from selected databases. 
     with in_database(use_db):
         input = MyModel.objects.filter(field_a="okay")
         output = complex_query_function(input)

【讨论】:

嘿@Kartheek,感谢您的回答!虽然我很感激,但我最终还是选择了 kt14,因为我发现他的答案更容易实现,因为我的项目处理接近一百个视图,因此为每个视图编写代码是不可行的。不过,我感谢您的宝贵时间! @Lorenzo 我已经提到了内联注释,您可以将该逻辑移至中间件。它的另一种实现方式。无论如何,我很高兴你得到了解决方案。【参考方案3】:

为了解决这个问题,我创建了一个只有一个 userid 字段的新模型。然后在路由器中指定 db 进行读写时,我从该模型获取数据,因此我不需要任何请求参数。 我刚刚在我的基本模板中添加了一个简短的 ajax 代码,所以每当我的网站打开时,它都会从 request.user 获取用户 ID 并更改模型行,因此当前用户 ID 会像这样更改。

【讨论】:

以上是关于基于当前用户登录的 Django 数据库路由的主要内容,如果未能解决你的问题,请参考以下文章

用户登录功能的实现

Django-基于session的登录

使用 uwsgi 的 Django 日志文件权限

如何在 Django 的 CreateView 提交操作中保存当前登录的用户名?

访问当前登录用户对象的 ACEGI 标记

案例:基于SSM的bbs论坛