基于当前用户登录的 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。添加了 init 和 call,现在一切正常。【参考方案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 数据库路由的主要内容,如果未能解决你的问题,请参考以下文章