In my app there will be many things that users
can vote up or down (in a Reddit like manner). This will include an upvote/downvote structure for jokes
, recipes
, rules
, etc. Right now I am trying to set up a polymorphic votes
using the joke
model as an example.
I have added votes
as a polymorphic variable, as can be seen in my schema
:
create_table "votes", force: :cascade do |t|
t.integer "value"
t.integer "user_id"
t.integer "voteable_id"
t.string "voteable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "votes", ["user_id"], name: "index_votes_on_user_id"
add_index "votes", ["voteable_type", "voteable_id"], name: "index_votes_on_voteable_type_and_voteable_id"
And I added a float
column to my jokes
table for rank
.
I created a vote
model:
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :voteable, polymorphic: true
after_save :update_vote
private
def update_joke
vote.update_rank
end
end
And made the appropriate updates to my joke
model:
class Joke < ActiveRecord::Base
belongs_to :user
has_many :votes, as: :voteable, dependent: :destroy
default_scope { order('rank DESC')}
def up_votes
votes.where(value: 1).count
end
def down_votes
votes.where(value: -1).count
end
def points
votes.sum(:value)
end
def update_rank
new_rank = points
update_attribute(:rank, new_rank)
end
end
And my user
model:
class User < ActiveRecord::Base
...
has_many :jokes, dependent: :destroy
has_many :votes, dependent: :destroy
end
I have a votes_controller
as follows:
class VotesController < ApplicationController
def up_vote
update_vote(1)
redirect_to :back
end
def down_vote
update_vote(-1)
redirect_to :back
end
private
def update_vote(new_value)
@joke = Joke.find(params[:joke_id])
@vote = @joke.votes.where(user_id: current_user.id).first
if @vote
@vote.update_attribute(:value, new_value)
else
@vote = current_user.votes.create(value: new_value, joke: @joke)
end
end
end
And finally this _voter.html.erb
partial:
<div class="text-center col-xs-1">
<% if current_user %>
<div class="width: 100%"><%= link_to " ", joke_up_vote_path(joke), class: 'glyphicon glyphicon-chevron-up', method: :post, style: "margin-right: 0; margin-left: 0" %></div>
<% else %>
<div class="width: 100%"><%= link_to " ", new_user_session_path, class: 'glyphicon glyphicon-chevron-up', method: :post, style: "margin-right: 0; margin-left: 0" %></div>
<% end %>
<div class="width: 100%"><h3 style="margin-top: 0; margin-bottom: 0"><strong><%= joke.points %></strong></h3></div>
<% if current_user %>
<div class="width: 100%"><%= link_to " ", joke_up_vote_path(joke), class: 'glyphicon glyphicon-chevron-down', method: :post, style: "margin-right: 0; margin-left: 0" %></div>
<% else %>
<div class="width: 100%"><%= link_to " ", new_user_session_path, class: 'glyphicon glyphicon-chevron-down', method: :post, style: "margin-right: 0; margin-left: 0" %></div>
<% end %>
</div>
Is shown on my otherjokes/kids.html.erb
page:
<% @jokes.each do |joke| %>
<div class="row">
<% if joke.approved == true && joke.kids == true %>
<%= render partial: 'votes/voter', locals: { joke: joke } %>
<div class="col-xs-11"> <!-- non vote container -->
<h2 id="joke-title" style="margin-top: 0px">
<%= joke.title %>
<% if joke.kids == true %>
<%= image_tag 'icon_kids.jpg', style: "height: 25px; margin-left: 10px" %>
<% end %>
<% if joke.mixed == true %>
<%= image_tag 'icon_mixed.jpg', style: "height: 25px; margin-left: 10px" %>
<% end %>
</h2>
<p id="joke-body"><%= joke.body %></p>
<p><strong>Submitted by: <%= joke.user.first_name %></strong></p>
<% if current_user && (current_user == joke.user || current_user.admin) %>
<%= link_to edit_joke_path(joke) do %>
<span style="color: blue" class="glyphicon glyphicon-pencil" aria-hidden="true"></span><span style="color: blue">Edit Joke</span>
<% end %>
<%= link_to joke_path(joke), data: {:confirm => 'Are you sure?'}, :method => :delete do %>
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>Delete Joke
<% end %>
<% end %>
</div> <!-- non vote container -->
</div> <!-- joke row -->
<% end %>
<div class="row text-center">
<hr style="width: 50%; margin-top: 30px; margin-bottom: 30px">
</div> <!-- row -->
<% end %>
I have made the following adjustments to my routes
as well:
resources :jokes do
patch :approve, on: :member
patch :reject, on: :member
post '/up-vote' => 'votes#up_vote', as: :up_vote
post '/down-vote' => 'votes#down_vote', as: :down_vote
end
Right now when I try to vote on a joke, I get unknown attribute 'joke' for Vote.
called on the @vote = current_user.votes.create(value: new_value, joke: @joke)
line at the end of my votes_controller
.
I would love to get this system running in a way that it could be applied to other future models that will require voting and also (if possible) to do it "the Ruby way". Can anyone help me structure this properly?
As the error states, joke
is not an attribute of Vote
. Because it is a polymorphic association you gave it the generic name votable
. Use this to create a Vote
for a Joke
.
@vote = current_user.votes.create(value: new_value, votable: @joke)
votable_id
and votable_type
will be derived automatically by the framework.
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.