Following implementation - ruby-on-rails

I have a user model and a store model. I would like to setup the model associations in order for a user to be able follow a store. Which means that the user will be the follower and the store will be the followed.
I created a follow model for this purpose and added associations to the other models as well:
class CreateFollows < ActiveRecord::Migration[6.0]
def change
create_table :follows do |t|
t.integer :store_id
t.integer :user_id
t.string :uid
t.timestamps
end
end
end
class Follow < ApplicationRecord
belongs_to :store
belongs_to :user
end
class User < ApplicationRecord
has_many :follows
end
It's working, but I'm pretty sure that this is not the correct setup for this. I also found this tutorial but I cant figure out how to use it in my case. Any ideas on how to setup the model associations correctly? Thanks in advance!

You've basically got it.
What you're missing is letting a User see the Stores they follow, and vice versa. Do that with has_many :through.
class User < ApplicationRecord
has_many :follows
has_many :stores, through: :follows
end
class Store < ApplicationRecord
has_many :follows
has_many :users, through: :follows
end
Now user.stores will fetch the Stores that User follows, and store.users will fetch which Users follow that Store. It will handle the joins for you and cache the results.

Related

Model that belongs to one of two parents

I'm trying to decide on the best relationship structure for three models I have in a Rails app.
The models are candidate, an employer, and a video.
Sometimes a video will belong to an employer, other times a candidate. Because of this I am loathe to put foreign keys for both employer and candidate on a video, because it feels wrong that one will always be nil.
The problem with the code below is that the video will not have many candidates, but I can't do a through relationship with belongs_to. What is a better way?
class Video < ActiveRecord::Base
has_many :video_candidates
has_many :candidates, :through => :video_candidates
end
class Candidate < ActiveRecord::Base
has_many :video_candidates
has_many :videos, :through => :video_candidates
end
class VideoCandidate < ActiveRecord::Base
belongs_to :candidate
belongs_to :vieo
end
Edit: Polymorphic association is the way to go. I'm going to put my solution below. Hope this helps someone.
class Video < ActiveRecord
belongs_to :watchable, polymorphic: true
end
class Candidate < ActiveRecord::Base
has_many :videos, as: :watchable
end
class Employer < ActiveRecord::Base
has_many :videos, as: :watchable
end
And the migration
class CreateVideos < ActiveRecord::Migration[5.0]
def change
create_table :videos do |t|
t.string :title
t.belongs_to :watchable, polymorphic: true
t.timestamps
end
add_index :videos, [:watchable_id, :watchable_type]
end
end
This is an ideal case to go for polymorphic association
Also refer Railscast episode

Has and belongs to many - check additional field

I have an User and Employer models. An User can have multiple Employers and vise versa and there is also a flag indicating if their relationship is active:
class User < ActiveRecord::Base
has_and_belongs_to_many :employers
end
class Employer < ActiveRecord::Base
has_and_belongs_to_many :users
end
and a migration:
class CreateUserEmployers < ActiveRecord::Migration
def change
create_table :users_companies do |t|
t.integer :user_id
t.integer :employer_id
t.boolean :is_active
end
end
end
If I have an User and one of their employers
test1 = User.find(1).employers.first
how do I check if an User's relationship with that Employer is active (the field is_active in users_employers table)?
Per the Rails Guides:
A has_and_belongs_to_many association creates a direct many-to-many connection with another model, with no intervening model.
If you want to add the is_active boolean field (or any other attributes), I suggest you use the has_many :through association. You would need to create a third model (i.e. UserEmployer, EmployerUser, or something else altogether) and your associations would be:
class User < ActiveRecord::Base
has_many :employers, through: :user_employers
has_many :user_employers
end
class Employer < ActiveRecord::Base
has_many :users, through: :user_employers
has_many :user_employers
end
class UserEmployer < ActiveRecord::Base
belongs_to :user
belongs_to :employer
end

How to implement playlist concept in Ruby on Rails

