I want to add relation that user can belongs_to teacher. Teachers are stored in Admins table. Typically it will be nobody (nil), but for active students, I would add a specific person who is their teacher at the moment.
Normally I think could do run such migration:
class AddTeacherToUser < ActiveRecord::Migration
def change
add_reference :users, :admin, index: true
end
end
Then in models, I could add like that:
class User < ApplicationRecord
belongs_to :admin
...
class Admin < ApplicationRecord
has_many :users
...
But I want to have on my user field teacher_id, not admin_id is this possible? can I rename a field in migration or inside my model?
Rails version: 4.2
In Rails 4.2+ you can set foreign keys in the db:
In your migration do:
add_reference :users, :teacher, index: true
add_foreign_key :users, :admins, column: :teacher_id
While in your User model do:
belongs_to :teacher, class_name: "Admin"
Related
If I wanted to have a "user_type" column in my "users" table that references to another table called "user_type", how do I write the correct association in rails? For example if my user_type is 1 and 1 is admin in my user_types table, and when I write this in my rails console
user = User.first
user.user_type #I want this to return admin
I've tried with
class AddTypeToUsers < ActiveRecord::Migration[5.2]
def change
add_reference :users, :user_type, foreign_key: true
end
end
But it won't work
thank you in advance
This is how you should define the model associations.
class UserType < ApplicationRecord
has_many :users
end
class User < ApplicationRecord
belongs_to :user_type
end
You should read once this association_basics guide to understand how associations works in Rails.
I have encountered a problem with my rails console when I try to create a new event based on a user. I have a feeling this is a very simple error that I am getting, but I am posting because I am unsure of how I should fix it. Here is the command I tried to run:
user.event = Event.create(:name => "Dummy")
Here is my db file for my event:
class CreateEvents < ActiveRecord::Migration[5.0]
def change
create_table :events do |t|
t.string :name
t.timestamps
end
end
end
Here is my Users database file:
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :event
t.integer :code
t.timestamps
end
end
end
Here is my User.rb file:
class User < ApplicationRecord
has_secure_password
has_many :events
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST : BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
end
Here is my Event.rb file:
class Event < ApplicationRecord
belongs_to :user
end
Its not a simple error. It's quite a few things which are wrong.
If you want a relation where a user can have many events, and an event can belong to many users you need to create a join table. As storing a foreign key on either the users or events table would create a one to many relationship which is probably not what you want.
class User < ApplicationRecord
has_many :user_events
has_many :events, through: :user_events
end
class Event < ApplicationRecord
has_many :user_events
has_many :users, through: :user_events
end
# this is a join model.
class UserEvent < ApplicationRecord
belongs_to :user
belongs_to :event
end
A has_many :through association is often used to set up a many-to-many
connection with another model. This association indicates that the
declaring model can be matched with zero or more instances of another
model by proceeding through a third model.
http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
You can generate the UserEvent model and the migration which creates the join table by running:
rails g model user_event user:belongs_to event:belongs_to
This will create a user_events table with the user_id and event_id foreign key columns. You should also roll back the migration which creates the users table and fix it:
class CreateUsers < ActiveRecord::Migration[5.0]
def change
create_table :users do |t|
t.string :name
t.string :email
t.string :password_digest # !!!
t.integer :code
t.timestamps
end
end
end
Note the addition of the password_digest column - this is required for has_secure_password. If you have already run this migration on the production database or committed and pushed it you should instead create separate migrations which fix the errors:
class AddPasswordDigestToUsers < ActiveRecord::Migration[5.0]
def change
add_column(:users, :password_digest, :string)
end
end
class RemoveEventFromUsers < ActiveRecord::Migration[5.0]
def change
remove_column(:users, :event)
end
end
To create an event which is associated with a user you can do:
event = user.events.new(name: "Dummy") # does not persist the record
event = user.events.create(name: "Dummy")
You can all assign records from either end by using the shovel operator:
user.events << event
event.users << user
Is this the right association for me?
My main goal for my application is to make it so a user has partys and
those parties have songs.
A party with only one user sounds pretty lame. However if you want to create a special relationship for the user you can create a separate association:
class User < ApplicationRecord
has_many :user_events
has_many :events, through: :user_events
has_many :owned_events, class_name: 'Event', foreign_key: 'owner_id'
end
class Event < ApplicationRecord
has_many :user_events
has_many :users, through: :user_events
belongs_to :owner, class_name: 'User'
end
class AddOwnerIdToEvents < ActiveRecord::Migration[5.0]
def change
add_column(:events, :owner_id, :integer)
add_foreign_key(:events, :users, column: :owner_id)
end
end
Another way to solve this is by adding a column to UserEvent join model which specifies what the association is. But this is pretty far beyond your skill level.
There is no foreign key in your tables. Supposing you are actually intending to have the models as explained ("event belongs to user"/"user has many events"), you need to add a column user_id to the events table, and not an event string to the users table.
You can create a migration or column definition with migration/model generators using the references type:
rails g model event name:string user:references
or
rails g migration add_user_id_to_event user:references
which will add column and the needed indexes.
Moreover, you have that a user has many events, so there is nothing like
user.event = Event.create
(there is no such method as User#event=) but instead
user.events << Event.create(...)
What is the preferred way of selecting a specific record out of a has_many relation for a specific model in Rails? (I'm using Rails 5.)
I have a model User and a model Picture which are related via the following code:
class User < ApplicationRecord
has_many :pictures, as: :imageable
# ...
end
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
# ...
end
What I want to do is to allow a User to set a profile picture from the images associated with it, so I can call #user.profile_picture on a User object and retrieve the profile picture.
You can add an additional one-to-one relationship.
# create by running:
# rails g migration AddPrimaryPictureToUser
class AddPrimaryPictureToUser < ActiveRecord::Migration[5.0]
def change
add_column :users, :primary_picture_id, :integer
add_index :users, :primary_picture_id
add_foreign_key :users, :pictures, column: :primary_picture_id
end
end
class User < ApplicationRecord
has_many :pictures, as: :imageable
# This should be belongs_to and not has_one as the foreign key column is
# on the users table
belongs_to :primary_picture,
class_name: 'Picture',
optional: true
end
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
has_one :primary_user,
foreign_key: 'primary_picture_id',
class_name: 'User'
# ...
end
The main reason to do it this way vs for example a boolean flag on the pictures table is that having a separate relationship makes it easy to join which is important for avoiding N+1 queries which is an issue if you are listing a bunch of users together with their primary image.
#users = User.includes(:primary_picture).all
I'm a little befuddled on how to create a joined table in ROR.
Let's say there are 2 tables:
User
Book
In order to create a joined table user_book, my initial impression was that you did this:
rails g migration user_book
But I've been told that to create an actual joined table, I generate an actual model:
rails g model user_book
Is that correct?
And if the second one is correct, then once I've created it, I open it and add both the book_id, and user_id to the migrated file, and then run rake db:migrate command. Is this correct?
EDIT:
Just wanted to added that
User model would have this: has_many :books, through: :user_book
Book model would have this: has_many :users, through: :user_book
If you want to use has_many through:, you need a table to handle that. As well as a model object to represent that table.
How you go about creating it (rails g model or rails g migration) doesn't ultimately matter. All those commands do is create either a model AND a migration file, or just a migration file. Personally, I would do the rails g model, since you need both.
You'd just add a user_books table with 2 IDs, user_id and book_id.
Migration would look something like this:
class CreateUserBooks < ActiveRecord::Migration
def change
create_table :user_books do |t|
t.references :user, index: true, foreign_key: true, null: false
t.references :book, index: true, foreign_key: true, null: false
t.timestamps null: false
end
end
end
Models would look like this:
class User < ActiveRecord::Base
has_many :user_books
has_many :books, through: :user_books
end
class Book < ActiveRecord::Base
has_many :user_books
has_many :users, through: :user_books
end
class UserBook < ActiveRecord::Base
belongs_to :user
belongs_to :book
end
I have tree model Meeting , Client, Contact.
When i create new meeting, i can select client or contact , but how better store this structure and association? !Use case client_id and contact_id in meeting table not good.
We assume someone (a creator) can create a meeting. The creator may be a client or a contact.
For that you need a "creator_type" and "creator_id" column on your Meetings Table first, so add an migration using script/rails generate migration AddTypeToMeetings
Then edit the migration file like:
class AddTypeToMeetings < ActiveRecord::Migration
def self.up
add_column :meetings, :creator_id, :integer
add_column :meetings, :creator_type, :string
end
end
Second, you have to adapt your models:
meeting.rb
class Meeting < ActiveRecord::Base
# polymorphic association
belongs_to :creator, :polymorphic => true
end
client.rb
class Client < ActiveRecord::Base
has_many :meetings, :as => :creator
end
contact.rb
class Contact < ActiveRecord::Base
has_many :meetings, :as => :creator
end
How to use:
#my_meeting = Meeting.new
#my_meeting.creator = #my_client
# or if you want a contact:
#my_meeting.creator = #my_contact
You can read more up on polymorphic associations here:
ASCII Casts
Documentation (scroll to Polymorphic Association)