Rails:具有多态关联和自定义class_name的嵌套连接查询

时间:2022-09-28 22:48:03

Consider the following example of a school. There is a classroom (chosen over the term 'class' to avoid any syntactic confusions). Each classroom has many students and one prefect, who is one among those students. Shown below is how I have written the models:

考虑以下学校的例子。有一个教室(在“课堂”一词中选择以避免任何语法混淆)。每个教室都有许多学生和一名长官,他们是这些学生中的一员。下面显示的是我如何编写模型:

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

The purpose of house is to track students who come from the same house. Each house has an address and one of the attributes of address is city. Of course there are other attributes in each of these models that I have not mentioned for brevity. Address, for instance, belongs to other models that are not listed here, hence the polymorphic association.

房子的目的是跟踪来自同一所房子的学生。每个房子都有一个地址,其中一个地址属性是城市。当然,为简洁起见,我在这些模型中还有其他属性。例如,地址属于此处未列出的其他模型,因此属于多态关联。

Objective

目的

My objective is to get the list of classrooms whose prefects are from a particular city. I know there are ways to solve this in Ruby, but how can I achieve this using an ActiveRecord/SQL query? I'm using Rails 5.0 and Postgres 9.6.

我的目标是获得其长官来自特定城市的教室清单。我知道有很多方法可以在Ruby中解决这个问题,但是我如何使用ActiveRecord / SQL查询实现这一目的呢?我正在使用Rails 5.0和Postgres 9.6。

What I Have Tried

我曾经尝试过什么

I have already tried a couple of ways, all by joining the tables up. However, the issue I face is that due to the unconventional names, AR is unable to generate the apt query. Here's the most promising variation of what I tried:

我已经尝试了几种方法,所有这些都是通过加入表格来完成的。但是,我面临的问题是由于非常规名称,AR无法生成apt查询。这是我尝试过的最有希望的变化:

Classroom.joins(prefect: {house: :address}).where(prefect: {house: {address: {city: "Barcelona"}}})

The join seems to be working fine. However, the where bit goes bonkers since the underlying table for prefect is students. The result of running this is just an ActiveRecord Relation object. But run .to_a on that and I get the following:

加入似乎工作正常。然而,因为知识的基础表是学生,所以位置变得疯狂。运行它的结果只是一个ActiveRecord Relation对象。但是运行.to_a,我得到以下内容:

ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR:  missing FROM-clause entry for table "address"

I was successfully able to retrieve classrooms whose prefects have a particular first name as follows:

我成功地能够检索其长官具有特定名字的教室,如下所示:

Classroom.joins(prefect: {house: :address}).where(students: {first_name: "Juliana"})

But that's the best I got. I'm not able to apply the same logic when the nesting comes in:

但这是我得到的最好的。嵌套进来时,我无法应用相同的逻辑:

Classroom.joins(prefect: {house: :address}).where(students: {house: {address: {city: "Barcelona"}}})

Running this one results in the following error:

运行此导致以下错误:

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'

Any ideas what I should do here?

有什么想法我应该在这做什么?

2 个解决方案

#1


1  

Classroom.joins(prefect: {house: :address}).where(prefects: { houses: { addresses: { city: "Barcelona"}}})

Side note: I think you're overusing :inverse_of. From the Rails guides

旁注:我认为你过度使用:inverse_of。来自Rails指南

Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain any of the following options:

Active Record支持大多数标准名称关联的自动识别。但是,Active Record不会自动识别包含以下任何选项的双向关联:

  • :conditions

    :条件

  • :through

    :通过

  • :polymorphic

    多态

  • :class_name

    :班级名称

  • :foreign_key

    :foreign_key

and

class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

So you can simplify your models a bit to this

所以你可以简化你的模型

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

#2


0  

I finally found something that worked for me, and it's much simpler than what I originally tried:

我终于找到了对我有用的东西,它比我最初尝试的要简单得多:

Classroom.joins(prefect: {house: :address}).where("addresses.city = ?", "Barcelona")

#1


1  

Classroom.joins(prefect: {house: :address}).where(prefects: { houses: { addresses: { city: "Barcelona"}}})

Side note: I think you're overusing :inverse_of. From the Rails guides

旁注:我认为你过度使用:inverse_of。来自Rails指南

Active Record supports automatic identification for most associations with standard names. However, Active Record will not automatically identify bi-directional associations that contain any of the following options:

Active Record支持大多数标准名称关联的自动识别。但是,Active Record不会自动识别包含以下任何选项的双向关联:

  • :conditions

    :条件

  • :through

    :通过

  • :polymorphic

    多态

  • :class_name

    :班级名称

  • :foreign_key

    :foreign_key

and

class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

So you can simplify your models a bit to this

所以你可以简化你的模型

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

#2


0  

I finally found something that worked for me, and it's much simpler than what I originally tried:

我终于找到了对我有用的东西,它比我最初尝试的要简单得多:

Classroom.joins(prefect: {house: :address}).where("addresses.city = ?", "Barcelona")