Rails 3 Active Record关系顺序:使用hash而不是string

时间:2011-11-15 14:36:47

标签: ruby-on-rails ruby activerecord ruby-on-rails-3.1

要在Rails 3中对关系进行排序,我们必须这样做:

User.where(:activated => true).order('id ASC')

但我认为:

User.where(:activated => true).order(:id => :asc)

会更有意义,因为字段名称的转义方式应取决于适配器(SqlLite vs Mysql vs PostgreSQL),对吗?

有类似的东西吗?

3 个答案:

答案 0 :(得分:2)

据我所知,ActiveRecord中内置的语法没有选项,但添加一个语句应该不难。我找到了order中定义的lib/active_record/relation/query_methods.rb方法。从理论上讲,你应该能够做到这样的事情:

module ActiveRecord
  module QueryMethods
    def order(*args)
      args.map! do |arg|
        if arg.is_a? Hash
          # Format a string out of the hash that matches the original AR style
          stringed_arg
        else
          arg
        end
      end

      super
    end
  end
end

答案 1 :(得分:2)

我认为关键问题是: ActiveRecord API不知道排序语义。它只接受一个字符串并绕过底层数据库。幸运的是,Sqlite,MySQL和PostgreSQL在order语法方面没有区别。

我认为ActiveRecord不能很好地完成这种抽象,也不需要这样做。它适用于关系数据库,但很难与NoSQL集成,例如。 MongoDB的。

另一个着名的Ruby ORM

DataMapper做了更好的抽象。 Take a look at its query syntax

@zoos_by_tiger_count = Zoo.all(:order => [ :tiger_count.desc ])

API知道排序语义。默认情况下,DataMapper将生成SQL订单声明:

https://github.com/datamapper/dm-do-adapter/blob/master/lib/dm-do-adapter/adapter.rb#L626-634

def order_statement(order, qualify)
  statements = order.map do |direction|
    statement = property_to_column_name(direction.target, qualify)
    statement << ' DESC' if direction.operator == :desc
    statement
  end

  statements.join(', ')
end

但是,可以在数据库适配器层覆盖:

https://github.com/solnic/dm-mongo-adapter/blob/master/lib/dm-mongo-adapter/query.rb#L260-264

def sort_statement(conditions)
  conditions.inject([]) do |sort_arr, condition|
    sort_arr << [condition.target.field, condition.operator == :asc ? 'ascending' : 'descending']
  end
end

TL; DR:

  • 如果您只使用SqlLite,Mysql和PostgreSQL,则无需担心语法问题。
  • 为了更好的抽象,您可以尝试使用DataMapper。

答案 2 :(得分:0)

对于这种特殊情况,您可以放弃“ASC”。 bit,因为所有数据库中的排序都是隐式升序

Foo.order(:bar)

我知道这并不能涵盖你想做order by bar desc的情况,但实际上由于这个并不重要,除非你使用的功能是order by子句,在这种情况下可能会有squeel之类的东西帮助