Rails - Devise/Acts-as-taggable-on Gems - 使标签所有权对用户唯一?
Rails - Devise/Acts-as-taggable-on Gems - Make tag ownership unique to a user?
用户可以登录 (devise gem), create a text post, & tag that post (via acts-as-taggable-on gem)。如果在 post 秒内多次使用一个标签,则该标签的 CSS 会发生变化。例如,在下面的示例中,#foobar 上的下划线样式消失了。但是,就目前而言,这是跨用户帐户发生的。这意味着用户输入的标记他们的 post 的标签不是他们帐户独有的。换句话说,如果 User1 标记 #foobar 之后,在一个单独的帐户上,User2 也标记#foobar - 两个用户帐户的下划线都消失了。我的目标是使用户在 posting 表单中输入的标签对该用户是唯一的。
示例:
用户 1 帐户
用户 2 帐户
我尝试了很多解决方案 - none 其中有效!!请帮忙!
posts_controller.rb
class PostsController < ApplicationController
def index
if params[:tag]
@posts = current_user.posts.tagged_with(params[:tag]).order(created_at: :desc)
else
@posts = current_user.posts.includes(:things).all.order(created_at: :desc)
end
end
def new
@post = current_user.posts.build
end
def create
@post = current_user.posts.build(post_params)
if @post.save
redirect_to '/posts'
else
render 'new'
end
end
new.html.erb
<button id='addtext'>text</button>
<%= form_for @post, html: { multipart: true } do |f| %>
<%= f.text_field :tag_list %>
<%= f.fields_for :things do |ff| %>
<% end %>
<%= f.submit %>
<% end %>
index.html.erb
<div id="posts">
<%= render @posts %>
</div>
_post.html.erb
<div class="post">
<%= raw post.tags.order("taggings_count DESC").map {|t| link_to t.name, tag_path(t.name), class: css_class_for_tag(t.taggings_count)}.join(' ') %>
<% post.things.each do |thing| %>
<%= thing.try(:text) %>
<% end %>
</div>
post_helper.rb
module PostsHelper
def css_class_for_tag count
case count
when 1
'new_tag'
else
'used_tag'
end
end
end
post型号
class Post < ActiveRecord::Base
acts_as_taggable
ActsAsTaggableOn.delimiter = ' '
ActsAsTaggableOn.force_lowercase = true
belongs_to :user
has_many :things, dependent: :destroy
accepts_nested_attributes_for :things, allow_destroy: true
end
事物模型
class Thing < ActiveRecord::Base
belongs_to :post
has_attached_file :image, styles: { small: "300x300>", medium: "600x600>" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
用户模型
class User < ActiveRecord::Base
acts_as_tagger
has_many :posts
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
架构
ActiveRecord::Schema.define(version: 20160303110608) do
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "things", force: :cascade do |t|
t.text "text"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "order"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
问题是 tags
table 上的 taggings_count
属性会在任何 tagger
使用标签时递增。所以你需要想出一个新的查询。
您可以通过在标记 table 上创建 select 来获取您正在寻找的数据,进行一些 table 连接,这样您就可以 select 标记由特定用户制作,您可以从中获得所需的计数。
我写了一个查询来演示您需要的结构:
class Post < ActiveRecord::Base
acts_as_taggable
ActsAsTaggableOn.delimiter = ' '
ActsAsTaggableOn.force_lowercase = true
belongs_to :user
has_many :things, dependent: :destroy
accepts_nested_attributes_for :things, allow_destroy: true
def tags_with_user_specific_counts
query = <<-SQL
SELECT taggings.tag_id,
tags.name,
count(taggings.id)
FROM taggings
INNER JOIN tags
ON taggings.tag_id = tags.id
WHERE taggings.tagger_id = '#{user_id}'
AND taggings.tagger_type = 'User'
AND taggings.taggable_id = '#{id}'
AND taggings.taggable_type = 'Post'
GROUP BY taggings.tag_id, tags.name
SQL
ActiveRecord::Base.connection.execute(query).to_a
end
end
唯一要注意的是我没有使用 ActiveRecord 方式编写这个,所以这会生成一个包含您想要的数据的哈希数组:
[{"tag_id"=>"1", "name"=>"jim", "count"=>"1"},
{"tag_id"=>"2", "name"=>"bob", "count"=>"2"}]
但是如果你真的想要这个 return ActsAsTaggableOn::Tag
s,我认为这是可能的。
编辑 - 此 post 已更新为 运行 使用 taggings.tagger_id
的查询。此 post 的先前版本建议 运行 使用此查询:
def tags_with_user_specific_counts
query = <<-SQL
SELECT taggings.tag_id,
tags.name,
users.id as user_id,
count(taggings.id)
FROM taggings
INNER JOIN tags
ON taggings.tag_id = tags.id
INNER JOIN posts
ON taggings.taggable_id = posts.id
AND taggings.taggable_type = 'Post'
INNER JOIN users
ON posts.user_id = users.id
WHERE users.id = '#{user_id}'
GROUP BY taggings.tag_id, tags.name, users.id
SQL
ActiveRecord::Base.connection.execute(query).to_a
end
一种方法:
tag_counts = ActsAsTaggableOn::Tag.joins(:taggings).
where(taggings: { taggable_type: "Post", taggable_id: current_user.post_ids }).
group("tags.id").count
给你这样的东西:
{ 7=>2, 1=>2, 5=>4 }
第一个数字是标签 ID,第二个数字是该标签在 current_user 中出现的次数。
现在您可以这样做了:
<div class="post">
<%= raw post.tags.map{ |t| [t.name, tag_counts[t.id]] }.sort_by{ |t| -t[1] }.
map{ |t| link_to t[0], tag_path(t[0]), class: css_class_for_tag(t[1])}.join(' ') %>
</div>
这可以写得更有表现力,但比...更长 ;-)
基本上,我们像以前一样遍历 post 的标签,然后在第一个 map 语句中将每个标签与其特定于 current_user 的计数连接起来。然后我们按计数倒序排序。在最后的 map 语句中,我们像以前一样生成链接,只是再次使用用户特定计数。
用户可以登录 (devise gem), create a text post, & tag that post (via acts-as-taggable-on gem)。如果在 post 秒内多次使用一个标签,则该标签的 CSS 会发生变化。例如,在下面的示例中,#foobar 上的下划线样式消失了。但是,就目前而言,这是跨用户帐户发生的。这意味着用户输入的标记他们的 post 的标签不是他们帐户独有的。换句话说,如果 User1 标记 #foobar 之后,在一个单独的帐户上,User2 也标记#foobar - 两个用户帐户的下划线都消失了。我的目标是使用户在 posting 表单中输入的标签对该用户是唯一的。
示例:
用户 1 帐户
用户 2 帐户
我尝试了很多解决方案 - none 其中有效!!请帮忙!
posts_controller.rb
class PostsController < ApplicationController
def index
if params[:tag]
@posts = current_user.posts.tagged_with(params[:tag]).order(created_at: :desc)
else
@posts = current_user.posts.includes(:things).all.order(created_at: :desc)
end
end
def new
@post = current_user.posts.build
end
def create
@post = current_user.posts.build(post_params)
if @post.save
redirect_to '/posts'
else
render 'new'
end
end
new.html.erb
<button id='addtext'>text</button>
<%= form_for @post, html: { multipart: true } do |f| %>
<%= f.text_field :tag_list %>
<%= f.fields_for :things do |ff| %>
<% end %>
<%= f.submit %>
<% end %>
index.html.erb
<div id="posts">
<%= render @posts %>
</div>
_post.html.erb
<div class="post">
<%= raw post.tags.order("taggings_count DESC").map {|t| link_to t.name, tag_path(t.name), class: css_class_for_tag(t.taggings_count)}.join(' ') %>
<% post.things.each do |thing| %>
<%= thing.try(:text) %>
<% end %>
</div>
post_helper.rb
module PostsHelper
def css_class_for_tag count
case count
when 1
'new_tag'
else
'used_tag'
end
end
end
post型号
class Post < ActiveRecord::Base
acts_as_taggable
ActsAsTaggableOn.delimiter = ' '
ActsAsTaggableOn.force_lowercase = true
belongs_to :user
has_many :things, dependent: :destroy
accepts_nested_attributes_for :things, allow_destroy: true
end
事物模型
class Thing < ActiveRecord::Base
belongs_to :post
has_attached_file :image, styles: { small: "300x300>", medium: "600x600>" }
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
end
用户模型
class User < ActiveRecord::Base
acts_as_tagger
has_many :posts
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
end
架构
ActiveRecord::Schema.define(version: 20160303110608) do
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "things", force: :cascade do |t|
t.text "text"
t.integer "post_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "order"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
问题是 tags
table 上的 taggings_count
属性会在任何 tagger
使用标签时递增。所以你需要想出一个新的查询。
您可以通过在标记 table 上创建 select 来获取您正在寻找的数据,进行一些 table 连接,这样您就可以 select 标记由特定用户制作,您可以从中获得所需的计数。
我写了一个查询来演示您需要的结构:
class Post < ActiveRecord::Base
acts_as_taggable
ActsAsTaggableOn.delimiter = ' '
ActsAsTaggableOn.force_lowercase = true
belongs_to :user
has_many :things, dependent: :destroy
accepts_nested_attributes_for :things, allow_destroy: true
def tags_with_user_specific_counts
query = <<-SQL
SELECT taggings.tag_id,
tags.name,
count(taggings.id)
FROM taggings
INNER JOIN tags
ON taggings.tag_id = tags.id
WHERE taggings.tagger_id = '#{user_id}'
AND taggings.tagger_type = 'User'
AND taggings.taggable_id = '#{id}'
AND taggings.taggable_type = 'Post'
GROUP BY taggings.tag_id, tags.name
SQL
ActiveRecord::Base.connection.execute(query).to_a
end
end
唯一要注意的是我没有使用 ActiveRecord 方式编写这个,所以这会生成一个包含您想要的数据的哈希数组:
[{"tag_id"=>"1", "name"=>"jim", "count"=>"1"},
{"tag_id"=>"2", "name"=>"bob", "count"=>"2"}]
但是如果你真的想要这个 return ActsAsTaggableOn::Tag
s,我认为这是可能的。
编辑 - 此 post 已更新为 运行 使用 taggings.tagger_id
的查询。此 post 的先前版本建议 运行 使用此查询:
def tags_with_user_specific_counts
query = <<-SQL
SELECT taggings.tag_id,
tags.name,
users.id as user_id,
count(taggings.id)
FROM taggings
INNER JOIN tags
ON taggings.tag_id = tags.id
INNER JOIN posts
ON taggings.taggable_id = posts.id
AND taggings.taggable_type = 'Post'
INNER JOIN users
ON posts.user_id = users.id
WHERE users.id = '#{user_id}'
GROUP BY taggings.tag_id, tags.name, users.id
SQL
ActiveRecord::Base.connection.execute(query).to_a
end
一种方法:
tag_counts = ActsAsTaggableOn::Tag.joins(:taggings).
where(taggings: { taggable_type: "Post", taggable_id: current_user.post_ids }).
group("tags.id").count
给你这样的东西:
{ 7=>2, 1=>2, 5=>4 }
第一个数字是标签 ID,第二个数字是该标签在 current_user 中出现的次数。
现在您可以这样做了:
<div class="post">
<%= raw post.tags.map{ |t| [t.name, tag_counts[t.id]] }.sort_by{ |t| -t[1] }.
map{ |t| link_to t[0], tag_path(t[0]), class: css_class_for_tag(t[1])}.join(' ') %>
</div>
这可以写得更有表现力,但比...更长 ;-) 基本上,我们像以前一样遍历 post 的标签,然后在第一个 map 语句中将每个标签与其特定于 current_user 的计数连接起来。然后我们按计数倒序排序。在最后的 map 语句中,我们像以前一样生成链接,只是再次使用用户特定计数。