Система голосования по порядку в rails

#ruby-on-rails #pins

#ruby-on-rails #пины

Вопрос:

Я реализовал простой способ, позволяющий пользователям голосовать за пины, но я хотел бы упорядочить пины по количеству голосов, вот моя система голосования:

приложение / контроллеры /pins_controller.rb

 def upvote
  @pin = Pin.find(params[:id])
  @pin.votes.create
  redirect_to(pins_path)
end
  

приложение / модели /vote.rb

 class Vote < ActiveRecord::Base

belongs_to :pin


end
  

приложение / модели/pin.rb

 has_many :votes, dependent: :destroy
  

И мой routes.rb выглядит так:

 resources :pins do
  member do
    post 'upvote'
  end
en
  

Итак, я хотел бы, чтобы мое приложение / views / pins / index.html.erb

чтобы отобразить пины с наибольшим количеством проголосовавших за меньшее количество проголосовавших.

Я знаю, что acts_as_votable предоставляет систему для этого, но поскольку я не использовал этот драгоценный камень для реализации своей системы голосования, я не уверен, что это хорошая идея использовать его.

Что вы думаете?

Мое приложение / views / pins /index.html.erb выглядит следующим образом:

 <% @pins.each do |pin| %>
    <div class="box panel panel-default">
      <%= image_tag pin.image.url(:medium) %>
      <div class="panel-body">

        <p class="startupinfo">
          <%= pin.startupname %><span class="reward"><i class="fa fa-heart"></i> <%= pin.reward %></p></span>

        <p class="startupname"><%= pin.description %></p>

        <p class="startuptag"><i class="fa fa-map-marker turquoise"></i> <%= pin.tag %> <i class="fa fa-tags turquoise"></i> <%= pin.city %><%= link_to upvote_pin_path(pin), method: :post do %> <i class="fa fa-chevron-circle-up fa-1x" style="color:#28c3ab"></i>  <% end %>
            <%= pluralize(pin.votes.count, "upvote") %></p>

        <p class="users-edit">
        <%= link_to pin_path(pin), class: "btn btn-success" do %> Tweet <% end %>

        <% if pin.user == current_user %>
          <%= link_to 'Edit', edit_pin_path(pin) %>
          <%= link_to 'Delete', pin, method: :delete, data: { confirm: 'Are you sure?' } %></p>
        <% end %>
      </div>
    </div>
  <% end %>
  

НА ОСНОВЕ ОТВЕТА БАЛУ:

Я побежал:

 rails generate migration add_votescount_to_pins votes_count:integer

rails generate migration add_countercache_to_votes counter_cache:integer
  

Я вручную обновил миграцию _add_votescount_to_pins.rb, так что это выглядит как:

 class AddVotescountToPins < ActiveRecord::Migration
  def change
    add_column :pins, :votes_count, :integer
    Pin.reset_column_information
        Pin.includes(:votes).all.each do |pin|
    Pin.update_counters(pin.id, :votes_count => pin.votes.size)
  end
end
  

Я побежал:

 rake db:migrate
  

Затем я добавил следующую строку в свой pins.controller.rb :

 def index
    @pins = Pin.all
    @pins = Pin.order(votes_count: :desc)
  end
  

Теперь мой файл schema.rb выглядит следующим образом:

 # encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20140704125839) do

  create_table "pins", force: true do |t|
    t.string   "description"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "user_id"
    t.string   "image_file_name"
    t.string   "image_content_type"
    t.integer  "image_file_size"
    t.datetime "image_updated_at"
    t.string   "tag"
    t.string   "reward"
    t.string   "startupname"
    t.string   "city"
    t.string   "tweet"
    t.string   "logo"
    t.string   "website"
    t.string   "rewardcode"
    t.string   "logo_file_name"
    t.string   "logo_content_type"
    t.integer  "logo_file_size"
    t.datetime "logo_updated_at"
    t.integer  "votes_count"
  end

  add_index "pins", ["user_id"], name: "index_pins_on_user_id"

  create_table "startups", force: true do |t|
    t.string   "name"
    t.text     "description"
    t.text     "location"
    t.text     "tag"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", force: true do |t|
    t.string   "email",                  default: "", null: false
    t.string   "encrypted_password",     default: "", null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,  null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "name"
    t.string   "provider"
    t.string   "uid"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true
  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

  create_table "votes", force: true do |t|
    t.integer  "pin_id"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "counter_cache"
  end

end
  

Но мой индекс пин-кода по-прежнему не упорядочен по количеству голосов, проголосовавших за!
Есть идеи?

Ответ №1:

Итак, когда вы отказываетесь голосовать, это уничтожает?

Вы можете добавить votes_count столбец integer к pins, а затем добавить counter_cache к голосованию.

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

Это увеличит количество голосов по pin-коду, когда кто-то проголосует за него.

Затем просто запрос на основе этого

 @pins = Pin.order(votes_count: :desc)
  

В процессе миграции вы захотите обновить votes_count на основе существующих голосов.

 add_column :pins, :votes_count, :integer, default: true
Pin.reset_column_information
Pin.all.each do |pin|
  Pin.reset_counters(pin.id, :votes)
end
  

Комментарии:

1. У меня нет системы голосования «против», я хочу предоставить пользователю возможность голосовать только «за». Я попробую ваш метод и буду держать вас в курсе. Спасибо, кстати

2. Я обновил свой вопрос тем, что вы сказали, но он по-прежнему не упорядочен, можете ли вы проверить, не допустил ли я где-нибудь ошибку?

3. Удалите эту строку @pins = Pin.all , также вам не нужен counter_cache целочисленный столбец в Pins — потому что имя класса — это Vote он будет искать в Pin столбце с именем votes_count это ваш столбец кэша счетчика

4. Я удаляю эту строку и заменяю ее на @pins = Pin.order(votes_count: :desc), но она по-прежнему упорядочивается не так, как я хочу. Я обновил свой pins / index.html.erb в своем вопросе — даже если я не думаю, что ошибка исходит из этого файла.

5. Итак, я должен удалить целое число counter_cache из моих столбцов Pins? Я обновил свой вопрос своим файлом schema.rb — Потому что у меня нет целочисленного столбца counter_cache для Pins — как и для столбцов votes (см. schema.rb, обновленный в моем вопросе)

Ответ №2:

Чтобы добиться этого без кеширования счетчика, вы можете попробовать это:

 @pins = Pin.joins(:votes).select('pins.*, COUNT(votes.id) AS vote_count').group('votes.pins_id').order('vote_count DESC')
  

Комментарии:

1. Где я должен это реализовать? В моем pins_controller.rb — внутренний индекс?

2. Да, если именно там вы хотите показать пины, отсортированные по голосам