2017-04-26 16 views
1

TL;博士:的Rails 5.0.1,PostgreSQL的 - 别名外键和别名表与包括失败

所有表和列(包括外键)的数据库模糊处理以获得在预先加载的结果导致问题来自postresql数据库的相关模型使用所有rails查询。 includes,joins等返回错误,而不是他们应该。

开始:

我有一个完全混淆的数据库,每个表和列使用的拉丁字母,包括foreignkeys一个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 

时,我想急于负载ModelOne我想使用标准的相关ModelTwo小号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小号。这些产生与.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)

免责声明:

我已阅读GitHub上的相关条目计算器和Rails的问题,他们没有出现针对这个特定问题,至少尝试实现内提供做工作的建议。

我改变了我的申请,将foreign_key添加到迁移中的直接references,这些迁移产生完全相同的行为。

我当然可以,写出正确的数据库查询或不急于负载相关的模型,但是这似乎效率不高WRT开发时间从书面JOIN还用手解析结果需要处理和(更多)真实模型有多个列和多个关联。

+1

我也告诉我的鸭子所有关于它,但她对这件事情非常安静。 – MarkJL

+2

太长了,您能否在问题的基础上添加TL; DR版本? –

回答

0

所以我做了一个模块来解决这个问题,随意使用你可怕的混淆数据库,如你所愿......准备冒风险:

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 
+0

另外,改进和建议非常受欢迎 – MarkJL

+0

条件是您的别名外键具有别名'model_name_id',当然。 – MarkJL