假设我有一个可以与 Book 或 Movie 实例关联的 Genre 模型。比如,
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
现在,假设我想标准化我的流派数据,因此每个流派在流派表中只有一个条目:
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 是“冒险”而电影 13 是“Point Break”,我可以在 genre_instances
中添加一个条目,例如 genre_id: 5、genreable_id: 13、genreable_type: '电影'
。
如果《Tom Sawyer》是第 13 本书,我可能会有另一个条目,例如 genre_id: 5、genreable_id: 13、genreable_type: 'Book'
。
如何以考虑“类型”列的方式强制在 Genre_instances 表中实现唯一性?我想确保“Point Break”只能有一个“冒险”条目,而不阻止“Tom Sawyer”也有这样的条目。
编辑:感谢 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 个字符的限制。
对这一切进行了测试运行,它的工作正如预期的那样。
最佳答案
使用唯一性验证的 :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 字段。
关于ruby-on-rails - 如何在多态连接表上强制唯一性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25214156/