提交 54122067 编写于 作者: G Godfrey Chan

Handle aliased attributes in ActiveRecord::Relation.

When using symbol keys, ActiveRecord will now translate aliased attribute names to the actual column name used in the database:

With the model

  class Topic
    alias_attribute :heading, :title
  end

The call

  Topic.where(heading: 'The First Topic')

should yield the same result as

  Topic.where(title: 'The First Topic')

This also applies to ActiveRecord::Relation::Calculations calls such as `Model.sum(:aliased)` and `Model.pluck(:aliased)`.

This will not work with SQL fragment strings like `Model.sum('DISTINCT aliased')`.

Github #7839

*Godfrey Chan*
上级 09ac1776
* Handle aliased attributes in ActiveRecord::Relation.
When using symbol keys, ActiveRecord will now translate aliased attribute names to the actual column name used in the database:
With the model
class Topic
alias_attribute :heading, :title
end
The call
Topic.where(heading: 'The First Topic')
should yield the same result as
Topic.where(title: 'The First Topic')
This also applies to ActiveRecord::Relation::Calculations calls such as `Model.sum(:aliased)` and `Model.pluck(:aliased)`.
This will not work with SQL fragment strings like `Model.sum('DISTINCT aliased')`.
*Godfrey Chan*
* Mute `psql` output when running rake db:schema:load.
*Godfrey Chan*
......
......@@ -27,7 +27,7 @@ def count(column_name = nil, options = {})
# Calculates the average value on a given column. Returns +nil+ if there's
# no row. See +calculate+ for examples with options.
#
# Person.average('age') # => 35.8
# Person.average(:age) # => 35.8
def average(column_name, options = {})
calculate(:average, column_name, options)
end
......@@ -36,7 +36,7 @@ def average(column_name, options = {})
# with the same data type of the column, or +nil+ if there's no row. See
# +calculate+ for examples with options.
#
# Person.minimum('age') # => 7
# Person.minimum(:age) # => 7
def minimum(column_name, options = {})
calculate(:minimum, column_name, options)
end
......@@ -45,7 +45,7 @@ def minimum(column_name, options = {})
# with the same data type of the column, or +nil+ if there's no row. See
# +calculate+ for examples with options.
#
# Person.maximum('age') # => 93
# Person.maximum(:age) # => 93
def maximum(column_name, options = {})
calculate(:maximum, column_name, options)
end
......@@ -54,7 +54,7 @@ def maximum(column_name, options = {})
# with the same data type of the column, 0 if there's no row. See
# +calculate+ for examples with options.
#
# Person.sum('age') # => 4562
# Person.sum(:age) # => 4562
def sum(*args)
if block_given?
ActiveSupport::Deprecation.warn(
......@@ -101,6 +101,10 @@ def sum(*args)
def calculate(operation, column_name, options = {})
relation = with_default_scope
if column_name.is_a?(Symbol) && attribute_aliases.key?(column_name.to_s)
column_name = attribute_aliases[column_name.to_s].to_sym
end
if relation.equal?(self)
if has_include?(column_name)
construct_relation_for_association_calculations.calculate(operation, column_name, options)
......@@ -149,11 +153,17 @@ def calculate(operation, column_name, options = {})
#
def pluck(*column_names)
column_names.map! do |column_name|
if column_name.is_a?(Symbol) && self.column_names.include?(column_name.to_s)
"#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
else
column_name
if column_name.is_a?(Symbol)
if attribute_aliases.key?(column_name.to_s)
column_name = attribute_aliases[column_name.to_s].to_sym
end
if self.columns_hash.key?(column_name.to_s)
column_name = "#{connection.quote_table_name(table_name)}.#{connection.quote_column_name(column_name)}"
end
end
column_name
end
if has_include?(column_names.first)
......
......@@ -6,6 +6,10 @@ def self.build_from_hash(klass, attributes, default_table)
attributes.each do |column, value|
table = default_table
if column.is_a?(Symbol) && klass.attribute_aliases.key?(column.to_s)
column = klass.attribute_aliases[column.to_s]
end
if value.is_a?(Hash)
if value.empty?
queries << '1=0'
......
......@@ -28,6 +28,10 @@ def test_should_average_field
assert_equal 53.0, value
end
def test_should_resolve_aliased_attributes
assert_equal 318, Account.sum(:available_credit)
end
def test_should_return_decimal_average_of_integer_field
value = Account.average(:id)
assert_equal 3.5, value
......@@ -352,6 +356,10 @@ def test_count_with_distinct
assert_equal 4, Account.select(:credit_limit).uniq.count
end
def test_count_with_aliased_attribute
assert_equal 6, Account.count(:available_credit)
end
def test_count_with_column_and_options_parameter
assert_equal 2, Account.where("credit_limit = 50 AND firm_id IS NOT NULL").count(:firm_id)
end
......@@ -488,6 +496,10 @@ def test_pluck_in_relation
assert_equal [contract.id], company.contracts.pluck(:id)
end
def test_pluck_on_aliased_attribute
assert_equal 'The First Topic', Topic.order(:id).pluck(:heading).first
end
def test_pluck_with_serialization
t = Topic.create!(:content => { :foo => :bar })
assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
......
......@@ -47,7 +47,8 @@ def test_find_with_string
def test_exists
assert Topic.exists?(1)
assert Topic.exists?("1")
assert Topic.exists?(:author_name => "David")
assert Topic.exists?(title: "The First Topic")
assert Topic.exists?(heading: "The First Topic")
assert Topic.exists?(:author_name => "Mary", :approved => true)
assert Topic.exists?(["parent_id = ?", 1])
assert !Topic.exists?(45)
......
......@@ -5,6 +5,7 @@
require 'models/post'
require 'models/comment'
require 'models/edge'
require 'models/topic'
module ActiveRecord
class WhereTest < ActiveRecord::TestCase
......@@ -80,6 +81,13 @@ def test_polymorphic_sti_nested_where
assert_equal expected.to_sql, actual.to_sql
end
def test_aliased_attribute
expected = Topic.where(heading: 'The First Topic')
actual = Topic.where(title: 'The First Topic')
assert_equal expected.to_sql, actual.to_sql
end
def test_where_error
assert_raises(ActiveRecord::StatementInvalid) do
Post.where(:id => { 'posts.author_id' => 10 }).first
......
......@@ -206,6 +206,8 @@ class Account < ActiveRecord::Base
belongs_to :firm, :class_name => 'Company'
belongs_to :unautosaved_firm, :foreign_key => "firm_id", :class_name => "Firm", :autosave => false
alias_attribute :available_credit, :credit_limit
def self.destroyed_account_ids
@destroyed_account_ids ||= Hash.new { |h,k| h[k] = [] }
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册