如何在Ruby on Rails中通过has_many关联进行排序?

62

给定以下AR模型,当给出任务句柄时,我希望按姓氏字母顺序对用户进行排序:

#user
has_many :assignments
has_many :tasks, :through => :assignments    

#assignment
belongs_to :task
belongs_to :user

#task
has_many :assignments
has_many :users, :through => :assignments
我想要获取一个任务,然后跳转到被分配的用户,并按字母顺序排序用户列表
我一直在考虑是否可以像这样将:order子句添加到has_many :users, :through => :assignments中:
#task.rb
has_many :assignments
has_many :users, :through => :assignments, :order => 'last_name, first_name'

然而这并不起作用。

当给定任务时,如何按last_name对用户进行排序?

8个回答

72

由于Rails 4中不再支持条件参数,因此应使用范围块:

has_many :users, -> { order 'users.last_name, users.first_name' }, :through => :assignments

1
目前看来,Rails 4 存在一个 bug,与此 https://github.com/rails/rails/issues/17904 相关。"在 Rails 4.0 中,order 子句被忽略了。在 Rails 4.1 中,会生成无效的 SQL。" - Nate
它甚至不给我任何提示,只是完全忽略了带有排序条件的语句,没有任何消息。 - Wylliam Judd

37

Rails 3.x 版本:

has_many :users, :through => :assignments, :order => 'users.last_name, users.first_name'

更新:此方法仅适用于Rails 3.x(或许在此之前也适用)。对于4+版本,请查看其他答案。


我也觉得这个方案很好。与第一个答案不同,这是一种干净的解决方案,不会影响排序模型的其他功能。 - barancw
2
无法工作,它说:“order不是一个键”。以下是Denial的答案有效。 - RohitPorwal

12

M.G.Palmer的方法效果不错,但涉及表名。有更好的方法:

has_many :users, :through => :assignments, :order => [ :last_name, :first_name ]

1
但是如果顺序是由连接表确定的怎么办?例如,分配...但是没有在分配上定义默认作用域... - Tilo

7

这对我有用(Rails 4.2)

对于通过映射进行排序是无效的,即使将流派进行排序也不足以保留排序。

has_many    :disk_genre_maps,
            -> {order('disk_genre_map.sort_order')},
            :inverse_of => :disk,
            :dependent  => :destroy,
            :autosave   => true


has_many    :genres,    # not sorted like disk_genre_maps
            :through    => :disk_genre_maps,
            :source     => :genre,
            :autosave   => true

所以我针对每个实例进行覆盖:
def genres  # redefine genres per instance so that the ordering is preserved
    self.disk_genre_maps.map{|dgm|dgm.genre}
end

为了使它适用于作业,应该像这样(未经测试):

要使其适用于作业,应该如下所示(未经测试)

def genres= some_genres
    self.disk_genre_maps = some_genres.map.with_index do |genre, index|
        DiskGenreMap.new(disk:self, genre:genre, sort_order:index)
    end
end

7
我使用Rails(5.0.0.1)可以在我的模型Group中通过group_users实现以下语法来进行排序:
# Associations.
has_many :group_users
has_many :users, -> { order(:name) }, through: :group_users

根据你的需求调整代码,这样就可以正常运行。


6

2
抱歉打扰了一个旧问题,但这可能与未来的读者有关:一些插件(例如acts_as_list)与default_scope无法正常工作。 - user269597
同时也将此顶起,因为我有一个类似的问题,但需要在 :through 表中对一个字段进行排序——令人惊奇的是,default_scope 也适用于 :through 表,并且通过该关系检索到的记录将遵守该顺序。 - MBHNYC
1
使用 default_scope 是一种非常糟糕的做法。关于这个话题已经有很多文章写过了,例如:https://piechowski.io/post/why-is-default-scope-bad-rails/ - dpaluy

2
您还可以在作业表上创建一个新的“sort_order”列,并添加默认范围,如下所示:
default_scope { order('sort_order desc')}

将其添加到您的作业模型中。


1

使用has_many方法创建与users关联的关系,通过:assignments关联表进行关联,关联表中的user作为源表,同时按照last_namefirst_name排序。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接