【问题标题】:How to join tables and apply where clause for active record RAILS如何连接表并为活动记录 RAILS 应用 where 子句
【发布时间】:2021-04-13 11:25:07
【问题描述】:

这似乎是一件非常简单的事情,但尽管查看了 rails 文档,但它并没有像我预期的那样工作。我可能在某处做出了不正确的假设。也许我正试图让 activerecord 用于它不打算用于的东西?

我正在尝试加入我的 Books 表和 Checkout_logs 表,并且只返回匹配 user_id 且返回日期为 null (nil) 的记录

我正在尝试在 sql 中执行类似此示例的操作(除了列出所有书籍信息,而不仅仅是标题并从参数中获取 user_id)

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

这是我的尝试

books_controller.rb

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

并查看

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

加载books.user_id 不存在或books.due_date 不存在的视图时会出错。我知道书籍没有这些东西,但我认为通过将其与 checkout_log 结合,我就可以通过这种方式访问​​它们?

我更愿意学习如何在 activerecord 中执行此操作,因为我认为这是最佳实践。我愿意使用另一种方法。我有另一个使用 AREL 执行类似操作的页面,但如果我可以学习如何更好地使用 activerecord,我更愿意清理它。

【问题讨论】:

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

标签: sql ruby-on-rails postgresql rails-activerecord


【解决方案1】:

您可以通过将.to_sql 传递给查询来调试您的查询

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

在这种情况下,我认为问题在于where 条件是针对books 表而不是checkout_logs 表。您可以通过这样做来解决此问题

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

【讨论】:

  • 主要问题是,无论 where 子句如何,我都无法访问 checkout_logs 表中的内容,例如book.due_date。即使我完全删除 where 子句,我也只能访问 book 表中的内容。所以问题在于连接或 activerecord 工作方式固有的问题。
【解决方案2】:

问题在于 where 子句。在 where 子句中,需要遍历关系才能访问正确表上的属性。 book 表上没有 user_id。我假设返回的日期也在 CheckoutLog 表上。如果您对此进行查询,应该没问题。

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

这里要获取结账日志信息,需要遍历关系。

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

执行上述操作的更好方法是使用包含和/或引用来急切加载相关的结帐日志以减少 n+1 问题

如果您只想要 CheckoutLog 记录,那么您需要从该模型开始。

CheckoutLog.where(book_id: &lt;BOOK ID&gt;, user_id: params[:user_id], returned_date: nil)

然后您可以遍历视图中的那些以获得所需的任何结帐日志信息。

【讨论】:

  • 这确实使 where 子句起作用。问题是在我加入表格后,我仍然无法在我的视图中访问 Book.due_date(来自 checkout_logs 的列)。如果我从视图中删除所有 checkout_log 列,它将根据我的 where 子句显示正确的记录,但我需要在视图中包含该列。所以跟关系有关系吗?型号?
  • 图书到期日在结帐日志表中?然后你需要遍历这本书的结帐日志@book.checkout_logs.each { |log| log.due_date }。如果您只想获取结帐日志并访问特定书籍的日志,那么您需要从 CheckouLog 模型开始,CheckoutLog.where(book_id: &lt;ID&gt;, user_id: params[:user_id], returned_date: nil) 这将为您提供所有结帐日志,然后您可以为此进行迭代
  • 是的,到期日在 checkout_logs 表中(每个条目代表从图书馆借书)。这个特定页面需要所有书籍信息(来自 books 表)和 checkout_logs 表中的到期日期。有没有办法连接两个表并在rails中显示组合表中的信息?我认为这将是直截了当的。
  • 是的,我用该信息更新了答案。要获取书籍信息,请从我给出的原始答案开始。然后在您看来,您需要遍历 checkout_logs 关系。如果是 api,那么您需要将该关联添加到序列化程序
  • 所以我通过 checkout_log join books 让它工作了,因为 checkout_log 正好有 1 本书我可以在视图中访问它,例如checkout_log.book.title。而我无法按照我尝试的方式进行设置,也无法从其他模型中获取这些字段。感谢您帮助我到达那里!
【解决方案3】:

所以我需要在

book_controllers.rb

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

然后视图可以通过以下方式访问该书:

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 %>

因为 checkout_log 有 1 本书,所以我可以通过这种方式访问​​这本书。我无法以其他方式访问信息,也无法创建一些将所有信息都放在同一级别上的混合体。 (至少我做不到)

【讨论】:

  • 您应该将joins 更改为includes,就像我在我提供的回答中所建议的那样,我在其中介绍了这种情况。包含将确保没有 n+1 查询
猜你喜欢
  • 1970-01-01
  • 2020-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-17
  • 1970-01-01
  • 2015-04-16
相关资源
最近更新 更多