Ruby-on-rails 您如何通过在 SQL 中没有属性的自定义模型方法进行排序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4055799/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:30:36  来源:igfitidea点击:

How do you order by a custom model method that has no attribute in SQL?

ruby-on-rails

提问by Trip

Previously I ordered my posts as this:

以前我这样订购我的帖子:

@posts = Post.find(:all, :order => "created_at DESC")

But now I want to replace created_atwith a custom method I wrote in the Post model that gives a number as its result.

但现在我想created_at用我在 Post 模型中编写的自定义方法替换,该方法给出一个数字作为其结果。

My guess:

我猜:

@posts = Post.find(:all, :order => "custom_method DESC")

which fails..

失败了..

回答by jdl

It fails because you are asking your db to do the sorting.

它失败是因为您要求您的数据库进行排序。

@posts = Post.all.sort {|a,b| a.custom_method <=> b.custom_method}

@posts = Post.all.sort {|a,b| a.custom_method <=> b.custom_method}

Note that this becomes non-trivial when you want to start paging results and no longer wish to fetch .all. Think about your design a bit before you go with this.

请注意,当您想要开始分页结果并且不再希望 fetch 时,这变得非常重要.all。在你开始之前考虑一下你的设计。

回答by jwarchol

Just to expand on @Robbie's answer

只是为了扩展@Robbie的回答

Post.all.sort_by {|post| post.custom_method }.reverse

回答by Sia

As the first answer noted, order is an Active Record command that essentially does a SQL query on your database, but that field doesn't actually exist in your database.

正如第一个答案所指出的,order 是一个 Active Record 命令,它本质上对您的数据库执行 SQL 查询,但该字段实际上并不存在于您的数据库中。

As someone else commented, you can more cleanly run the Ruby method sort_by by using the ampersand (more info here):

正如其他人评论的那样,您可以使用与号(更多信息在这里)更干净地运行 Ruby 方法 sort_by :

Post.all.sort_by(&:custom_method)

Post.all.sort_by(&:custom_method)

However, things do get complicated depending on what you want to do in your view. I'll share a case I recently did in case that helps you think through your problem. I needed to group my resource by another resource called "categories", and then sort the original resource by "netvotes" which was a custom model method, then order by name. I did it by:

但是,根据您在视图中想要做什么,事情确实会变得复杂。我将分享我最近做的一个案例,以帮助您思考问题。我需要按另一个名为“类别”的资源对我的资源进行分组,然后按“netvotes”(一种自定义模型方法)对原始资源进行排序,然后按名称排序。我是这样做的:

  • Ordering by name in the controller: @resources = Resource.order(:name)
  • Grouping by category in the outer loop of the view: <% @resources.group_by(&:category).each do |category, resources| %>
  • Then sorting the resources by votes in the partial for resources: <%= render resources.sort_by(&:netvotes).reverse %>
  • 在控制器中按名称排序: @resources = Resource.order(:name)
  • 在视图的外循环中按类别分组: <% @resources.group_by(&:category).each do |category, resources| %>
  • 然后在资源的部分中通过投票对资源进行排序: <%= render resources.sort_by(&:netvotes).reverse %>

The view is a bit confusing, so here is the full view loop in index.html.erb:

视图有点混乱,所以这里是 index.html.erb 中的完整视图循环:

<% @resources.group_by(&:category).each do |category, resources| %>
  <div class="well">
    <h3 class="brand-text"><%= category.name %></h3>
    <%= render resources.sort_by(&:netvotes).reverse %>
  </div>
<% end %>

And here is the _resource.html.erb partial:

这是 _resource.html.erb 部分:

<div class="row resource">
  <div class="col-sm-2 text-center">
    <div class="vote-box">
      <%= link_to fa_icon('chevron-up lg'), upvote_resource_path(resource), method: :put %><br>
      <%= resource.netvotes %><br>
      <%= link_to fa_icon('chevron-down lg'), downvote_resource_path(resource), method: :put %>
    </div>
  </div>
  <div class="col-sm-10">
    <%= link_to resource.name, resource.link, target: "_blank" %>
    <p><%= resource.notes %></p>
  </div>
</div>

回答by MZaragoza

This is a bit more complicated than what I like but this I like to keep my sort to stay as a active record model so its bit more complicated than just

这比我喜欢的要复杂一些,但我喜欢保持我的排序作为一个活跃的记录模型,所以它比仅仅复杂一点

Post.all.sort_by {|post| post.custom_method }

what I do is:

我做的是:

ids = Post.all.sort_by {|post| post.custom_method }.map(&:ids)
Post.for_ids_with_order(ids)

this is a custom scope in the Post model

这是 Post 模型中的自定义范围

#app/models/post.rb
class Post < ApplicationRecord
  ...
    scope :for_ids_with_order, ->(ids) {
    order = sanitize_sql_array(
      ["position(id::text in ?)", ids.join(',')]
    )
    where(:id => ids).order(order)
  }

  ...
end

I hope that this help

我希望这有帮助

回答by Robbie

Well, just Post.find(:all)would return an array of AR objects. So you could use Array.sort_by and pass it a block, and since those records are already fetched, you can access the virtual attribute inside the block that sort_by takes.

好吧,只会Post.find(:all)返回一组 AR 对象。因此,您可以使用 Array.sort_by 并将其传递给一个块,并且由于这些记录已经被提取,您可以访问 sort_by 所采用的块内的虚拟属性。

RDoc: Enumerable.sort_by

RDoc:Enumerable.sort_by

回答by yogendra

in rails 3 we can do this as: Post.order("custom_method DESC")
When upgrading app from rails2 to rails3

在 rails 3 中,我们可以这样做:Post.order("custom_method DESC")
当将应用程序从 rails2 升级到 rails3 时