Rails 5:能否在查询中使用合成属性?
解决非持久化属性无法用于ActiveRecord查询的问题
嗨,我完全懂你的困扰——你定义的:is_synthetic是Ruby层面的非持久化属性,数据库里根本没有这个字段,所以直接用where(is_synthetic: true)肯定会触发PostgreSQL的错误,因为SQL会试图查找不存在的列。
下面给你几个实用的解决方案,帮你实现想要的查询效果:
1. 直接把合成逻辑转换成SQL查询条件
既然:is_synthetic的判断逻辑是is_class_account或is_expense_account为true,那我们直接把这个逻辑写到查询里就行:
# 方式一:直接写SQL片段(这里字段固定,不存在SQL注入风险) Account.where(project_id: project_id).where("is_class_account = TRUE OR is_expense_account = TRUE") # 方式二:用ActiveRecord的or方法更安全(Rails 5+支持) Account.where(project_id: project_id).where(is_class_account: true).or(Account.where(is_expense_account: true))
2. 封装成模型Scope,方便复用
如果这个查询会多次用到,最好把逻辑封装到Account模型的scope里,代码更整洁:
# 在Account模型中定义scope class Account < ApplicationRecord scope :synthetic, -> { where("is_class_account OR is_expense_account") } end # 调用的时候就很简单 Account.where(project_id: project_id).synthetic
如果想直接把project_id的条件也整合进去,可以定义带参数的scope:
class Account < ApplicationRecord scope :synthetic_for_project, ->(project_id) { where(project_id: project_id).synthetic } end # 调用 Account.synthetic_for_project(project_id)
3. 用Arel构建条件(更Ruby化的方式)
如果你不想写原生SQL片段,也可以用Arel来构建查询条件,保持代码的Ruby风格:
class Account < ApplicationRecord def self.synthetic where(arel_table[:is_class_account].eq(true).or(arel_table[:is_expense_account].eq(true))) end end # 同样可以结合project_id查询 Account.where(project_id: project_id).synthetic
核心思路就是:非持久化属性是在Ruby内存中计算的,数据库无法识别,所以必须把它的判断逻辑转换成数据库能理解的列条件。
内容的提问来源于stack exchange,提问作者GGizmos
相关产品推荐
相关产品推荐

