I recently come across this code. User has many Answer. What is the purpose of the :class_name and :foreign_key ?
class Answer < ApplicationRecord
belongs_to :user, :class_name => 'Question", :foreign_key => 'question_id'
end
The naming here is kind of strange, but the purpose of :class_name is to allow you to use a class that is different from the one Rails expects. When you have a belongs_to :user on a model, Rails would expect that to point to a parent class called User. In your example, Rails skips looking for a User class and instead looks to the Question model.
The most common usage of this, though, is when a non-default association name makes more sense than the default. So a more apt example is when you have a User model and Competition model wherein each competition has one user as a winner. You could have each Competition belong to a User, but that wouldn't be as expressive. Instead you may want to have the relationship be referred to as winner:
class User < ActiveRecord::Base
has_many :winners, class_name: "Competition", foreign_key: "competition_id"
end
class Competition < ActiveRecord::Base
belongs_to :winner, class_name: "User", foreign_key: "winner_id"
end
This allows you to refer to users as winners:
competition = Competition.first
competition.winner
This is a lot more expressive than if you were to write competition.user.
Related
I am using devise as a user model with rolify for roles
A user has one role called 'client'
A user has one role called 'agent'
I want to make a association like this
class User < ApplicationRecord
rolify
has_many :agents # if user role is client will get all agents
has_many :clients # if user role is agent will get all clients
end
How to make above associations. Please help me.
Thanks
I solved the above problem with following associations. Let me know if I am wrong
class User < ApplicationRecord
rolify
has_many :user_agents
has_many :agents, through: :user_agents # if user role is client will get all agents
has_many :user_clients, class_name: 'UserAgent', foreign_key: :agent_id
has_many :clients, source: :user, through: :user_clients # if user role is agent will get all clients
end
# field: user_id and agent_id
class UserAgent < ApplicationRecord
belongs_to :user
belongs_to :agent, class_name: 'User'
end
I have some tables that are joined through a polymorphic association...
I am trying to find a way to make a single query to return data from multiple of these tables...
My models are as follow:
#profile.rb
class Profile < ActiveRecord::Base
has_many :user_profiles, dependent: :destroy
has_many :wizards, through: :user_profiles, source: :user, source_type: "Wizard"
end
#user_profile.rb
class UserProfile < ActiveRecord::Base
belongs_to :user, polymorphic: true, dependent: :destroy
belongs_to :profile
end
#wizard.rb
class Wizard < ActiveRecord::Base
has_one :user_profile, as: :user, dependent: :destroy
has_one :profile, through: :user_profile
has_one :wizard_specialization, dependent: :destroy
has_one :career, through: :wizard_specialization
end
#career.rb
class Career < ActiveRecord::Base
has_many :wizard_specializations, dependent: :destroy
has_many :wizards, through: :wizard_specializations
end
How can I write a join( or :includes) query to return the profile of all wizards, as well as their information from the profiles table, and also include their specialization from the careers table through the wizard_specializations?
Thanks in advance.
PS: It will be great if I can exclude fields like created_at & updated_at
You can use ActiveRecord's includes method to eager-load associated data. Combine that with the select method to include or exclude just the columns that you want:
Wizard.includes(:career, :profile).select('wizards.name', 'wizards.etc', 'careers.name', 'careers.etc', 'profiles.*')
I'm not sure if Rails has support for direct AR queries with all my setup configurations...
Anyways, I finally resolved to write it with pure SQL and execute with the help of:
ActiveRecord::Base.connection.execute(query)
where query contains my pure SQL statement with the SELECTs and JOINs and WHEREs
class Book
belongs_to :library
end
class Library
belongs_to :city
has_many :books
end
class City
has_many :libraries
has_many :books, through: :library
end
I want to be able to query
Book.where("library.city.name = ?", "Alexandria")
How do I correctly do this with ActiveRecord?
Your city model already has many books through libraries, so I believe
City.find_by(name: "Alexandria").books
should do the trick
In the following "department store pattern" I have three models:
class Store
has_many :items, inverse_of: :store, autosave: true
has_many :departments, inverse_of: :store, autosave: true
accepts_nested_attributes_for :departments, allow_destroy: true
class Department
belongs_to :store, inverse_of: :departments
has_many :items, autosave: true, inverse_of: department
accepts_nested_attributes_for :items, allow_destroy: true
class Item
belongs_to :store, inverse_of: :items
belongs_to :department, inverse_of: :items
When I try the following:
store = Store.new
department = store.departments.build
item = department.items.build
store.save
Then the item does not associate with the store.
My solution to the problem was to add the following to the Item model:
class Item
before_validation :capture_store_info
def capture_store_info
self.store = self.department.store
end
I added it to the before_validation callback because in my non-trivial code I have a bunch of validations, including one that checks for the presence of the store model.
Question: My solution works, but is it the correct (ie. Rails conventional) way of solving this problem? Is there a better solution. This feels kinda dirty, and every time I have done something in Rails that felt "kinda dirty" it has come back to bite me later.
Thanks,
JB
Given
User:
class User < ActiveRecord::Base
has_many :discussions
has_many :posts
end
Discussions:
class Discussion < ActiveRecord::Base
belongs_to :user
has_many :posts
end
Posts:
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
end
I am currently initializing Posts in the controller via
#post = current_user.posts.build(params[:post])
My question is, how do I set/save/edit the #post model such that the relationship between the post and the discussion is also set?
Save and edit discussions along with post
Existing Discussion
To associate the post you're building with an existing discussion, just merge the id into the post params
#post = current_user.posts.build(
params[:post].merge(
:discussion_id => existing_discussion.id
)
You will have to have a hidden input for discussion id in the form for #post so the association gets saved.
New Discussion
If you want to build a new discussion along with every post and manage its attributes via the form, use accepts_nested_attributes
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
accepts_nested_attributes_for :discussion
end
You then have to build the discussion in the controller with build_discussion after you built the post
#post.build_discussion
And in your form, you can include nested fields for discussions
form_for #post do |f|
f.fields_for :discussion do |df|
...etc
This will create a discussion along with the post. For more on nested attributes, watch this excellent railscast
Better Relations
Furthermore, you can use the :through option of the has_many association for a more consistent relational setup:
class User < ActiveRecord::Base
has_many :posts
has_many :discussions, :through => :posts, :source => :discussion
end
class Discussion < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :discussion
end
Like this, the relation of the user to the discussion is maintained only in the Post model, and not in two places.