简体   繁体   English

Rails查询通过关联使用了多个has_many

[英]Rails query using more then one has_many through associations

Hi I have 6 models with relations with each other: 嗨,我有6个相互关联的模型:

Spell model which can have many tags and elements and one ring(aka level) 可以具有多个标签和元素以及一个环(也称为级别)的咒语模型

class Spell < ActiveRecord::Base

  belongs_to :spell_ring

  has_many :element_of_spells, dependent: :destroy
  has_many :spell_elements, through: :element_of_spells

  has_many :tag_of_spells, dependent: :destroy
  has_many :spell_tags, through: :tag_of_spells

  validates_presence_of :name
end

Spell element which can have many spells 可以有很多咒语的咒语元素

class SpellElement < ActiveRecord::Base

  has_many :element_of_spells, dependent: :destroy
  has_many :spells, through: :element_of_spells
end

Spell tag, which can have many spells: 咒语标记,可以包含许多咒语:

class SpellTag < ActiveRecord::Base

  has_many :tag_of_spells, dependent: :destroy
  has_many :spells, through: :tag_of_spells
end

Spell ring: 咒语环:

class SpellRing < ActiveRecord::Base

  has_many :spells
end

And join models: 并加入模型:

class ElementOfSpell < ActiveRecord::Base
  belongs_to :spell
  belongs_to :spell_element
end

class TagOfSpell < ActiveRecord::Base
  belongs_to :spell
  belongs_to :spell_tag
end

Ok now I want to put them to good use :) 好吧,现在我想好好利用它们了:)

What I know: 我知道的:

That if I take any spell_tag or spell_element or spell_ring object, I can get all associated spells. 如果我带了任何spell_tag或spell_element或spell_ring对象,那么我可以获得所有关联的咒语。

element = SpellElement.first
spells_of_element = element.spells >> give me all associated spells

I know I can scope this with spell_ring_id since it is part of the spell object. 我知道我可以使用spell_ring_id范围,因为它是spell对象的一部分。

spell_ring = SpellRing.first
spells_of_element_and_ring = spells_of_element.where( spell_ring_id: spell_ring.id ) >> returns spells of given element and ring

