ActiveRecord 生成属性的具体过程是什么?

What is the specific process ActiveRecord generates attributes?

我正在使用 Rails 连接到远程 MySQL 数据库。在执行 ActiveRecord select 甚至 find_by_sql 时,我注意到很多列丢失了。这些列在 Model.connection.select_all 中正确显示,因此问题不在于发送它们的数据库。 Model.new.attributes.keys 给出

["ORIG_ID", "DEST_ID", "CTRL_NUM", "\nTRL_NUM", "INTEGERM", "DTTEGERM", 
"TMTEGERM", "DOC_TYPE", "STEPTYPE", "\nTEPTYPE", "TEXTTYPE", "RT_SEQPE", 
"OSYSEQPE", "OVOLUMEE", "OSUBVOLE", "OFNAMELE", "\nFNAMELE", "TEXTMELE", 
"FVOLUMEE", "FSUBVOLE", "FFNAMELE", "ASYSMELE", "AVOLUMEE", "\nVOLUMEE", 
"TEXTUMEE", "AFNAMEEE", "CONTACTE", "INTSACTE", "GRPSACTE", "\nRPSACTE", 
"INTEGERE", "SEGSGERE", "BYTESERE", "IC_NUMRE", "MSG_NUME", "\nSG_NUME", 
"TEXTNUME", "ODT_DTME", "ODT_ORIG_ID", "ODT_DEST_ID", "\nDT_DEST_ID", 
"INTEGERT_ID", "DDT_DTRT_ID", "DDT_DEST_ID", "DDT_ORIG_ID", "\nDT_ORIG_ID", 
"INTEGERG_ID", "FORMAT_IDID", "AGENCY_IDID", "VERSIONIDID", "\nERSIONIDID", 
"TEXTIONIDID", "ASETIONIDID", "PTETIONIDID", "FILLER1IDID", "SESS_NUMDID"] 

只有 ORIG_IDDEST_IDCTRL_NUMDOC_TYPEODT_ORIG_IDODT_DEST_IDDDT_ORIG_ID, DDT_DEST_ID 除了 /n 导致列类型成为属性并覆盖下一列之外,其余列似乎都继承了前一列的字符

 ORIG_ID
 DEST_ID  Same length, success
 CTRL_NUM New max length, success
\nTRL_NUM First \n character, adds new column and replaces next with field type
 INTEGERM Should be TSTAMP, took type instead
 DTTEGERM Should be DT Shorter than previous, carries all extra characters
 TMTEGERM Should be TM
 DOC_TYPE Same length as previous, overwrites all characters successfully
 STEPTYPE Should be STEP
\nTEPTYPE
 TEXTTYPE Should be PNAME, took type instead
 RT_SEQPE Should be RT_SEQ
 OSYSEQPE etc..
 OVOLUMEE
 OSUBVOLE
 OFNAMELE
\nFNAMELE
 TEXTMELE Should be FSYS
 FVOLUMEE
 FSUBVOLE
 FFNAMELE
 ASYSMELE
 AVOLUMEE
\nVOLUMEE Should be ASUBVOL
 TEXTUMEE
 AFNAMEEE
 CONTACTE
 INTSACTE
 GRPSACTE
\nRPSACTE
 INTEGERE Should be SETS
 SEGSGERE
 BYTESERE
 IC_NUMRE
 MSG_NUME
\nSG_NUME
 TEXTNUME Should be CDT
 ODT_DTME
 ODT_ORIG_ID
 ODT_DEST_ID
\nDT_DEST_ID
 INTEGERT_ID Should be ODT_CTRL_NUM
 DDT_DTRT_ID
 DDT_DEST_ID
 DDT_ORIG_ID
\nDT_ORIG_ID
 INTEGERG_ID Should be DDT_CTRL_NUM
 FORMAT_IDID
 AGENCY_IDID
 VERSIONIDID
\nERSIONIDID
 TEXTIONIDID  Should be TSET, took type instead
 ASETIONIDID
 PTETIONIDID
 FILLER1IDID
 SESS_NUMDID

那么 ActiveRecord 为获取属性的模式而执行的 SQL 是什么?将结果转换为属性的过程是什么?

我的ActiveRecord版本是4.1.8,Rails是4.1.8,MySql2是0.3.18,Ruby是2.1.5p273

Rake:db:schema:dump 给出同样的东西

