I have three models: User, Company, and Subscription. What I am trying to accomplish is a Subscription belongs to either a User OR a Company.
To try accomplish this, I referenced this guide, but I have been unsuccessful as the record creation keeps rolling back.
here's my Company model:
# app/models/company.rb
class Company < ApplicationRecord
has_many :subscriptions, dependent: :destroy, as: :imageable
end
here's my User model:
# app/models/user.rb
class User < ApplicationRecord
has_many :subscriptions, dependent: :destroy, as: :imageable
end
and finally, here's my Subscription model:
class Subscription < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
Now as far as the migration file, this is my Subscription migration file:
class CreateSubscriptions < ActiveRecord::Migration[5.1]
def change
create_table :subscriptions do |t|
t.references :imageable, polymorphic: true, index: true
t.date :start_date
t.date :stop_date
t.timestamps
end
end
end
As far as what I can see, this is pretty much exactly like the guide shows, but it keeps rolling back. Here's the output of the rails console:
Loading development environment (Rails 5.1.6)
2.5.1 :001 > Subscription.create(imageable_id: 1, start_date: Time.now, stop_date: 2.days.from_now)
(8.6ms) SET NAMES utf8, ##SESSION.sql_mode = CONCAT(CONCAT(##sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), ##SESSION.sql_auto_is_null = 0, ##SESSION.wait_timeout = 2147483
(0.2ms) BEGIN
(0.3ms) ROLLBACK
=> #<Subscription id: nil, imageable_type: nil, imageable_id: 1, start_date: "2018-10-10", stop_date: "2018-10-12", created_at: nil, updated_at: nil>
2.5.1 :002 >
Here are the questions that I have:
Why is there an imageable_type field? Is that created by t.references and if so, do I need this? Can I just use imageable_id instead of t.references as the other part of the suggestion shows?
Why is it rolling back? Are polymorphic associations done differently in Rails 5.x or something by chance?
According to the graph shown in the guide, it looks like if a picture belongs to imageable_id 4, then if there is an employee AND a production with the ID of 4, then a picture would belongs to both instead of one or the other like I'm trying to accomplish. Correct?
In your association, Imageable type will contain the class name and imageble id will contain the id of that class. So if you want to create subscription for user you can do like below
User.first.subcriptions.create(start_date: Time.now, stop_date: 2.days.from_now)
So it will automatically pick up First user's id in imageable id and take "User" as imageable type.
If you want to create subscription manually, you must have to pass both fields imageable type and imageble id like below,
Subscription.create(imageable_id: 1, imageable_type: "User", start_date: Time.now, stop_date: 2.days.from_now)
Why is there an imageable_type field? Is that created by
t.references and if so, do I need this? Can I just use imageable_id
instead of t.references as the other part of the suggestion shows?
=> imageable_type will contain the class of associate model like "User" or "Company"
Why is it rolling back? Are polymorphic associations done
differently in Rails 5.x or something by chance?
=> No, you setup it correctly
According to the graph shown in the guide, it looks like if a picture belongs to imageable_id 4, then if there is an employee AND a production with the ID of 4, then a picture would belongs to both instead of one or the other like I'm trying to accomplish. Correct ?
=> It depends on both imageable_id and imageble_type , so by combination of both this you will get record. If imageable_id is 4 and imageable_type is "Picture" then it will take Picture with id 4.
Please check this link for understaing
For polymorphic association, you should also pass imageable_type along with imageable_id. You don't do it and that's why it doesn't work, most probably (i.e. there might be other reasons, I don't know, but this one is pretty obvious).
imageable_type holds the name of the class of the record given Subscription is associated to.
Related
I'm trying to determine the appropriate relationship to use in Rails. I'm going to have Users who can belong to one other User (their spouse). They will both be of the same model type (User).
I want to be able to call User.spouse on either user and get the user that is associated with them, and also take advantage of being able to build nested attributes for each other. Can I add a tag in the model specify a call to .spouse should return this user? Or would it just be user.user?
In your user.rb model you can make an association like this:
class AdminUser < ApplicationRecord
has_one :spouse, through: :user, foriegn_key: :spouse_id
This is assuming you have a field in your users table called spouse_id which is a foreign_key to users. (see below)
More information about this can be found here: Rails has_one :through association
When you add your spouse_id (or spouse_user_id might be a better name) via a migration don't forget to add a foreign key to strongly enforce legitimate data at the DB level.
add_column :users, :spouse_user_id, :integer
add_foreign_key :users, :users, column: 'spouse_user_id'
Example usage:
User id: 1, name: 'Fred', spouse_user_id: 2
User id: 2, name: 'Wilma', spouse_user_id: 1
User.find(1).spouse
=> Wilma
User.find(2).spouse
=> Fred
I'm making a movie review application. A user can scroll through the different reviews that have been created and save them to a list. In my console, I'm unable to access the user's list reviews. User.list.reviews. What am I missing? Thanks in advance!!
Here are my current models and associations.
User Model
has_one :list
has_many reviews, :through => :list
List Model
belongs_to :user
has_many :reviews
Review Model
has_many :lists
has_many :users, :through => :lists
Schema: List
user_id
review_id
In your schema, a List has one user ID and one review ID. So a List can only ever have one of those things. But you want a User to have just one List, while the List has many Reviews.
It then gets more complex, because a List can have many Reviews. But since many different Users can put Reviews into their own lists, one Review might appear in several Lists. In short, List has_and_belongs_to_many :reviews and Review has_and_belongs_to_many :lists. This means you need somewhere to put the List ID and Review ID pair that express this relationship - it's called a join table. The convention is just concatenate the two names of the related two tables to get the name of the join table - so if we have tables lists and reviews, we need a join table called lists_reviews (you can override this but it's easier to just go with the convention).
A bare minimum Rails migration would be:
create_table :users do |t|
end
create_table :lists do |t|
t.belongs_to :user # Leads to "user_id" column
end
create_table :reviews do |t|
end
create_table :lists_reviews do |t|
t.belongs_to :list # Leads to a "list_id" column
t.belongs_to :review # Leads to a "review_id" column
end
Given this, and given that with #has_one you're supposed to put #belongs_to in the thing it has, too, so List should belong_to :user, we get:
class User < ActiveRecord::Base # Rails <= v4
has_one :list
has_many :reviews, :through => :list
end
class List < ActiveRecord::Base
belongs_to :user
has_and_belongs_to_many :reviews
end
class Review < ActiveRecord::Base
has_and_belongs_to_many :lists
has_many :users, :through => :lists
end
And with all this dumped into an empty Rails shell we can test it at the console:
u1 = User.new; u1.save!
u2 = User.new; u2.save!
l1 = List.new( user: u1 ); l1.save!
l2 = List.new( user: u2 ); l2.save!
r1 = Review.new; r1.save!
r2 = Review.new; r2.save!
r3 = Review.new; r3.save!
l1.reviews << r1
l1.reviews << r2
l1.save!
l2.reviews << r2
l2.reviews << r3
l2.save!
u1.list
# => #<List id: 1, user_id: 1>
u1.list.reviews
# => #<ActiveRecord::Associations::CollectionProxy [#<Review id: 1>, #<Review id: 2>]>
u2.list
# => #<List id: 2, user_id: 2>
u2.list.reviews
# => #<ActiveRecord::Associations::CollectionProxy [#<Review id: 2>, #<Review id: 3>]>
l1.user
# => #<User id: 1>
l2.user
# => #<User id: 2>
r1.users
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1>]>
r1.lists
=> #<ActiveRecord::Associations::CollectionProxy [#<List id: 1, user_id: 1>]>
r2.users
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 1>, #<User id: 2>]>
r2.lists
=> #<ActiveRecord::Associations::CollectionProxy [#<List id: 1, user_id: 1>, #<List id: 2, user_id: 2>]>
r3.users
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 2>]>
r3.lists
=> #<ActiveRecord::Associations::CollectionProxy [#<List id: 2, user_id: 2>]>
...it works.
Schema: List
user_id
review_id
This implies that your List belongs-to one user and also belongs-to one review... but you've defined your list associations like this:
belongs_to :user
has_many :reviews
So Rails is getting confused as to why a list has a review_id (which is a belongs_to association thing). and is looking at the Review model, hoping it will have a list_id column... because that's how you'd solve that one... and yet your Review model has many list, so it can't resolve the issue.
Can you tell us what you'd actually like to happen? How should these things be related? should we change the associations you've defined to match the id-columns or can you more completely specify the relationships between models (eg with an object diagram) and then we can tell you how to alter your id-columns/associations to match your object-diagram?
You'll need to use a has_and_belongs_to_many for this. Take a look at http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association.
The problem is that your List model has_many reviews, and your Review model has_many lists. If two models both have a has_many relation to each other, how can you model this in the database? That is, where would you put the foreign key? Whichever table has the foreign key can only have a belongs_to relation to the other table. i.e. it can only belong to one record on the other table.
The solution to this is to use a join table. In rails this will usually have a name of something like lists_reviews. This join table would have two foreign keys, one for the lists table, and one for reviews. This way, each table can have a has_many relation to the other. In rails, you can use has_and_belongs_to_many to do this. Check out the link above for more.
I have 2 models in my project. 1 is Users and 1 is courses.
A user has many courses
and courses has many users.
The main problem is that I can't figure out how to assign users to courses without creating a new course.
user = User.first
course = Course.new(title:"course01")
My output would then be something like
Course id: 2, title: "course01", created_at: "2016-03-20 07:05:23",
updated_at: "2016-03-20 07:05:23", user_id: 1>
Now I can't figure out how to add another user to this same course.
user = User.second
?
Remove user_id from courses table, no need of it
Create join table for HABTM
create_table :users_courses, id: false do |t|
t.references :course
t.references :user
end
In user.rb file
has_and_belongs_to_many :courses
In course.rb file
has_and_belongs_to_many :users
after creating new course
course = Course.create(title:"course01")
users = User.all or whatever you need to assign to same course
users.each do |user|
course.users << user ##assign
end
In rails console
course = Course.first or Course.find(1) or whatever you want
course.users << User.find(1) or whatever you want
Let's say a new 'UserCourseAssignment' is created when a user is assigned to a course.
You could create a new UserCourseAssignment model with attributes user_id & course_id to store which users are assigned to which courses. This new table will have a new entry every time a user is assigned to a course.
User model
has_many :user_course_assignments
has_many :courses, through: :user_course_assignments
Course model
has_many :user_course_assignments
has_many :users, though: :user_course_assignments
UserCourseAssignment model
belongs_to :user
belongs_to :course
Edit:
I think you currently have user_id attribute in Course model. You won't need it after implementing the has_many :through association.
I have two ActiveRecord models like so:
class User < ActiveRecord::Base
has_one :contact_information
end
class ContactInformation < ActiveRecord::Base
belongs_to :user
end
This is my setup for a One-to-One relationship between the user and contact information table.
The issue I am encountering is when I create a new contact_information entry that points to a user that already has a existing contact_information, the new record is created including the relationship, meaning I have two contact_information records pointing to the same user, even though it is a one-to-one relationship.
Rails seems to pick the first record and returns it.
For example in irb:
> User.create
> ContactInformation.create(user: User.last)
> ContactInformation.create(user: User.last)
> ContactInformation.all
=> #<ActiveRecord::Relation [#<ContactInformation id: 3, created_at: "2016-01-04 22:28:11", updated_at: "2016-01-04 22:28:11", user_id: 2>, #<ContactInformation id: 4, created_at: "2016-01-04 22:28:18", updated_at: "2016-01-04 22:28:18", user_id: 2>]>
Both with user_id set to 2.
The ideal scenario would be a validation failure from contact_information when a record is created for a user that already has a contact_information.
I came up with this initial custom validation method for the Contact Information model that kind of works for creation (Need to make one for update)
validate :check_parent, on: :create
def check_parent
if user.contact_information.id.nil?
errors.add(:user, "already has contact information")
end
end
I am wondering if there is a more rails-y way of solving this problem? Or will I need to create a custom validator like this for all has_one relationships?
Thanks
A few options are available to you.
You could use validates_uniqueness_of on your ContactInformation model:
class ContactInformation < ActiveRecord::Base
validates_uniqueness_of :user_id
end
However, this will query the database to ensure that no duplicates exist on every save and could be very slow.
Another option is to have your database handle this for you. Depending on what you're using ActiveRecord with, adding a uniqueness constraint on the user_id column of your contact_informations table could be valuable.
I would suggest not using validates_uniqueness_of if your dataset is large, otherwise it might be perfect.
Make sure you at least add an index to the user_id column on that table:
class AddIndexOnContactInformationToUserId < ActiveRecord::Migration
add_index :contact_information, :user_id
end
You will need to have a validation but you don't need a custom one:
class ContactInformation < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :user_id
end
You may also want to add a unique index in the database for that column. It would not only make the query performant but will also protect your app against race conditions in case you are running multiple instances of it in parallel.
I have two objects: Team and Player. As you could probably guess, a Team has many Players and a Player belongs to a team.
I understand that we can model this relationship with another model, Team_Players but I want to focus on the raw relationship here, as shown by many of the guides I'm seeing online.
When creating Players, it's easy to assign them a Team.id, since they only have one, but the reverse -has_many- is more complicated. The Rails guide on associations only shows the model file, so this is what mine looks like in reflection:
class Team < ActiveRecord::Base
has_many :players
end
Now, I would expect to be able to do something like Team.first.players and be given an array or something, but instead I just get undefined method player for #<Team:0x> and in fact in this video, I do see a developer doing something just like that. So what am I missing? Do I have to make an intersection model here? I would imagine not since has_many is inherent in Rails.
After creating the tables, I added the reference for Team to Player in this migration:
def change
add_reference :players, :team, index: true
add_foreign_key :players, :team
end
Again, since the has many relationship can't be modeled with a single column, I avoided that part in the migration. Is that what's necessary for the desired functionality of Team.first.players returning an array or something?
Here's what I did to get this to work:
rails new teams - followed by bundle
rails g model Team name:string player_count:integer
rails g model Player player_name:string player_number:integer
rails g migration add_team_id_to_players:
class AddTeamIdToPlayers < ActiveRecord::Migration
def change
add_column :players, :team_id, :integer
end
end
rake db:migrate
Here are my models:
class Player < ActiveRecord::Base
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :players
end
Then, in the console:
Team.create(name: "Cats", player_count:1).save
Player.create(player_name: "Ryan", player_number:1, team_id:1).save
Then voila:
Team.first.players returns:
Team Load (0.2ms) SELECT "teams".* FROM "teams" ORDER BY "teams"."id" ASC LIMIT 1
Player Load (0.1ms) SELECT "players".* FROM "players" WHERE "players"."team_id" = ? [["team_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Player id: 2, player_name: "ryan", player_number: 1, created_at: "2015-12-18 19:32:39", updated_at: "2015-12-18 19:32:56", team_id: 1>]>