#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 таким образом, с доступом к методам этого класса.