Добавлять поля в модель ActiveRecord динамически в Rails 2.2.2? - PullRequest
3 голосов
/ 22 февраля 2009

Скажем, я хотел позволить администратору добавить поле в модель ActiveRecord через интерфейс в приложении Rails. Я полагаю, что нормальный код ActiveRecord :: Migration был бы достаточен для изменения структуры таблицы модели AR (что-то, что не будет разумным для многих приложений - я знаю) Конечно, только определенные типы полей могут быть добавлены ... в теории.

Очевидно, что формы, которые добавляют (или редактируют) записи в эту недавно измененную модель ActiveRecord, должны создаваться динамически во время выполнения. Обычный подход form_for не подходит. Это обсуждение предполагает, что это может быть достигнуто только с помощью JavaScript.

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/fc0b55fd4b2438a5

В прошлом я использовал Ruby для запроса объекта на предмет доступных методов. Кажется, я помню, что это было безумно медленно. Я слишком зелен с Ruby и Rails, чтобы знать элегантный подход к этому. Я надеюсь, что кто-то здесь может. Я также открыт для совершенно разных подходов к этой проблеме, которые не включают изменение базы данных.

Ответы [ 6 ]

6 голосов
/ 22 февраля 2009

Чтобы получить доступ к столбцам, которые в настоящее время определены для модели, используйте метод columns - он предоставит вам для каждого столбца его имя, тип и другую информацию (например, является ли она первичной). ключ и т. д.)

Однако изменение схемы во время выполнения деликатно.

Схема предварительно загружается (и кэшируется из драйвера БД) каждым классом модели при первой загрузке. В режиме production Rails делает это один раз для каждой модели при запуске.

  1. Чтобы заставить Rails обновлять свою кэшированную схему после вашей модификации, вы должны заставить Ruby перезагружать класс уязвимой модели (почти то, что Rails делает для вас автоматически, после каждого запроса, при работе в режиме development - см. как перезагрузить класс, используя remove_const, за которым следует load.)
  2. Если у вас есть кластер Mongrel, вы также должны сообщить другим процессам в кластере, которые выполняются в отдельном пространстве памяти, чтобы также перезагрузить классы их модели (некоторые кластеры позволят вам создать файл restart.txt). 'файл, который приведет к автоматическому перезапуску всех процессов в вашем кластере без дополнительной работы от вашего имени.)

Теперь, как уже было сказано, в зависимости от реальной проблемы, которую вам нужно решить, вам, возможно, не понадобится динамически изменять схему. Вместо добавления, скажем, столбцов col1, col2 и col3 в некоторую таблицу entries (модель Entry), вы можете использовать таблицу с именем dyn_attribs, где Entry has_many :dyn_attribs и где dyn_attribs имеет столбец key (который в данном случае может иметь значения col1, col2 или col3) и столбец value (в котором перечислены соответствующие значения для col1, col2 и т. Д. .)

Таким образом, вместо:

my_entry = Entry.find(123)
col1 = my_entry.col1
#do something with col1

вы бы использовали:

my_entry = Entry.find(123, :include => :dyn_attribs)
dyn_attribs = my_entry.dyn_attribs.inject(HashWithIndifferentAccess.new) { |s,a|
  s[a.key] = a.value ; s
}
col1 = dyn_attribs[:col1]
#do something with col1

Вышеупомянутый inject вызов может быть вынесен в модель или даже в базовый класс, унаследованный всеми моделями, которым могут потребоваться дополнительные динамические столбцы / атрибуты (см. Полиморфные ассоциации о том, как заставить несколько моделей использовать одну и ту же таблицу dyn_attribs для динамических атрибутов.)


UPDATE

Добавление или переименование столбца с помощью обычной HTML-формы.

Предположим, у вас есть модель DynAttrTable, представляющая таблицу с динамическими атрибутами, а также DynAttrDef, определяющий имена динамических атрибутов для данной таблицы.

Пробег:

script/generate scaffold_resource DynAttrTable name:string
script/generate scaffold_resource DynAttrDef name:string
rake db:migrate

Затем отредактируйте сгенерированные модели:

class DynAttrTable < ActiveRecord::Base
  has_many :dyn_attr_defs
end

class DynAttrDef < ActiveRecord::Base
  belongs_to :dyn_attr_table
end

Вы можете продолжить редактирование контроллеров и представлений , как в этом учебном пособии , заменив Recipe на DynAttrTable и Ingredient на DynAttrDef.

В качестве альтернативы , используйте один из плагинов , рассмотренный здесь , для автоматического управления таблицами dyn_attr_tables и dyn_attr_defs с помощью автоматизированного интерфейса (со всеми его прибамбасами) , практически без усилий по реализации от вашего имени.

Это должно помочь вам.

3 голосов
/ 23 февраля 2009

Скажи, что я хотел разрешить администратор, чтобы добавить поле в Модель ActiveRecord через интерфейс в приложении Rails.

Я решил эту проблему раньше, имея дополнительную модель под названием AdminAdditions. Таблица содержит идентификатор, идентификатор пользователя с правами администратора, строку имени модели, строку типа и строку значения по умолчанию.

Я переопределяю методы поиска и сохранения модели, чтобы добавить атрибуты из ее admin_additions и соответствующим образом сохранить их при изменении. Таблица модели имеет большое текстовое поле, изначально пустое, в котором я сохраняю значения по умолчанию для добавленных атрибутов.

По сути, представления и контроллеры могут делать вид, что каждый атрибут модели имеет свой собственный столбец. Это значит form_for и т. Д. На всю работу.

2 голосов
/ 16 сентября 2010

ActiveRecord :: Migration.add_column (пользователь, «электронная почта»,: строка)

1 голос
/ 17 июля 2015

Если бы вы делали это с PostgreSQL, теперь вы, вероятно, могли бы обойтись без поля типа JSON, а затем просто сохранить все что угодно в хэше json.

1 голос
/ 22 февраля 2009

Я много раз видел динамическое изменение / миграцию таблиц, предлагаемых в качестве решения, но на самом деле я никогда не видел его реализованным. Существует множество причин, по которым это решение редко применяется.

  • Если таблица большая, то таблица может / будет заблокирована на длительные периоды времени, которое должно быть работоспособным.
  • Почему ваша модель динамически меняется? Очень редко для структуры моделей требуется динамическое изменение. Чаще всего это указывает на то, что вы пытаетесь смоделировать что-то конкретное в обобщенном виде.
  • Часто это попытка создать модель "по категориям", которая может быть лучше решена с помощью другого подхода.
  • Операторы DDL часто не допускаются тем же пользователем, который используется для повседневных требований DML. Хотя это может иметь место, и часто это происходит на арене ROR, это не всегда «правильный» способ сделать это.

Чего вы пытаетесь достичь здесь? Лучшее понимание проблемы, вероятно, позволило бы найти более естественное решение.

1 голос
/ 22 февраля 2009

Вы можете использовать Атрибуты Flex для этого, хотя если вы хотите иметь возможность поиска или упорядочения по этим новым столбцам, вам придется написать (много) пользовательских SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...