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:
- Created a
Books
scaffold for CRUD operations - Created a
Users
model using devise - Created a 3rd model
Checkouts
to linkBooks
andUsers
in ahas_many through:
association
What I need help with:
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 theBooks
scaffold, which references books as@books
, would theCheckouts
controller be able to identify whichBook
andUser
object I am attempting to create a link for or how does this work???????If
Checkouts
does not need to be a scaffold for CRUD operations, how do I create functions in the controller to reference abook
anduser
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:
- You do not need to create CRUD for
Checkouts
, all you need is theCheckout
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 caseCheckouts
is what will link yourUsers
andBooks
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.
- 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 thebook_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
Yes,
build
is a built-in method in rails. Not sure about the tutorial/guides, but forhas_many
, you can do that. And the same way forbelongs_to
, you can dobuild_model_name(...)
. The tutorials you saw show you one way, the usual way to create/save to DB. But using thebuild
method, it is easier when it is a linktable. In this documentation, you can see all the class methods available with associations in ActiveRecord.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.