ruby on rails

I am using acts-as-taggable-on for a Rails application and every time I create a new Photo (for example) I get duplicate rows in the 'taggings' table.

My model classes look something like:

class User < ActiveRecord::Base
  acts_as_tagger
  ...
end

and

class Photo < ActiveRecord::Base
   acts_as_taggable_on :tags
   ...
end

and in the create action of my photos_controller

def create
  @user = current_user
  ... 
  @user.tag(@photo, :with => params[:photo][:tag_list], :on => :tags)
  ...
end

The strange thing is that I get duplicate rows in the 'taggings' table where the first row has 'tagger_id' and 'tagger_type' set to NULL while the duplicate row has the correct values.

my Gemfile looks like this

gem 'rails', '3.2.8'
gem 'acts-as-taggable-on', '~> 2.3.1'

Has anyone seen this behavior before? Is it a configuration problem on my end?

Update: Looking at the console I can clearly see two transactions being performed and in the first one there is this:

SQL (0.6ms)  INSERT INTO "taggings" ("context", "created_at", "tag_id", 
"taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?)
[["context", "tags"], ["created_at", Thu, 27 Sep 2012 21:49:22 UTC +00:00], ["tag_id",
 12], ["taggable_id", 10], ["taggable_type", "Photo"], 
["tagger_id", nil], ["tagger_type", nil]]

and it is clear that tagger_id is set to null as well as tagger_type.

Here is a full console output, I have separated the lines to help read it. You will notice two separate transactions and in the first one there is the insert with NULL values while at the end of the second one you will see the correct one.

Started POST "/photo" for 127.0.0.1 at 2012-09-28 07:39:58 +0200 Processing by PhotoController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"IOmnfDpU7V7vYw3h6RXXzXPsXf/B0fcVihXhb+S8JHU=", "photo"=>{"url"=>"www.another.com/photo.jpg", "title"=>"Another", "tag_list"=>"a_tag", "description"=>"", "private"=>"0"}, "commit"=>"Add Photo"} Redirected to http://www.somedomain.com:3000/users/christiangiacomi

Completed 302 Found in 414ms (ActiveRecord: 20.5ms)

User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."username" = 'christiangiacomi' LIMIT 1 ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" IS NULL AND "taggings"."taggable_type" = 'Photo' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)

(0.1ms) begin transaction

SQL (6.2ms) INSERT INTO "photos" ("created_at", "description", "favorite", "private", "title", "updated_at", "url", "user_id") VALUES (?, ?, ?, ?, ?, ?, ?, ?) [["created_at", Fri, 28 Sep 2012 05:39:59 UTC +00:00], ["description", ""], ["favorite", false], ["private", false], ["title", "Another"], ["updated_at", Fri, 28 Sep 2012 05:39:59 UTC +00:00], ["url", "http://www.another.com/photo.jpg"], ["user_id", 1]]

ActsAsTaggableOn::Tag Load (3.2ms) SELECT "tags".* FROM "tags" WHERE (lower(name) = 'a_tag') ActsAsTaggableOn::Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = 'a_tag' LIMIT 1

SQL (0.2ms) INSERT INTO "tags" ("name") VALUES (?) [["name", "a_tag"]]

ActsAsTaggableOn::Tag Load (0.1ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 13 AND "taggings"."taggable_type" = 'Photo' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)

ActsAsTaggableOn::Tagging Exists (0.2ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 16 AND "taggings"."taggable_type" = 'Photo' AND "taggings"."taggable_id" = 13 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" IS NULL AND "taggings"."tagger_type" IS NULL) LIMIT 1

SQL (0.7ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Fri, 28 Sep 2012 05:39:59 UTC +00:00], ["tag_id", 16], ["taggable_id", 13], ["taggable_type", "Photo"], ["tagger_id", nil], ["tagger_type", nil]]

(4.1ms) commit transaction

(0.1ms) begin transaction

ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" WHERE (lower(name) = 'a_tag')

ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 13 AND "taggings"."taggable_type" = 'Photo' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)

CACHE (0.0ms) SELECT "tags".* FROM "tags" WHERE (lower(name) = 'a_tag')

