Rails model associations - ruby-on-rails

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 :)

Related

has_many and belongs_to with join table

I have Users and Trucks. I want the ability to say #truck.drivers << #users and #user.truck = #truck.
The solution is simple until I want the relationship to be stored in a join table.
# tables
:users
:id
:truck_drivers
:user_id
:truck_id
:truck
:id
I've gotten it to where I can say #truck.drivers << #user and #user.trucks << #truck, but I would like to limit a user to occupy one truck at a time, for my sanity.
Is it possible? A has_many/belongs_to with a join table? Or should I try a different approach? I'm not using a third model for the join table. It's just a table. Here's what I have so far.
class User < ApplicationRecord
has_and_belongs_to_many :trucks,
join_table: :truck_drivers, # points to the table
class_name: :Truck # looks for the truck model in the table
end
class Truck < ApplicationRecord
belongs_to :company
has_and_belongs_to_many :drivers,
join_table: :truck_drivers,
class_name: :User
end
The reason I need a join table in the first place is because each User can have many Roles: Admin, Manager, Driver, Customer Service, etc. I thought it didn't make sense to add a truck_id to all the users if all the users are not going to be using trucks.
It seems like you ought to be able to do something like:
#user.trucks << #truck unless #user.trucks.any?
Yes this is a standard strategy with rails using the :through keyword.
rails documentation: http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
Make a model called TruckUser with truck_id and user_id
then edit your classes:
class User < ApplicationRecord
has_many :truck_users
has_many :trucks, through: :truck_users
end
class Truck < ApplicationRecord
belongs_to :company
has_many :truck_users
has_many :drivers, through: :truck_users
end
class TruckUser < ApplicationRecord
belongs_to :truck
belongs_to :user
end

rails4 activerecord associations

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

Create new parent record with has_many :through relationship for existing children

I am working on a Ruby on Rails API (version 4.0) to create and update invoices. The relationship between invoices and products is a has_many trough: relationship. Imagine I have product 1, 2, & 3. I am having trouble creating a new invoice that contains product 1 & 3.. When I run the code below I get the error:
Unknown primary key for table invoices_products in model InvoicesProduct.
This error doesn't really make sense to me since InvoicesProduct is a join table and shouldn't require a primary key.
One tricky part about the design is that it needs to track which employee added which products to the invoice, which is why invoices_product has employee_id. It does not seem to be the cause of the problem at the moment. Here is the DB design of the tables in questions:
InvoicesController
This is the code I currently have in the controller. The error message occurs on the first line of create:
def create
invoice = Invoice.new(create_invoice_params)
invoice.created_by = #current_user
# eventually need to set employee_id on each invoice_product,
# but just need to get it working first
# invoice.invoices_products.map!{|link| link.employee = #current_user }
invoice.save
respond_with invoice
end
def create_invoice_params
params.fetch(:invoice).permit(:customer_id, :status_code, :payment_method_code, product_ids: [])
end
Invoice
# /app/models/invoice.rb
class Invoice < ActiveRecord::Base
validates_presence_of :customer
validates_presence_of :created_by
belongs_to :customer, inverse_of: :invoices
belongs_to :created_by, inverse_of: :invoices, class_name: 'Employee'
has_many :invoices_products, inverse_of: :invoice
has_many :products, through: :invoices_products
end
InvoicesProduct
class InvoicesProduct < ActiveRecord::Base
validates_presence_of :invoice
validates_presence_of :product
validates_presence_of :employee
belongs_to :product, inverse_of: :invoices_products
belongs_to :invoice, inverse_of: :invoices_products
belongs_to :employee, inverse_of: :invoices_products
end
Product
# /app/models/product.rb
class Product < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 100 }
has_many :invoices_products, inverse_of: :product
has_many :invoices, through: :invoices_products
end
Request
This is what I've got in mind for a working request, the solution doesn't need to match this, but its what I've been trying
{
"invoice": {
"customer_id": "1",
"product_ids": ["1", "5", "8"]
}
}
I was able to fix the relationship by adding a primary key to the invoices_products. For some reason I was under the impression that join tables did not require a primary key for has_many :through relationships. However, looking at the example on the Rails guide site, the example join table does have a primary key.
That is because you are using has_many :through. If you don't want id (or any other additional field) in the join table, use has_many_and_belongs_to instead

How to reshare articles using association with the same table

I have a model called article with columns : id, title, content, user_id, i want to use association with the same table to say that article can be reshared by other users, so when a user reshare an article i think this is not necessary to copy the whole row of the origianl article, but just copy a reference to it , but how to do this using active record ?
remove the user_id from table articles, add a new association table call user_articles,
class User < ActiveRecord::Base
has_many :user_articles
has_many :articles, :through => :user_articles
end
class Article < ActiveRecord::Base
has_many :user_articles
has_many :user, :through => :user_articles
end
class User < ActiveRecord::Base
belongs_to :user
belongs_to :article
end

Rails ActiveRecord model association

I have a User model and a product model.
User has_many :products, :dependent => :destroy
Product belongs_to :user, :foreign_key => "user_id", touch: true
I want to create a wishlist for every user.
So i have to create a wishlist model with proper association.
But i don't know how to start.
I presume that the wishlist model contain an id, user_id and product_id field
Do i have to use has_many through association or a has_and_belongs_to_many ?
I also want that if a user is destroyed to destroy his wishlist.
What is the best way to do?
Many thanks!
As #JZ11 pointed out, you shouldn't be linking a Product directly to a User (unless a User actually 'owns' a product for some reason). However, what was missed is the model that makes up a Wishlist item:
class User < ActiveRecord::Base
has_many :wishlists # or has_one, depending on how many lists a User can have...
end
class Product < ActiveRecord::Base
has_many :wishlist_items
end
class Wishlist < ActiveRecord::Base
belongs_to :user
has_many :wishlist_items
has_many :products, :through => :wishlist_items
end
class WishlistItem < ActiveRecord::Base
belongs_to :product
belongs_to :wishlist
end
Naturally, you should be adding :dependent => :destroy where necessary.
You don't need the has_many :products relationship on User.
I don't think it makes sense for User and Product to be linked outside of a Wishlist.
class Wishlist < ActiveRecord::Base
has_many :products
belongs_to :user
end
class User < ActiveRecord::Base
has_one :wishlist, dependent: :destroy
end
class Product < ActiveRecord::Base
belongs_to :wishlist
end
To create your join table, do:
rails g migration create_products_users_table
Once you've done that, you need to add some code, below, to create the fields in the join table. Notice the :id => false, because you do not need an id in the join table:
class CreateProductsUsersTable < ActiveRecord::Migration
def change
create_table :products_users, :id => false do |t|
t.references :product
t.references :user
end
add_index :products_users, [:product_id, :user_id]
add_index :products_users, :user_id
end
end
The code above also creates some indexes and ensures that you don't have duplicates even at the database level.
Your models would then have to look like this:
class Product < ActiveRecord::Base
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
has_and_belongs_to_many :products
end
When you destroy a user correctly, like user.destroy and not just delete it (there is a difference), then the related rows in the join table will be deleted as well. This is built in to ActiveRecord.
Notice though, that doing this will not really let you use the join table. It will accept code like user.products = [product1, product2] etc, and other goodies, but no real use of a wish list.
If you do want to use a wish list, you will have to create and use the middle join table differently, using has_many :through (I didn't check PinnyM's answer but that might be the way to do it).

Resources