#ruby-on-rails #ruby #vote #pins
#ruby-on-rails #ruby #Голосовать #выводы
Вопрос:
В моем приложении rails есть система повышения голосов, которая позволяет пользователям повышать голос за пин-код. Но я хотел бы ограничить возможность повышать голос только один раз за пин-код.
app /controllers/pins_controller.rb
def upvote
@pin = Pin.find(params[:id])
@pin.votes.create
redirect_to(pins_path)
end
app /models/pin.rb
class Pin < ActiveRecord::Base
belongs_to :user
has_many :votes, dependent: :destroy
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }
has_attached_file :logo, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
app /config/routes.rb
resources :pins do
member do
post 'upvote'
end
end
Я не уверен, как это реализовать, поскольку я пытался внедрить систему, которая позволяла пользователям повышать голос только один раз, это не то, что я хочу, я хочу, чтобы они могли повышать голос за «PIN» только один раз.
Я знаю, что драгоценный камень acts_as_votable предоставляет эту функцию, но поскольку я ее не использую, я хотел бы знать, есть ли способ реализовать это в моем собственном коде.
Есть идеи?
ОБНОВЛЕНИЕ: этот метод разрешает только один голос за пин-код. смотрите решение @Ege
заставить это работать с этим:
def upvote
@pin = Pin.find(params[:id])
if @pin.votes.count == 0
@pin.votes.create
redirect_to(pins_path)
else flash[:notice] = "You have already upvote this!"
redirect_to(pins_path)
end
end
Ответ №1:
Вы выбрали ответ beautifulcoder как правильный, но вы должны знать, что он потенциально неверен и может быть неочевиден, если вы новичок в Rails.
Вы говорите, что у Pin-кода должен быть только один голос, но, предположительно, вы имеете в виду, что у него должно быть по одному голосу на пользователя, то есть каждый пользователь должен иметь возможность повысить голос за pin-код только один раз. Обычно так работают механизмы голосования.
С ответом beautifulcoder, если я повышу голос за пин-код, вы не сможете повысить его, потому что ваш контроллер подсчитает количество голосов по Пин-коду, вернет 1 (потому что я за него проголосовал) и не позволит вам повысить голос. Кроме того, появится сообщение о том, что вы проголосовали за это, в то время как вы этого не делали!
Как решить эту проблему? К счастью, Rails делает это очень простым. Ваш голос на самом деле является замаскированной моделью объединения. Это устанавливает связь (т. Е. ассоциацию) между пользователями и выводами. Пользователь может повысить голос за пин-код, и пин-код может быть повышен пользователем. Другими словами, голоса «соединяют» пользователей и пин-коды! Что вам нужно сделать, так это определить эту взаимосвязь, используя ассоциации ActiveRecord.
В вашей модели выводов была бы добавлена эта ассоциация:
class Pin < ActiveRecord::Base
has_many :votes, dependent: :destroy
has_many :upvoted_users, through: :votes, source: :user
...
end
Это позволяет вам делать что-то вроде @pin.upvoted_users
и получать список пользователей, которые повысили голос за этот пин-код. Довольно неплохо, если вы хотите иметь возможность уведомлять владельца пин-кода!
Вы также хотите добавить обратную связь к своей пользовательской модели:
class User < ActiveRecord::Base
has_many :votes, dependent: :destroy
has_many :upvoted_pins, through: :votes, source: :pin
...
end
А затем измените модель голосования следующим образом:
class Vote < ActiveRecord::Base
belongs_to :user
belongs_to :pin
validates_uniqueness_of :pin_id, scope: :user_id
end
И, наконец, в вашем контроллере вы бы сделали:
def upvote
@pin = Pin.find(params[:id])
if @pin.votes.create(user_id: current_user.id)
flash[:notice] = "Thank you for upvoting!"
redirect_to(pins_path)
else
flash[:notice] = "You have already upvoted this!"
redirect_to(pins_path)
end
end
Вуаля! Теперь у вас есть решение, в котором пользователи могут повышать голос за элементы, но только один раз за элемент.
Комментарии:
1. Привет @Ege Я действительно ценю ваш ответ, это действительно любезно с вашей стороны. Дело в том, что с вашим методом, когда я пытался повысить голос за пин-код, я получал сообщение об ошибке: неизвестный атрибут: user_id
2. Если в вашей модели голосования нет целого числа user_id, вам нужно добавить его при переносе. Это имеет смысл, если вы подумаете об этом: голоса принадлежат пользователям, и, следовательно, их модель должна иметь внешний ключ user_id.
3. Это правильный ответ. Я бы создал метод голосования в пользовательской модели и вызвал current_user.vote (@pin), чтобы упростить понимание.
4. @Ege Спасибо, я добавил целое число user_id в свой столбец голосов, и теперь оно работает хорошо. Единственная проблема связана с флэш-сообщением — когда я повышаю голос много раз, повышающие голоса принимаются только один раз, чего я и хочу, но флэш-сообщение остается неизменным, даже если оно не повышает голос, потому что пользователь уже сделал это, он получил флэш-сообщение: «спасибо за повышение»
Ответ №2:
небольшое обновление условия действия контроллера
def upvote
@pin = Pin.find(params[:id])
@pin.votes.create(user_id: current_user.id)
if @pin.save
flash[:notice] = "Thank you for upvoting!"
redirect_to(pins_path)
else
flash[:notice] = "You have already upvoted this!"
redirect_to(pins_path)
end
end