Rails 5.0.1,PostgreSQL-别名外国钥匙和其他桌子,包括失败



tl; dr:

所有表和列的数据库混淆(包括外键(正在引起问题,即使用所有Rails查询从PoststresQL数据库中急切地加载相关模型的结果。 includesjoins等返回错误,而不是应有的。

开始:

我有一个完全混淆的数据库,每个表和列都使用20个字符的拉丁字符串命名,包括外国人。has_many关系的典型迁移是:

class CreateModelOne < ActiveRecord::Migration[5.0]
  def change
    create_table :abcdfeghijklmnopqrst do |t|
      t.boolean :bcdfeghijklmnopqrstu, null: true
      t.float :cdfeghijklmnopqrstuv, null: true, default: 0
      t.timestamps
    end
  end
end
class CreateModelTwo < ActiveRecord::Migration[5.0]
  def change
    create_table :dfeghijklmnopqrstuvx do |t|
      t.date :feghijklmnopqrstuvxy, null: true
      t.time :eghijklmnopqrstuvxyz, null: true
      t.integer :ghijklmnopqrstuvxyza
      t.timestamps
    end
    add_foreign_key :dfeghijklmnopqrstuvx, :abcdfeghijklmnopqrst, column: :ghijklmnopqrstuvxyza
  end
end

在模型文件中,我已将关系列为:

class ModelTwo < ApplicationRecord
  alias_attribute :model_one_id, :ghijklmnopqrstuvxyza
end

当我想急切地加载 ModelTwo S与ModelOne关联时,我正在尝试使用标准includes方法:

@model_one = ModelOne.includes(:model_two).find(particular_model_one_id)

这产生了错误:

NoMethodError: undefined method `association' for nil:NilClass
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/associations/preloader/association.rb:67:in `block in associated_records_by_owner'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/core.rb:349:in `init_with'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/persistence.rb:69:in `instantiate'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/querying.rb:50:in `block (2 levels) in find_by_sql'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/result.rb:52:in `block in each'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/result.rb:52:in `each'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/result.rb:52:in `each'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/querying.rb:50:in `map'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/querying.rb:50:in `block in find_by_sql'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activesupport-5.0.2/lib/active_support/notifications/instrumenter.rb:21:in `instrument'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/activerecord-5.0.2/lib/active_record/querying.rb:49:in `find_by_sql'
    from /home/mark/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/bullet-5.5.1/lib/bullet/active_record5.rb:25:in `find_by_sql'
    from 

对我来说这似乎很奇怪,因为单独运行查询正常工作,如:

ModelOne.find(particular_model_one_id)

返回预期的ModelOne

ModelTwo.where(model_one_id: particular_model_one_id)

返回ModelTwo s的预期集。这些产生与.includes完全相同的SELECT查询。

查看ModelOne.includes(:model_two)返回的对象返回我期望的查询,以及消息:

ModelOne Load (2.3ms)  SELECT "abcdfeghijklmnopqrst".* FROM "abcdfeghijklmnopqrst"
  ModelTwo Load (2.6ms)  SELECT "dfeghijklmnopqrstuvx".* FROM "dfeghijklmnopqrstuvx" WHERE "dfeghijklmnopqrstuvx"."ghijklmnopqrstuvxyza" IN (4, 1, 2, 3, 8, 5, 7, 6, 9, 10, 11, 14, 12, 13, 18, 15, 17, 16, 19, 20, 21, 22, 28, 23, 25, 24, 27, 26, 29, 30, 31, 34, 32, 33, 38, 35, 37, 36, 39, 40, 41, 42, 48, 43, 45, 44, 47, 46, 49, 50, 51, 52, 57, 53, 55, 54, 58, 56)
(Object doesn't support #inspect)

我认为这也意味着(Object doesn't support #association)

免责声明:

我已经阅读了相关的Stackoverflow条目和GitHub上的Rails问题,它们都没有出现在此特定问题上,至少尝试实施工作中提供的建议的尝试。

我已经更改了将foreign_key添加到迁移中的直接references的应用,该迁移产生了完全相同的行为。

我当然可以将适当的查询写入数据库或不急切的加载相关模型,但是这似乎效率低下的WRT开发人员时间是通过手写的JOIN来解析结果,还需要处理(更多((更多(真实模型具有多个列和一个以上的关联。

,所以我做了一个模块来围绕该问题,随时可以在您想要的可怕混淆的DBS中使用...准备黑客:

module AliasedAttrsHelper
  def find_by_includes_aliased(id_col, id_match, *associations)
    # get foreign key column name
    class_name = self.to_s
    fk_column = class_name[0]
    class_name.split('').each_with_index do |char,i| 
      next if i == 0 
      fk_column += char.match(/[A-Z]/) ? "_#{char}" : char
    end
    fk_column += '_id'
    fk_column.downcase!
    # make id string and table name
    id_column = self.attribute_aliases[id_col.to_s]
    id_table = self.table_name
    # make the set of associations to query on
    associations.map! do |ass|
      name = ass.to_s.singularize.capitalize.constantize
      { class: name, table: name.table_name, fk: name.new.attribute_aliases[fk_column] }
    end
    # perform the composite query transaction
    klass = ""
    ActiveRecord::Base.transaction do
      klass = self.find_by_sql("SELECT c.* FROM #{id_table} AS c WHERE c.#{id_column} = '#{id_match}'")[0]
      associations.each do |ass|
        ass[:class].find_by_sql("SELECT b.* FROM #{ass[:table]} AS b JOIN #{id_table} as c ON c.id = b.#{ass[:fk]} WHERE c.#{id_column} = '#{id_match}'")
      end
    end
    klass
  end
end

相关内容

最新更新