ActsAsTaggableOn::Tag Load (0.2ms) SELECT "tags".* FROM "tags" INNER JOIN "taggings" ON "tags"."id" = "taggings"."tag_id" WHERE "taggings"."taggable_id" = 13 AND "taggings"."taggable_type" = 'Photo' AND (taggings.context = 'tags' AND taggings.tagger_id = 1 AND taggings.tagger_type = 'User')

ActsAsTaggableOn::Tagging Exists (0.4ms) SELECT 1 AS one FROM "taggings" WHERE ("taggings"."tag_id" = 16 AND "taggings"."taggable_type" = 'Photo' AND "taggings"."taggable_id" = 13 AND "taggings"."context" = 'tags' AND "taggings"."tagger_id" = 1 AND "taggings"."tagger_type" = 'User') LIMIT 1

SQL (0.5ms) INSERT INTO "taggings" ("context", "created_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?, ?, ?, ?, ?, ?, ?) [["context", "tags"], ["created_at", Fri, 28 Sep 2012 05:39:59 UTC +00:00], ["tag_id", 16], ["taggable_id", 13], ["taggable_type", "Photo"], ["tagger_id", 1], ["tagger_type", "User"]]

(2.4ms) commit transaction


Ok, this is really strange, but I have figured out how to avoid the problem.

The fist thing I did was to create a spike solution to test acts_as_taggable_on and that seemed to work. It consisted of two model classes and that's it... and it worked when I tested it via the rails console.

So I added a form like I had in my rails app and the tested it again...

I had a form that was something like this:

   <div>
      <%= form_for @photo, :html => { :class => "dialog" } do |f| %>

      ...

      <%= f.label :title%>
      <%= f.text_field :title, :class => "wide" %>

      <%= f.label 'Tags' %>
      <%= f.text_field :tag_list, :value => @tags %>

      ...

      <%= f.submit button_text(@photo), :class => "btn btn-large btn-primary" %>
      <%= f.submit "Cancel", :class => "btn btn-large"  %>
   </div>

And when I implemented this and tested it I got a 'Cannot mass assign :tag_list'

I read about the changed in Rails 3.2.3 regarding mass assignment and decided not to compromise security.

So I added to my Photo model class

 attr_accessible :tag_list

That solved the error BUT when I tested it I found that the duplicate rows where NOW appearing! So now the bug is reproducible!!

I solved it by changing the form so that I don't bind the form to the Photo model object.

Like so:

<div>
   <%= form_tag({:controller => "photos", :action => "create"}, :method => "post", :class => "dialog") do %>

   ...

   <%= label_tag :title, 'Title'%>
   <%= text_field_tag :title, nil, :class => "wide" %>

   <%= label_tag :tag_list, 'Tags'%>
   <%= text_field_tag :tag_list, nil, :class => "wide" %>

   ...

   <%= submit_tag('Add', :class => "btn btn-large btn-primary") %>
   <%= submit_tag("Cancel", :class => "btn btn-large")  %>
</div>

I also removed the attr_accessible :tag_list and modified the controller to accept the different values from the form.

@photo = current_user.photos.build(:title => params[:title],
                                   ...
                                   )
@user.tag(@photo, :with => params[:tag_list], :on => :tags)

And that solved the problem!

I will now try to look into exactly why this is happening! :)


I know this question is a good five years old now, but this still happens in Rails 5.1 with acts-as-taggable 4.0 and I just wanted to show how it's solved for anyone having this issue.

It's a very simple fix and in your strong params in you controller you simply have to add tag_list: [] rather than :tag_list like so:

def photo_params
  params.require(:photo).permit(:title, tag_list: [])
end

This stops duplicate and blank Taggings from being created in the database and you can use a form_for rather than a form_tag

<%= form_for @photo do |f| %>
  <%= f.text_field :title %>
  <%= f.text_field :tag_list %>
  <%= f.submit %>
<% end %>
链接地址: http://www.djcxy.com/p/35936.html

上一篇: 使用行为优化查询

下一篇: 红宝石在轨道上