在rails中处理has_many到...的关联,但深入了两个连接表

时间:2015-08-27 22:53:18

标签: ruby-on-rails ruby rails-activerecord

编辑4.我的解释中的一些东西是不正确的...附件是一个(做得很差的图形),它显示了表格以及模式是如何的,然后我的主要问题是给出我提供的提供者通过provider_location表通过组位置ID连接,该组位置保存组,最终保存组名。这就是我想知道的,构建一个这样的表如何获得组名?来自提供商的所有方式。这就像我需要一个has_many贯穿。 (大注:图像中的提供商地址和组地址实际上是提供商位置和组位置)

rough model diagram.  a provider can have many addresses to many group locations.  Generally a group has many addresses

编辑x 3:谢谢@mrshoco。它让我更接近,现在它像我的结构其他东西不太正确....我在运行provider_test_location.rb时出现此错误

vagrant@precise32:/vagrant/ipanmv2$ rake test test/models/provider_location_tes
t.rb
Run options: --seed 18117

# Running:

E

Finished in 0.190900s, 5.2383 runs/s, 5.2383 assertions/s.

  1) Error:
ProviderLocationTest#test_fetching_a_group_name_for_a_provider:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: provider_
locations.group_id: SELECT groups.group_name FROM "groups" INNER JOIN "provider_
locations" ON "provider_locations"."group_id" = "groups"."id" INNER JOIN "provid
ers" ON "providers"."id" = "provider_locations"."provider_id" WHERE "providers".
"first_name" = 'Shane'
    test/models/provider_location_test.rb:37:in `puts'
    test/models/provider_location_test.rb:37:in `puts'
    test/models/provider_location_test.rb:37:in `block in <class:ProviderLocatio
nTest>'

1 runs, 1 assertions, 0 failures, 1 errors, 0 skips

这是完整的单元测试

require 'test_helper'

class ProviderLocationTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end

   test "adding a provider to a group location" do
    group = Group.new
    group.group_name = 'My Awesome'
    group.save
    adr = Address.new
    adr.city = 'Las Cruces'
    adr.state = 'New Mexico'
    adr.zip = '88012'
    adr.address_line_one = '382 Dark side of moon'
    adr.address_line_two = ''
    adr.save    

    gl  = GroupLocation.new
    gl.group = group
    gl.address=adr      
    gl.save


    prv = Provider.new
    prv.first_name = 'Shane'
    prv.last_name = 'Thomas'
    prv.save

    pl = ProviderLocation.new      
    pl.provider = prv
    pl.group_location = gl   ###############ISSUE

    assert pl.save, 'Provider location did not save'

    puts Group.joins(:providers).where(providers: { first_name: 'Shane' }).select('groups.group_name')




  end
end

模型如下:

class Provider < ActiveRecord::Base
    belongs_to :designation
    belongs_to :specialty
    has_many :provider_locations
    has_many :invoices
    has_many :groups, through: :provider_locations
end

class ProviderLocation < ActiveRecord::Base
  belongs_to :provider
  belongs_to :group_location
end

class Group < ActiveRecord::Base

    #validations
    validates :group_name, presence: true


    #associations
    has_many :providers, through: :provider_locations
    has_many :invoices
    has_one  :billing
    has_many :addresses, through: :group_locations


    has_many :group_locations
    belongs_to  :billing_address, class_name: 'Address', foreign_key: 'billing_address_id'
    belongs_to  :mailing_address, class_name: 'Address', foreign_key: 'mailing_address_id'
    has_and_belongs_to_many :insurances
    has_many :provider_locations



end


class GroupLocation < ActiveRecord::Base
  belongs_to :address
  belongs_to :group

end

class Address < ActiveRecord::Base
    has_many :group_locations
    has_many :billings
end

3 个答案:

答案 0 :(得分:2)

# Group name given a provider name
Group.joins(:providers).where('providers.first_name' => 'Shane').select('groups.name')

# address/location of the provider given the group name
Provider.joins(:groups).where("groups.name" => "your_group_name").first.provider_locations.first

答案 1 :(得分:1)

让我试着去理解,我看到它的方式你有5个表,其中三个表示模型:

  • 提供商
  • 地址

另外两个是连接表:

  • 提供商定位
  • 组定位

如果我理解正确,你有这个,因为一个组有很多地址,提供者也是如此。提供者也直接与组建立关系,因为它不受地址的影响,我认为你缺少的是groups_providers的连接表,这当然需要迁移来创建所述表。在这种情况下,你会有一些排序:

class Provider < ActiveRecord::Base
  has_many_and_belongs_to_many :groups
end

但是。提供者和组之间的关系仅取决于地址,提供者和组之间的关系通过地址:

class Provider < ActiveRecord::Base
  has_many :addresses
  has_many :groups, though: addresses
end

# This is not tested, just illustrating the idea.

组,提供者和地址之间的关系也可能依赖于三个嵌入式中的两个的ID,在这种情况下,我想知道更多关于此设计背后的原因。

答案 2 :(得分:1)

我创建了一个项目,说明了问题的解决方案,并且可行。你可以在https://github.com/dimakura/stackoverflow-projects/tree/master/32260679-dealing-with-associations找到它。下面是相同概念的描述,因此浏览存储库是完全可选的。

实际上,有两种方法可以解决您的问题,它们都可以在适当的环境中使用。

但是,让我们首先创建表格和模型。

创建表

我简化了你的表格,省略了不相关的细节。以下是创建它们的脚本。

群组表格:

create_table :groups do |t|
  t.string :group_name

  t.timestamps null: false
end

地址表格:

create_table :addresses do |t|
  t.string :city
  t.string :state
  t.string :zip
  t.string :address_line_one
  t.string :address_line_two

  t.timestamps null: false
end

group_locations 表格:

create_table :group_locations do |t|
  t.references :group, index: true, foreign_key: true
  t.references :address, index: true, foreign_key: true

  t.timestamps null: false
end

提供商表:

create_table :providers do |t|
  t.string :first_name
  t.string :last_name

  t.timestamps null: false
end

provider_locations 表格:

create_table :provider_locations do |t|
  t.references :provider, index: true, foreign_key: true
  t.references :group_location, index: true, foreign_key: true

  t.timestamps null: false
end

模型

以下是AddressGroupGroupLocation模型之间的关系:

class Address < ActiveRecord::Base
  has_many :group_locations
end

class Group < ActiveRecord::Base
  has_many :group_locations
end

class GroupLocation < ActiveRecord::Base
  belongs_to :group
  belongs_to :address
  has_many :provider_locations
end

ProviderLocation也很简单:

class ProviderLocation < ActiveRecord::Base
  belongs_to :provider
  belongs_to :group_location
end

相对复杂的是Provider

class Provider < ActiveRecord::Base
  has_many :provider_locations
  has_many :group_locations, through: :provider_locations
  has_many :groups, through: :group_locations
end

您现在可以看到,从给定的Provider开始,您可以轻松导航到Group

现在让我们测试一下我们的设置。

测试

我们在此请求中定义了您的问题:

  

...如何通过组位置通过提供商位置获取提供商可能属于的所有组...

正如我在开始时说的,有两种方法可以解决您的问题,具体取决于给出的内容。

让我们首先假设我们选择了一个提供商,并希望所有与此单一提供商相关的群组。

class ProviderLocationTest < ActiveSupport::TestCase
  def setup
    group = Group.create(group_name: 'My Awesome')
    adr = Address.create(city: 'Las Cruces', state: 'New Mexico', zip: '88012', address_line_one: '382 Dark Side of the Moon', address_line_two: '')
    gl =GroupLocation.create(group: group, address: adr)
    @provider = Provider.create(first_name: 'Shane', last_name: 'Thomas')
    pl = ProviderLocation.create(provider: @provider, group_location: gl)
  end

  test 'getting groups for a single provider' do
    assert_equal ['My Awesome'], @provider.groups.map{ |group| group.group_name }
  end
end

与单一提供商合作是一种很好的方式。

但是还有另一种情况,我们想要选择属于多个提供商的所有组。更糟糕的是,我们可能会收到请求:&#34;为年龄不超过50岁的提供者提供所有群组。

在这种情况下,我们可以利用直接加入。

下面我们将说明如何在我们想要为名字为Shane的所有提供商获取组时使用加入。

test 'getting groups based on arbitrary query' do
  group_names = Group.joins(group_locations: [ provider_locations: :provider]).where("providers.first_name=?", 'Shane').map{ |group| group.group_name }
  assert_equal ['My Awesome'], group_names
end