Nei*_*eil 12 ruby ruby-on-rails
我发现很少有关于如何在rails中编写多态关联的范围,更不用说如何在多态关联上编写查询.
在Rails文档中,我查看了" 多态关联"部分," 连接表"部分和" 范围"部分.我也做了相当广泛的谷歌搜索.
以此设置为例:
class Pet < ActiveRecord::Base
belongs_to :animal, polymorphic: true
end
class Dog < ActiveRecord::Base
has_many :pets, as: :animal
end
class Cat < ActiveRecord::Base
has_many :pets, as: :animal
end
class Bird < ActiveRecord::Base
has_many :pets, as: :animal
end
Run Code Online (Sandbox Code Playgroud)
所以Pet
可以是animal_type
"狗","猫"或"鸟".
要显示所有表结构:这是我的schema.rb:
create_table "birds", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "cats", force: :cascade do |t|
t.integer "killed_mice"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "dogs", force: :cascade do |t|
t.boolean "sits"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "pets", force: :cascade do |t|
t.string "name"
t.integer "animal_id"
t.string "animal_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Run Code Online (Sandbox Code Playgroud)
然后我继续做了一些记录:
Dog.create(sits: false)
Dog.create(sits: true)
Dog.create(sits: true) #Dog record that will not be tied to a pet
Cat.create(killed_mice: 2)
Cat.create(killed_mice: 15)
Cat.create(killed_mice: 15) #Cat record that will not be tied to a pet
Bird.create
Run Code Online (Sandbox Code Playgroud)
然后我去做了一些pet
记录:
Pet.create(name: 'dog1', animal_id: 1, animal_type: "Dog")
Pet.create(name: 'dog2', animal_id: 2, animal_type: "Dog")
Pet.create(name: 'cat1', animal_id: 1, animal_type: "Cat")
Pet.create(name: 'cat2', animal_id: 2, animal_type: "Cat")
Pet.create(name: 'bird1', animal_id: 1, animal_type: "Bird")
Run Code Online (Sandbox Code Playgroud)
这就是设置!现在困难的部分:我想在Pet
模型上创建一些范围,挖掘多态关联.
以下是我想写的一些范围:
Pets
可以坐的动物类型=="狗"Pets
杀死至少10只老鼠的animal_type =="Cat"Pets
不是animal_type"狗"的东西,不能坐.(换句话说:给我所有的宠物:所有的宠物:除了不能坐的狗)所以在我的Pet
模型中,我想把我的范围放在那里:
class Pet < ActiveRecord::Base
belongs_to :animal, polymorphic: true
scope :sitting_dogs, -> {#query goes here}
scope :killer_cats, -> {#query goes here}
scope :remove_dogs_that_cannot_sit, -> {#query goes here} #only removes pet records of dogs that cannot sit. All other pet records are returned
end
Run Code Online (Sandbox Code Playgroud)
我发现编写这些范围非常困难.
我在网上找到的一些东西看起来你只能用原始SQL编写这些范围.我想知道是否可以使用Hash语法来代替这些范围.
任何提示/帮助将不胜感激!
在回顾了之前的答案并尝试过之后:这就是我要做的工作。
(请注意,Pet.remove_dogs_that_cannot_sit
返回一个数组。此类方法是可读的,但具有速度慢的缺点N + 1
。任何修复此问题的建议将不胜感激。)
class Dog < ActiveRecord::Base
has_many :pets, as: :animal
scope :sits, -> {where(sits: true)}
end
class Cat < ActiveRecord::Base
has_many :pets, as: :animal
scope :killer, ->{ where("killed_mice >= ?", 10) }
end
class Pet < ActiveRecord::Base
belongs_to :animal, polymorphic: true
scope :by_type, ->(type) {where(animal_type: type)}
scope :by_dogs, -> {by_type("Dog") }
scope :by_cats, -> {by_type("Cat") }
def self.sitting_dogs
all.by_dogs
.joins("INNER JOIN dogs on animal_type = 'Dog' and animal_id = dogs.id")
.merge(Dog.sits)
end
def self.killer_cats
all.by_cats
.joins("INNER JOIN cats on animal_type = 'Cat' and animal_id = cats.id")
.merge(Cat.killer)
end
# returns an Array not Pet::ActiveRecord_Relation
# slow due to N + 1
def self.remove_dogs_that_cannot_sit
all.reject{|pet| pet.animal_type == "Dog" && !pet.animal.sits}
end
end
Run Code Online (Sandbox Code Playgroud)