Model Structure & has_many Association Migrations - ruby-on-rails

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

Related

How to create a one to many relation between models in Ruby on Rails?

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.

How to implement projects with project admins in Rails

What I'm looking for is an appropriate way to set up a system where users can create projects and therefor become the admin of that project. The user can then add other admins to the project. Finally, other non-admin users can join the project.
I want to be able to verify whether a user is an admin of a project to check whether he has edit/update privileges. Any thoughts?
I figure I'll probably have a users_projects table and a projects_admins table, but I can't figure out how that translates to Rails relationships....
Thanks!
Ok, I will give it a try, but without too much code here in.
I see here 3 models:
User
Project
ProjectAdmin
The first 2 are simple models, with some attributes. The third one is the relation between the two and will be a n:m relation. So it is best to use the has-many-through relation here.
class ProjectAdmin < ActiveRecord::Base
belongs_to :project
belongs_to :user
end
class User < ActiveRecord::Base
has_many :project_admins
has_many :projects, :through => :project_admins
end
class Project < ActiveRecord::Base
has_many :project_admins
has_many :admins, :through => :project_admins
end
Of course you have to create additionally the 3 tables by migrations, and add later a similar relation for project users, named then ProjectUser as model. Have at least a look at the rails guide about relations, section "has-many :through".
To add the creator to a project, this should be a one-one relation between the two, so it should be sufficient to have:
class Project
has_one :creator, :class_name => "User"
end
(and of course the creator_id in the migration)

Adding another attribute to a many-to-many table in rails 3

I am currently using has_and_belongs_to_many to implement a many-to-many relationship. I would however want to put in a attribute in the many_to_many table.
Basically I am creating a email system. I have users and conversations. A user can have many conversations and a conversations can also have many users. However, I am trying to make it so that I can have a read/unread attribute to show which messages are read. Since conversations can have many users, it is not practicable to put the attribute in the conversations table as then it would mean that the conversation is read by all. So I think it would work best in the middle table. I am wondering though how I can access that attribute in the middle table. If the attribute is read. What code do I put in to access that and how do I update the attribute. As mention above I am using has_and_belongs_to_many
If you want to have additional attributes to your has-and-belongs-to-many association, you have to build a model class for that relation. See the detailed description in the Rails Guides about it.
After having read it for myself, this is now deprecated with the current version of Rails, so you really should switch to has_many :through. Your models could be (copied and changed from the Rails Guides, I don't know if connection is a good name for the m2n relation):
class User < ActiveRecord::Base
has_many :connections
has_many :conversations, :through => :connections
end
class Connection < ActiveRecord::Base
belongs_to :user
belongs_to :conversation
end
class Conversation < ActiveRecord::Base
has_many :connections
has_many :users, :through => :connections
end
There you are able to add additional attributes to your connections table, and refer in the code to them.

.build method not creating association in the join table

I have three Models setup with the following associations
class User < ActiveRecord::Base
has_many :faculties
has_many :schools, :through => :faculties
end
class School < ActiveRecord::Base
has_many :faculties
has_many :users, :through => :faculties
end
class Faculty < ActiveRecord::Base
belongs_to :user
belongs_to :school
end
and in my controller i go to create a school and assign the user
class SchoolsController < ApplicationController
def create
#school = current_user.schools.build(params[:school])
...
end
end
When I login and submit the form the flash displays success, but the association doesn't build on the join table.
I tried it inside the apps console and it builds the association just fine.
I've been stuck on this for a couple days now and I just cannot figure out what I am missing. Thank in advance for any and all advice
The build method does not save the object. You need to explicitly call #school.save.
Two things: If the schools association is :through a has_many association, you will have to select which parent the School exists through.
So, for instance, if you were to nest School resources under users as in /users/:id/faculties/:id you could create a school via current_user.faculties.find(params[:faculty_id]).schools.build(params[:school]).save
Based on the example code, it looks like the fundamental problem is that the has_many xxx, :through syntax is being used without specifying the id of the faculties record. Remember two things: 1) ActiveRecord doesn't natively support composite primary keys, and 2) you must call #save on associated records created using #build. If you remember these, you should be fine.

rails, model naming question

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

Resources