简体   繁体   中英

How to join tables and apply where clause for active record RAILS

This seems like a very simple thing to do but despite looking through the rails docs it's not working like I expect. I'm likely making an incorrect assumption somewhere. Maybe I'm trying to get activerecord to be used for something it is not intended for?

I'm trying to join my Books table and Checkout_logs table and only return records matching user_id and with returned_date of null (nil)

I'm trying to do something like this example in sql (except list all the book info not just title and get user_id from parameters)

select b.title,c.due_date from 
(select * from books) b
JOIN
(select * from checkout_logs) c
ON b.id = c.book_id
where c.user_id = 1 and returned_date is null

This is my attempt

books_controller.rb

def my_books    
    @books = Book.joins(:checkout_logs).where(user_id: params[:user_id], returned_date: nil)
  end

and view

my_books.html.erb

<table>
  <thead>
    <tr>
      <th>Title</th>
      <th>Author</th>
      <th>Genre</th>
      <th>Subgenre</th>
      <th>Pages</th>
      <th>Publisher</th>      
      <th>due_date</th>
      <th colspan="1"></th>
    </tr>
  </thead>

  <tbody>
    <% @books.each do |book| %>
      <tr>
        <td><%= link_to book.title, book %></td>
        <td><%= book.author %></td>
        <td><%= book.genre %></td>
        <td><%= book.subgenre %></td>
        <td><%= book.pages %></td>
        <td><%= book.publisher %></td>
        <td><%= book.due_date %></td>
        <td><%= link_to 'Return', books_return_path %></td>        
      </tr>
    <% end %>
  </tbody>
</table>

book.rb

class Book < ApplicationRecord
    has_many :checkout_logs, dependent: :destroy
end

checkout_log.rb

class CheckoutLog < ApplicationRecord
  belongs_to :user
  belongs_to :book
end

It gives an error upon loading the view that books.user_id doesn't exist or books.due_date doesn't exist. I understand books doesn't have those things but I thought that by joining it with checkout_log I would be able to access them that way?

I'd prefer to learn how to do this in activerecord because I assume it is the best practice. I'm open to using another method. I have another page that does something similar using AREL, but I'd prefer to clean that up too if I can learn how to use activerecord better.

You can debug your queries by passing .to_sql to them

 @books = 
   Book
   .joins(:checkout_logs)
   .where(user_id: params[:user_id], returned_date: nil)

in this case, I think the problem is that the where conditions are made against books table instead of checkout_logs table. You can fix this by doing

.where(checkout_logs: { user_id: params[:user_id], returned_date: nil })

The issue is the where clause. In the where clause, you need to traverse the relationship to access the attributes on the correct table. There is no user_id on the book table. Im assuming the returned date is on the CheckoutLog table as well. If you cahnge your query to this, it should be ok.

Book.joins(:checkout_logs).where(checkout_logs: {user_id: params[:user_id], returned_date: nil})

To get the checkout log information here, you need to iterate through the relationship.

@book.checkout_logs.each do |log|
  puts log.due_date
  ...
end

A better way of doing the above would be to use includes and/or references to eager load the related checkout logs to reduce n+1 issues

If you just want the CheckoutLog records, then you need to start on that model.

CheckoutLog.where(book_id: <BOOK ID>, user_id: params[:user_id], returned_date: nil)

And then you could iterate through those in your view to get whatever checkout log information you needed.

So I needed to do the following in

book_controllers.rb

@checkout_logs = CheckoutLog.joins(:book).where(checkout_logs: {user_id: current_user.id, returned_date: nil})

and then the view can access the book by doing:

my_books.html.erb

<% @checkout_logs.each do |checkout_log| %>
      <tr>
        <td><%= checkout_log.book.title %></td>
        <td><%= checkout_log.book.author %></td>
        <td><%= checkout_log.book.genre %></td>
        <td><%= checkout_log.book.subgenre %></td>
        <td><%= checkout_log.book.pages %></td>
        <td><%= checkout_log.book.publisher %></td>
        <td><%= checkout_log.due_date %></td>
        <td><%= link_to 'Return', books_return_path(:checkout_log_id => checkout_log.id) %></td>        
      </tr>
    <% end %>

Because checkout_log has 1 book I can access the book this way. I can't access the information the other way around or create some hybrid that has it all on the same level. (at least I wasn't able to)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM