假设我正在构建一个动物园应用程序,并为每种动物类型实现了一个模型:哺乳动物、鸟类、爬行动物、无脊椎动物、鱼类和两栖动物。所有这些都有一个标题、描述和图像字段,但它们也可能有一些其他字段。如何从一个控制器按字母顺序显示所有动物的列表?我想让他们用自己的偏旁。
如何从一个控制器按字母顺序显示所有动物的列表?
如果它们都在单独的表中,则选项不太好。
- 将它们全部加载到内存中并进行排序。如果没有那么多动物,这可能会奏效
- 制作一个执行SQL并集的SQL视图,使它们看起来像一个表,并对该视图执行
order by
。这既混乱又低效
另一种选择是将所有动物放在一个表中,并使用单表继承将类型区分为子类。
$ bin/rails generate model animal type:string title:string description:string
class Animal < ApplicationRecord
def eat
...
end
def drink
...
end
def make_little_animals
...
end
end
class Mammal < Animal
def lactate
end
end
class Bird < Animal
def make_little_animals
lay_egg
end
def lay_egg
...
end
def fly?
...
end
end
这三个都将存储在animals
中。Animal.order(:title)
将归还按其名称订购的所有动物。但是那些类型为Bird
的对象将作为Bird
对象加载。类型为Mammal
的对象将作为Mammal
对象加载。Bird.all
将只返回类型为Bird
的动物。
考虑是否需要子类。如果它们没有任何不同的功能,请为它们的分类法添加一列,并创建一些范围。
class Animal < ApplicationRecord
scope :mammals -> { where(taxonomic_class: :mammalia }
scope :birds -> { where(taxonomic_class: :aves }
def eat
...
end
def drink
...
end
def make_little_animals
...
end
end
然后你可以要求所有动物Animal.order(:title)
或只要求鸟类Animal.birds.order(:title)
。
我建议从那里开始。您以后总是可以将其拆分为子类。
我找到了两个很棒的选项,但仍然不确定是否会遇到以列表形式显示的问题:
1-创建一个多表继承,如下所示。
class Animal < ActiveRecord::Base
def eat
end
end
class Bird < Animal
set_table_name "birds"
def fly
end
end
class Mammal < Animal
set_table_name "mammals"
def run
end
end
class Fish < Animal
set_table_name "fish"
def swim
end
end
class CreateMammal < ActiveRecord::Migration
def change
create_table :mammals do |t|
t.integer :num_of_legs
# ... more column fields #
t.timestamps
end
end
end
class CreateFish < ActiveRecord::Migration
def change
create_table :fish do |t|
t.integer :num_of_fins
# ... more column fields #
t.timestamps
end
end
end
class CreateBird < ActiveRecord::Migration
def change
create_table :birds do |t|
t.float :wing_span
# ... more column fields #
t.timestamps
end
end
end
view raw
2-创建类似于此处所示的产品示例。
class ActiveRecord::Base
def self.acts_as_product
include Sellable
end
end
class ProductProperties < ActiveRecord::Base
belongs_to :sellable, :polymorphic => true, :dependent => :destroy
validates_presence_of :title # for example
end
module Sellable
def self.included(base)
base.has_one :product_properties, :as => :sellable, :autosave => true
base.validate :product_properties_must_be_valid
base.alias_method_chain :product_properties, :autobuild
base.extend ClassMethods
base.define_product_properties_accessors
end
def product_properties_with_autobuild
product_properties_without_autobuild || build_product_properties
end
def method_missing(meth, *args, &blk)
product_properties.send(meth, *args, &blk)
rescue NoMethodError
super
end
module ClassMethods
def define_product_properties_accessors
all_attributes = ProductProperties.content_columns.map(&:name)
ignored_attributes = ["created_at", "updated_at", "sellable_type"]
attributes_to_delegate = all_attributes - ignored_attributes
attributes_to_delegate.each do |attrib|
class_eval <<-RUBY
def #{attrib}
product_properties.#{attrib}
end
def #{attrib}=(value)
self.product_properties.#{attrib} = value
end
def #{attrib}?
self.product_properties.#{attrib}?
end
RUBY
end
end
end
protected
def product_properties_must_be_valid
unless product_properties.valid?
product_properties.errors.each do |attr, message|
errors.add(attr, message)
end
end
end
end
class Tee < ActiveRecord::Base
acts_as_product
end
class Pen < ActiveRecord::Base
acts_as_product
end
我建议使用此处描述的委托类型:
# Schema: entries[ id, account_id, creator_id, created_at, updated_at, entryable_type, entryable_id ]
class Entry < ApplicationRecord
belongs_to :account
belongs_to :creator
delegated_type :entryable, types: %w[ Message Comment ]
end
module Entryable
extend ActiveSupport::Concern
included do
has_one :entry, as: :entryable, touch: true
end
end
# Schema: messages[ id, subject ]
class Message < ApplicationRecord
include Entryable
has_rich_text :content
end
# Schema: comments[ id, content ]
class Comment < ApplicationRecord
include Entryable
end