I'm trying to play around with association tables as well as single table inheritance ('STI'). I have a Man, Woman, and a Relationship model. The Relationship model has a type column, so that I can use STI. I also created a Friend model that inherits from Relationship, as Friend will be one type of relationship.
Man.rb
attr_accessible :name
has_many :relationships
has_many :women, :through => :relationships
Woman.rb
attr_accessible :name
has_many :relationships
has_many :men, :through => :relationships
In the meetup model, I also wish to keep track of when and where the date took place.
Relationship.rb
attr_accessible :type
belongs_to :woman
belongs_to :man
Friend.rb
class Friend < Relationship
end
However, when I try to create a relationship with type friend, I get the warning message that the subclass doesn't exist. In the console, I'm doing this
sarah = Woman.create!(name: 'Sarah')
jim = Man.create!(name: 'Jim')
jim.relationships.build(type: 'Friend', woman_id: 1)
=> #<Relationship id: nil, type: "Friend", man_id: 1, woman_id: 1, created_at: nil, updated_at: nil>
jim.save!
but then when I try to pull up the relationships for jim, I get
>> jim.relationships
Relationship Load (0.3ms) SELECT "relationships".* FROM "relationships" WHERE "relationships"."man_id" = 1
ActiveRecord::SubclassNotFound: The single-table inheritance mechanism failed to locate the subclass: 'friends'. This error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this column if you didn't intend it to be used for storing the inheritance class or overwrite Relationship.inheritance_column to use another column for that information.
The same is true when I try to create the relationship with 'friends' instead of 'Friend'
jim.relationships.build(type: 'friends', woman_id: 1)
can you explain what I'm doing wrong?
I didn't create a table in the db for friends. I thought everything could be stored in the Relationship model. Since this is just a play-around app, I only assigned the type attribute for the Relationship model.
i think the type value needs to be be lower case and singular - friend ?
but really you probably want to use the objects and let rails handle that for you
sarah = Woman.create!(name: 'Sarah')
jim = Man.create!(name: 'Jim')
Friend.create!(women: sarah, man: jim)
the Man Women models don't make sense to me - they are both a Person and a relationship should just have 2 people, regardless of gender
Related
I have two models and the association between them is has_and_belongs_to_many.
I need to refactor the code as it causes n + 1 queries.
Problem: n+1 queries
1. Task
class Task < ActiveRecord::Base
has_and_belongs_to_many :user_groups
end
2. UserGroup
class UserGroup < ActiveRecord::Base
has_and_belongs_to_many :tasks
end
Requirement:
Assign Tasks to user_groups
Previous Controller Code:
task_ids.each do |task_id|
find(task_id).update_attributes! user_group_ids: params[:user_group_ids], release_date: params[:release_date]
end
My Attempt:
tasks = Task.where(id: task_ids)
tasks.update_all(user_group_ids: params[:user_group_ids], release_date: params[:release_date])
Error:
ActiveRecord::StatementInvalid (PG::UndefinedColumn: ERROR: column
"user_group_ids" of relation "tasks" does not exist
Query generated:
SQL (0.6ms) UPDATE "tasks" SET "user_group_ids" = 4,
"release_date" = '2017-04-27 07:40:26.525357' WHERE "tasks"."deleted_at" IS NULL AND "tasks"."id" = 47394
Please let me know how to do it with update_all
Why did you have to create the UserGroup model? That's not the way we use a habtm relationship.
And your model Task, in the middle of it all, kind of confused me. What do you need to do? I suspect that you don't have a habtm relationship in Task.
For the error you're getting, it's totally understandable. You do not have an attribute (column) called user_group_ids in the tasks table. And also, for a habtm relationship, this attribute user_group_ids seems misspelled to me.
Anyway, for a habtm relationship, if your User has many Groups and a Group can have many Users, you're in the right path, however for Ruby on Rails there's two ways you can accomplish that.
If you don't need to manage other data (relationship attributes) in the relationship table:
Then you'll need only the table groups_users, in your case. Rails will look for habtm relationship table in alphabetic order.
If your groups_users relationship holds any other data than model's reference ids, then it's better for you to use a has_many :through relationship.
In this case your UserGroup (mind the singularity) table will hold the additional data you need in the relationship. Then you declare as follows:
User model will have declared: has_many :groups, through: :user_groups
Group model will have declared: has_many :users, through: :user_groups
UserGroup model: belongs_to :user and belongs_to :group. Be careful not to index any of those as unique, database or application level.
For more details check http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
Hope you get your results!
Your solution (without causing an N+1) is activerecord-import
You can't mass-assign the association using update_all. update_all does a single UPDATE query but for association you actually need an INSERT query to insert the associated models. You could use a gem like bulk_insert to insert all the join objects with one INSERT query https://github.com/jamis/bulk_insert
I'm attempting to use global ids to specify polymorphic associations. However, when I want to reference the object through the association, no object is found.
Consider the following models:
class Person < ActiveRecord::Base
has_many :optionees, as: :entity
belongs_to :company
end
and
class Optionee < ActiveRecord::Base
belongs_to :entity, polymorphic: true
belongs_to :option
end
If I seed the above as follows:
Person.create(fname: "John", lname:"Smith", email:"john.smith#email.com", telephone:"555-555-5555", street:"333 Street St", city:"Salt Lake City", state:"UT", zip:"99999", company_id:"1")
(Giving the Person an ID of 1)
and
Optionee.create total_shares: "332", exercise_price: "33.22" option_id: "1" entity_id: "gid://legal/Person/1"
(Giving the Optionee an ID of 1)
I am unable to look up the person with the following command:
Optionee.find(1).entity
Instead it gives me a nill result.
I realize that the traditional way to do polymorphic associations is to provide the model name AND id; is there somthing I'm missing to do this with global ids? Many thanks in advance.
You need to add entity_type filed in Optionee table as well. And if person id is 1, then entity_id is equals to 1:-
Optionee.create total_shares: "332", exercise_price: "33.22" option_id: "1" entity_id: "1", entity_type: "Person"
Or you can create record for person in optionee table by doing this:-
#person = Person.find(params[:id])
#person.optionees.create(params)
It will set entity_id and entity_type for person. By doing this, no need to set entity_id and entity_type explicitly.
TL;DR: How do I use the ID of the respective parent object in a has_many SQL clause to find child objects?
Long version:
I have the following example code:
class Person < AR::Base
has_many :purchases, -> {
"SELECT * from purchases
WHERE purchase.seller_id = #{id}
OR purchase.buyer_id = #{id}"
}
This was migrated from Rails 3 which worked and looked like
has_many :purchases, :finder_sql => proc { #same SQL as above# }
I want to find all purchases associated with a Person object in one association, no matter whether the person was the one selling the object or buying it.
Update: I corrected the SQL, it was inside out. Sorry! Also: The association only needs to be read-only: I am never going to create records using this association, so using id twice should be OK. But I do want to be able to chain other scopes on it, e.g. #person.purchases.paid.last_5, so creating the sum of two associations in a method does not work (at least it didn't in Rails 3) since it doesn't return an AM::Relation but a simple Array.
When using this above definition in Rails 4.2, I get
> Person.first.purchases
undefined method `id' for #<Person::ActiveRecord_Relation:0x...>
The error is clear, but then how do I solve the problem?
Since this is only an example for much more complicated SQL code being used to express has_many relationships (e.g. multiple JOINS with subselects for performance), the question is:
How do I use the ID of the parent object in a has_many SQL clause?
I don't think your code will work at all. You are defining an association with two foreign keys ... that'd mean that in case you want to create a new Person from a present Purchase, what foreign key is to be used, seller_id or buyer_id? That just don't make sense.
In any case, the error you are getting is clear: you are calling a variable id which is not initialized in the block context of the SQL code.
A better approach to the problem I understand from your question would be to use associations in the following way, and then define a method that gives you all the persons, both buyers and sellers that a product has. Something like this:
class Purchase < ActiveRecord::Base
belongs_to :buyer, class_name: 'Person'
belongs_to :seller, class_name: 'Person'
def persons
ids = (buyer_ids + seller_ids).uniq
Person.where(ids: id)
end
end
class Person < ActiveRecord::Base
has_many :sold_purchases, class_name: 'Purchase', foreign_key: 'buyer_id'
has_many :buyed_purchases, class_name: 'Purchase', foreign_key: 'seller_id'
end
Im my approach, buyer_id and seller_id are purchase's attributes, not person's.
I may have not understood correctly, in that case please clarify.
I'm having an issue with several many-to-many relations in my Rails project. It can be illustrated with an example:
Say I have the models Person and PhoneNumber, joined by PersonPhoneNumber. The relation is many-to-many because people can have more than one phone number, and more than one person can be reached at the same phone number (in a case such as a help desk).
class Person < ActiveRecord::Base
has_many :person_phone_numbers
has_many :phone_numbers, :through => :person_phone_numbers
end
class PhoneNumber < ActiveRecord::Base
has_many :person_phone_numbers
has_many :people, :through => :person_phone_numbers
validates :number, :uniqueness => true
end
class PersonPhoneNumber < ActiveRecord::Base
belongs_to :person
belongs_to :phone_number
end
I have a person form that lets me create/update people's contact information. I use it to assign the number 555-555-1212 to Bob. If a PhoneNumber object with that number doesn't exist, I want it to be created (as in the standard accepts_nested_attributes_for behavior). But if it does exist, I want to just create a PersonPhoneNumber object to associate Bob with that PhoneNumber.
How can I accomplish this most elegantly? I tried putting a before_validation hook in PersonPhoneNumber to look for a matching PhoneNumber and set phone_number_id, but this caused really bizarre behavior (including making my Rails server crash with the message Illegal instruction: 4).
You can use exists? method to check for existence first, like this:
#person.phone_numbers.build(number: "555-555-1212") unless #person.phone_numbers.exists(number: "555-555-1212")
Or you can do something like this:
PhoneNumber.find_or_create(person_id: #person.id, number: "555-555-1212")
Rachel the Rails documentation says this:
A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening model.
What is the difference?
There are many tutorials that show you how to create the model instructions for a has_many :through relationship in Rails, but there doesn't seem to be many articles related to the process of setting up forms to create and edit these relationships. I am seeking some assistance (or good examples) of how to create an interface that will allow users to manage these types of relationships in a Rails app.
Here's the scenario:
I have Users, Relationships, and Athletes. A User can have a
Relationship with an Athlete in a variety of roles: Coach, Mentor,
Parent, or Fan.
Here are my models:
class User < ActiveRecord::Base
has_many :relationships
has_many :athletes, :through => :relationships
end
class Athlete < ActiveRecord :: Base
has_many :relationships
has_many :users, :through => :relationships
end
class Relationship < ActiveRecord :: Base
belongs_to :users
belongs_to :athletes
end
So, the next step is to build the views and controllers that allows me to create a User-to-Athlete relationship (with a coach, parent, etc role), edit the relationship, or destroy the relationship.
Ultimately, my aim is to have a scenario where Users can create Athletes and choose the associated Relationship.
Unfortunately, I can't find any specific tutorials or references that gives me much more than the model instructions or the example for a has_many relationship.
If anyone has a link or example that can solve this problem at a simple level, I should be able to customize the rest.
The relationship you have here between your User and Athlete model is essentially a has_and_belongs_to_many relationship (HABTM). By going back and forth with you it seems you're confused about what the best way to create these relationships would be.
A good place to start reading would be in the documentation for ActiveRecord's Associations, specifically the documentation for HABTM relationships.
You're model setup is fine. Now that you have your HABTM relationship setup, here's what you can do. Let's assume both your Athlete and User model are very simple and have nothing but a name attribute, which is a string. You can now do code like this (this is console output from the rails console):
User.create(:name => "Jeff")
usr = User.first
=> #<User id: 1, name: "Jeff">
usr.athletes
=> []
atl = usr.athletes.create(:name => "Mike")
=> #<Athlete id: 1, name: "Mike">
The line above will create a user with name Mike, and automatically create a relationship entry with the appropriate attributes to link the two. So now if you call this:
usr.athletes
=> [#<Athlete id: 1, name: "Mike">]
Now if you wanted to allow the user to dictate what the relationship is between themselves and an Athlete upon Athlete creation, you could setup your Relationship class to have a relation field of type string, and when creating the relationships (as I just displayed above), you can then do something like this:
rel = usr.relationships.where(:user_id => usr.id, :athlete_id => atl.id).first
=> #<Relationship id: 1, user_id: 1, athlete_id: 2, :relation => nil>
rel.relation = "Friend"
rel.save
=> #<Relationship id: 1, user_id: 1, athlete_id: 2, :relation => "Friend">
Hopefully this is more helpful than my original answer. Let me know if you have any questions. And definitely be sure to look at the ActiveRecord Associations documentation I mentioned above.
Try railscasts or ascii casts. That's where I usually start. Not sure if this is what you're after but there's a tutorial on those sites for nested forms. I think it's under complex forms. Would e worth reading / watching anyway.