Rails ActiveRecord: устаревшая таблица без первичного ключа показывает ноль для результата?

#ruby-on-rails #database #activerecord

#ruby-on-rails #База данных #activerecord

Вопрос:

У меня есть приложение Rails, которое будет располагаться поверх устаревшей базы данных с некоторыми уродливыми таблицами, с которыми мне приходится иметь дело. Одна feature_attributes из них — таблица, связанная с features . Проблема в том, что у этой 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 дает мне nil . Результат FeatureAttribute.any? возвращает false . Я обеспокоен тем, что ActiveRecord не считывает какие-либо данные из таблицы, потому что нет первичного ключа. Это то, что здесь происходит?

В feature_attribute таблице есть следующие столбцы.

 feature_id
attribute_name
attribute_value
created_date
modified_date
  

Помогите!

Я также отмечу, что запуск сгенерированного SQL непосредственно на сервере MySQL фактически дает мне нужные строки.

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

РЕДАКТИРОВАТЬ: мне очень жаль. В следующий раз я научусь проверять свой database.yml . Очевидно, я читал из тестовой базы данных, в которой была идентичная таблица функций, но feature_attribute таблица была полностью пустой.

Я чувствую себя идиотом. Спасибо за вашу помощь, все; Я голосую за вас всех за ваши проблемы. Мне понравился ответ почти каждого. (Могу ли я сам проголосовать? :))

Ответ №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
  

Комментарии:

1. Не уверен, что вы хорошо читаете, но вызывается таблица FeatureAttribute , и она belongs_to :feature , поэтому feature_id никогда не может быть первичным ключом.

2. feature_id на самом деле принадлежит feature , а не feature_attribute … фактическая feature_attribute таблица не имеет первичного ключа, о котором можно говорить.

3. Да, вы правы, я плохо читал. Но в чем тогда проблема с добавлением первичного ключа в feature_attributes таблицу?

4. Я только что провел тест. Прочитайте мое ОБНОВЛЕНИЕ

5. Также прочитайте ОБНОВЛЕНИЕ 2.) На вашем месте я бы просто добавил первичный ключ.

Ответ №2:

Если определенный атрибут может встречаться только один раз для функции, вы предполагаете attribute_name , что это ключ в сочетании с feature_id . Rails не поддерживает составные первичные ключи из коробки, но есть драгоценный камень под названием composite_primary_keys, который поддерживает именно это.

Затем в вашей модели вы можете написать

 set_primary_keys :feature_id, :attribute_name
  

Если имя атрибута может встречаться несколько раз для одной функции, комбинация feature_id , attribute_name и attribute_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
  

Надеюсь, что это поможет 🙂

Комментарии:

1. Да, это была бы хорошая идея, но, к сожалению attribute_name(s) , некоторые из них повторяются несколько раз для нескольких функций, поэтому они не уникальны для каждой функции.

2. Мммм, вы говорите, что attribute_name используется для нескольких функций? Значит feature_id , attribute_name может быть ключом, верно? Или вы хотите сказать, что attribute_name это происходит в разное время в пределах одной функции, так что тогда feature_id attribute_name attribute_value должен быть ключом, верно? Так почему вы не можете использовать это?

3. Ах, я думаю, ваше второе условие не было обработано для меня мысленно; имя атрибута может встречаться несколько раз для одной функции, поэтому я попробую второй вариант. Ого!

4. Я просмотрел SQL за рассматриваемой таблицей атрибутов, и, конечно же, эти три поля перечислены как первичные ключи ( PRIMARY KEY (feature_id,attribute_name,attribute_value ). Я загрузил composite_primary_keys и определил эти три поля как первичные ключи, но без любви. Я все еще не могу запросить ни одной строки из этой таблицы с помощью ActiveRecord, связанной с feature или самой по себе.

5. Какие запросы генерирует 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
  

Комментарии:

1. Ваш код (сам по себе) выдает ошибку, поскольку Rails не может найти find_by_sql метод в экземпляре Feature . Добавление к началу позволяет запустить его, но он не получит никаких результатов (то же самое с заменой на ). Feature Feature FeatureAttribute Сгенерированный SQL выглядит нормально, хотя: select * from feature_attribute where feature_id = 24

2. @BenjaminKreeger Вы правы, извините, я забыл, что find_by_sql — это уровень класса. Вы должны использовать FeatureAttribute.find_by_sql, потому что тогда массив, возвращаемый с объектами FeatureAttribute таким образом, с доступом к методам этого класса.