简体   繁体   中英

Rails: How to increment counter_cache?

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

::: SOLVED :::

Was able to figure out counter_cache on the Vote model and use it in Topics. Updated question below. Once the Topic model had a votes_count, all I had to do was put that in the view and update my counting in the controllers. Turned out to be pretty simple. However finding information was not, so this should help others out, in all its confusing glory. Go way way down for the answer.

Cheers

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:::EDIT:::

:::See below for history:::

This is an edit to the original post. I'm really close with what I have, before I try a gem solution. I used a comment_cache on the vote model but now I don't know how to increment the cache.

class Vote < ActiveRecord::Base
    belongs_to :topic, :counter_cache => true
end

and called topic.votes.size in the view and it now shows the count without an Active Record enumerable.

<td><%= pluralize(topic.votes.size, "vote") %></td>
        <td><%= button_to '+1', upvote_topic_path(topic), method: :post %></td>
        <td><%= button_to '-1', downvote_topic_path(topic), method: :post %></td>

index.html.erb

Listing Topics

Title   
test    1 vote  [+1] [-1]   Delete

New Topic

But comment_cache doesn't allow negative numbers. I'm still using create/destroy on the records rather than adding and subtracting a number to the count. How to increment and decrement? Those methods say I'm missing values. Currently it makes sense @topic.votes.last.destroy fails in that there are no more votes to destroy thus the tally cannot be less than zero.

How to setup so that I'm incrementing/decrementing the comment_cache value such that negative numbers are possible? I tried @topic.votes.increment and @topic.votes.increment_counter but they ask for 2 params that I'm not sure what to use. Tried

@topic.increment_counter(:votes_count, params[:id])

but no luck.

NoMethodError in TopicsController#upvote
undefined method `increment_counter' for #<Topic:0x007f87d974a4b0>

Extracted source (around line #433):
431
432
433
434
435
436

      else
        match = match_attribute_method?(method.to_s)
        match ? attribute_missing(match, *args, &block) : super
      end
    end

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

:::HISTORY BELOW THIS LINE:::

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Revisiting Rails again and I'm trying to do a simple vote counter on topics. What I want to do is increment/decrement the tally (integer) column in the votes record.

Topic has_may :votes, and Vote belongs_to :topic

In the topics_controller.rb I've used

def upvote
    @topic = Topic.find(params[:id])
    @topic.votes.increment_counter(:tally, params[:id])
    redirect_to(topics_path)
  end

  def downvote
    @topic = Topic.find(params[:id])
    @topic.votes.decrement_counter(:tally, params[:id])
    redirect_to(topics_path)
  end

But I get an interesting ActiveRecord key instead of a value in the view instead of the count of votes.

index.html.erb:

from the view
<td><%= pluralize(topic.votes(:tally), "vote") %></td>
<td><%= button_to '+1', upvote_topic_path(topic), method: :post %></td>
<td><%= button_to '-1', downvote_topic_path(topic), method: :post %></td>

result of view
    test    #<Vote::ActiveRecord_Associations_CollectionProxy:0x007fbb16451f68> votes   

        Delete

From Rails Console though the increment doesn't show up:

  2.1.5 :022 > Vote.first
  Vote Load (0.5ms)  SELECT  "votes".* FROM "votes"  ORDER BY "votes"."id" ASC LIMIT 1
 => nil 

2.1.5 :023 > Vote
 => Vote(id: integer, topic_id: integer, created_at: datetime, updated_at: datetime, tally: integer) 

I think I've setup the routes.rb ok but the double do I'm not sure about

Rails.application.routes.draw do
  resources :topics do
    member do
      post 'upvote'
      post 'downvote'
    end
  end

  root 'topics#index'  
end

Anyway I think it's either the wrong method to increment or something I'm missing. Any help appreciated. Cheers

Do you have a user model working the votes? If so, there is a pretty awesome gem that handles this called acts_as_votable - https://github.com/ryanto/acts_as_votable

You just bundle the gem, run the migrations, then add acts_as_votable to your topics model, and acts_as_voter to your user model, you can then use a load of helper methods.

TopicsController.rb

    def upvote
        @topic.upvote_by current_user
        redirect_to :back
    end

    def downvote
        @topic.downvote_by current_user
        redirect_to :back
    end

Routes.rb

    resources :topics do
      member do
        get "upvote", to: "topics#upvote"
        get "downvote", to: "topics#downvote"
      end
    end

/topics/show.html.erb

    <%= link_to upvote_topic_path(@topic), method: :get  do %>
    <%= link_to downvote_topic_path(@topic), method: :get  do %>

    <%= pluralize(@topic.get_upvotes.size, 'Vote') %>

::SOLVED::

I was able to fix the problem using counter_cache in the Vote model. Voting is now possible with both positive and negative values. Hooray!

Added a new column in topic.

class AddVotesCount < ActiveRecord::Migration
  def change
    add_column :topics, :votes_count, :integer, :default => 0

  end
end

Update Vote model

class Vote < ActiveRecord::Base
        belongs_to :topic, :counter_cache :true
end

Now in the topics controller I can increment the up/down methods with

@topic.votes_count += 1
.
.
.
@topic.votes_count -= 1

I can use @topic.update_attributes but I found it unreliable for some reason so I opted for @topic.save in each method.

The view now uses just topics.votes_count and works great.

<td><%= pluralize(topic.votes_count, "vote") %></td>
        <td><%= button_to '+1', upvote_topic_path(topic), method: :post %></td>
        <td><%= button_to '-1', downvote_topic_path(topic), method: :post %></td>

This works without needing a User model and/or the acts_as_votable gem. Although I will probably use a gem in future it was good to figure out how to make this work without it.

Cheers

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM