if we have data in a table and table that has a has_and_belongs_to_many association and we want to change the association to has_many through and add a foreign key to that table and we also want the previous data of the table to be saved with foreign key what will we do?
You would have to add a model to your application with the same name as your join table, or rename the join table with a migration.
What are the names of your models?
For example if you now have countries and companies. A country can have multiple companies and a company can have locations in multiple countries. You start with a model for Company and Country and a join table companies_countries.
You can create a model: CompaniesCountry and use it for the has_many :through
class Company < ApplicationRecord
has_many :companies_countries
has_many :countries, through: :companies_countries
end
class CompaniesCountry < ApplicationRecord
has_many :companies
has_many :countries
end
I think it would be nicer here to rename the join table to something more sensible like company_locations with a migrations and make a model for this instead. You could also store additional info like address or such on the model which you weren't able to do with the join table
The rails guides are a nice read on this as well: https://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
Related
i've students and courses in my ruby models and controllers, so i want to connect this two things and show an user and the courses he have registered and click those courses to see what is inside that course. i'm new to ruby so i dont know much about has_many and i cant find something that can make what i want to work
i've use scaffold to create the models and controllers,
user only have a name, email and courses only have course_name
student:
create_table :student do |t|
t.string :name
t.string :email
course:
create_table :course do |t|
t.string :name
t.timestamps
in the index of students i only list all of the students i have.
halp pls
Looks like you want to use a many-to-many association between students and courses. There are a number of ways to achieve this. I would go with the has_many :though option described here, in which you add an additional model called StudentCourse.
So in your scenario, you would:
generate this StudenCourse model with rails generate model StudentCourse student:references model:references
add the following to your Student model
class Student
...
has_many :student_courses
has_many :courses, through: student_courses
...
end
add the following to your Course model
class Course
...
has_many :student_courses
has_many :students, through: student_courses
...
end
run migrations with rake db:migrate
now you can start adding students to courses and vice versa. For example:
Student.last.courses << Course.last
Course.first.students << Student.first
and in your controllers, you can simply call student.courses to see courses that are associated with a given student, or course.students to view students taking a specific course.
notice how both Course and Student models will are now associated with each other using has_many: ..., through: :student_courses.
Another benefit of using this type of many-to-many association is flexibility. For instance, you might want to start recording whether students have dropped specific courses. You can do so by simply adding a dropped_at column to this new student_courses table.
EDIT
adding a few more detailed examples of how to use this new association:
as mentioned, you can append courses to students and vice versa via rails console. For instance, a student with id of 1 wants to enroll into a course with id of 2:
Student.find(1).courses << Course.find(2)
similarly, you can just add students to courses like so:
Course.find(2).students << Student.find(1)
under the hood, both of these associations would be creating a new instance of the StudentCourse class we added. So a third option of creating this association would be:
StudentCourse.create(student: Student.find(1), course: Course.find(2))
What you most likely want here is actually a many to many assocation. Not one to many.
class Student < ApplicationRecord
has_many :enrollments
has_many :courses, through: :enrollments
end
class Course < ApplicationRecord
has_many :enrollments
has_many :courses, through: :enrollments
end
class Enrollment < ApplicationRecord
belongs_to :student
belongs_to :course
end
This lets you use courses as a normalization table which contains the information about the course instead of duplicating it for each student.
enrollments works as a join table and lets you join any number of students to any number of courses. Its also where you would store information that describes the relation between student and course - like for example the students grade (marks).
You need to set the relations i.e. associations in your model classes.
You propably have two model classes in a folder named app/models by now (assuming scaffolding has created them):
app/models/student.rb
app/models/curso.rb
in app/models/student.rb you need to have something like this:
class Student < ActiveRecord::Base
belongs_to :curso
end
in app/models/curso.rb you need to have something like this:
class Curso < ActiveRecord::Base
has_many :students
end
That's how associations are being created in rails.
I have a Match and User model with a has_and_belongs_to_many between them.
How do I retrieve match.users.first and match.users.second based on when the MatchUser association was created, rather than by when the User itself was created?
You don't want to be using has_and_belongs_to_many in the first place. has_and_belongs_to_many relations are headless - there is no join model. The only columns that are ever used on the join table are the two foreign keys. Even if you added a created_at column to the join table there is no way to access it or use it to order the records. And AR won't set the timestamps anyways.
While you can kind of assume that a has_and_belongs_to_many association is ordered in the same order that the records where inserted you can't really order it.
You want to use has_many through: which uses a model to join the two records:
class User < ApplicationRecord
has_many :user_matches
has_many :matches, through: :user_matches
end
class Match < ApplicationRecord
has_many :user_matches
has_many :users, through: :user_matches
end
class UserMatch < ApplicationRecord
belongs_to :user
belongs_to :match
end
You can then order the association by:
match.users.order("user_matches.created_at DESC")
match.users.first
will return the first user by :id.
If you want to it ordered by created_at then you must do something like
user_id = matches_users.where(match_id: match.id).first.user_id
user.find(user_id)
Hope this is what you are looking at.
I'm trying to set up models in such a way that Users can create Lessons and then other users can sign up for them.
Right now my models are set up like this:
class Lesson < ApplicationRecord
belongs_to :teacher, class_name: 'User'
has_many :students, class_name: 'User'
end
class User < ApplicationRecord
has_many :lessons
has_many :students, :through => :lessons
end
I want to be able to access the users signed up for a lesson by #lesson.students for example. I'd also like to be able to get all the lessons that a student is participating in (can't really see how I'd do this with my current set up).
Are my model associations right for how I'd like to use them? If so, how can I create the migrations to add the necessary references to my database models?
If you want the ability to create nested resources from it's parents then you have to add:
accepts_nested_attributes_for
to the parent model.
Also, I recommend you to read how to set up has_many through relationships, you need a join model for rails to do its magic and link the 2 models
Once you set everything up, create the join model (with it's respective foreign keys, one for lesson and the other for user) rails will take care of the associations between the models, allowing you to do things like:
User.last.lessons #lessons created by the last user
and
Lesson.first.users #users subscribed to a lesson, in this case the first one
Alright, so here's the deal. I have two tables and a join table since it's a many-to-many relationship. I have an order and an order can have many products. Obviously it goes the other way since products can be on many orders. I've got the following classes:
class Order < ActiveRecord::Base
has_many :orders_products
has_many :products, :through => :orders_products
end
class OrderProduct < ActiveRecord::Base
belongs_to :order
belongs_to :product
end
class Product < ActiveRecord::Base
has_many :orders_products
has_many :orders, :through => :orders_products
end
I'm getting a page to display and I'm able to enter stuff and when I go to interact with the products on the saved order via #order.products I'm getting the following error:
SQLite3::SQLException: no such table: order_products: SELECT "products".* FROM "products" INNER JOIN "order_products" ON "products".id = "order_products".product_id WHERE (("order_products".order_id = 1))
My join table is named orders_products, but as you can see it's trying to join through order_products. My limited knowledge of Rails naming conventions tells me that orders_products is the correct way to name it, and then name my model as OrderProduct. I'm really pounding my head against a wall on this one.
EDIT: I see that even though it saved my order it and I selected multiple checkboxes it did not save any values in the orders_products table, presumably for the same reason as it is erroring now.
orders_products is not the correct naming convention for a has_many :through relationship--it is correct for a has_and_belongs_to_many relationship. In a has_many :through, the "join" model is not just for joining--it is also its own model that has its own data, that also happens to join two other models together.
If your OrderProduct model doesn't have any of it's own data, logic, or constraints, then you could use a has_and_belongs_to_many relationship instead, remove the model completely, and then your join table is named right. Otherwise, it is named according to regular model naming conventions, namely order_products.
I'm creating a model called Chats. And I want to assign users to a discussion. They are either a part of the Chats or they aren't...
So I create one model Chats.
What's the standard Rails naming convention for the other table?
ChatUsers?
While has_and_belongs_to_many is an ok option here, I recommend going with has_many :through instead.
In essence you will have an explicit join model, which you can call something like ChatSession.
class Chat < ActiveRecord::Base
has_many :chat_sessions
has_many :users, :through => :chat_sessions
end
class User < ActiveRecord::Base
has_many :chat_sessions
has_many :chats, :through => :chat_sessions
end
class ChatSession < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
Now you will need a table called chat_sessions with columns :user_id, and :chat_id in it. This is your join table.
Advantage
You get a model which is fully under your control, and isn't just a dumb join table managed by rails. So for example, if you want to track number of messages particular user left in particular chat, it could be a column in chat_sessions table. Presence of :through renders habtm unneeded in most cases. There is no complexity overhead either.
If it is a join table, it would be both table names joined by '_' and in alphabetical order of table names:
chats_users
This is called a has_and_belongs_to_many association in rails. You basically have two models that call has_and_belongs_to_many and create a linking table that uses the two models in the name (alphabetical and plural).
models:
class Chat < ActiveRecord::Base
has_and_belongs_to_many :users
end
class user < ActiveRecord::Base
has_and_belongs_to_many :chats
end
Then your tables would be
chats
users
chats_users