如何更便捷地结合acts_as_tenant与blueprinter gem处理关联?
简化Blueprinter结合ActsAsTenant处理关联的方案
下面提供几种更简洁的实现方式,避免每次关联都重复写租户上下文包裹:
1. 构建基类Blueprint统一处理租户上下文
创建一个所有Blueprint都继承的基类,在渲染流程中自动注入租户上下文,这样所有关联都能自动应用租户过滤:
class BaseBlueprint < Blueprinter::Base def self.prepare(object, options) # 在渲染前切换到指定租户上下文 ActsAsTenant.with_tenant(options[:account]) do super end end end
之后你的业务Blueprint只需继承这个基类,关联部分无需额外处理:
class UserBlueprint < BaseBlueprint fields :uuid, :name, :plan # 直接定义关联即可,基类已处理租户上下文 association :posts, blueprint: PostBlueprint end
2. 用自定义关联方法封装租户逻辑
如果不想修改全局基类,可以在BaseBlueprint里封装一个带租户处理的关联方法,减少重复代码:
class BaseBlueprint < Blueprinter::Base def self.tenant_association(name, **kwargs) association(name, **kwargs) do |object, options| ActsAsTenant.with_tenant(options[:account]) do object.send(name) end end end end
使用时替换原来的association为tenant_association:
class UserBlueprint < BaseBlueprint fields :uuid, :name, :plan tenant_association :posts, blueprint: PostBlueprint end
3. 结合全局租户存储简化模型层过滤
如果你的项目用了类似Current这样的全局存储(比如通过request middleware设置当前租户),可以在模型层默认应用租户过滤,同时在Blueprint里把options传入全局存储:
# 先在BaseBlueprint里设置全局租户 class BaseBlueprint < Blueprinter::Base def self.prepare(object, options) Current.account = options[:account] super ensure # 渲染完成后清理,避免污染后续请求 Current.account = nil end end # 模型层设置默认租户作用域 class Post < ApplicationRecord acts_as_tenant :account default_scope { where(account_id: Current.account.id) } end # 业务Blueprint正常定义关联即可 class UserBlueprint < BaseBlueprint fields :uuid, :name, :plan association :posts, blueprint: PostBlueprint end
内容的提问来源于stack exchange,提问作者Ashbury
相关产品推荐
相关产品推荐

