A Ruby on Rails has_many relationship. - ruby-on-rails

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

Related

Ruby on Rails - Polymorphic associations + joint table + reference the same model twice?

I am having a hard time using polymorphic associations in a joint table where one option could lead to the association of two instances of the same table. I am relatively new to programming so I hope my question makes sense.
Basically, I have 3 models where my aim is to make associations between different products:
Product
ExtrenalProduct
Integration (polymorphic)/joint table
The Integration table will link 2 Products or 1 Product and 1 ExternalProduct
Here is my migration file:
class CreateIntegrations < ActiveRecord::Migration[5.1]
def change
create_table :integrations do |t|
t.references :main_product
t.belongs_to :integratable, polymorphic: true
t.timestamps
end
add_index :integrations, [:integratable_id, :integratable_type]
add_foreign_key :integrations, :products, column: :main_product_id, primary_key: :id
end
end
Here is my Integration Model:
class Integration < ApplicationRecord
belongs_to :integratable, polymorphic: true
belongs_to :main_product, class_name: 'Product'
end
Here is my ExternalProduct Model:
class ExternalProduct < ApplicationRecord
has_many :integrations, as: :integratable
end
Here is my Product Model:
has_many :integrations, class_name: 'Integration', foreign_key: 'main_product_id', dependent: :destroy
has_many :integrations, as: :integratable
My question is regarding the way I can query all Integrations from #product. As for now, I have to build a custom method:
def all_integrations
Integration.where(main_product_id: id).or(Integration.where(integratable_type: 'Product', integratable_id: id))
end
I would like to be able to do a: #product.integrations (which currently retrieves an empty array)
Any clue on what I am doing wrong or how I could make this code DRY? I feel I am missing something.
Thanks for reading!

Creating a new table in mongoid for has_and_belongs_to_many relationship

I just started to code in rails using mongoid ,previously I coded in sql,sqlite etc,Now Iam a bit confused between associations.like in sql when you want a has_and_belongs_to_many association between two models you do in such a way
for example
class Student < ActiveRecord::Base
has_and_belongs_to_many :subjects
end
class Subject < ActiveRecord::Base
has_and_belongs_to_many :students
end
and we create a new table as
rails g migration CreateJoinTableStudentSubject student subject
In our migration file we do like this
def change
create_table :students_subjects do |t|
t.references :student
t.references :subject
t.timestamps
end
end
now my question is is this neccesary to create a new table when using mongoid or der is a alternative way for doing this.plz help Iam new to mongoid and rails.thank you
You just need to include some code into your classes like this:
class Student
include Mongoid::Document
has_and_belongs_to_many :subjects
end
class Subject
include Mongoid::Document
has_and_belongs_to_many :students
end
There is a great documentation here.
Hope it will help you!

ActiveRecord, polymorphic has_many, :through, :as

I am new to Rails and I'm having a superb deal of difficulty wrapping my head around what seems to be a very simple database structure, but I'm thrown by the idea that objects must belong to other objects.
In a site that I am creating, a User may create many Posts.
A Post may fit into any number of many different Topics.
So what Rails would like is that Posts belong to both Topics and to Users, while Topics also belong to Posts (many-to-many?). This makes some sense in my head, but then I can't imagine how to create a Topic independent of a Post (which is reasonable to the site's function).
Is this possible? Any help would be greatly appreciated - this is giving me a headache!
You can use has_and_belongs_to_many (HABTM) for this kind of relationship:
class User < ActiveRecord::Base
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :topics
end
class Topic < ActiveRecord::Base
has_and_belongs_to_many :posts
end
in addition to the tables for users, posts and topics, you will need to create a migration for the HABTM relationship:
rails g migration create_posts_users post:references user:references
Note that the model names appear in alphabetical order in the table name
I don't think you are using polymorhpic relations here, just a regular many-to-many relation. Your model relations should look something like:
class User
has_many :posts
end
class Post
has_many :post_topics
has_many :topics, through: :post_topics
belongs_to :user
end
class Topic
has_many :post_topics
has_many :posts, through: :post_topics
end
class PostTopic
belongs_to :post
belongs_to :topic
end
This scenario is perfectly fine, a Topic can have may Posts, and a Post can have many Topics, which is a many-to-many relationship. In rails that would generally translate to has_and_belongs_to_many. Therefore you can define your models as follows:
class Post < ActiveRecord::Base
has_and_belongs_to_many :topics
end
class Topic < ActiveRecord::Base
has_and_belongs_to_many :posts
end
The corresponding generated Migration and database tables will look like this:
class CreatePotsAndTopics < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.timestamps
end
create_table :topics do |t|
t.string :name
t.timestamps
end
create_table :posts_topics, id: false do |t|
t.belongs_to :post
t.belongs_to :topic
end
end
end
As you can see both Topic and Post tables are standalone tables with no reference to any other table, which means they can be treated on their own. they way they get linked is through the posts_topics join table. Which enables you to do have access to #post.topics and #topic.posts
If you are uncomfortable with such a scenarios pick up a good rails book or do an online tutorial or course which walk you through creating a full rails application.

