actions-as-taggable-on создает дубликаты строк в базе данных

Я использую действия-как-taggable-on для приложения Rails, и каждый раз, когда я создаю новую фотографию (например), я получаю дублирующиеся строки в таблице "тегов".

Мои классы моделей выглядят примерно так:

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

а также

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

и в действии создания моего photos_controller

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

Странно то, что я получаю повторяющиеся строки в таблице "taggings", где в первой строке "tagger_id" и "tagger_ty pe" установлены в NULL, в то время как дублирующаяся строка имеет правильные значения.

мой Gemfile выглядит так

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

Кто-нибудь видел такое поведение раньше? Это проблема конфигурации на моем конце?

Обновление: Глядя на консоль, я ясно вижу две транзакции, которые выполняются, и в первой есть:

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]]

и ясно, что tagger_id имеет значение null, а также tagger_type.

Вот полный вывод на консоль, я разделил строки, чтобы помочь его прочитать. Вы заметите две отдельные транзакции, и в первой есть вставка со значениями NULL, а в конце второй вы увидите правильную.

Запуск POST "/photo" для 127.0.0.1 в 2012-09-28 07:39:58 +0200 Обработка PhotoController#create в виде HTML-параметров: {"utf8"=>"✓", "authenticity_token"=>"IOmnfDpU7V7vYw3h6RXXzXPsXf/B0fcVihXhb+S8JHU=", "photo"=>{"url"=>"www.another.com/photo.jpg", "title"=>"Another", "tag_list"=>"a_tag", "description"=>"", "private"=>"0"}, "commit"=>"Добавить фотографию"} Перенаправлено на http://www.somedomain.com:3000/users/christiangiacomi

Завершено 302 Найдено за 414 мс (ActiveRecord: 20,5 мс)

Пользовательская нагрузка (0,3 мс) ВЫБРАТЬ "пользователи".* ИЗ "пользователей" ГДЕ "пользователи". "Username" = 'christiangiacomi' LIMIT 1 ActsAsTaggableOn::Tag Load (0,2 мс) ВЫБРАТЬ "теги".* ИЗ "теги" INNER ПРИСОЕДИНЯЙТЕСЬ "taggings" ON "tags". "Id" = "taggings"."Tag_id" ГДЕ "taggings". "Taggable_id" IS NULL AND "taggings"."Taggable_type" = 'Фотография' AND (taggings.context = 'tags 'AND taggings.tagger_id IS NULL)

(0,1 мс) начать транзакцию

SQL (6,2 мс) INSERT INTO "photos" ("made_at", "description", "favourite", "private", "title", "updated_at", "url", "user_id") ЗНАЧЕНИЯ (?,?,?,?,?,?,?,?) [["creat_at", пт, 28 сен 2012 05:39:59 UTC +00:00], ["description", ""], ["favourite", false), ["private", false], ["title", "Another"], ["updated_at", пт, 28 сентября 2012 г. 05:39:59 UTC +00: 00], ["url", "http: / /www.another.com/photo.jpg "], [" user_id ", 1]]

ActsAsTaggableOn:: Tag Load (3.2ms) ВЫБРАТЬ "теги".* FROM "теги" WHERE (lower(name) = 'a_tag') ActsAsTaggableOn::Tag Существует (0,1 мс) SELECT 1 AS one FROM "теги" WHERE "теги "." name "= 'a_tag' LIMIT 1

SQL (0,2 мс) INSERT INTO "tags" ("name") VALUES (?) [["Name", "a_tag"]]

ActsAsTaggableOn:: Tag Load (0.1ms) ВЫБРАТЬ "теги".* FROM "теги" INNER JOIN "теги" ON "теги". "Id" = "теги". "Tag_id" ГДЕ "теги". "Taggable_id" = 13 AND "taggings"."Taggable_type" = 'Фото' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)

ActsAsTaggableOn:: Tagging существует (0,2 мс) ВЫБЕРИТЕ 1 КАК один из "taggings" WHERE ("taggings"."Tag_id" = 16 AND "taggings"."Taggable_type" = 'Photo' AND "taggings"."Taggable_id" = 13 AND "taggings"."Context" = "теги" AND "taggings". "Tagger_id" IS NULL AND "taggings"."Tagger_ty pe" IS NULL) LIMIT 1

