Какую ассоциацию я должен использовать, чтобы описать `Типы вещей 'в Ruby on Rails? - PullRequest
2 голосов
/ 06 февраля 2010

Я хотел бы иметь возможность описывать различные типы моделей с использованием ассоциаций RoR. Пример:

Модель:
Сообщение

ImagePost
post_id: целое число
URL: строка

MessagePost
post_id: целое число
сообщение: строка

ImagePost и MessagePost - это тип сообщения. Я бы хотел, чтобы @posts = Post.all получал оба типа сообщений и разрешал мне доступ к их атрибутам через @ posts.url или @ posts.message.

Я уверен, что упускаю что-то простое, пожалуйста, просветите меня!

Приветствия

Бен.

Ответы [ 2 ]

3 голосов
/ 06 февраля 2010

Изучите концепцию наследования одной таблицы - основная идея состоит в том, что у вас будет одна таблица Post, в которой будет столбец Type, поэтому данный ImagePost все равно будет просто строкой в ​​таблице Post. Таблица Post будет содержать столбцы для всех возможных свойств, поэтому в ней будут столбцы url и message.

Тогда соответствующие классы модели будут наследовать от Post: ImagePost

0 голосов
/ 06 февраля 2010

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

Идея состоит в том, что вы используете STI для обработки всех общих полей Post, а также делегируете уникальные поля для каждого подкласса в другую таблицу и загружаете эту связь.

Базовый класс Post может выглядеть следующим образом. Обратите внимание, что class_eval может быть абстрагирован в модуль, который включается и расширяется в подкласс.

#columns: id:integer, timestamps, user_id:integer, 
#  topic_id:integer, type:string
class Post < ActiveRecord::Base
  # common methods/validations/associations
  belongs_to :user
  belongs_to :topic 

  def self.relate_to_detail
    class_eval <<-"EOF"
      has_one :detail, :class_name => "#{self.name}Detail"
      accepts_nested_attributes_for :detail

      default_scope :include => :detail

      def method_missing(method, *args)
        build_detail if detail.nil?
        if detail && detail.respond_to?(method, true)
          detail.send(method, *args)
        else 
          super(method, *args)
        end
      end

      def respond_to?( method, include_private = false)
        build_detail if detail.nil?
        super(method, include_private) || 
          detail.respond_to?(method, include_private)
      end
    EOF
  end
end

Затем вам нужно будет определить класс sub и detail для каждого типа.

#uses posts table
class ImagePost < Post
  relate_to_detail
end

#columns: id, image_post_id, url:string, height:integer, :width:integer
class ImagePostDetail < ActiveRecord::Base
  belongs_to :image_post
end

#uses posts table
class MessagePost < Post
  relate_to_detail
end

#columns: id, message_post_id, message:string
class MessagePostDetail < ActiveRecord::Base
  belongs_to :image_post
end

Теперь мы можем делать такие вещи:

@user.message_posts.create(:message => "This is a message")
@image_post.url

, который создаст новый MessagePost, в котором все user_id, timestamps, post_id, post_type хранятся в таблице posts, а сообщение хранится в таблице MessagePostDetails и возвращает URL-адрес ImagePost соответственно.

Новый метод_missing и response_to? определения работают магию, чтобы скрыть разделение.

@Post.all теперь будет перечислять сообщения всех типов. Единственным недостатком является то, что подробные поля не будут отображаться при получении сообщения.

...