2013-05-23 2 views
0

У меня есть модель мультимедиа, которая имеет кучу стандартных атрибутов метаданных и сохраняется в базе данных как обычно. Теперь я хочу добавить к этой модели дополнительные атрибуты метаданных поверх существующих атрибутов. Список этих атрибутов будет определен в файле конфигурации и загружен во время выполнения. Они будут храниться в базе данных в другой таблице как ряд пар значений свойств с привязкой к основной модели.Преобразование атрибутов Rails в пары значений свойств

Итак, мой код в настоящее время,

class Media < ActiveRecord::Base 
    has_many :custom_metadata 

    attr_accessible :title, :language, :copyright, :description 
end 

и

class CustomMetadata < ActiveRecord::Base 
    belongs_to :media 

    attr_accessible :name, :value 
end 

То, что я хочу сделать, это быть в состоянии получить доступ и обновить пользовательские атрибуты метаданных на модели медиа в том же как стандартные атрибуты метаданных. Например, если атрибуты пользовательских метаданных называется издателем и вкладчика, то я хочу, чтобы получить доступ к ним в модели СМИ, как @media.publisher и @media.contributor несмотря на то, что они будут находиться в ассоциации @media.custom_metadata, где его значение будет что-то вроде [{:name => 'publisher', :value => 'Fred'}, {:name => 'contributor', :value => 'Bill'}]

Кажется, что виртуальные атрибуты были бы лучшим способом достижения этого, но все примеры, которые я могу найти для людей, использующих виртуальные атрибуты, - это то, где имена атрибутов являются статическими и известными, а не динамическими из времени выполнения конфигурации, поэтому они могут определять такие методы, как publisher и publisher=, которые затем содержат код для записи в соответствующий связанный файл rty-value.

я могу определить атрибуты в классе с attr_accessor *Settings.custom_metadata_fields (при условии, Settings.custom_metadata_fields возвращается [:publisher, :contributor]), а также позволяют массовое назначение, используя подобную технику с attr_accessible.

Часть, на которую я застреваю, заключается в том, как заполнить виртуальные атрибуты из ассоциации при загрузке данных из записи, а затем, наоборот, как передать данные в виртуальных атрибутах обратно в ассоциацию до того, как запись сохранены.

два способов я в настоящее время вижу эту работу, либо с помощью method_missing или attribute_missing, или, возможно, через initialize и before_save обратный вызов? В любом случае, я не уверен, как бы я определил его, учитывая, что моя модель имеет сочетание обычных атрибутов и виртуальных атрибутов.

Любые предложения?

ответ

1

Использование обратных вызовов звучит разумно. Какую базу данных вы используете? Если PostgreSQL, возможно, вам стоит взглянуть на расширение HStore (http://www.postgresql.org/docs/9.2/static/hstore.html) , он будет работать лучше, и есть некоторые драгоценные камни, которые упрощают его использование.

+0

Спасибо за предложение. К сожалению, мы стандартизированы здесь в MySQL, так как наша база данных, поэтому переход на PostgreSQL на самом деле не вариант. Как вы могли бы написать обратные вызовы, учитывая смесь нормальных и виртуальных атрибутов? – richard

+0

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

0

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

В конце концов, это был последний код модели СМИ, и я ничего не изменило в модели CustomMetadata от того, что я определил в вопросе,

class Media < ActiveRecord::Base 
    has_many :custom_metadata 

    attr_accessor *Settings.custom_metadata_fields 

    attr_accessible :title, :language, :copyright, :description 
    attr_accessible *Settings.custom_metadata_fields 

    validates_presence_of *Settings.required_custom_fields 

    before_save :save_custom_metadata 
    after_initialize :load_custom_metadata 

    def load_custom_metadata 
    MediaMetadata.custom_all_fields.each do |field| 
     custom_record = custom_metadata.where(:name => field.to_s).first_or_initialize() 
     send("#{field}=", custom_record.value) 
    end 
    end 

    def save_custom_metadata 
    MediaMetadata.custom_all_fields.each do |field| 
     custom_record = custom_metadata.where(:name => field.to_s).first_or_initialize() 
     custom_record.value = send(field) 
     if custom_record.value.blank? 
     custom_record.destroy 
     else 
     custom_record.save 
     end 
    end 
    end 
end 

Это решение было несколько хорошо выгоды. Во-первых, это не влияет на какие-либо из обычных атрибутов в модели Media. Во-вторых, только пользовательские метаданные с фактическими значениями хранятся в таблице настраиваемых метаданных. Если значение пустое, запись полностью удаляется. Наконец, я могу использовать стандартные проверки атрибутов модели, как показано для моих обязательных атрибутов метаданных.

+0

Еще одна мысль - вы собираетесь делать SQL-запросы против записей CustomMetadata? Если нет, возможно, вы можете просто сериализовать хэш атрибутов в один столбец, используя #serialize (http://apidock.com/rails/ActiveRecord/Base/serialize/class). Дело в том, что использование отношений «один ко многим» может вызвать проблемы с производительностью. И использование after_initialize не запускается, когда вы запрашиваете несколько записей с помощью Media # find - посмотрите на hook_ after. – cthulhu

Смежные вопросы