SQL (0,7 мс) INSERT INTO "taggings" ("context", "create_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") ЗНАЧЕНИЯ (?,?,?,?,?,?,?) [["context", "tags"], ["creation_at", пт, 28 сентября 2012 05:39:59 UTC +00: 00], ["tag_id", 16], ["taggable_id", 13], ["taggable_ty pe", "Photo"], ["tagger_id", nil], ["tagger_ty pe", nil]]

(4.1ms) совершить транзакцию

(0,1 мс) начать транзакцию

ActsAsTaggableOn::Tag Load (0.2ms) ВЫБРАТЬ "теги".* FROM "теги" ГДЕ (нижний (имя) = 'a_tag')

ActsAsTaggableOn:: Загрузка тегов (0,2 мс) ВЫБРАТЬ "теги".* FROM "теги" INNER JOIN "теги" ON "теги". "Id" = "теги". "Tag_id" ГДЕ "теги". "Taggable_id" = 13 AND "taggings"."Taggable_type" = 'Фото' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)

CACHE (0.0ms) ВЫБЕРИТЕ "теги".* ИЗ "тегов" ГДЕ (нижний (имя) = 'a_tag')

ActsAsTaggableOn:: Загрузка тегов (0,2 мс) ВЫБРАТЬ "теги".* FROM "теги" INNER JOIN "теги" ON "теги". "Id" = "теги". "Tag_id" ГДЕ "теги". "Taggable_id" = 13 AND "taggings"."Taggable_type" = 'Фото' AND (taggings.context = 'tags' AND taggings.tagger_id = 1 AND taggings.tagger_type = 'Пользователь')

ActsAsTaggableOn:: Tagging Exists (0,4ms) ВЫБЕРИТЕ 1 КАК один ИЗ "taggings" WHERE ("taggings"."Tag_id" = 16 AND "taggings"."Taggable_type" = 'Photo' AND "taggings"."Taggable_id" = 13 AND "taggings"."Context" = "теги" AND "taggings". "Tagger_id" = 1 AND "taggings"."Tagger_ty pe" = 'User') LIMIT 1

SQL (0,5 мс) INSERT INTO "taggings" ("context", "creation_at", "tag_id", "taggable_id", "taggable_type", "tagger_id", "tagger_type") VALUES (?,?,?,?,?,?,?) [["context", "tags"], ["creation_at", пт, 28 сентября 2012 05:39:59 UTC +00: 00], ["tag_id", 16], ["taggable_id", 13], ["taggable_ty pe", "Photo"], ["tagger_id", 1], ["tagger_ty pe", "User"]]

(2,4 мс) совершить транзакцию

2 ответа

Решение

Хорошо, это действительно странно, но я понял, как избежать этой проблемы.

Первое, что я сделал, - это создал шип-решение для проверки act_as_taggable_on, и это, похоже, сработало. Он состоял из двух классов моделей и все... и он работал, когда я тестировал его через консоль rails.

Поэтому я добавил форму, как у меня в приложении rails, и протестировал ее снова...

У меня была форма, которая была что-то вроде этого:

   <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>

И когда я реализовал это и протестировал, я получил 'Cannot mass assign:tag_list'

Я прочитал об изменениях в Rails 3.2.3, касающихся массового назначения, и решил не ставить под угрозу безопасность.

Поэтому я добавил в свой класс фото модель

 attr_accessible :tag_list

Это решило ошибку, НО, когда я проверял это, я обнаружил, что дублирующиеся строки, где СЕЙЧАС появляются! Так что теперь ошибка воспроизводима!!

Я решил это, изменив форму так, чтобы я не связывал форму с объектом модели Photo.

Вот так:

<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>

Я также удалил attr_accessible:tag_list и изменил контроллер, чтобы принимать различные значения из формы.

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

И это решило проблему!

Я сейчас попробую разобраться, почему именно это происходит!:)

Я знаю, что этому вопросу уже добрых пять лет, но это все еще происходит в Rails 5.1 с acts-as-taggable 4.0 и я просто хотел показать, как это решается для всех, кто имеет эту проблему.

Это очень просто исправить, и в ваших сильных параметрах в контроллере вы просто должны добавить tag_list: [] скорее, чем :tag_list вот так:

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

Это перестает дублировать и пусто Taggings от создания в базе данных, и вы можете использовать form_for а не form_tag

<%= form_for @photo do |f| %>
  <%= f.text_field :title %>
  <%= f.text_field :tag_list %>
  <%= f.submit %>
<% end %>
Другие вопросы по тегам