假设我有一个可以与书籍或电影的实例相关联的流派模型。像,
class Genre < ActiveRecord::Base
belongs_to :genreable, polymorphic: true
end
class Book < ActiveRecord::Base
has_many :genres, as: :genreable
end
class Movie < ActiveRecord::Base
has_many :genres, as: :genreable
end
现在,假设我想规范化我的流派数据,因此每个流派在Genres表中只有一个条目:
class Genre < ActiveRecord::Base
has_many :genre_instances
end
class GenreInstance < ActiveRecord::Base
belongs_to :genre
belongs_to :genreable, polymorphic: true
end
class Book < ActiveRecord::Base
has_many :genre_insances, as: :genreable
has_many :genres, through: :genre_instances
end
class Movie < ActiveRecord::Base
has_many :genre_insances, as: :genreable
has_many :genres, through: :genre_instances
end
我的genre_instances表包含genre_id
,genreable_id
和genreable_type
的字段。
所以,如果类型5是&#34;冒险&#34;和电影13是#34; Point Break&#34;,我可以在genre_instances
genre_id: 5, genreable_id: 13, genreable_type: 'Movie'
中输入一个条目。
如果&#34; Tom Sawyer&#34;是第13册,我可能会有另一个条目,如genre_id: 5, genreable_id: 13, genreable_type: 'Book'
。
如何以一种考虑&#39;类型&#39;的方式强制genre_instances表中的唯一性?柱?我想确保&#34; Point Break&#34;只能进行一次冒险&#34;进入,没有阻止&#34; Tom Sawyer&#34;也有这样的条目。
编辑 :感谢cwsault提供以下答案。这是死路一条。我还在数据库级别添加了一个唯一索引,其迁移如下:
class CreateGenreInstances < ActiveRecord::Migration
def change
create_table :genre_instances do |t|
t.references :genre
t.references :genreable, polymorphic: true
t.timestamps
end
add_index :genre_instances, [:genre_id, :genreable_id, :genreable_type],
unique: true, name: 'genre_and_genreable'
end
end
...和旁注:我必须指定索引的名称,因为ActiveRecord的自动生成的索引名称超过了64个字符的限制。
全部用于测试运行并且它正如预期的那样工作。
答案 0 :(得分:3)
使用唯一性验证的{scope属性(documentation)。例如,在GenreInstance模型中,尝试:
class GenreInstance < ActiveRecord::Base
...
validates :genre_id, :uniqueness => {
:scope => [:genreable_id, :genreable_type]
}
emd
以下是我的一个项目的简化示例,其中包含根据不同主题分类的文章:
class Article < ActiveRecord::Base
has_many :topic_mappings, :as => :entity, :dependent => :destroy
has_many :topics, :through => :topic_mappings
end
class Topic < ActiveRecord::Base
has_many :topic_mappings, :dependent => :destroy
end
class TopicMapping < ActiveRecord::Base
belongs_to :entity, :polymorphic => true
belongs_to :topic
validates :topic_id, :presence => true
validates :entity_id, :presence => true
validates :entity_type, :presence => true
validates :topic_id, :uniqueness => {
:scope => [:entity_id, :entity_type]
}
end
请注意,您还应该通过唯一数据库索引强制执行唯一性(在这种情况下,相关字段上的复合索引),否则创建对象的并发请求可能会导致竞争条件,其中Rails会检查数据库在插入之前存在两个新项目。
此外,对于非多态关联,可以这样做:
:scope => [:model_name]
...但快速测试表明它只检查_id字段,即使是多态关联,因此需要同时指定_id和_type字段。