ActiveRecord Multi-association query - mysql

So I've got three models:
User
User Interest
Interest Tags
User Model
class User < ActiveRecord::Base
has_many :user_interests
end
InterestTag Model
class InterestTag < ActiveRecord::Base
has_many :user_interests, :dependent => :destroy
validates :name, :uniqueness => true
end
UserInterest Model
class UserInterest < ActiveRecord::Base
belongs_to :interest_tag
belongs_to :user
end
I'd like to use ActiveRecord to include the name of the user's interests when loading their profile using the following query:
#user = User.find(current_user.id, :include => [{:user_interests => :interest_tags}])
Migrations for interest_tags + user_interests
create_table :interest_tags do |t|
t.string :name, :null => false, :size => 30
t.timestamp :created_at
end
create_table :user_interests do |t|
t.integer :user_id
t.integer :interest_tag_id
end
What am I doing wrong?

You have to add an has_many :through association on User model.
class User < ActiveRecord::Base
has_many :user_interests
has_many :interest_tags, :through => :user_interests
end
class UserInterest < ActiveRecord::Base
belongs_to :interest_tag
belongs_to :user
end
class InterestTag < ActiveRecord::Base
has_many :user_interests, :dependent => :destroy
validates :name, :uniqueness => true
end
Now you can eager load the tags as follows:
User.find(current_user.id, :include => :interest_tags)
Note:
You might want to look at the acts_as_taggable_on gem for your requirement.

I assume you are building a tagging system, like in stackoverflow: every user kann have multiple tags that they are interested in. In that case the user_interests table is only a join table and does not need a model. Just use has_and_belong_to_many on the two real models.
See also this article on different ways to implement tagging with more or less normalized relational databases. You could also use a non-relational database like Mongodb, there you would need only one table to do tagging, see the cookbook.

Related

Struggling to add has_many through relationship

