Sam*_*ron 57 activerecord ruby-on-rails
我有一套非常简单的HABTM模型
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts
end
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
def tags= (tag_list)
self.tags.clear
tag_list.strip.split(' ').each do
self.tags.build(:name => tag)
end
end
end
Run Code Online (Sandbox Code Playgroud)
现在一切正常,除了我在Tags表中得到了大量的重复项.
我需要做些什么来避免标签表中的重复(基于名称)?
Jer*_*nch 43
以下内容不会阻止向数据库写入重复关系,它只会确保find方法忽略重复项.
在Rails 5中:
has_and_belongs_to_many :tags, -> { distinct }
Run Code Online (Sandbox Code Playgroud)
注意:Relation#uniq在Rails 5(提交)中折旧
在Rails 4中
has_and_belongs_to_many :tags, -> { uniq }
Run Code Online (Sandbox Code Playgroud)
选项1:防止来自控制器的重复:
post.tags << tag unless post.tags.include?(tag)
Run Code Online (Sandbox Code Playgroud)
但是,多个用户可能同时尝试post.tags.include?(tag),因此这受到竞争条件的影响.这在这里讨论.
为了增强稳定性,您还可以将其添加到Post模型(post.rb)
def tag=(tag)
tags << tag unless tags.include?(tag)
end
Run Code Online (Sandbox Code Playgroud)
选项2:创建唯一索引
防止重复的最简单方法是在数据库层具有重复的约束.这可以通过unique index在表本身上添加一个来实现.
rails g migration add_index_to_posts
# migration file
add_index :posts_tags, [:post_id, :tag_id], :unique => true
add_index :posts_tags, :tag_id
Run Code Online (Sandbox Code Playgroud)
获得唯一索引后,尝试添加重复记录将引发ActiveRecord::RecordNotUnique错误.处理此问题超出了本问题的范围.查看这个问题.
rescue_from ActiveRecord::RecordNotUnique, :with => :some_method
Run Code Online (Sandbox Code Playgroud)
spy*_*yle 25
另外上面的建议:
:uniq到has_and_belongs_to_many关联我会做一个明确的检查,以确定该关系是否已经存在.例如:
post = Post.find(1)
tag = Tag.find(2)
post.tags << tag unless post.tags.include?(tag)
Run Code Online (Sandbox Code Playgroud)
cyr*_*ier 21
在Rails4中:
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags, -> { uniq }
Run Code Online (Sandbox Code Playgroud)
(注意,-> { uniq }必须直接在关系名称之后,在其他参数之前)
Jos*_*eek 13
设置uniq选项:
class Tag < ActiveRecord::Base
has_and_belongs_to_many :posts , :uniq => true
end
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags , :uniq => true
Run Code Online (Sandbox Code Playgroud)
我更愿意调整模型并以这种方式创建类:
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, :through => :taggings
end
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :post
end
Run Code Online (Sandbox Code Playgroud)
然后我将创建包装在逻辑中,以便Tag模型在已经存在的情况下被重用.我甚至可能在标签名称上加上一个唯一的约束来强制执行它.这样可以更有效地搜索任何一种方式,因为您只需使用连接表上的索引(查找特定标记的所有帖子,以及特定帖子的所有标记).
唯一的问题是您不能允许重命名标记,因为更改标记名称会影响该标记的所有使用.让用户删除标签并改为创建一个新标签.
我通过创建一个 before_save 过滤器来解决这个问题。
class Post < ActiveRecord::Base
has_and_belongs_to_many :tags
before_save :fix_tags
def tag_list= (tag_list)
self.tags.clear
tag_list.strip.split(' ').each do
self.tags.build(:name => tag)
end
end
def fix_tags
if self.tags.loaded?
new_tags = []
self.tags.each do |tag|
if existing = Tag.find_by_name(tag.name)
new_tags << existing
else
new_tags << tag
end
end
self.tags = new_tags
end
end
end
Run Code Online (Sandbox Code Playgroud)
它可以稍微优化以与标签一起批量工作,而且它可能需要一些稍微更好的事务支持。
| 归档时间: |
|
| 查看次数: |
25335 次 |
| 最近记录: |