You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ruby on Rails应用管理员无法删除他人慢跑记录问题

问题描述

我正在开发一款Web应用,用户可创建并管理自己的慢跑记录(jogs)。管理员用户应能删除任意慢跑记录,普通用户仅可删除自己的记录,经理用户同样仅能删除自己的记录。我已在控制器的destroy动作中配置了对应的权限条件,但删除功能出现异常:
作为管理员,我可以删除自己的慢跑记录,但无法删除其他用户创建的记录——删除按钮正常显示,点击后会跳转至root_url,无任何删除操作,也未出现错误提示。

相关代码

JogsController 代码

class JogsController < ApplicationController
    before_action :logged_in_user, only: [:create, :destroy]
    before_action :correct_user, only: :destroy
    before_action :set_jog, only: [:show, :edit, :update, :destroy]
    before_action :authorize_jog_deletion, only: :destroy

    def weekly_report
      @jog = Jog.find(params[:id])
      @selected_week = params[:week].to_i if params[:week].present?
    end


    def create
    @jog = current_user.jogs.build(jog_params)
    if @jog.save
    flash[:success] = "Jog session created!"
    redirect_to root_url
    else
        @feed_items = current_user.feed.paginate(page: params[:page])
        render 'static_pages/home'        
    end
    end
  
    def destroy
      authorize_jog_deletion
      @jog.destroy
     flash[:success] = "Jog deleted"
     redirect_to request.referrer || root_url
    end

    
    
    private

    def jog_params
        params.require(:jog).permit(:distance_km, :duration_minutes, :date)
    end  

    def correct_user
        @jog = current_user.jogs.find_by(id: params[:id])
        redirect_to root_url if @jog.nil?
      end
      
      def set_jog
        @jog = Jog.find(params[:id])
      end

      def authorize_jog_deletion
        unless current_user.admin? ||
               (@jog.user == current_user) ||
               (current_user.manager? && current_user == @jog.user)
          flash[:danger] = "You are not authorized to delete this jog"
          redirect_to (root_url)
        end
      end
     
end

视图文件 _jog.html.erb

<div class="weekly-report-button-container">
<%= link_to "View Weekly Report", weekly_report_jog_path(jog), class: "btn btn-primary" %>
</div class="weekly-report-button-container">
<li id="jog-<%= jog.id %>">

  <%= link_to gravatar_for(jog.user, size: 50), jog.user %>
  <span class="user"><%= link_to jog.user.name, jog.user %></span>
  <span class="content">
    Distance: <%= jog.distance_km %> km |
    Duration: <%= jog.duration_minutes %> minutes |
    Date: <%= jog.date.strftime('%Y-%m-%d') %>
  </span>
  <span class="average-speed">Average Speed: <%= jog.average_speed %> km/h</span>
  <span class="timestamp">
    Jogged on the site <%= time_ago_in_words(jog.date) %> ago.
    
    <% if current_user.admin? || (current_user.manager? && jog.user_id == current_user.id) || (jog.user_id == current_user.id) %>
    <%= button_to "delete", jog_path(jog), method: :delete, data: { confirm: "Are you sure you want to delete this jog?" } %>
  <% end %>
  
  </span>
</li>

Jog 模型代码

class Jog < ApplicationRecord
belongs_to :user

validates :user_id, presence: true
validates :date, presence: true , inclusion: { in: (Date.new(2020)..Date.today) }
validates :distance_km, presence: true, numericality: { greater_than: 0 }
validates :duration_minutes, presence: true

def admin?
admin
end

def manager?
manager
end

def self.calculate_weekly_total_distance(user)
weekly_total_distance = {}

user.jogs.each do |jog|
  week = jog.date.cweek
  weekly_total_distance[week] ||= 0
  weekly_total_distance[week] += jog.distance_km
end

weekly_total_distance
end

def average_speed
return 0 if duration_minutes.zero?

(distance_km / duration_hours).round(2)
end

private

def duration_hours
duration_minutes / 60.0
end
end

路由文件 routes.rb

Rails.application.routes.draw do
    root 'static_pages#home'
    get 'help' => 'static_pages#help'
    get 'about' => 'static_pages#about'
    get 'contact' => 'static_pages#contact'
    get 'signup' => 'users#new'
    get 'login' => 'sessions#new'
    post 'login' => 'sessions#create'
    delete 'logout' => 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :users
  resources :account_activations, only: [:edit]
  resources :password_resets, only: [:new, :create, :edit, :update]
  resources :jogs, only: [:create, :destroy]
  resources :relationships, only: [:create, :destroy]
  resources :jogs do
    member do
      get :weekly_report
    end
  end
  
end

已尝试操作

  • 将删除链接改为按钮
  • 确认控制器destroy动作中的条件设置无误
  • 应用中其他删除功能均可正常使用

问题排查与修复

核心问题:before_action 执行顺序错误

你的JogsController中,before_action的执行顺序是:

  1. logged_in_user
  2. correct_user
  3. set_jog
  4. authorize_jog_deletion

当管理员尝试删除其他用户的 jog 时,correct_user方法会先执行:

def correct_user
  @jog = current_user.jogs.find_by(id: params[:id])
  redirect_to root_url if @jog.nil?
end

这个方法会尝试从当前用户的 jogs 中查找目标记录,而管理员要删除的是其他用户的 jog,所以@jog会是nil,直接触发重定向到root_url,后续的set_jog和authorize_jog_deletion根本不会执行,这就是为什么删除操作没执行就跳转了。

修复方案

  1. 调整before_action执行顺序:确保set_jog在correct_user和authorize_jog_deletion之前执行,让@jog先被正确赋值:
class JogsController < ApplicationController
    before_action :logged_in_user, only: [:create, :destroy]
    before_action :set_jog, only: [:show, :edit, :update, :destroy] # 移到最前面
    before_action :correct_user, only: :destroy
    before_action :authorize_jog_deletion, only: :destroy

    # ... 其他方法保持不变
  1. 修改correct_user方法:让管理员跳过所属用户验证:
def correct_user
  # 管理员无需验证记录所属用户
  return if current_user.admin?
  
  @jog = current_user.jogs.find_by(id: params[:id])
  redirect_to root_url if @jog.nil?
end
  1. 简化authorize_jog_deletion方法:经理用户的权限逻辑和普通用户一致,无需重复判断:
def authorize_jog_deletion
  unless current_user.admin? || @jog.user == current_user
    flash[:danger] = "You are not authorized to delete this jog"
    redirect_to root_url
  end
end

额外优化点

  • 合并重复路由定义:路由文件中重复定义了resources :jogs,合并为一个避免冲突:
resources :jogs, only: [:create, :destroy] do
  member do
    get :weekly_report
  end
end

删除原来单独的resources :jogs, only: [:create, :destroy]和resources :jogs do ... end。

  • 简化视图删除按钮条件:经理用户的判断可以合并到普通用户逻辑中:
<% if current_user.admin? || jog.user_id == current_user.id %>
  <%= button_to "delete", jog_path(jog), method: :delete, data: { confirm: "Are you sure you want to delete this jog?" } %>
<% end %>

内容的提问来源于stack exchange,提问作者agent000flour

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:55:54