问题
Edit 4. Something in my explanation is just not going right... attached is a (very poorly done graphic) that shows the tables and how the schema is, and then my main question is given what I have that a provider is joined via a group location id via the provider_location table, which the group location holds the group and ultimately the group name. This is what I want to know, building a table like this how do I get to the group name? all the way from a provider. It is like I need a has_many through through. (BIG NOTE: Provider Address and Group Address in the image is really Provider Location and Group Location)
EDIT x 3: Thanks @mrshoco. It got me closer, now its like something else with my structure is not quite right.... I get this error when running 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
Here is the complete unit test
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
The models are following:
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
回答1:
I've created a project which illustrates solution of your problem and it works. You can find it here https://github.com/dimakura/stackoverflow-projects/tree/master/32260679-dealing-with-associations. Below is description of the same concepts, so browsing the repository is completely optional.
Actually there are two ways to solve your problem both of them are usable in appropriate context.
But let's first create tables and models.
Create tables
I simplified your tables, omitting irrelevant details. Below are scripts for creating them.
groups table:
create_table :groups do |t|
t.string :group_name
t.timestamps null: false
end
addresses table:
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 table:
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
providers table:
create_table :providers do |t|
t.string :first_name
t.string :last_name
t.timestamps null: false
end
provider_locations table:
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
Models
Below are relations between Address
, Group
and GroupLocation
models:
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
is also simple:
class ProviderLocation < ActiveRecord::Base
belongs_to :provider
belongs_to :group_location
end
Relatively complicated one is Provider
:
class Provider < ActiveRecord::Base
has_many :provider_locations
has_many :group_locations, through: :provider_locations
has_many :groups, through: :group_locations
end
You can see now, that from a given Provider
you can easily navigate down to Group
s.
Let's now test our setup.
Testing
We have definition of your problem in this request:
... how to get all the groups a provider might belong to through the provider locations through group locations...
As I said in the begining there are two ways to solve your problem, depending on what's given.
Let's first assume we have a provider selected and want all groups related to this single provider.
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
It's a great way when working with a single provider.
But there is another scenario, when we want to select all groups belonging to more than one provider. Even worse, we might be given request: "Give us all groups for providers whose age not exceeds 50".
In this case we can exploit direct joining.
Below we illustrate, how to use joining when we want to get groups for all providers whose first name is 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
回答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
回答3:
Previous answer is correct
Group.joins(:providers).where(providers: { first_name: 'Shane' }).select('groups.name')
But you have an error 'no such table groups_providers' because in Provider model there is has_and_belongs_to_many :groups
so rails are searching for providers_groups or groups_providers model. Instead of that you should write has_many :groups, through: :provider_locations
and belongs_to :group
in ProviderLocation model
回答4:
Let me try to understand, the way i see it you have 5 tables, three which represent models:
- Provider
- Group
- Address
And another two that are join-tables:
- Provider-location
- Group-location
If I understand correctly, You have this because a group has many addresses and so does a provider. Also a provider has a relationship with group directly because it is not affected by the addresses, what i think you are missing is a the join table for groups_providers, this of course would need a migration to create said table. in which case you would have something of sort:
class Provider < ActiveRecord::Base
has_many_and_belongs_to_many :groups
end
If however. the relationship between provider and group is dependent on the address only, the relationship between provider and groups passes through address:
class Provider < ActiveRecord::Base
has_many :addresses
has_many :groups, though: addresses
end
# This is not tested, just illustrating the idea.
It is also possible that the the relationship, between groups, providers and addresses is dependent on the ids for two of the three embedded?, in this case I'd like to know more for the reasoning behind this design.
来源:https://stackoverflow.com/questions/32260679/dealing-with-associations-in-rails-for-has-many-through-but-two-join-tables-de