Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird behaviors on acts-as-taggable-on gem of not clearing taggings

I have a model called Recipe which uses the acts-as-taggable-on gem to add taggings to the recipes. The weird behavior is when I edit and update the recipe via the controller, the recipe taggings are added instead of updating to proper taggings. There was a similar Github issue being reported but there's no reply from anyone. My acts-as-taggable-on gem version is 3.2.6.

For example, my recipe has three taggings: fruit, fruit and vegetables. The fruit tag is not relevant, so I removed these two fruit tag in my f.tag_list input field to only include vegetables.

Processing by Users::RecipesController#update as JS
Parameters: {"utf8"=>"✓", "recipe"=>{"image_ids"=>"46746", "cover_image_id"=>"46746",
"name"=>"Cauliflower", "description"=>"this is the description", "preparation_time_hr"=>"0",
"preparation_time_min"=>"50", "servings"=>"4", 
"category_ids"=>"", "cuisine_ids"=>"", "tag_list"=>"vegetables", "ingredients"=>....}

users/recipes_controller.rb

def update
  if @recipe.update_attributes(recipe_params)
    if params[:publish_recipe] && params[:publish_recipe] == "true"
      update_status!(:publish!)
    end
    redirect_to user_recipes_url
  else
    respond_to do |format|
      format.html{ render "edit" }
      format.js { render partial: "shared/flash_message", locals: { flash_type: "error", flash_message: "#{@recipe.errors.full_messages.to_sentence}" } }
    end
  end
end

The recipe_params included tag_list as the permitted parameter.

def recipe_params
  params.required(:recipe).permit(:name, :category_ids, :cuisine_ids, :tag_list, :preparation_time_hr,
  :cover_image_id, :preparation_time_min, :servings, :description, :image_ids, :commentable,
  ingredients_attributes: [:id, :name, :_destroy],
  instructions_attributes: [:id, :text, :_destroy, image_attributes: [:id, :picture, :_destroy]]
) rescue {}
end

But ended up the old taggings are not removed, and the new one has been added to the list: fruit, fruit, vegetables, vegetables.

Another weird thing is that when I try to see the tag_list of the recipe, it is an empty array. Tried edit the tag_list in rails console, it added another tagging but when I do r.reload the tag_list it is still an empty array. Now the relationship between the recipe and the tags are like so:

r = Recipe.find(20980)
[56] pry(main)> r.tag_list
 ActsAsTaggableOn::Tag Load (14.9ms)  SELECT `tags`.* FROM `tags` INNER JOIN `taggings` ON `tags`.`id` = `taggings`.`tag_id` WHERE `taggings`.`taggable_id` = 20980 AND `taggings`.`taggable_type` = 'Recipe' AND (taggings.context = 'tags' AND taggings.tagger_id IS NULL)
=> []
[57] pry(main)> r.tags
=> [#<ActsAsTaggableOn::Tag id: 2, name: "fruit", taggings_count: 1138, tagger_id: nil, tagger_type: nil, created_at: "2015-07-25 15:47:20", updated_at: "2015-07-25 15:47:53">,
 #<ActsAsTaggableOn::Tag id: 2, name: "fruit", taggings_count: 1138, tagger_id: nil, tagger_type: nil, created_at: "2015-07-25 15:47:20", updated_at: "2015-07-25 15:47:53">,
 #<ActsAsTaggableOn::Tag id: 531, name: "vegetables", taggings_count: 21, tagger_id: nil, tagger_type: nil, created_at: "2015-07-25 15:56:15", updated_at: "2015-07-25 15:56:16">,
 #<ActsAsTaggableOn::Tag id: 531, name: "vegetables", taggings_count: 21, tagger_id: nil, tagger_type: nil, created_at: "2015-07-25 15:56:15", updated_at: "2015-07-25 15:56:16">,
 #<ActsAsTaggableOn::Tag id: 531, name: "vegetables", taggings_count: 21, tagger_id: nil, tagger_type: nil, created_at: "2015-07-25 15:56:15", updated_at: "2015-07-25 15:56:16">]
[58] pry(main)> r.taggings
  ActsAsTaggableOn::Tagging Load (35.8ms)  SELECT `taggings`.* FROM `taggings`  WHERE `taggings`.`taggable_id` = 20980 AND `taggings`.`taggable_type` = 'Recipe'
=> [#<ActsAsTaggableOn::Tagging id: 20408, tag_id: 2, taggable_id: 20980, taggable_type: "Recipe", tagger_id: 200422, tagger_type: "User", context: "tags", created_at: "2015-08-21 03:56:13">,
 #<ActsAsTaggableOn::Tagging id: 20409, tag_id: 2, taggable_id: 20980, taggable_type: "Recipe", tagger_id: 200422, tagger_type: "User", context: "tags", created_at: "2015-08-21 03:56:14">,
 #<ActsAsTaggableOn::Tagging id: 20410, tag_id: 531, taggable_id: 20980, taggable_type: "Recipe", tagger_id: 200422, tagger_type: "User", context: "tags", created_at: "2015-08-21 04:01:36">,
 #<ActsAsTaggableOn::Tagging id: 20411, tag_id: 531, taggable_id: 20980, taggable_type: "Recipe", tagger_id: 200422, tagger_type: "User", context: "tags", created_at: "2015-08-21 04:47:38">,
 #<ActsAsTaggableOn::Tagging id: 20412, tag_id: 531, taggable_id: 20980, taggable_type: "Recipe", tagger_id: 200422, tagger_type: "User", context: "tags", created_at: "2015-08-21 04:53:38">]

The tag_list database query is weird too, why the query includes this taggings.tagger_id IS NULL ?

I'm still new to this gem, any idea on how to update the taggings properly using the gem methods? I hope I could avoid updating the taggings using my own code to avoid further problems. It is noted that my Recipe model is versioned by paper_trail gem, I hope it's not related to this problem.

like image 993
nayiaw Avatar asked Oct 31 '22 21:10

nayiaw


2 Answers

Since there's no solution from the internet, and I only use this taggable on Recipe, for now I'm manually calling a method to re-update the tags after model save.

def update_tags(tag_list)
  self.taggings.destroy_all
  list = tag_list.split(",")
  list.each do |t|
    tag = Tag.find_or_create_by(name: t)
    self.taggings.create(tag_id: tag.id, context: "tags")
  end
  return self.taggings
end

I still think there are better solutions to this problem, I'm open to alternatives as the best answer.

like image 149
nayiaw Avatar answered Nov 15 '22 05:11

nayiaw


I can't see anything in the act-as-taggable-on docs about what happens when the tags_list goes as a parameter into the update_attributes method, but in your case it seems to add the tags without removing existing ones. I did notice in the docs that the tag_list can be assigned with the = which replaces existing tags:

You can also add and remove tags by direct assignment. Note this will remove existing tags so use it with attention.

@user.tag_list = "awesome, slick, hefty"
@user.save

So you can do that in your update method

def update
  if @recipe.update_attributes(recipe_params)
    @recipe.tag_list = recipe_params["tag_list"]
    @recipe.save
    ...
  else
    ...
  end
end
like image 27
Toby 1 Kenobi Avatar answered Nov 15 '22 05:11

Toby 1 Kenobi