What I don`t know: 我不知道的是:

How to scope spells_of_element_and_ring with given tag. 如何用给定的标签来spells_of_element_and_ring范围。

tag = SpellTag.first

spells_of_element_ring_and_tag = ?? 

Updated 更新

What I want? 我想要的是?

Is to be able to query spells: 是为了能够查询拼写:

  • by the spell_tags 通过spell_tags
  • by the spell_rings 通过spell_rings
  • by the spell_elements 通过spell_elements

and any combination of those three models. 以及这三个模型的任意组合。

It's a good idea, when posting to StackOverflow, to weed out as much code as possible, and really boil your question down to the simplest possible example. 在发布到StackOverflow时,最好删除尽可能多的代码,然后将您的问题归结为最简单的示例,这是一个好主意。 This will get quicker answers, and also be useful to more people. 这将获得更快的答案,并且对更多人有用。

Let's start out with the simpler example of a school, which has many classrooms, and each classroom has many students. 让我们从一个简单的学校示例开始,该学校有很多教室,每个教室有很多学生。

Let's create our models: 让我们创建模型:

rails generate model school name:string
rails generate model classroom school_id:integer grade:integer
rails generate model student name:string classroom_id:integer

Now let's create our associations: 现在让我们创建关联:

class School < ActiveRecord::Base
  has_many :classrooms
  has_many :students, through: :classrooms
end

class Classroom < ActiveRecord::Base
  belongs_to :school
  has_many :students
end

class Student < ActiveRecord::Base
  belongs_to :classroom
end

Finally, we'll create three quick records: 最后,我们将创建三个快速记录:

school = School.create name: 'City Elementary'
classroom = school.classrooms.create grade: 4
student = classroom.students.create name: 'Bob'

Now we can get a list of all students at the school like so: 现在,我们可以像这样获取学校所有学生的列表:

school.students

This works because a school has_many students, through classrooms. 之所以has_many ,是因为学校through教室有很多学生。

I think what you actually want is a little more complicated - a spell can have many elements, and an element can belong to many spells. 我认为您实际想要的有点复杂-一个咒语可以包含许多元素,而一个元素可以属于许多咒语。 In this case, you need a "join table". 在这种情况下,您需要一个“联接表”。 Let's simplify your example by eliminating everything except spells and elements. 让我们通过消除除咒语和元素之外的所有内容来简化示例。

We start by creating our models: 我们首先创建模型:

rails generate model spell name:string
rails generate model element name:string

Now we create a join table, which keeps track of which spells and elements belong to each other: 现在,我们创建一个联接表,该表跟踪哪些咒语和元素彼此所属:

rails generate migration create_elements_spells element_id:integer spell_id:integer

Now we define our associations (relationships): 现在,我们定义关联(关系):

class Element < ActiveRecord::Base
  has_and_belongs_to_many :spells
end

class Spell < ActiveRecord::Base
  has_and_belongs_to_many :elements
end

has_and_belongs_to_many automatically looks for a table with the combined name of the two models, in plural form, in alphabetical order. has_and_belongs_to_many自动以字母顺序以复数形式查找具有两个模型的组合名称的表。 Now we can do things like: 现在我们可以做类似的事情:

spell = Spell.create name: 'set on fire'

flint = Element.create name: 'flint'
steel = Element.create name: 'steel'

spell.elements << flint
spell.elements << steel

Now, spell.elements lists both flint and steel. 现在, spell.elements列出了火石和钢铁。 flint.spells will list our 'set on fire' spell. flint.spells将列出我们的“放火”咒语。 steel.spells will also list our spell. steel.spells也会列出我们的咒语。 You can expand from there. 您可以从那里扩展。

But what if you need to know more than just what element - what if you need to know how much? 但是,如果您不仅需要了解什么元素,该怎么办-如果您需要了解多少,该怎么办? now you have extra data that doesn't belong in the Spell record or the Element record. 现在您有了额外的数据,这些数据不属于Spell记录或Element记录。 It belongs on the association itself. 它属于协会本身。 We might call an element/amount combo an "ingredient", and create a table for it like so: 我们可以将元素/数量组合称为“成分”,并为其创建一个表,如下所示:

rails generate model ingredient spell_id:integer element_id:integer amount:string

And we update our associations: 并且我们更新了关联:

class Element < ActiveRecord::Base
  has_many :ingredients
  has_many :spells, through: :ingredients
end

class Spell < ActiveRecord::Base
  has_many :ingredients
  has_many :elements, through: :ingredients
end

class Ingredient < ActiveRecord::Base
  belongs_to :element
  belongs_to :spell
end

Now we can add ingredients to our spell: 现在,我们可以为我们的咒语添加成分:

spell.ingredients.create element: flint, amount: '1 gram'
spell.ingredients.create element: steel, amount: '1 piece'

So spell.ingredients will list both flint and steel, and the amounts for each. 因此, spell.ingredients将同时列出火石和钢铁,以及每种的含量。 This should get you well on your way to building you application. 这应该使您在构建应用程序的过程中顺利进行。

@Jaime explain querying through the whole process very good. @Jaime解释了整个查询过程非常好。 But I wanted my query to be more flexible. 但是我希望查询更加灵活。

I don't know it this is the Rails way. 我不知道这是Rails的方式。 But I found something like this to fit me the best. 但是我发现这样的东西最适合我。

Because SpellElement.spells, SpellRing.spells, SpellTag.spells all returns an array. 因为SpellElement.spells,SpellRing.spells,SpellTag.spells都返回一个数组。 My idea is to just compare them and return only matched elements as a result. 我的想法是只比较它们,结果只返回匹配的元素。

So 所以

spell_element_ring_and_tag = some_spell_element.spells & some_spell_ring.spells & some_spell_tag.spells

Will return only spell objects shared by all three arrays. 将仅返回所有三个数组共享的咒语对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM