如何在 Ruby on Rails 中通过关联订购 has_many?

Posted

技术标签:

【中文标题】如何在 Ruby on Rails 中通过关联订购 has_many?【英文标题】:How do I order a has_many through association in Ruby on Rails? 【发布时间】:2011-01-13 10:04:20 【问题描述】:

鉴于以下 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 对用户进行排序?

【问题讨论】:

【参考方案1】:

has_many :users, -> order(:last_name, :first_name) , :through => :assignments, source: 'user'

【讨论】:

【参考方案2】:

我正在使用 Rails (5.0.0.1),并且可以在我的模型组中使用此语法进行排序,该组通过 group_users 拥有许多用户:

# Associations.
has_many :group_users
has_many :users, ->  order(:name) , through: :group_users

根据您的需要调整代码。

【讨论】:

【参考方案3】:

Rails 3.x 版本:

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

更新:这只适用于 Rails 3.x(可能在此之前)。对于 4+,请参阅其他答案。

【讨论】:

对我也很好。干净的解决方案,不会像第一个答案那样影响排序模型的其他功能。 不工作,它说:order is not a key。以下拒绝的答案是有效的。【参考方案4】:

这对我有用(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

【讨论】:

【参考方案5】:

由于 Rails 4 不推荐使用条件参数,因此应该使用范围块:

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

【讨论】:

目前在 Rails 4 中似乎存在一个错误,github.com/rails/rails/issues/17904 "在 Rails 4.0 中,order 子句被忽略。在 Rails 4.1 中,生成了无效的 SQL。" 它甚至没有给我,只是完全忽略了没有消息的订单子句。【参考方案6】:

您还可以在分配表上创建一个新的“排序顺序”列,并添加一个默认范围,例如

default_scope  order('sort_order desc')

到您的分配模型。

【讨论】:

【参考方案7】:

M.G.Palmer's 方法效果很好,但是涉及到表名。有更好的方法:

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

【讨论】:

但是如果顺序是由连接表决定的呢?例如分配...但没有定义分配的默认范围..【参考方案8】:

这对你有用吗?

# User.rb

class User < ActiveRecord::Base
 default_scope :order => 'last_name ASC'
 ...
end

当排序需要不同时,您可以定义其他命名范围。

http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping

【讨论】:

很抱歉提出一个老问题,但这可能与未来的读者有关:某些插件(例如 acts_as_list)不能与 default_scope 一起正常工作。 也遇到了这个问题,因为我遇到了类似的问题,但需要对 :through 表中的字段进行排序 - 令人敬畏的是 default_scope 也适用于 :through 表,并且通过检索记录这种关系将尊重秩序。 拥有default_scope 是一种非常糟糕的做法。有很多关于这个主题的文章,例如:piechowski.io/post/why-is-default-scope-bad-rails

以上是关于如何在 Ruby on Rails 中通过关联订购 has_many?的主要内容,如果未能解决你的问题,请参考以下文章

在 ruby​​ on rails 3 中通过电子邮件创建订阅

Ruby on Rails 高级 JSON 序列化

如何正确销毁 ruby​​ on rails 中的关联记录?

ruby on rails 在关联调用中使用变量

在 Ubuntu 上为 Ruby on Rails 安装 PostgreSQL

如何关联数据库中通过 Rails 中的多对多关系连接的两个条目?