Rails adding table and relation - ruby-on-rails

I've got a user table with all my users
Now I want to introduce user-groups
So I need a second table for the groups and a relation between those groups.
It should be possible to set a user in different groups. so I need a n:m relation
It read about that rails can generate most things I need and also generate the migration file? is that true? How does it works?
Migrations
class CreateUserGroups < ActiveRecord::Migration
def up
create_table :user_groups do |t|
t.string :name
t.integer :size
t.timestamps
end
end
def self.down
drop_table :user_groups
end
end
class CreateUserGroupUsers < ActiveRecord::Migration
def self.up
create_table :user_group_users do |t|
t.user_id:integer
t.user_groups_id:integer
t.timestamps
end
end
def self.down
drop_table :user_group_users
end
end

Create your User and UserGroup model and migration from terminal
rails g model User email:string password:string
rails g model UserGroup name:string size:integer
You'll also want to create a UserGroup and User relationship managing table called UserGroupManager
rails g model UserGroupManager user_id:integer user_group_id:integer
Update your database by running this command in terminal
rake db:migrate
Set up the relationships within app/models/
# app/models/user.rb
class User < ActiveRecord::Base
has_many :user_group_managers
has_many :user_groups, through: :user_group_managers
end
# app/models/user_group.rb
class UserGroup < ActiveRecord::Base
has_many :user_group_managers
has_many :users, through: :user_group_managers
end
# app/models/user_group_manager.rb
class UserGroupManager < ActiveRecord::Base
belongs_to :user_group
belongs_to :user
end

Related

RAILS 6 has_and_belongs_to_many >> join_table << does not work

I want to use a join_table between two many to many relations.
student -> join_table <- teacher
MODEL definition :
class Student < ApplicationRecord
has_and_belongs_to_many :teachers, join_table: map_student_teacher
end
class Teacher < ApplicationRecord
has_and_belongs_to_many :students, join_table: map_student_teacher
end
Migration Definition :
class CreateStudents < ActiveRecord::Migration[6.0]
def change
create_table :students do |t|
t.string : student_name
end
end
end
class CreateTeachers < ActiveRecord::Migration[6.0]
def change
create_table :teachers do |t|
t.string : teacher_name
end
end
end
class CreateStudentsTeachersJoinTable < ActiveRecord::Migration[6.0]
def change
create_table :map_student_teacher, id: false do |t|
t.belongs_to :student, index: true
t.belongs_to :teacher, index: true
end
end
end
Now I have a seeds.rb file to initialize the students names.
students_defaults = ["hans","otto","paul","elsa"]
students_defaults.each do |name|
Student.create(student_name: name)
end
When I load the seed by rails db:seed
I got this error message:
rails aborted!
NameError: undefined local variable or method `map_student_teacher' for Student (call 'Student.connection' to establish a connection):Class
/home/sven/.rvm/gems/ruby-2.5.3/gems/activerecord-6.0.2.2/lib/active_record/dynamic_matchers.rb:22:in `method_missing'
What is going wrong ?
In your model classes it should say join_table: :map_student_teacher. Note the extra colon to make map_student_teacher into a symbol. Without that Ruby is trying to look inside a local called map_student_teacher, which is undefined.
class Student < ApplicationRecord
has_and_belongs_to_many :teachers, join_table: :map_student_teacher
end
class Teacher < ApplicationRecord
has_and_belongs_to_many :students, join_table: :map_student_teacher
end
If this is ugly to you, you can also use the older :join_table => :map_student_teacher syntax.

Rollback transaction rails console

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(...)

How to add association to relations, that are already created , through migration?

I have created 2 table students and issued_books. but forgot to add t.belongs_to :students in migration, while creating issued_books table.
Now I have modified the corresponding model as:
class Student < ActiveRecord::Base
has_many :issued_book
end
class IssuedBook < ActiveRecord::Base
belongs_to :student
end
How would I do it now through migration in rails?
$ bin/rails generate migration AddUserRefToProducts user:references
generates
will generate the following:
class AddUserRefToProducts < ActiveRecord::Migration[5.0]
def change
add_reference :products, :user, index: true, foreign_key: true
end
end
source: http://edgeguides.rubyonrails.org/active_record_migrations.html
So in your case it would be:
$ bin/rails generate migration AddStudentRefToIssuedBooks student:references
You just have to populate the foreign_key in your belongs_to model:
$rails g migration AddForeignKeyToIssuedBook
#db/migrate.rb
class AddForeignKeyToIssuedBook < ActiveRecord::Migration
def change
change_table :issued_books do |t|
t.belongs_to :student #-> student_id
end
end
end
$rake db:migrate
This will create the appropriate column in the issued_books db, which should allow you to reference its associated Student with it.
Ref for t.belongs_to
--
You should also consider looking up about has_many/belongs_to associations for scope on the table structure:

Ruby On Rails scaffold need to include foreign keys?

I'm learning the basics of ruby on rails and I want to make some simple queries but I have a doubt:
I will have these models:
class Client < ActiveRecord::Base
has_one :address
has_many :orders
has_and_belongs_to_many :roles
end
class Address < ActiveRecord::Base
belongs_to :client
end
class Order < ActiveRecord::Base
belongs_to :client, counter_cache: true
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :clients
end
Now, I will use scaffold to generate all the things, and I want to know if I have to directly put the foreign keys in the scaffols, like:
rails generate scaffold Adress street:string number:integer client_id:integer
Or when I make those associations and then migrate my db they will be implicit?
I don't know if I explain myself in the best way.
Thanks
Yep, there is no reference. You need to either pass the client_id or a reference to Client model, e.g:
rails generate scaffold Address street:string number:integer client_id:integer:index
or
rails generate scaffold Address street:string number:integer client:references
Either, in rails 4 you can use belongs_to this way:
Assume that you have a user model in your application
rails g scaffold comment belongs_to:user text:string
it generates that class in your migrate folder:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :text
t.belongs_to :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
you should execute rake db:migrate then, this command create user_id properties as a index column in your database table.

Ruby on Rails :Creating Migration Script for Authorization with Role Model

My Application as a User Model generate via Devise Gem. I want to associate with each user some roles via many-to-many association through Assignment between User and Role. How do I generate migration script so that user is associated with some roles.Model classes will look like the following
class User < ActiveRecord::Base
has_many :assignments
has_many :roles, :through => :assignments
end
class Assignment < ActiveRecord::Base
belongs_to :user
belongs_to :role
end
class Role < ActiveRecord::Base
has_many :assignments
has_many :users, :through => :assignments
end
Add a migration like this:
rails g migration create_assignments_table
And fill that file with:
class CreateAssignmentsTable < ActiveRecord::Migration
def change
create_table :assignments do |t|
t.references :user
t.references :role
t.timestamps
end
end
end
I like to use t.references instead of t.integer to (just semantically) reflect the relations between the tables, but that's up to you.
Just need a create table migration.
rails g migration create_assignments
And be sure it has the columns you need.
class CreateAssignments < ActiveRecord::Migration
def change
create_table :assignments do |t|
t.integer :user_id
t.integer :role_id
t.timestamps
end
end
end

Resources