I am trying to make a has_many through relationship like this:
#user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :availabilities
has_many :timeslots, :through => availabilities
end
#availability.rb
class Availability < ApplicationRecord
belongs_to :timeslot
belongs_to :user
end
#timeslot.rb
class Timeslot < ApplicationRecord
has_many :availabilities
has_many :timeslots, :through => availabilities
end
I created the two models and than ran rake db:migrate without adding the code in the models (to create the tables).
I made a migration file:
class AddFieldsToTables < ActiveRecord::Migration[5.0]
def change
add_column :users, :availability_id, :integer
add_column :timeslots, :availability_id, :integer
add_column :availabilities, :user_id, :integer
add_column :availabilities, :timeslot_id, :integer
end
end
and ran rake db:migrate
Than I added the code above to all the files.
And then if I try to generate anything it gives me NameError: undefined local variable or method availabilities for User (call 'User.connection' to establish a connection):Class
I am new to Ruby on Rails.
One issue I see is that in your timeslot.rb you have has_many :timeslots, :through => availabilities. I'm guessing you want has_many :users, :through => :availabilites.
Another is in user.rb, you have has_many :timeslots, :through => availabilities but you need the symbol :availabilites. This is what is causing the error you posted, I believe. It should look like this (all I've changed is the second-to-last line):
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :availabilities
has_many :timeslots, :through => :availabilities
end
I see a tiny problem in your code:
#timeslot.rb
class Timeslot < ApplicationRecord
has_many :availabilities
has_many :timeslots, :through => availabilities
end
it should be:
#timeslot.rb
class Timeslot < ApplicationRecord
has_many :availabilities
has_many :users, :through => availabilities
end
I'm not sure if it can solve your problem but your code (exclude the above mistake) sounds fine for me.
In order to setup has_many through relationship between two tables users and timeslots, you need to setup join table availabilities with columns user_id and timeslot_id.
Setup your rails models like below:
# models/user.rb
class User < ApplicationRecord
has_many :availabilities
has_many :timeslots, :through => :availabilities
end
# models/availability.rb
# projects table should have these columns - user_id:integer, timeslot_id:integer
class Availability < ApplicationRecord
belongs_to :timeslot
belongs_to :user
end
# models/timeslot.rb
class Timeslot < ApplicationRecord
has_many :availabilities
has_many :users, :through => :availabilities
end
You need a migration to create availabilities table which acts as a join table for your has_many through relationship between Timeslot Object and User Object. Migration file looks something like this:
class CreateAvailabilities < ActiveRecord::Migration[5.0]
def change
create_table :availabilities do |t|
t.integer :user_id
t.integer :timeslot_id
end
end
end
Access
User.last.timeslots gives 0, 1 or many timeslots associated with User.last
Timeslot.last.users gives 0, 1 or many users associated with Timeslot.last

Join table in rails; many-to-many; trying to call join method on instance

I just did this a few hours ago and now I can't getting it working on a second attempt. There are many Users and many EventGoals. Users have many EventGoals and EventGoals have many Users, both through the join table called EventGoalClaims. The #event_goal is getting passed properly but I get a `
undefined method `event_goal_claims' for #<EventGoal:0x007fd11b0ce178>`
Here's the view (the first line is hitting an error):
<%= form_for([#event_goal, #event_goal.event_goal_claims.build]) do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.hidden_field :user_last_name, :value => current_user.last_name %>
Controller
def create
#event_goal = EventGoal.find(params[:event_goal_id])
#event_goal_claim = #event_goal.event_goal_claims.build(eventgoalclaim_params)
#event_goal_claim.event_goal_id = #event_goal.id
#event_goal_claim.user_id = current_user.id
#event_goal_claim.user_last_name = current_user.last_name
...
private
def eventgoalclaim_params
params.require(:event_goal_claim).permit(:event_goal_id, :user_id, :user_last_name, :approved)
end
EDIT
class EventGoal < ActiveRecord::Base
belongs_to :event
has_many :users, through: :event_goal_claims
end
The answer likely lies in the associations of one of the three respective models. Make sure they look like this:
user.rb
Class User < ActiveRecord::Base
has_many :event_goal_claims
has_many :event_goals, through: :event_goal_claims
event_goal.rb
Class EventGoal < ActiveRecord::Base
has_many :event_goal_claims
has_many :users, through: :event_goal_claims
event_goal_claim.rb
Class EventGoalClaim < ActiveRecord::Base
belongs_to :user
belongs_to :event_goal
Also worth noting: This is all assuming your db has the foreign keys on the event_goal_claims table in your schema.rb labeled as user_id and event_goal_id. If they are different than that, you will need to explicitly state the name of the foreign key in the model's associations.

rails_admin has_many through with extra field

I have 2 models. User and Project. And there is a many_to_many relation with and extra position field between them. I can't see that extra field on rails_admin edit page.
How can add that field to form?
user.rb
has_many :projects, :through => :works_ons
project.rb
has_many :users, :through => :works_ons
works_on.rb
attr_accessible :position, :project_id, :user_id
belongs_to :user
belongs_to :project
Is it true that your user model has_many :users, :through => :works_ons?
I'm wondering if you need
user.rb
has_many :projects, :through => :works_ons
This is the closest I found. You should probably add a custom field below the association and enter the extra fields.
https://github.com/sferik/rails_admin/wiki/Has-many-%3Athrough-association

Associate foreign key through another foreign key

I've these 3 Models
class User < ActiveRecord::Base
has_many :answers, :as => :owner
end
class Answer < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
has_one :test
end
class Test < ActiveRecord::Base
belongs_to :answer
end
so I want to associate the Test model with User model through Answer Model without need of creating new association between them, so I put the following into Test Model:
has_one :owner, :through => :answer
but it doesn't work and I got this error
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'Test#owner' on the polymorphic object 'Owner#owner'.
any help?
In Test:
delegate :owner, :to => :answer
You have to specify source_type option as owner is a polymorphic association
class Test < ActiveRecord::Base
belongs_to :answer
has_one :owner, :through => :answer, :source_type => "User"
end

Direct association for a Group with a User

I have Group model that has_many institutions, and schools.
class Group
has_many :institutions
has_many :schools
end
However I need my Group to have_and_belongs_to_many :users but I want all the users that are associated through the institutions and schools like this:
class Group
has_many :institutions
has_many :users, :through => :instiutions
has_many :schools
has_many :users, :through => :schools
end
class School
belongs_to :group
has_many :educations
has_many :users, :through => :educations
end
class Institution
belongs_to :group
has_many :institutional_educations
has_many :users, :through => :institutional_educations
end
Obviously this isn't possible so what should I do?
Just offhand, have you considered using single table inheritance, so that school is a type of institution?
Your School class would inherit from Institution ("class School < Institution"). Plus, I guess you'd call it something else but you could have "class GenericInstitution < Institution" as well. Then your Group class could look like this:
class Group
:has_many :school_users, :through => :schools,
:has_many :generic_institution_users, :through => :generic_institutions
# if you need all of the users at once:
:has_many :users, :through => :institutions
end
You'd probably have to specify a foreign key or two to get this to work for you.
Also, I can't quite figure see what an :institutional_education is, but if you really need it you could do the same thing there (maybe "class institutional_education < education", or maybe the other way around.