【问题标题】:Rails ActiveRecord: legacy table without primary key shows nil for result?Rails ActiveRecord:没有主键的旧表显示结果为零?
【发布时间】:2011-11-01 21:45:47
【问题描述】:

我有一个 Rails 应用程序,它将位于一个遗留数据库之上,其中包含一些我必须处理的丑陋表。一个是与features 相关的feature_attributes 表。问题是这个feature_attributes 表没有主键。我不认为这会是一个问题,但显然它是。我的模型名称与表名称不同,但我使用 set_table_name 指定正确的名称。

class Feature < ActiveRecord::Base
  has_many :feature_attributes
end

class FeatureAttribute < ActiveRecord::Base
  set_table_name 'feature_attribute'
  belongs_to :feature
end

一旦我加载了一个我知道与feature_attributes 相关的功能并在其上调用feature.feature_attributes,我就会得到nil。事实上,即使FeatureAttribute.first 给了我nilFeatureAttribute.any? 的结果返回 false。我担心 ActiveRecord 没有从表中读取任何数据,因为没有主键。这是怎么回事?

feature_attribute 表具有以下列。

feature_id
attribute_name
attribute_value
created_date
modified_date

救命!

我还会注意到,直接针对 MySQL 服务器运行生成的 SQL 实际上得到了我想要的行。

SELECT `feature_attribute`.* FROM `feature_attribute` WHERE `feature_attribute`.`feature_id` = 24;

编辑:非常抱歉。下次我将学习检查我的database.yml。显然,我正在从具有相同特征表的测试数据库中读取数据,但 feature_attribute 表完全是空的。

我觉得自己像个白痴。谢谢大家的帮助;我赞成大家为你们的麻烦投票。我确实喜欢几乎每个人的回答。 (我可以自己投反对票吗?:))

