Why doesn't my associations go both ways in ActiveRecord? - activerecord

So I'm connecting to a legacy database. I have two tables, Sites and States.
A Site has one State and a State can belong to many Sites
# Sites.rb
has_one :state, :primary_key => "StateKey", :foreign_key => "StateKey"
# States.rb
belongs_to :sites, :class_name => "Sites", :primary_key => "SiteKey", :foreign_key => "SiteKey"
As you can see I have to manually set the foreign keys and primary keys.
So this works:
Sites.first.state # one record returned (the state)
This does not:
States.first.sites # nil returned. Doesn't even appear to hit AR
What am I doing wrong?
Thanks.

You should use the pair has_many, belongs_to:
# Sites.rb
belongs_to :state, :primary_key => "StateKey", :foreign_key => "StateKey"
# States.rb
has_many :sites, :class_name => "Sites", :primary_key => "StateKey", :foreign_key => "StateKey"
Take a look at this guide.
When you have a one-to-many association it is standard practice to use belongs_to and has_many in the two model classes. has_one is a special case of has_many. belongs_to says that the foreign key is in the model declaring the association, and has_one, has_many say the foreign key is in the other model.

Related

Many to Many relationship :through giving 'Could not find the association' error

In my model an Item is created by a User and can be purchased by many Users, and a User can purchase many Items.
User, Item, and Purchase are defined, using AcvtiveRecord with superfluous details snipped for brevity as follows:
class User < ActiveRecord::Base
# various other fields
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
class Purchase < ActiveRecord::Base
belongs_to :item
belongs_to :user
# various other fields
end
and an rspec test also snipped as follows:
describe "user purchasing" do
it "should allow a user to purchase an item" do
a_purchase = Purchase.create!(:item => #item, # set up in `before :each`
:user => #user # set up in `before :each`
)
a_purchase.should_not eq(nil) # passes
#item.buyers.should include #user # fails
#user.purchased_items.should include #item # fails
end
end
This results in
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #item.buyers.should include #user
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model Item
Likewise if I swap around #file_item.buyers.should include #user and #user.purchased_items.should include #item I get the equivalent
1) Purchase user purchasing should allow a user to purchase an item
Failure/Error: #user.purchased_items.should include #item
ActiveRecord::HasManyThroughAssociationNotFoundError:
Could not find the association :purchases in model User
My migration looks like
create_table :users do |t|
# various fields
end
create_table :items do |t|
t.integer :creator_id # file belongs_to creator, user has_many items
# various fields
end
create_table :purchases do |t|
t.integer :user_id
t.integer :item_id
# various fields
end
What have I done wrong?
You have to specify the following.
class User < ActiveRecord::Base
has_many :purchases
has_many :items, :foreign_key => :creator_id
has_many :purchased_items, :through => :purchases, :source => :item
end
class Item < ActiveRecord::Base
# various other fields
has_many :purchases
belongs_to :creator, :class_name => 'User'
has_many :buyers, :through => :purchases, :source => :user
end
Only when you specify
has_many :purchases
the model will be able to identify the association.

ActiveRecord::HasManyThroughAssociationPolymorphicSourceError

I need a player to have many structures and the structure to belong to the player. Structure is a polymorphic relationship.
class Player < ActiveRecord::Base
has_many :player_structures
has_many :structures, :through => player_structures
end
class PlayerStructures < ActiveRecord::Base
belongs_to :structure, polymorphic: true
belongs_to :player
end
class StructureA < ActiveRecord::Base
has_one :player_structure, :as => :structure
has_one :player, :through => :player_structure
end
class StructureB < ActiveRecord::Base
has_one :player_structure, :as => :structure
has_one :player, :through => :player_structure
end
But if I pull out Player.first and ask for its structures, it gives:
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'Player#structures' on the polymorphic object 'Structure#structure'.
But it should be able to generate a SQL query where it finds all player_structures with its id, then fetches the structure based on the structure_id and structure_type. Why does this fail and how can I validly construct a polymorphic join table?
UPDATE
If I do what I want it to do manually, it works:
player_structures.collect(&:structure)
Rails, y u no do that?
I think you need to be more specific in defining your relationships in your Player model. For example:
class Player < ActiveRecord::Base
has_many :player_structures
has_many :structureas, :through => player_structures, :source => :structure, :source_type => 'StructureA'
has_many :structurebs, :through => player_structures, :source => :structure, :source_type => 'StructureB'
end
Then you can make a method that'll return all the structures defined in the relationships instead of having to access each one individually.

