Rails Rails 中关于:scope 的设计问题

Amosasas · March 07, 2020 · Last by Amosasas replied at March 08, 2020 · 2683 hits

假设有个 table 叫做 People,有个属性叫做 sex,类型是 string,还有个属性叫 country,类型也是 string 有个 controller 中要做这样的事情: app/controller/person_controller.rb

class PersonController < ApplicationController
  def some_method
    sex = params[:sex] # sex可以是nil
    country = params[:country] # country可以是nil
    # 返回指定sex country的记录 是nil不限制
  end
end

我现在想到一种方法是用 scope(根据大佬提醒已经修改) app/models/person.rb

class Person < ApplicationRecord
  # 省略一些validate
  scope :by_sex, ->(sex) { where(sex: sex) if sex.present? }
  scope :by_country, ->(country) { where(country: country) if country.present? }
end

现在的问题是,怎么改动支持一种 by_sex(!"male") 查询不是 male 的方法?

·使用where.not就行了 是我想多了 hmm

其实你这里 sex 可以用 enum 来做的,然后自动就生成 malefemale 两个 scope 了,rails 6 加了 not_male 这样的 scope,但没有的话你可以自己实现(当然这样就没必要用 enum 了)

!"male" 这种做法没法搞,这样写必定等价于 false,你可以改写成 by_sex(:not_male) 把 symbol 当 枚举 去用,更改你的 scope 实现做一个 case...when 就好了

Reply to jasl

嗯,感觉确实是考虑得有点多了,不现实。 最近刚看过 ruby meta-programming,想试着用 method_missing 去定义,但是看了一下scope:貌似是生成了一个 class method 而且返回了一个·ActivateRelation·,所以可能得用其他方法实现,但是我对 Rails 里面的 class 和 module 关系完全不熟,还是老老实实在 model 里一个个实现 case 吧。

很简单的需求,搞那么复杂干嘛。
这和元编程、method_missing 都没关系,直接 where.not 或者加 scope 就行了。再或者传数组 ['female', nil]

scope :not_sex, ->(sex) { where.not(sex: sex) if sex.present? }

另外,你上面很多细节都是可以优化的。
where("people.country= ?", country) => where(country: country)
if !sex.nil? => if sex.present?
!"male" 没有这种写法,虽然语法上是没错

多谢提醒 感觉 where.not 确实最简单的方法

Amosasas closed this topic. 08 Mar 17:41
You need to Sign in before reply, if you don't have an account, please Sign up first.