Home > other >  Rails - MVC explanation of how to use has_many through
Rails - MVC explanation of how to use has_many through

Time:10-16

I am a rails beginner and I am having trouble understanding how to use the MVC for a many-many relationship. All of the resources I can find, don't explain how to use the has_many through association in a MVC context, they always use the console to create the objects.

Overview of what I am trying to do:

Library system where there are many users and many books, each user can checkout multiple books, and a book can be checked out by many users.

Here is what I have:

  1. Created a Books scaffold for CRUD operations
  2. Created a Users model using devise
  3. Created a 3rd model Checkouts to link Books and Users in a has_many through: association

What I need help with:

  1. Should I also create a Checkouts scaffold for CRUD operations? because I need to create/update/destroy links between books/users when they checkout/return books in a MVC context? How would this work as the only experience I have with controller is the one created through the Books scaffold, which references books as @books, would the Checkouts controller be able to identify which Book and User object I am attempting to create a link for or how does this work???????

  2. If Checkouts does not need to be a scaffold for CRUD operations, how do I create functions in the controller to reference a book and user object and insert them into the database? As well as make it a usable route for the view?

PLEASE DO NOT GIVE ME RAW LINKS WITHOUT EXPLANATION, I have read a bunch of rails documentation and watched hundreds of "tutorials" but none of them explain it in a practical MVC usage.

CodePudding user response:

  1. You do not need to create CRUD for Checkouts, all you need is the Checkout model. Rails uses the associations (has_many, belongs_to, etc) to create the sort of links between models. Think of these associations as Rails connecting the data in your DB based on foreign keys. In your case Checkouts is what will link your Users and Books data. If you look at the relation in your code, your models will look something like the following:

checkout.rb

class Checkout
  belongs_to :book # Relation to book_id since it is the link table
  belongs_to :user # Relation to user_id
end

user.rb

class User
  has_many :checkouts # This is your main relation as checkouts table holds the user_id
  has_many :books, through: :checkouts # Since checkout.rb holds the foreign key for books, you can use through to go to it from checkout.rb
end

book.rb

class Book
  has_many :checkouts
  has_many :users, through: :checkouts
end

At high level, in the background the trail goes as: User has_many Checkouts and Checkouts has a relation to books. Now, from User get all Books by going through Checkouts.

To get the data, these associations in your Controller look like the following:

@user = User.find(1) # Find the user you want
@user.books # Get all the books that were checkout by the user

As mentioned before, @user.books is possible because of the associations declared in the models.

  1. Depending on your application, but a simple example. Lets say that you are in the Books module, and in the view, there is a checkbox for "Checkout book?". If the checkbox is marked and before you save the entry, you can do @book.checkouts.build(user_id: id_of_user). Notice that you do not need to add the book_id, this is because you are building the relation from @book, so it is implicit in the code already. Then when you save, you will see the entry in the DB. If you are in Users, you can do the same but in reverse @user.checkouts.build(book_id: id_of_book).

If after, you want to retrieve the users that took the book you can do:

@book = Book.find(1) # Find the book you want
@book.users # Get all the users that checkout that book

UPDATE

  1. Yes, build is a built-in method in rails. Not sure about the tutorial/guides, but for has_many, you can do that. And the same way for belongs_to, you can do build_model_name(...). The tutorials you saw show you one way, the usual way to create/save to DB. But using the build method, it is easier when it is a linktable. In this documentation, you can see all the class methods available with associations in ActiveRecord.

  2. I am not sure about the user using device, but you do not need to create routes or functions (depends on application) for the linktable Checkouts. If you have access to the current user from devise you just need to grab that.

Based on you application, I believe your main logic is in Books, so in create/update you can do

@book = Book.new(book_params)
@book.checkouts.build(user_id: @current_user.id)
@book.save

You do not necessarily need the controller to make the connections, Rails uses the Models for this. Controllers are just the actions.

  • Related