2

Each question has an array of tags.

schema.rb:

create_table "questions", force: true do |t|
  t.text     "tags", default: [], array: true

How to atomically append to tags?

How to prevent dups within the array?

I tried question.update_attribute tags: tags << :ruby, but this doesn't work.

Rails 4.17 and Postgres.

EDIT: This seems to have been fixed in Rails 4.2

1 Answer 1

2

I don't think update_attribute is going to be useful as it will replace the array with the new value rather than append to it (but see better explanation below in --Update-- section).

I'm not sure what best practices are here, but this should work to just add something if it isn't already there:

question.tags << :ruby unless question.tags.include?(:ruby)
question.save

I would write a custom method on the Question model to manage adding tags and checking for uniqueness:

def add_tag(tag)
  if tags.include?(tag)
    # do whatever you want to do when tag isn't unique
    p "#{tag} is already in tags!"
  else
    tags << tag
    save
  end
end

Then call it with question.add_tag(:ruby).

-------------------- Update -----------------------

If this doesn't work, there could be an issue with ActiveRecord not recognizing the field has changed (although it seems to work OK in Rails 4.2).

These links explain the issue:

New data not persisting to Rails array column on Postgres

http://paweljaniak.co.za/2013/07/28/rails-4-and-postgres-arrays/

As they explain, you can use update_attribute here, but you need to replace the entire array rather than push a value onto it, like this:

question.update_attribute(:tags, question.tags << tag)

You should also be able to force ActiveRecord to consider the attribute updated by including will_change! like this:

def add_tag(tag)
  if tags.include?(tag)
    # do whatever you want to do when tag isn't unique
    p "#{tag} is already in tags!"
  else
    tags_will_change!
    tags << tag
    save
  end
end
Sign up to request clarification or add additional context in comments.

7 Comments

I actually wrote this method. But it doesn't work for some reason. Apparently ActiveRecord can't update an array field from within the model. However, it does work from the controller.
Hmm, not sure what the trouble is -- were you calling it on a question object or the model class? I did a quick test in rails console on an object and it seemed to work. The use case I was picturing was an instance method on the model that is called in the controller like: question = Question.find(params[:id] then something like question.add_tag(params[:tag]).
I called it on the question object. Did question.add_tag work for you? I guess there could be something else going on because the code in add_tag works in the controller, but not when calling add_tag on the object. It is strange because updating other fields works when calling add_tag on the object.
Another strange thing is that the save method returns true, but the record is not updated. So I think there is an issue with ActiveRecord and array fields.
I have a Rack app, instead of Rails. So that is one possible cause. self.tags << tag; save and will not update the tags field. It will update other fields.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.