In my Ruby on Rails project I have a User model and a Content model.
A User has_many :contents and a Content belongs_to :user.
Now, I want to create the idea of playlist. There will be more than one playlists, and each one will have some contents in some order. At this moment, it doesn't matter if a user owns a playlist or not, they'll be general.
A playlist doesn't have any kind of association with a User. They will be general, owned by the system.
I think the solution will be something like having a model Playlist and another table with these attributes: playlist_id:integer content_id:integer order:integer. But do I really need to create all the MVC parts to this new relationship?
As I looked into Rails associations, I got confused and I don't know how to do this, if using the through property, using has_and_belongs_to_many in both Content and Playlist or even how to create this new relationship.
I'd be glad if someone could help me, as you can see, I'm a bit confused.
The solution for you is use has_many through
class User < ActiveRecord::Base
... user code in here with no association
end
class Playlist < ActiveRecord::Base
has_many :content_playlists
has_many :contents, through: :content_playlists
end
class Content < ActiveRecord::Base
has_many :content_playlists
has_many :playlists, through: :content_playlists
end
class ContentPlaylist < ActiveRecord::Base
belongs_to :content
belongs_to :playlist
end
The migration:
class CreateAll < ActiveRecord::Migration
def change
create_table :contents do |t|
t.string :name
t.timestamps
end
create_table :playlists do |t|
t.string :name
t.timestamps
end
create_table :content_playlists do |t|
t.belongs_to :content
t.belongs_to :playlist
t.integer :order
t.timestamps
end
add_index(:content_playlists, :content_id)
add_index(:content_playlists, :playlist_id)
end
end
Now, you can assign a order integer on content_playlists, and in the future you can reorder your playlist changing the value on contents_playlists.
To add a new content_playlist:
c = Content.create(name: "Song 2")
p = Playlist.create(name: "My Playlists2)
ContentPlaylist.create(content: c, playlist: p, order: 1)
Reference:
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
You can see (fork, clone, do whatever you want) here:
https://github.com/bdfantini/hmt_example
I'm guessing something like this is what you want:
class User < ActiveRecord::Base
...
has_many :contents
has_many :playlists
has_many :playlisted_contents, :through => :playlists
...
end
class Playlist < ActiveRecord::Base
...
has_many :contents
...
end
class Content < ActiveRecord::Base
...
belongs_to :user
belongs_to :playlist
...
end
I'd start there, and write some tests to see if it's behaving as you want. If your design has other constraints, we might need to adjust this some.

A Ruby on Rails has_many relationship.

I'm new to RoR and working on my first project. The basic concept behind the idea is to connect "Users" that have chosen a set a "Skills" with other Users that that have submitted a "Help Request" that deals specifically with those chosen skills. An app that connects Skilled Users with Users that need help if you will. My question has to do with the relationship between the Users, Skills, and Help_Request Models. It feels like a "has_many :through association" or maybe "polymorphic association" might be in order for this kind of three way relationship? Really not sure?
Any thoughts or suggestions would be greatly appreciated.
A polymorphic association is when a model should belong to another model. Let's say when you a comment model. You can comment on a post and a comment itself. That's when you would use a polymorphic.
In your case a simple has_many through would do.
It should look like this
class User < ActiveRecord::Base
has_many :skills
has_many :help_requests, through: :skills
end
class Skill < ActiveRecord::Base
belongs_to :user
belongs_to :helpRequest
end
class HelpRequest < ActiveRecord::Base
has_many :skills
has_many :users, through :skills
end
For more information the docs
I'm afraid ShivamD's answer will not suffice. That would require duplicate Skills, and queries the relevant User.help_requests via relationship, where instead you need an intersection.
Not just that, Users who create HelpRequests will already have a has_many relationship. That makes sense.
I won't include schema for the HABTM I'm about to suggest (see here), but I will cover the models. Essentially what you want:
skill = Skill.create(name: "My Skill")
user.skills << skill
help_request.skills << skill
user.matched_help_requests
#> [help_request]
Which can be achieved as follows:
class User
has_and_belongs_to_many :skills
def matched_help_requests
HelpRequest.joins(:skills).where("skills.id IN(?)", skills.pluck(:id))
end
end
class HelpRequest
has_and_belongs_to_many :skills
end
class Skill
has_and_belongs_to_many :users
has_and_belongs_to_many :help_requests
end
EDIT: here's the schmea for the HABTM:
rails g migration add_skills_join_tables
within migration
def change
create_table :skills_users, id: false do |t|
t.references :skill
t.references :user
end
create_table :help_requests_skills, id: false do |t|
t.references :skill
t.references :help_request
end
add_index :skills_users, [:skill_id, :user_id]
add_index :help_request_skills, [:skill_id, :help_request_id]
end
One hay is to create a has_and_belongs_to_manyrelation between the Users and Helprequests and Skills, so you end up with this:
class User < ActiveRecord::Base
has_and_belongs_to_many :skills
end
class Help_request < ActiveRecord::Base
has_and_belongs_to_many :skills
end
class Skills < ActiveRecord::Base
has_and_belongs_to_many :users
has_and_belongs_to_many :help_requests
end
Then you need to create the tables
rails g migration add_skills_users_table
rails g migration add_help_requests_skills_table
In the end run rake db:migrate
You can then search for it using User.first.skills

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