新手问题 关于 Rails 实现 Tags 功能

alixiaomiao · October 23, 2014 · Last by alixiaomiao replied at October 23, 2014 · 2447 hits

我有两个 model 分别叫 post, page,然后有个 concerns 叫 taggable

先是数据表:

tags

t.string :name

taggings

t.integer :taggable_id
t.string :taggable_type
t.integer :tag_id

这是 model 的关系:

concerns/taggable.rb

module Taggable
  extend ActiveSupport::Concern

  included do
    has_many :taggings, as: :taggable
    has_many :tags, through: :taggings

    scope :tagged, ->(tagname) { select { |p| p.tag_list.include? tagname } }
  end

  def tag_list
    tags.collect(&:name)
  end
end

然后分别在 post.rb page.rbinclude Taggable

tag.rb

has_many :taggings

最后是连接表 tagging.rb

belongs_to :tag
belongs_to :taggable, polymorphic: true

那么问题来了:

  1. 我在控制台分别用 Post.first.tagsPage.first.tags 都能实现功能,但是我用 Tag.first.taggable_idTag.first.taggable_type 却不能逆向访问,请问这是怎么回事?需要怎么修改?
  2. Post.tagged('gentoo') 的结果是个 Array(毫无疑问),因此之后就无法继续使用 where 等查询语句了,怎么修改上面那个 taggedscope

有现成的 gem 实现 tag 功能啊,act_as_taggable_on

#1 楼 @alucardpj 对,我知道有,但是我想自己实现一下。

  1. taggable_typetaggable_id 是 tagging 这个 model 的,而不是 tag 的属性,而 taggging 与 tag 是多对一,也就是 Tag.first.taggable_id 从逻辑上来看就是错误的,因为 单个 tag 会有多个 tagging,每个 tagging 记录的 taggable_typetaggable_id 都不一样。

  2. 你在 scope 里调用了 select 方法,这个方法是数组的方法,rails 会调用 to_a 立即查询数据库并将结果加载到内存,然后再将结果集转换成数组进行操作。这个过程会产生大量的内存消耗,所以你要将过滤的过程交给数据库来做,具体就是用 join 三表联查

#3 楼 @saiga 第一个是我脑子糊涂了,现在想明白了。那第二个问题有没有解决办法呢?

You need to Sign in before reply, if you don't have an account, please Sign up first.