Has many :through association not found

I have two models that can have tags added to them.
Player
Ticket
and I have a Tag model which belongs to both so I have two join models
tag_ticket
tag_player
I am getting a Could not find the association :tag_tickets in model Ticket error but my association is in there.
class Ticket < ActiveRecord::Base
has_many :tag_tickets
has_many :tags, :through => :tag_tickets
end
I'm just focusing on the Ticket model but the player model should look similar.
this is my migration for TagTicket
class CreateTagTickets < ActiveRecord::Migration
def change
create_table :tag_tickets do |t|
t.integer :ticket_id
t.integer :tag_id
t.timestamps
end
end
end
You need to specify the :tag_tickets join first like this:
class Ticket < ActiveRecord::Base
has_many :tag_tickets
has_many :tags, :through => :tag_tickets
end
You would also need to specify the joins in your TagTicket model:
class TagTicket < ActiveRecored::Base
belongs_to :ticket
belongs_to :tag
end
Alternatively, you can skip all this and use a habtm join (only recommended if the tag_tickets join is truly only used as a join and has no primary key for itself). In this case you would have no TagTicket model (just a tag_tickets table) and the Ticket model would look like this:
class Ticket < ActiveRecord::Base
has_and_belongs_to_many :tags
end

In RoR, how do I create TWO one to one relationship between two tables?

In RoR3,
I have Users and Skills and each skill is created by a user. I wanted to record that, so I created a one to many relationship.
class User < ActiveRecord::Base
has_many :skills
end
class Skill < ActiveRecord::Base
belongs_to :user
end
However, each user also has many skills in the sense that, user "Bob" created skill "Kung Fu", user "Charlie" created skill "Karate" and user "Bob" both created and is able to do both "Kung Fu" and "Karate"
How should I represent this with ActiveRecord? Should I just create a new table "user_skills" which has_many :skills? and belong_to :user?
There are two different associations here. The first is a one-to-many association. An user can be the creator of any number of skills. The second one is a many-to-many association, an user can have many skills and a skill can have many users.
The first one is a simple belongs_to <-> has_many declaration. For the second one, you either need a has_and_belongs_to_many declaration in both models, and a related join table, or a dedicated join model, and a has_many :through declaration. Let's try the first one:
Method 1: HABTM
class User < ActiveRecord::Base
has_many :created_skills, :class_name => 'Skill', :inverse_of => :creator
has_and_belongs_to_many :skills
end
class Skill < ActiveRecord::Base
belongs_to :creator, :class_name => 'User', :inverse_of => :created_skills
has_and_belongs_to_many :users
end
This requires a join table called "skills_users" that has columns named user_id and skill_id
Method 2: Has many through (Join model)
The second one is similar, but adds a model that acts as the middleman. This has an added benefit that you can include additional columns in the join model, like for example a skill level.
class User < ActiveRecord::Base
has_many :created_skills, :class_name => 'Skill', :inverse_of => :creator
has_many :user_skills
has_many :skills, :through => :user_skills
end
class Skill < ActiveRecord::Base
belongs_to :creator, :class_name => 'User', :inverse_of => :created_skills
has_many :user_skills
has_many :users, :through => :user_skills
end
class UserSkill < ActiveRecord::Base
belongs_to :user
belongs_to :skill
end
Having those two models
class User < ActiveRecord::Base
has_and_belongs_to_many :skills
end
class Skill < ActiveRecord::Base
has_and_belongs_to_many :users
end
You would have to create an extra migration (without the model)
rails generate migration CreateSkillsUsersJoin
which will give you
class CreateSkillsUsersJoin < ActiveRecord::Migration
def self.up
create_table :skills_users, id => false do |t|
t.references "user"
t.references "skill"
end
add_index :skills_users,["user_id","skill_id"]
end
def self.down
drop_table :skills_users
end
end
The methods self.up and self.down you will have yo add them
You'd be well served using a gem like acts_as_taggable_on which you'd be able to simply setup and use in your User model, something like:
acts_as_taggable_on :skills
Honestly, they've figured all this stuff out, as it's not as simple as what you're trying to do, OR I should rephrase that and say, what you are trying to do is overtly 'complex' and this gem allows you to just keep on, keeping on after it's set up.
Read the Readme.

Resources