考虑以下学校的例子。有一个教室(在术语'课程中选择,以避免任何语法混淆)。每个教室都有许多学生和一名长官,他们是这些学生中的一员。下面显示的是我如何编写模型:
class Classroom
has_many :students, inverse_of: :classroom
belongs_to :prefect, class_name: "Student", inverse_of: :classroom
end
class Student
belongs_to :classroom, inverse_of: :students
belongs_to :house, inverse_of: :members
end
class House
has_many :members, class_name: "Student", inverse_of: :house, foreign_key: "house_id"
has_one :address, as: :addressable
end
class Address
# attribute city: :string
belongs_to :addressable, polymorphic: true
end
房子的目的是跟踪来自同一所房子的学生。每个房子都有一个地址,其中一个地址属性是city
。当然,为简洁起见,我在这些模型中还有其他属性。例如,Address
属于此处未列出的其他模型,因此属于多态关联。
目标
我的目标是获得其长官来自特定城市的教室清单。我知道有很多方法可以在Ruby中解决这个问题,但是如何使用ActiveRecord / SQL查询实现这一目标呢?我使用的是Rails 5.0和Postgres 9.6。
我尝试过什么
我已经尝试了几种方法,都是通过加入表格来完成的。但是,我面临的问题是由于非常规名称,AR无法生成apt查询。这是我尝试过的最有希望的变体:
Classroom.joins(prefect: {house: :address}).where(prefect: {house: {address: {city: "Barcelona"}}})
联接似乎工作正常。但是,由于完美的基础表为where
,因此students
位变得疯狂。运行它的结果只是一个ActiveRecord Relation对象。但是运行.to_a
,我得到以下结果:
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing FROM-clause entry for table "address"
我成功地能够检索其长官具有特定名字的教室,如下所示:
Classroom.joins(prefect: {house: :address}).where(students: {first_name: "Juliana"})
但那是我得到的最好的。当嵌套进来时,我无法应用相同的逻辑:
Classroom.joins(prefect: {house: :address}).where(students: {house: {address: {city: "Barcelona"}}})
运行此错误会导致以下错误:
NoMethodError: undefined method `_reflect_on_association' for nil:NilClass
from /home/vagrant/.rvm/gems/ruby-2.3.1@school/gems/activerecord-5.0.0.1/lib/active_record/table_metadata.rb:47:in `associated_table'
我在这里应该做些什么?
答案 0 :(得分:1)
Classroom.joins(prefect: {house: :address}).where(prefects: { houses: { addresses: { city: "Barcelona"}}})
旁注:我认为你过度使用:inverse_of
。来自Rails指南
Active Record支持大多数关联的自动识别 标准名称。但是,Active Record不会自动进行 识别包含以下任何内容的双向关联 选项:
:条件
:通过
:多晶型
:CLASS_NAME
:foreign_key
和
class Author < ApplicationRecord
has_many :books, inverse_of: 'writer'
end
class Book < ApplicationRecord
belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
因此,您可以将模型简化为此
class Classroom
has_many :students
belongs_to :prefect, class_name: "Student"
end
class Student
belongs_to :classroom
belongs_to :house, inverse_of: :members
end
class House
has_many :members, class_name: "Student", foreign_key: "house_id"
has_one :address, as: :addressable
end
class Address
belongs_to :addressable, polymorphic: true
end