Comparing two instantances of the same activerecord object returns false if attr_accessible is present Ruby

Using Ruby 1.9.3 and ActiveRecord 3.2.6.
I'm having an issue when trying to compare an ActiveRecord Object that has attr_accessible :property set on it, that is contained in an Array of associated Objects using include?(object).
These are my 2 ActiveRecord models, Account and Role.
Account:
class Account < ActiveRecord::Base
# Associations
#
has_many :role_assignments, :dependent => :destroy
has_many :roles, :through => :role_assignments
end
Role:
class Role < ActiveRecord::Base
attr_accessible :title
# Associations
#
has_many :role_assignments, :dependent => :destroy
has_many :accounts, :through => :role_assignments
end
If I then create a couple of Roles (say "Admin" and "Editor") and assign the "Admin" one to an Account, I would assume this would work:
role = Role.find_by_title("Admin")
account = Account.first # => The Account we assigned the "Admin" role to
account.roles.include?(role) # => Should be true but returns false
But this actually returns false!
If I remove the 'attr_accessible :title' from the Role model and repeat the above then it does return true.
So I guess my question is... why would attr_accessible cause this particular issue? or is it a case that I have to do my check to see if role does exist in account.roles a different way?
You could try that
account.role_ids.include?(role.id)

ActiveRecord has_one and has_many relation with the same :foreign_key

I have two models, Story and Chapter. A story has_many chapters, one of those is a chapter which serves as its first chapter. I used to have a foreign key start_id in the stories table to indicate which chapter is the first. Hovewer, the the database schema had to be changed a little, now every chapter has a code. If the code is '1a', then that is first chapter of the story which owns the chapter.
The following seems to work, including #create_start:
has_many :chapters, :dependent => :destroy, :inverse_of => :story
has_one :start, :class_name => 'Chapter', :foreign_key => 'story_id', :conditions => {:code => '1a'}
This way, the foreign key start_id of the stories table is unneeded, and #start still remains an association, with all the benefits (I need #start as an association, because I use CanCan with associations for authorization).
Does my approach has any drawbacks that I currently fail to realize, or I am relatively safe with it?
Relying on the code == '1a' to find the first chapter is a bit wonky. I'd probably add a flag to the chapters table like 'first_chapter' or something that was true or false indicating whether it was the first chapter. This way the first_chapter-ness of a chapter would survive a change to the code field, but this is a bit nitpicky.
Also, to avoid repeating yourself, you could change the declaration of has_one :start to something like:
has_one :start, :through => :chapters, :conditions => { :code => '1a' }

Rails 3.1 has_many, :through => not working (joined model returns nil)

Update: This was all due to a stupid error: previously, I had defined a method with the same name as one of the methods ActiveRecord creates, which was masking the proper behaviour and breaking everything. I can't answer/close the question for a few more hours, apologies to anyone who looked into this!
I have an infuriating problem with a has_many, :through => relationship in my Rails 3.1 app.
It is infuriating because as far as I can see it is identical to two similar relationships which both work.
The owner of these relationships declares them like this:
has_many :user_skills, :dependent => :destroy
has_many :skills, :through => :user_skills
has_many :user_roles, :dependent => :destroy
has_many :roles, :through => :user_roles
has_many :conversation_users
has_many :conversations, :through => :conversation_users
(I am aware I have not followed standard nomenclature for join tables here - I only read about the convention of both-plural, names-alphabetical after setting this up, and I will refactor later)
The first two pairs of relationships (skills and roles) work just fine.
The final relationship (conversations) does not work fully. user.conversation_users returns the expected array, but user.conversations returns nil. Not an empty array, nil.
I may well be doing something stupid here, so I would be very grateful to anyone who can spot something wrong with the ConversationUser or Conversation models below.
conversation_user.rb
class ConversationUser < ActiveRecord::Base
belongs_to :user, :inverse_of => :conversation_users
belongs_to :conversation, :inverse_of => :conversation_users
validates_presence_of :user
validates_presence_of :conversation
end
conversation.rb
class Conversation < ActiveRecord::Base
has_many :messages, :dependent => :destroy
has_many :conversation_users, :dependent => :destroy
has_many :users, :through => :conversation_users
validates_presence_of :unique_id
end
(I am also aware that these are not really complex enough to justify has_many, :through => over has_and_belongs_to_many, but planned additional functionality will require join models.)
Answering to close question:
This was all due to a stupid error: previously, I had defined a method with the same name as one of the methods ActiveRecord creates, which was masking the proper behaviour and breaking everything.

Resources