42

I have a Picture model that contains a variable for a view count (integer). The view count is incremented by +1 every time someone views the Picture object.

In getting this done, what is the difference between

   @picture.view_count += 1
   @picture.save

and

   @picture.increment(:view_count, 1)

also if i use increment, is .save necessary?

Myrddin Emrys
  • 42,126
  • 11
  • 38
  • 51
James Pleasant
  • 707
  • 1
  • 6
  • 12
  • 2
    Shouldn't the syntax be `@picture.increment(:view_count, 1)`? – Yo Ludke Apr 10 '13 at 14:03
  • 3
    Be aware of concurrency!! Your both solutions are not safe in this case! Imagine two users run the code at the same time. Both work with the same `view_count` value. Then second user overwrites the value of the first user on save and the final count is one less than expected. You should use `increment_counter` to avoid this. – Jay Dinse Oct 19 '16 at 10:39

3 Answers3

52

The source of increment is below, which initializes attribute to zero if nil and adds the value passed as by (default is 1), it does not do save, so .save is still necessary.

def increment(attribute, by = 1)
  self[attribute] ||= 0
  self[attribute] += by
  self
end
xdazz
  • 158,678
  • 38
  • 247
  • 274
  • 31
    or you can use `increment!` method if you don't want to save it manually – Vasiliy Ermolovich Jul 16 '12 at 06:40
  • 2
    This is a bit dated, new method is increment_counter – timroman Jun 28 '14 at 04:00
  • do you think this increment thing could be scoped with a relation ? post has_many images, the image model having a position attribute that would require to be incremented – Ben Dec 06 '14 at 15:33
  • 1
    @timroman method increment_counter works on Model class, if you have a record you just call 'increment' on it to increment the value by 1. – DivinesLight Jan 28 '15 at 13:33
33

I often use counter_cache and increment_counter in that case.

like this:

Picture.increment_counter(:view_count, @picture.id)

This way is more simple and faster than self-made method.

Incidentally, ActiveRecord::CounterCache has also decrement_counter.

http://api.rubyonrails.org/classes/ActiveRecord/CounterCache/ClassMethods.html

nekova
  • 331
  • 3
  • 3
5

You should use counter_cache. counter_cache helps you increment number of records automaticaly.

class Picture < ActiveRecord::Base
  has_many :views
end

class View < ActiveRecord::Base
  belongs_to :picture, counter_cache: true
end

pictures table needs column with name views_count, or you can use your own name for this column, for example:

belongs_to :picture, counter_cache: :number_of_views

But I recommend you to use default name for counter_cache column which is views_count.

egoholic
  • 317
  • 1
  • 6