Rails论坛应用提交Post时出现‘User must exist’错误求助
解决Rails中Post关联User时出现的"User must exist"错误
问题场景
开发类Reddit论坛应用时,Post功能需接收标题、帖子内容、匿名状态、敏感内容状态及发布用户的用户名参数。已配置好新建帖子表单,但提交时始终报错:User must exist。
相关代码如下:
- 控制器参数处理:
# app/controllers/posts_controller.rb def post_params params.require(:post).permit(:title, :content, :anonymous, :graphic, :user_name); end
- 用户表schema:
# db/schema.rb create_table "users", force: :cascade 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.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "name" # 用户名字段 t.index ["email"], name: "index_users_on_email", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true end
- 帖子表单(ERB):
<div class="form-group" > <%= form.text_field :user_name, name: :post_user_name, value:current_user.name, type: :hidden %> </div>
- 控制器create方法:
# app/controllers/posts_controller.rb def create @post = Post.new(post_params) respond_to do |format| if @post.save format.html { redirect_to post_url(@post), notice: "Post was successfully created." } format.json { render :show, status: :created, location: @post } else format.html { render :new, status: :unprocessable_entity } format.json { render json: @post.errors, status: :unprocessable_entity } end end end
- 模型关联:
- User模型:
has_many :posts - Post模型:
belongs_to :user
- User模型:
- 帖子表schema:
# db/schema.rb create_table "posts", force: :cascade do |t| t.string "title" t.string "content" t.boolean "anonymous" t.boolean "graphic" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "user_name" t.index ["user_name"], name: "index_posts_on_user_name" end
问题原因
核心问题在于模型关联的实现逻辑不符合Rails规范:
- Post模型使用
belongs_to :user时,Rails默认期望posts表存在user_id(整数类型外键),用来关联users表的id主键。 - 当前帖子表仅存储
user_name字符串字段,Rails无法通过该字段自动关联到对应的User记录,因此触发User must exist的关联验证错误。 - 即便表单传入正确用户名,也无法满足Rails关联模型的默认验证规则。
解决方案
正确做法是通过user_id外键关联用户,而非用户名,具体步骤如下:
1. 生成迁移添加user_id字段
运行命令生成迁移:
rails generate migration AddUserIdToPosts user:references
生成的迁移文件会自动添加user_id整数字段并设置外键关联:
# db/migrate/[timestamp]_add_user_id_to_posts.rb class AddUserIdToPosts < ActiveRecord::Migration[7.0] def change add_reference :posts, :user, null: false, foreign_key: true # 若无需保留user_name字段,可添加此行移除 # remove_column :posts, :user_name, :string end end
执行迁移:
rails db:migrate
2. 修改控制器create方法
直接通过当前用户创建帖子,无需从表单接收用户名参数,更安全且符合关联规范:
# app/controllers/posts_controller.rb def create # 借助current_user的posts关联新建帖子,自动设置user_id @post = current_user.posts.new(post_params) respond_to do |format| if @post.save format.html { redirect_to post_url(@post), notice: "Post was successfully created." } format.json { render :show, status: :created, location: @post } else format.html { render :new, status: :unprocessable_entity } format.json { render json: @post.errors, status: :unprocessable_entity } end end end
3. 更新post_params,移除user_name参数
调整参数白名单,不再接收user_name:
# app/controllers/posts_controller.rb def post_params params.require(:post).permit(:title, :content, :anonymous, :graphic) end
4. 移除表单中的user_name隐藏字段
删除表单内的以下代码,无需再传递用户名:
<div class="form-group" > <%= form.text_field :user_name, name: :post_user_name, value:current_user.name, type: :hidden %> </div>
5. 实现发布者权限验证
要达成"仅允许发布者编辑自己的帖子",可在PostsController中添加权限拦截:
# app/controllers/posts_controller.rb before_action :authorize_post_owner, only: [:edit, :update, :destroy] private def authorize_post_owner @post = Post.find(params[:id]) unless @post.user == current_user redirect_to posts_path, alert: "你没有权限执行该操作" end end
内容的提问来源于stack exchange,提问作者Caecilius-rgb
相关产品推荐
相关产品推荐

