Rails multiple belongs_to assignment - ruby

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.

Related

class_name foreign_key in Rails model

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.

How to query a polymorphic associated relationship to return data from multiple tables - rails4

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

activerecord - query through attributes of related record

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

ActiveRecord::Reflections Adding personal meta data to Reflection

Hi I have the following model
class Tale < ActiveRecord::Base #TODO model should be created automatically based on the structure
attr_accessor :sex, :family, :me
after_initialize do
#sex = nil
#family = []
#me = []
end
before_create do
self.joined = false
true
end
has_many :tale_culture_joins
has_many :tale_purpose_joins
has_many :tale_book_joins
has_many :tale_keyword_joins
has_many :tale_character_joins
has_many :tale_moral_joins
has_many :tale_event_joins
#TODO Grouping models with a unique single identifier that can be searched for as tag. Like these are associations for searching
#TODO need to fix so that on query only uniq results are returned ... can be added #runtime
has_many :cultures, through: :tale_culture_joins, dependent: :destroy
has_many :purposes, through: :tale_purpose_joins, dependent: :destroy
has_many :books, through: :tale_book_joins, dependent: :destroy
has_many :keywords, through: :tale_keyword_joins, dependent: :destroy
has_many :characters, through: :tale_character_joins, dependent: :destroy
has_many :morals, through: :tale_moral_joins, dependent: :destroy
has_many :values, through: :morals
has_many :events, through: :tale_event_joins, dependent: :destroy
has_many :hyperlinks
has_many :content_types, through: :hyperlinks
end
Some of the reflections are used to get and post data from a form.
As can be seen values is related to tales through morals. However I will not be storing that in a tale_value join table.
I perform data entry and search functions. want to define certain reflections for only search. How can I add a tag | meta data | key to a reflection that I can later check on
For example on the has_many :values, through: : morals I would like to add option :searchable .. this is for me then when i am building the search interface I want to query those reflections that have searchable as a tag.

Rails nested attributes in department store pattern not autosaving foreign key

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

Resources