ActiveRecord::Schema.define() do

  create_table "STEPS", id: false, force: true do |t|
    t.string  "ORIG_ID",      limit: nil, default: "NULL"
    t.string  "DEST_ID",      limit: nil, default: "NULL"
    t.integer "CTRL_NUM",                 default: 0
    t.string  "\nTRL_NUM",    limit: nil, default: "NULL"
    t.string  "INTEGERM",     limit: nil, default: "NULL"
    t.string  "DTTEGERM",     limit: nil, default: "NULL"
    t.string  "TMTEGERM",     limit: nil, default: "NULL"
    t.string  "DOC_TYPE",     limit: nil, default: "NULL"
    t.string  "STEPTYPE",     limit: nil, default: "NULL"
    t.string  "\nTEPTYPE",    limit: nil, default: "NULL"
    t.string  "TEXTTYPE",     limit: nil, default: "NULL"
    t.integer "RT_SEQPE",                 default: 0
    t.string  "OSYSEQPE",     limit: nil, default: "NULL"
    t.string  "OVOLUMEE",     limit: nil, default: "NULL"
    t.string  "OSUBVOLE",     limit: nil, default: "NULL"
    t.string  "OFNAMELE",     limit: nil, default: "NULL"
    t.string  "\nFNAMELE",    limit: nil, default: "NULL"
    t.string  "TEXTMELE",     limit: nil, default: "NULL"
    t.string  "FVOLUMEE",     limit: nil, default: "NULL"
    t.string  "FSUBVOLE",     limit: nil, default: "NULL"
    t.string  "FFNAMELE",     limit: nil, default: "NULL"
    t.string  "ASYSMELE",     limit: nil, default: "NULL"
    t.string  "AVOLUMEE",     limit: nil, default: "NULL"
    t.string  "\nVOLUMEE",    limit: nil, default: "NULL"
    t.string  "TEXTUMEE",     limit: nil, default: "NULL"
    t.string  "AFNAMEEE",     limit: nil, default: "NULL"
    t.string  "CONTACTE",     limit: nil, default: "NULL"
    t.integer "INTSACTE",                 default: 0
    t.integer "GRPSACTE",                 default: 0
    t.string  "\nRPSACTE",    limit: nil, default: "NULL"
    t.string  "INTEGERE",     limit: nil, default: "NULL"
    t.integer "SEGSGERE",                 default: 0
    t.integer "BYTESERE",                 default: 0
    t.integer "IC_NUMRE",                 default: 0
    t.integer "MSG_NUME",                 default: 0
    t.string  "\nSG_NUME",    limit: nil, default: "NULL"
    t.string  "TEXTNUME",     limit: nil, default: "NULL"
    t.string  "ODT_DTME",     limit: nil, default: "NULL"
    t.string  "ODT_ORIG_ID",  limit: nil, default: "NULL"
    t.string  "ODT_DEST_ID",  limit: nil, default: "NULL"
    t.string  "\nDT_DEST_ID", limit: nil, default: "NULL"
    t.string  "INTEGERT_ID",  limit: nil, default: "NULL"
    t.string  "DDT_DTRT_ID",  limit: nil, default: "NULL"
    t.string  "DDT_DEST_ID",  limit: nil, default: "NULL"
    t.string  "DDT_ORIG_ID",  limit: nil, default: "NULL"
    t.string  "\nDT_ORIG_ID", limit: nil, default: "NULL"
    t.string  "INTEGERG_ID",  limit: nil, default: "NULL"
    t.string  "FORMAT_IDID",  limit: nil, default: "NULL"
    t.string  "AGENCY_IDID",  limit: nil, default: "NULL"
    t.string  "VERSIONIDID",  limit: nil, default: "NULL"
    t.string  "\nERSIONIDID", limit: nil, default: "NULL"
    t.string  "TEXTIONIDID",  limit: nil, default: "NULL"
    t.string  "ASETIONIDID",  limit: nil, default: "NULL"
    t.string  "PTETIONIDID",  limit: nil, default: "NULL"
    t.string  "FILLER1IDID",  limit: nil, default: "NULL"
    t.integer "SESS_NUMDID",              default: 0
  end

  add_index "STEPS", ["ECT"], name: "STEPS_05", length: {"ECT"=>"NULL"}, using: :btree
  add_index "STEPS", ["LECT"], name: "STEPS_01", length: {"LECT"=>"NULL"}, using: :btree
  add_index "STEPS", ["LECT"], name: "STEPS_02", length: {"LECT"=>"NULL"}, using: :btree
  add_index "STEPS", ["LECT"], name: "STEPS_03", length: {"LECT"=>"NULL"}, using: :btree
  add_index "STEPS", ["LECT"], name: "STEPS_04", length: {"LECT"=>"NULL"}, using: :btree
  add_index "STEPS", ["LECT"], name: "STEPS_PK", length: {"LECT"=>"NULL"}, using: :btree

end

运行 Model.connection.select_all("SHOW CREATE TABLE STEPS").to_hash 给出

CREATE_TABLE = CREATE TABLE STEPS (ORIG_ID TEXT,DEST_ID TEXT,CTRL_NUM INTEGER,TSTAMP INTEGER,DT TEXT,TM TEXT,DOC_TYPE TEXT,STEP TEXT,PNAME TEXT,RT_SEQ INTEGER,OBJECT TEXT,DATAFILE TEXT,ACTION TEXT,CONTACT TEXT,INTS INTEGER,GRPS INTEGER,SETS INTEGER,SEGS INTEGER,BYTES INTEGER,IC_NUM INTEGER,MSG_NUM INTEGER,CDT TEXT,FORMAT_ID TEXT,AGENCY TEXT,VERSION TEXT,TSET TEXT,ASET TEXT,PT TEXT,SESS_NUM INTEGER,SENDER TEXT, RECEIVER TEXT,GS_SENDER TEXT, GS_RECEIVER TEXT, GS_CTRL_NUM INTEGER)

您的数据库和 schema.rb 中的属性似乎不同步。

您需要查看 this answer to figure out what is shown in the model vs the database and look at this answer,其中显示了当它们不同步时该怎么做。

我在 ActiveRecord Gem 的 abstract_mysql_adapter.rb 文件中找到了我认为是 ActiveRecord 用来获取列名的 SQL。 \Ruby2.1.0\lib\ruby\gems.1.0\gems\activerecord-4.1.8\lib\active_record\connection_adapters\abstract_mysql_adapter.rb

  def columns(table_name)#:nodoc:
    sql = "SHOW FULL FIELDS FROM #{quote_table_name(table_name)}"
    execute_and_free(sql, 'SCHEMA') do |result|
      each_hash(result).map do |field|
        field_name = set_field_encoding(field[:Field])
        new_column(field_name, field[:Default], field[:Type], field[:Null] == "YES", field[:Collation], field[:Extra])
      end
    end
  end