Внедрение абстрактного базового модельного класса, Rails Way ™

У меня есть модель Book и Download, в которой есть много атрибутов, поэтому моя цель - наследовать общие атрибуты из модели DownloadableResource.
Посмотрел на STI, но вместо этого я пошел абстрактным способом базовой модели:

  • модели:

    class DownloadableResource < ActiveRecord::Base
      self.abstract_class = true
    
      attr_accessible :title, :url, :description, :active, :position
      validates :title, :url, :description, presence: true
      scope :active, where(active: true).order(:position)
    end
    
    class Book < DownloadableResource
      attr_accessible :cover_url, :authors
      validates :cover_url, :authors, presence: true
    end
    
    class Download < DownloadableResource
      attr_accessible :icon_url
      validates :icon_url, presence: true
    end
    
  • миграции:

    class CreateDownloadableResources < ActiveRecord::Migration
      def change
        create_table :downloadable_resources do |t|
          t.string    :title
          t.string    :url
          t.text      :description
          t.boolean   :active,      default: false
          t.integer   :position
          t.timestamps
        end
      end
    end
    
    class CreateBooks < ActiveRecord::Migration
      def change
        create_table :books do |t|
          t.string :cover_url
          t.string :authors
          t.timestamps
        end
      end
    end
    
    class CreateDownloads < ActiveRecord::Migration
      def change
        create_table :downloads do |t|
          t.string :icon_url
          t.timestamps
        end
      end
    end
    

После миграции, когда я создаю новую книгу, результат далеко не ожидался:

> Book.new
=> #<Book id: nil, cover_url: nil, authors: nil, created_at: nil, updated_at: nil> 

Может ли кто-нибудь пролить свет на то, как реализовать метод абстрактного базового модельного класса, поэтому модели ActiveRecord могут совместно использовать общий код через наследование, но сохраняются в разных таблицах базы данных?

Ответ 1

Объявляя модель абстрактной, вы на самом деле говорите, что нет базовой таблицы, и вы хотите разрешить подкласс. Это значит:

  • Вам не нужна таблица downloadable_resources
  • Book.table_name печатает books вместо downloadable_resources

Как уже упоминалось в @Finbarr, это также означает, что обе модели Book and Download должны иметь все атрибуты в своих таблицах.

Что это действительно полезно для этого? По-моему, не очень. Вы можете делиться валидациями, областями и т.д., Но вы можете достичь всего этого легче, включив в него настраиваемые модули.

Чтобы решить вашу проблему, я бы, вероятно, пошел с другим подходом. Я бы создал еще одну модель под названием DownloadableContent которая была бы автономной. Он будет включать проверки, и таблица будет иметь все атрибуты. И, наконец, модели Book and Download будут иметь полиморфное отношение has_one к модели DownloadableContent.

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

Ответ 2

В этом случае не должно быть таблицы downloadable_resources. Обе таблицы книг и загрузок должны объявлять все поля, в которых они нуждаются.