When creating a website that needs voting functionality, you have two main options:
Let's compare these options.
Users
-----
- id (Primary Key)
- username
- password_digest
- created_at
Polls
-----
- id (Primary Key)
- title
- description
- created_by (Foreign Key -> Users.id)
- created_at
- ends_at
Options
-------
- id (Primary Key)
- poll_id (Foreign Key -> Polls.id)
- content
Votes
-----
- id (Primary Key)
- user_id (Foreign Key -> Users.id)
- option_id (Foreign Key -> Options.id)
- created_at
Key points about this schema:
This schema allows for:
You could expand this schema to include more features like:
When designing your own system, focus on these key elements:
# app/models/user.rb
class User < ApplicationRecord
has_many :votes
end
# app/models/poll.rb
class Poll < ApplicationRecord
has_many :options
has_many :votes, through: :options
end
# app/models/option.rb
class Option < ApplicationRecord
belongs_to :poll
has_many :votes
end
# app/models/vote.rb
class Vote < ApplicationRecord
belongs_to :user
belongs_to :option
end
Set up routes in config/routes.rb:
Rails.application.routes.draw do
resources :users, only: [:new, :create]
resources :polls do
resources :votes, only: [:create]
end
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :current_user
def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end
end
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(user_params)
if @user.save
session[:user_id] = @user.id
redirect_to polls_path
else
render :new
end
end
private
def user_params
params.require(:user).permit(:username, :password)
end
end
# app/controllers/votes_controller.rb
class VotesController < ApplicationController
def create
@poll = Poll.find(params[:poll_id])
@option = @poll.options.find(params[:option_id])
if !current_user.votes.exists?(option: @option)
@vote = current_user.votes.create(option: @option)
redirect_to @poll, notice: 'Vote recorded!'
else
redirect_to @poll, alert: 'You have already voted.'
end
end
end
<!-- app/views/polls/show.html.erb -->
<h1><%= @poll.title %></h1>
<% @poll.options.each do |option| %>
<p>
<%= option.content %>
<%= button_to 'Vote', poll_votes_path(@poll, option_id: option.id), method: :post %>
</p>
<% end %>
<h2>Results:</h2>
<% @poll.options.each do |option| %>
<p><%= option.content %>: <%= option.votes.count %> votes</p>
<% end %>
This is a basic implementation. You'd need to add more features like creating polls, handling edge cases, and improving the user interface. Remember to implement proper security measures, such as input validation and protection against CSRF attacks.