【问题讨论】:

    标签: ruby-on-rails database activerecord


    【解决方案1】:

    尝试同时设置主键:

    class FeatureAttribute < ActiveRecord::Base
      set_table_name 'feature_attribute'
      set_primary_key 'feature_id'
    
      belongs_to :feature
    end
    

    更新

    我认为您的问题出在其他任何地方。我刚刚测试过,ActiveRecords 可以很好地处理没有主键的表:

    对于一个简单的表格:

    class CreateThings < ActiveRecord::Migration
      def change
        create_table :things, :id => false do |t|
          t.string :name
    
          t.timestamps
        end
      end
    end
    

    在控制台中:

    Loading development environment (Rails 3.1.1)
    irb(main):001:0> Thing.create(:name=>'A name for the thing')
       (0.1ms)  BEGIN
      SQL (0.3ms)  INSERT INTO `things` (`created_at`, `name`, `updated_at`) VALUES ('2011-11-02 16:33:48', 'A name for the thing', '2011-11-02 16:33:48')
       (40.3ms)  COMMIT
    => #<Thing name: "A name for the thing", created_at: "2011-11-02 16:33:48", updated_at: "2011-11-02 16:33:48">
    irb(main):002:0> Thing.first
      Thing Load (0.7ms)  SELECT `things`.* FROM `things` LIMIT 1
    => #<Thing name: "A name for the thing", created_at: "2011-11-02 16:33:48", updated_at: "2011-11-02 16:33:48">
    irb(main):003:0> 
    

    更新 2 不太好:

    irb(main):003:0> Thing.create(:name=>'Another thing')
       (0.2ms)  BEGIN
      SQL (0.4ms)  INSERT INTO `things` (`created_at`, `name`, `updated_at`) VALUES ('2011-11-02 16:40:59', 'Another thing', '2011-11-02 16:40:59')
       (35.4ms)  COMMIT
    => #<Thing name: "Another thing", created_at: "2011-11-02 16:40:59", updated_at: "2011-11-02 16:40:59">
    irb(main):004:0> Thing.first
      Thing Load (0.5ms)  SELECT `things`.* FROM `things` LIMIT 1
    => #<Thing name: "A name for the thing", created_at: "2011-11-02 16:33:48", updated_at: "2011-11-02 16:33:48">
    irb(main):005:0> Thing.last
      Thing Load (11.8ms)  SELECT `things`.* FROM `things` ORDER BY `things`.`` DESC LIMIT 1
    Mysql2::Error: Unknown column 'things.' in 'order clause': SELECT  `things`.* FROM `things`  ORDER BY `things`.`` DESC LIMIT 1
    ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'things.' in 'order clause': SELECT  `things`.* FROM `things`  ORDER BY `things`.`` DESC LIMIT 1
    

    【讨论】:

    • 不确定你是否读得好,但表名为FeatureAttribute,它是belongs_to :feature,所以feature_id永远不可能是主键。
    • feature_id 实际上属于feature 而不是feature_attribute...实际的feature_attribute 表没有主键可言。
    • 是的,你是对的,我没读好。但是给feature_attributes表添加主键有什么问题呢?
    • 我刚做了一个测试。阅读我的更新
    • 另请阅读更新 2。)如果我是你,我只会添加一个主键。
    【解决方案2】:

    如果某个属性对于一个特征只能出现一次,则假设attribute_name 是键,与feature_id 结合使用。 Rails 不支持开箱即用的复合主键,但有一个名为 composite_primary_keys 的 gem 支持它。

    然后你可以在你的模型中写

    set_primary_keys :feature_id, :attribute_name
    

    如果一个属性名可能出现多次,那么feature_idattribute_nameattribute_value的组合是关键,你应该写

    set_primary_keys :feature_id, :attribute_name, :attribute_value
    

    希望这会有所帮助。

    [编辑] 替代方法:

    以上显然是不够的,所以你还可以这样做:

    class Feature
      has_many :feature_attributes, :finder_sql => 'select * from feature_attributes where feature_id=\'#{id}\''
    end
    

    希望这会有所帮助:)

    【讨论】:

    • 是的,这是个好主意,但不幸的是,一些 attribute_name(s) 会针对多个功能重复多次,因此每个功能并不是唯一的。
    • 嗯,您是说attribute_name 用于多个功能?那么feature_id + attribute_name 可能是关键,对吧?或者,您是说attribute_name 在单个功能中出现不同时间,那么feature_id + attribute_name + attribute_value 应该是关键,对吧?那你为什么不能用这个呢?
    • 啊,我猜你的第二个条件对我来说没有心理处理;一个属性名称可以针对单个功能多次出现,因此我将试一试第二个选项。哦!
    • 我查看了相关属性表背后的 SQL,果然,这三个字段被列为主键 (PRIMARY KEY (feature_id,attribute_name,attribute_value)。我加载了composite_primary_keys 并将这三个字段定义为主键,但没有爱。我仍然无法使用 ActiveRecord 从该表中查询与 feature 相关的单行或单独查询。
    • ActiveRecord 会生成哪些查询?也许我们可以在那里看到一些错误。
    【解决方案3】:
    class Feature < ActiveRecord::Base
      self.table_name = 'legacy_table_name'
      self.primary_key = nil
    end
    

    然后使用Feature.find_by field:'value' 或其他 ActiveRecord 查询进行查询。

    【讨论】:

      【解决方案4】:

      如果无法更新表以仅添加一个自动增量主键,那么您最好手动处理它:

      class Feature < ActiveRecord::Base
        # get rid of has_many feature_attributes and use this instead
        def feature_attributes
          FeatureAttribute.find_by_sql(["select * from feature_attribute where feature_id = ?", self.id])
        end
      end
      

      【讨论】:

      • 您的代码(本身)会引发错误,因为 Rails 无法在 Feature 的实例上找到 find_by_sql 方法。将Feature 附加到前面允许它运行,但它仍然不会检索任何结果(与将Feature 替换为FeatureAttribute 相同)。不过,生成的 SQL 看起来不错:select * from feature_attribute where feature_id = 24
      • @BenjaminKreeger 你是对的,很抱歉我忘记了 find_by_sql 是类级别的。您应该使用 FeatureAttribute.find_by_sql,因为这样返回的数组是 FeatureAttribute 对象,可以访问该类的方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-15
      相关资源
      最近更新 更多