【发布时间】:2018-07-27 14:02:10
【问题描述】:
不知道为什么会这样。
PostsController#update 中的 NoMethodError nil:NilClass 的未定义方法“用户”
我的用户拥有 admin : true 并且我无法更新其他 users.posts。
我想让所有用户都能看到内容,但只有注册用户才能创建内容。如果用户是管理员或该内容的创建者,他也可以编辑/更新/销毁它。
post.rb
class Post < ApplicationRecord
belongs_to :user
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { minimum: 5 }
validates :body, presence: true, length: { minimum: 240 }
end
用户.rb
class User < ApplicationRecord
include Encryptable
has_many :posts, dependent: :destroy
has_many :comments, dependent: :destroy
has_attached_file :avatar
validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\z/
validates :level, numericality: { less_than_or_equal_to: 100, only_integer: true }, allow_blank: true
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable
before_validation :downcase_email #, :populate_iv_fields #if you need/want iv to change more often
before_create :create_encryption_key
after_create :save_encryption_key
after_create :build_user_consents
attr_encrypted :email, key: :encryption_key
has_many :user_consents, dependent: :destroy
#entry point for exporting user's personal information
def self.export_personal_information(user_id)
return nil unless User.exists?(user_id)
descendants = ApplicationRecord.descendants.reject{|model| !model.has_personal_information?}
result = Hash.new
descendants.each do |descendant|
result[descendant.name] = descendant.export_personal_information_from_model(user_id)
end
return result
end
#simplest example, we just export to json
def self.export_personal_information_from_model(user_id)
return User.find(user_id).to_json
end
#overwrite this to true for methods that you will want to be included in export_personal_information
def self.has_personal_information?
true
end
#helper method if you are creating a user from console and want them to have all consents set
def fill_consents
hash = Hash.new
ConsentCategory.all.map(&:id).each do |id|
hash[id]='on'
end
self.registration_consents=hash
end
#unfortunately not having an email field that you can just "write to" breaks
#Devise. Below some necessary workarounds
def email_changed?
encrypted_email_changed?
end
def downcase_email
self.email = self.email.downcase
end
def registration_consents=(consents)
@consents = consents
end
def registration_consents
@consents
end
validate :validate_consents_completeness
validates_presence_of :email, if: :email_required?
validates_uniqueness_of :username, allow_blank: false, if: :username_changed?
validates_length_of :username, within: 6..20, allow_blank: true
validate :validate_email_uniqueness #validates_uniqueness_of :email, allow_blank: true, if: :email_changed?
validates_format_of :email, with: Devise.email_regexp, allow_blank: true, if: :email_changed?
validates_presence_of :password, if: :password_required?
validates_confirmation_of :password, if: :password_required?
validates_length_of :password, within: Devise.password_length, allow_blank: true
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
#end original devise
def email_changed?
self.encrypted_email_changed?
end
def email_required?
true
end
def email_unique?
records = Array(self.class.find_by_email(self.email))
records.reject{|u| self.persisted? && u.id == self.id}.empty?
end
#unfortunately, this is an O(n) operation now that has to go through ALL the users to see if an email is unique. Sorry!
#if you need it to ne O(1) then consider adding email_hash field instead
def self.find_by_email(email)
users = User.all
users.each do |user|
return user if user.email.downcase == email.downcase
end
return nil
end
protected
def validate_email_uniqueness
errors.add(:email, :taken) unless email_unique?
end
def validate_consents_completeness
return if self.id #we assume that already created user has all consents
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless self.registration_consents
consents = ConsentCategory.where(mandatory: true).map(&:id)
ids = self.registration_consents.keys.map(&:to_i) #we are relying on a fact that checkboxes that are not checked are not sent to Rails back-end at all
consents.each do |consent_type|
errors.add(:registration_consents, 'Sie müssen allen erforderlichen Bedingungen zustimmen um fortzufahren.') and return unless ids.include?(consent_type)
end
end
def build_user_consents
ids = registration_consents.keys
categories = ConsentCategory.where(id: ids)
raise 'Die vom Benutzer eingereichte Zustimmungsliste enthält Objekte, die nicht in der Datenbank vorhanden sind!' if categories.count != ids.count
categories.each do |category|
consent = UserConsent.new
consent.consent_category = category
consent.user = self
consent.requires_revalidation = false
consent.agreed_at = self.created_at
consent.save!
end
end
end
post_policy.rb
class PostPolicy < ApplicationPolicy
attr_reader :user, :post
def initialize(user, post)
@user = user
@post = post
end
def index?
true
end
def create?
user.present?
end
def new?
user.present?
end
def update?
return true if post.user_id == user.id || user == user.admin?
end
def destroy?
return true if post.user_id == user.id || user == user.admin?
end
private
def post
record
end
end
application_policy.rb
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
false
end
def show?
false
end
def create?
false
end
def new?
create?
end
def update?
user.admin?
end
def edit?
user.admin?
end
def destroy?
user.admin?
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope.all
end
end
end
post_controller
class PostsController < ApplicationController
before_action :find_post, only: %i[destroy edit update comment_owner upvote downvote]
after_action :verify_authorized, except: [:index, :show]
layout '_app_nav'
def index
return redirect_to post_path(params[:post_id]) if params[:post_id]
return redirect_to user_path(params[:user_id]) if params[:user_id]
@post = Post.all.order('created_at DESC')
@posts = Post.all.order('created_at DESC')
@user = User.all
@posts = if params[:suche]
else
Post.all.order('created_at DESC')
end
@comments = Comment.all
end
def new
@post = Post.new
end
def create
@post = current_user.posts.build(post_params)
authorize @post
if @post.save!
redirect_to @post
else
render 'new'
end
end
def show
@post = Post.find(params[:id])
@user = @post.user
@comments = Comment.where(post_id: @post).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def edit
authorize @post
@post = Post.find(params[:id])
end
def update
@user = User.find(params[:id])
@post = Post.find(params[:id])
authorize @post
@post.update(title: params[:title], body: params[:post_params])
redirect_to post_path(@post)
end
def destroy
@post = Post.find(params[:id])
@post.destroy
authorize @post
redirect_to posts_path
end
=begin
def upvote
@post.upvote_from current_user
redirect_to post_path(@post)
end
def downvote
@post.downvote_from current_user
redirect_to post_path(@post)
end
=end
private
def post_params
params.require(:post).permit(:title, :body, :user_id)
end
def find_post
@post = Post.find(params[:id])
end
end
应用程序控制器
class ApplicationController < ActionController::Base
include Pundit
protect_from_forgery with: :exception
before_action :authenticate_user!
before_action :configure_permitted_parameters, if: :devise_controller?
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) do |user_params|
user_params.permit(:username, :email, :password, :password_confirmation, registration_consents: {})
end
end
private
def user_not_authorized
flash[:alert] = 'test'
redirect_to(root_path)
end
end
registrations_controller:
class RegistrationsController < Devise::RegistrationsController
private
def account_update_params
params.require(:user).permit(:email, :username, :avatar, :current_password, :password, :password_confirmation)
end
end
编辑:
使用@post 更新我的 post_policy.rb,例如 return true if user.present? && user == @post.user || user == user.admin? 解决 -> Pundit::NotAuthorizedError in PostsController#update
不允许更新?
【问题讨论】:
-
您可以发布您的
PostsController代码吗?这会很有帮助。
标签: ruby-on-rails ruby