1

У меня есть устаревший db (oracle), в этом db У меня есть несколько таблиц, которые содержат разные данные, но структурно одинаковы. Мне не разрешено изменять схему БД каким-либо образом!Как динамически установить имя_таблицы модели?

Мне нужна СУХАЯ модель ActiveRecord для получения правильных данных из правых таблиц. Проблема заключалась в том, что мне нужно было динамически перезаписать self.table_name, чтобы он работал.

Вот мой код:

ActiveRecord: Базовый класс который будет наследоваться всеми подобными таблицами

class ListenLoc < ActiveRecord::Base 
    @@table_name = nil 

    def self.table_name 
    @@table_name 
    end  

    default_scope { where(validated: 1).where("URL_OK >= 0") } 
    scope :random_order, -> { order('DBMS_RANDOM.VALUE') } 
    scope :unvalidated, -> { unscope(:where).where(validated: 0) } 


    def self.get_category(cat_id) 
    where("cat_id = ?", cat_id) 
    end  

    def self.rand_sample(cat_id, lim) 
    where("cat_id = ?", cat_id).random_order.limit(lim) 
    end  
end 

по уходу за детьми Классы выглядеть так:

class ListenLocA < ListenLoc 
    @@table_name = 'LISTEN_ADDR_A' 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatA', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 

B.

class ListenLocB < ListenLoc 
    @@table_name = 'LISTEN_ADDR_B' 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 

Приведенные выше работы, однако, я уже заметил, что при выполнении определенных выборочных поисков есть некоторые подводные камни.

Это хороший подход? Есть ли лучший способ передать self.table_name динамически?

Update:

Казалось бы, что это должно работать, но я получаю сообщение об ошибке, что таблица не существует, так как ActiveRecord пытается проверить таблицу перед созданием объекта и self.table_name не установлен в модели ListenLoc динамически.

class ListenLocB < ListenLoc 
    self.table_name = 'LISTEN_ADDR_B' 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 
+1

Лично у меня были бы разные модели для каждой таблицы в вашем случае. Вы можете извлечь подобный код в 'ListenLoc' в группу –

ответ

2

Я понял, что я мог бы просто использовать superclass без использования глобалов, которые я в конечном итоге использовал. Это не представляет проблемы с условиями гонки, как с глобальными.

class ListenLocB < ListenLoc 
    superclass.table_name = 'LISTEN_ADDR_B' # or ListenLoc.table_name 
    self.sequence_name = :autogenerated 
    belongs_to :category, class_name: 'ListenLocCatB', foreign_key: 'cat_id' 
    belongs_to :country, class_name: 'Country', foreign_key: 'country_id' 
end 
1

В классе Ruby переменные распределены по всей иерархии, поэтому ваш подход не будет работать. Общая идея переменных класса - не используйте его, если вы на 100% уверены, что знаете, что делаете. И даже когда вы находитесь - есть, скорее всего, лучший подход.

Что касается актуальной проблемы - то, что вы сделали с table_name, не сушит, так как вы добавили больше строк, чем вы сохранили. Более того, это затрудняет чтение.

Просто поместите

self.table_name = 

, где она должна быть в каждой модели - это будет кратким и удобочитаемым.

Другой вариант заключается в использовании локализованной константы вместо которые связаны с ListenLoc класса:

class ListenLoc 
    def self.table_name 
    TABLE_NAME 
    end 
end 

class ListenLocB < ListenLoc 
    ::TABLE_NAME = 'LISTEN_ADDR_B' 
end 

Почему это работает?

Мое понимание заключается в следующем:

Написав ::TABLE_NAME вы определяете константу TABLE_NAME в глобальном масштабе.

Когда ваш вызов распространяется до класса ListenDoc, он пытается разрешить константу ListenDoc::TABLE_NAME, и она не находит его в этом поле. Затем выясняется, что константа TABLE_NAME определена во внешней области видимости, и она обнаруживает, что ::TABLE_NAME действительно определен, и это значение равно 'LISTEN_ADDR_B'. Таким образом, работает.

Возможно, я был неясен, так как мое понимание по этой теме все еще плавает, но это определенно связано с тем, как Ruby ищет константы.

Это не очень прямолинейно, так как существует несколько предостережений (как и все, в конце концов).

+0

, как я добавил больше строк, чем я сохранил? Класс listenLoc имеет только небольшое извлечение методов, которые в противном случае могли бы существовать в других моделях. – mahatmanich

+0

@mahatmanich Я имел в виду только переменную 'table_name'/class, другие на самом деле прекрасны (хотя я мог бы также выбрать заботу о наследовании, поскольку j-dexx предлагает в комментариях, но это зависит от вас). –

+0

'self.table_name =' в реальных моделях не работает, вот в чем проблема. Он не распространяется на ListenLoc, хотя он и должен! Поэтому решение выше - это то, что я выбрал. – mahatmanich