rails4 activerecord associations - ruby-on-rails

I had this setup in my app:
User has_many :products
User has_many :product_leads, through: :products
Product belongs_to :user # (in products table there is user_id)
ProductLead belongs_to :product # (in product_leads table there is no user_id)
ProductLead belongs_to :user
With this setup user.product_leads is working but product_lead.user is not. So I deleted the last line.
I don't have user_id in the product_leads table, but I don't even want to. ProductLead form is filled as nested attribute together with Product form.
Is there a better way to define product_lead.user than below? Or should I rather have user_id in the product_leads table?
product_lead.rb
def user
product.user
end

I believe the has_one :through association would help you.
def ProductLead < ActiveRecord::Base
has_one :product
has_one :user, through: :product
# ... other code
end

I don't see any problem with the approach you've proposed as long as it's read-only you don't need to assign a user to a product.
You should, however, guard against calling user on nil in the case product doesn't exist:
def user
product.try(:user) # Only call `user` if product is non-nil.
end

Related

Rails model associations

I have a User model having comment id as primary key. Now I have another model note. I want to add note_id in user model. I have changed my associations
Previous associations:
# user.rb
belongs_to :comment
# comment.rb
has_many :users, -> { order('users.display_date DESC') },
dependent: :destroy, inverse_of: :comment
Current associations:
# user.rb
belongs_to :note, optional: true
# note.rb
has_many :users, -> { order('users.display_date DESC') },
dependent: :destroy
I have added records in user model and inserting note_id. But fetching records in console showing nil value.
User.last.note
=> nil
Could someone help me where I am missing in associations?
As pointed out in comment section,
something doesn't "feel right" in your association model.
You can "feel it" just by reading out loud user belongs to comment instead of User has many Comments or Comment belongs to User.
I am not sure if you are adding the notes model to your existing system or you switching from comments to notes, but please mind something doesn't seem okay!
Let's fix the "User-Comment" relation problem first
# `users` table has no reference to comments
class User < ApplicationRecord
has_many :comments, dependent: :destroy
end
# `comments` table should have a `user_id` column
class Comment < ApplicationRecord
belongs_to :user
end
Let's fix the "User-Note" relation problem
If you want just one note per user, I'd recommend:
# `users` table has no reference to notes
class User < ApplicationRecord
has_one :note
end
# `notes` table should have a `user_id` column
class Note < ApplicationRecord
belongs_to :user
end
Please let me know if it worked :)

How to create a record when it depends on another record

I have 2 models, each of them depend on another.
class Company < ActiveRecord
belongs_to :user
end
class User < ActiveRecord
belongs_to :company
end
The company has a required user_id column.
The user has a required company_id column.
How can I create a company and user when they both depend on each other?
In your scenarion this relationship is called many to many relationship, You can create middle table between this called users_companies with foreign key of both table
For more details please refer this link Associations
Like example
class Company < ActiveRecord
has_many :users_companies, dependent: :destroy
has_many :users, through: users_companies
end
class User < ActiveRecord
has_many :users_companies, dependent: :destroy
has_many :companies, through: users_companies
end
class UsersCompany < ActiveRecord
belongs_to :user
belongs_to :company
# This model have two foreign key
user_id and company_id
end
Use a dummy value to fake one of the object is valid
company=Company.create(user_id: dummy)
user=User.new
user.company = company
user.save
company.update_attribute(user_id: user.id)

Rails 4 - set conditional default value in join table on create action

I have a data model with three models, Team has many Users through Members (join table). My Member model have a admin boolean column. When a user creates a new team (signs up), I want to set the admin field for that User to true. I'm not sure what's the best way to do this. I have tried the following:
# Team.rb (model for team)
[...]
after_save :set_admin
private
def set_admin
self.members.first.admin = true
end
This results in a nil pointer error (self.members.first == nil).
So, what's the correct and Rails way to do what I want? (When a user creates a new team make that user an admin for that team as well.)
Update
My model associations looks like this:
# Team.rb
has_many :members
has_many :users, through: :members
# Member.rb
belongs_to :user
belongs_to :team
# User.rb
has_many :members
has_many :teams, through: :members
Use after_add instead
has_many :members, after_add: Proc.new { |u, m| m.first.admin = true; m.save }

How to access the tables for has_many through association

Say I have following association.
class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, through: :account
end
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
end
class AccountHistory < ActiveRecord::Base
belongs_to :account
end
How can I access account table through supplier model
How can I access supplier table through account model
How can I access join table accounthistory without through other model
How can I add a field by migration to accounthistory and then get value from that field through supplier or account model
Surely this is too simplistic, but this what I'd propose:
1. #supplier.account
#supplier = Supplier.find(id)
#supplier.account #-> brings account data
2. #account.supplier
#account = Account.find(id)
#account.supplier #-> brings supplier data
3. #supplier.account_history
#supplier = Supplier.find(id)
#supplier.account_history #-> brings account_history
4. Migration
def change
add_column :account_histories, :your_column, :type, after: :column
end
You can deal with the join model / table directly (unlike HABTM's), meaning you will be able to create migrations as you wish. The above migration code shows how you can add a column to the table directly
Delegate
To access the data from that model, you're in luck
Because you're using a singular association (has_one / belongs_to), you should be able to delegate the call to another model:
#app/models/supplier.rb
Class Supplier < ActiveRecord::Base
has_one :account
has_one :account_history, through: :account
end
#app/models/account.rb
class Account < ActiveRecord::Base
belongs_to :supplier
has_one :account_history
delegate :extra, :vars, :here, to: :account_history
end
This will allow you to call: #supplier.account.extra etc
Hope this helps? Your question was rather scant on context, so I can fix my answer if you update!

Can't update rails has_many :through relationship

I try:
#item.associations.update_attributes(:tag_id=>params[:tag])
and
#item.associations.tag_id=params[:tag]
Both give me undefined method errors for update_attributes and tag_id=, respectively. Here's my setup:
class Item < ActiveRecord::Base
has_many :associations,:foreign_key=>"item_id",:dependent=>:destroy
has_many :reverse_associations,:foreign_key=>"tag_id",:class_name=>"Association"
has_many :tags,:through=>:associations
end
class Tag < ActiveRecord::Base
has_many :associations,:foreign_key=>"tag_id",:dependent=>:destroy
has_many :reverse_associations,:foreign_key=>"item_id",:class_name=>"Association"
has_many :items,:through=>:associations
attr_accessible :name
end
class Association < ActiveRecord::Base
belongs_to :item
belongs_to :tag
end
What am I doing wrong?
You're trying to update tag_id on the entire #item.associations collection instead of updating a single Assocation instance.
The proper way to solve this depends on what you're trying to accomplish. To update the tag_id for all associations in #item.association, try:
#item.associations.each do |association|
association.update_attributes(:tag_id => params[:tag])
end
If you want to update the tag id for a specific Association, then you somehow need to get that association first:
# Just picking the first association for the item as an example.
# You should make sure to retrieve the association that you actually
# want to update.
retagged_association = #item.associations.first
# Now, retag the association
retagged_association.update_attributes(:tag_id => params[:tag])

Resources