Rails 嵌套表单提交但不持久化数据
Posted
技术标签:
【中文标题】Rails 嵌套表单提交但不持久化数据【英文标题】:Rails Nested Form submits but does not persist the data 【发布时间】:2020-08-02 00:17:00 【问题描述】:我正在尝试让我的嵌套表单工作。这是一张新专辑的表格,下面有一个可以写评论的空间。表单提交并且相册显示在页面上,但评论没有,它只是显示为空白。我在日志“Unpermitted parameter: reviews_attributes”中只收到一个错误
日志:
Started POST "/albums" for ::1 at 2020-04-19 12:10:58 -0400
Processing by AlbumsController#create as html
Parameters: "authenticity_token"=>"jYHM+yeExcTJtENvjQBDsOMo8Ig1g5bRa+hYZ9kCkiI4NO3KP3xdV7SpSZ2IeIOp0wC+5WLxflu22NTIXtoibg==", "album"=>"artist"=>"Blink 182", "title"=>"California", "avatar"=>#<ActionDispatch::Http::UploadedFile:0x00007fc372d8d5f0 @tempfile=#<Tempfile:/var/folders/26/p006tryd6yb9sp9rq446p07c0000gn/T/RackMultipart20200419-64975-1bp8ang.jpg>, @original_filename="71GfPCWJHXL._SL1500_.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"album[avatar]\"; filename=\"71GfPCWJHXL._SL1500_.jpg\"\r\nContent-Type: image/jpeg\r\n">, "reviews_attributes"=>"0"=>"title"=>"Blink 182 review", "date"=>"2020-04-19", "content"=>"NOT THE BLINK182 I KNOW AND LOVE WHERE IS ToM BRING BACK TOM", "commit"=>"Create Album"
User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 11], ["LIMIT", 1]]
↳ app/controllers/application_controller.rb:10:in `current_user'
Unpermitted parameter: :reviews_attributes
(0.1ms) begin transaction
↳ app/controllers/albums_controller.rb:29:in `create'
Album Create (0.4ms) INSERT INTO "albums" ("artist", "title", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["artist", "Blink 182"], ["title", "California"], ["created_at", "2020-04-19 16:10:58.838672"], ["updated_at", "2020-04-19 16:10:58.838672"]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Blob Load (0.3ms) SELECT "active_storage_blobs".* FROM "active_storage_blobs" INNER JOIN "active_storage_attachments" ON "active_storage_blobs"."id" = "active_storage_attachments"."blob_id" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 6], ["record_type", "Album"], ["name", "avatar"], ["LIMIT", 1]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Attachment Load (0.2ms) SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ? [["record_id", 6], ["record_type", "Album"], ["name", "avatar"], ["LIMIT", 1]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Blob Create (0.3ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["key", "l22w59ulprgmmqrs025woawhi1h6"], ["filename", "71GfPCWJHXL._SL1500_.jpg"], ["content_type", "image/jpeg"], ["metadata", "\"identified\":true"], ["byte_size", 137504], ["checksum", "IXxJAt318tAPkwmaLBUW/A=="], ["created_at", "2020-04-19 16:10:58.852999"]]
↳ app/controllers/albums_controller.rb:29:in `create'
ActiveStorage::Attachment Create (0.4ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES (?, ?, ?, ?, ?) [["name", "avatar"], ["record_type", "Album"], ["record_id", 6], ["blob_id", 11], ["created_at", "2020-04-19 16:10:58.856948"]]
↳ app/controllers/albums_controller.rb:29:in `create'
Album Update (0.2ms) UPDATE "albums" SET "updated_at" = ? WHERE "albums"."id" = ? [["updated_at", "2020-04-19 16:10:58.862445"], ["id", 6]]
↳ app/controllers/albums_controller.rb:29:in `create'
(3.3ms) commit transaction
↳ app/controllers/albums_controller.rb:29:in `create'
Disk Storage (2.5ms) Uploaded file to key: l22w59ulprgmmqrs025woawhi1h6 (checksum: IXxJAt318tAPkwmaLBUW/A==)
[ActiveJob] Enqueued ActiveStorage::AnalyzeJob (Job ID: 9114fadd-5897-4939-9cd6-1d0751f129b2) to Async(active_storage_analysis) with arguments: #<GlobalID:0x00007fc372ddf5d0 @uri=#<URI::GID gid://review-project/ActiveStorage::Blob/11>>
Redirected to http://localhost:3000/albums/6
Completed 302 Found in 63ms (ActiveRecord: 5.5ms | Allocations: 15980)
嵌套表单(albums/_form.html.erb)
<%= form_for(@album) do |f| %>
<% if @album.errors.any? %>
<ul>
<% @album.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
<% end %>
<%= f.label :artist %>
<%= f.text_field :artist %>
<br><br>
<%= f.label :title %>
<%= f.text_field :title %>
<br><br>
<%= f.label "Album Image:" %><br>
<%= f.file_field :avatar %>
<br><br>
<h2>Write your review of the album</h2>
<%= f.fields_for :reviews do |ff| %>
<%= ff.label :title %>
<%= ff.text_field :title %>
<br>
<%= ff.label :date %>
<%= ff.date_field :date %>
<br>
<%= ff.label :content %>
<%= ff.text_area :content %>
<% end %>
<br>
<%= f.submit %>
<% end %>
<br><br><br>
<%= link_to "Back to Album", albums_path(@album) %>
相册控制器:
class AlbumsController < ApplicationController
before_action :set_album, only: [:show, :edit, :update, :destroy]
before_action :must_login, only: [:new, :show, :create, :edit, :update, :destroy]
def index
@albums = Album.all
@user = current_user
end
def show
@review = @album.reviews.build
@review.user = current_user
@review.save
@reviews = Review.recent #scope
end
def new
@album = Album.new
@review = @album.reviews.build
@user = current_user
end
def create
#@user = User.find(current_user.id)
@album = current_user.albums.build(album_params)
#@album.user_id = current_user.id
@album.reviews.each |r| r.user ||= current_user # I'm using ||= so i can use the same code on update without changing reviews that already have a user
if @album.save
redirect_to album_path(@album)
else
render :new
end
end
def edit
@user = current_user
end
def update
#@album = current_user.albums.build(album_params)
@album.user_id = current_user.id
if @album.update(album_params)
redirect_to album_path(@album), notice: "Your album has been updated."
else
render 'edit'
end
end
def destroy
@album.delete
@album.avatar.purge
redirect_to albums_path
end
private
def set_album
@album = Album.find(params[:id])
end
def album_params
params.require(:album).permit(:artist, :title, :avatar, :user_id, review_attributes:[:title, :date, :content])
end
end
评论控制者
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
before_action :set_current_user, only: [:index, :show, :new, :edit, :destroy]
before_action :find_album, only: [:show, :create, :edit, :update, :destroy]
before_action :must_login, only: [:index, :show, :new, :create, :edit, :update, :destroy]
def index
@albums = Album.with_recent_reviews
end
def show
#@reviews = Review.where("album_id = ?", params[:album_id])
end
def new
if params[:album_id] && @album = Album.find_by(id: params[:client_id])
@review = @album.reviews.build
else
redirect_to albums_path
end
end
def create
@review = current_user.reviews.build(review_params)
@review.album = @album
if @review.save
redirect_to album_path(@album)
else
@album = @review.album
render :new
end
end
def edit
end
def update
if @review.update(review_params)
redirect_to album_path(params[:album_id])
else
render 'edit'
end
end
def destroy
if current_user.id == @review.user_id
@album.reviews.find(params[:id]).destroy
redirect_to album_path(params[:album_id])
else
flash[:error] = "Unable to delete your review. Please try again."
redirect_to album_reviews_path(@review)
end
end
private
def set_review
@review = Review.find(params[:id])
end
def set_current_user
@user = current_user
end
def find_album
@album = Album.find(params[:album_id])
end
def review_params
params.require(:review).permit(:title, :date, :content, album_attributes:[:artist, :title, :user_id])
end
end
专辑型号:
class Album < ApplicationRecord
has_many :reviews
has_many :users, through: :reviews
has_one_attached :avatar
accepts_nested_attributes_for :reviews
validates_presence_of :artist
validates_presence_of :title
scope :with_recent_reviews, -> includes(:reviews).where(reviews: date: [(Date.today - 7.days)..Date.tomorrow] ) #scope relies on include method and custom query on related model (reviews)
end
审查模型:
class Review < ApplicationRecord
belongs_to :album, optional: true
belongs_to :user
validates_presence_of :content
validates :title, presence: true, uniqueness: true
validates :date, presence: true
accepts_nested_attributes_for :album
scope :recent, -> where("date(date) >= ?", Date.today - 7.days) #scope
end
Routes.rb
Rails.application.routes.draw do
get '/auth/:provider/callback' => 'sessions#omniauth'
get 'auth/failure', to: redirect('/')
get '/signup' => 'users#new', as: 'signup'
post '/signup' => 'users#create'
get '/signin' => 'sessions#new'
post '/signin' => 'sessions#create'
get '/signout' => 'sessions#destroy'
post '/logout', to: "sessions#destroy"
resources :albums do
resources :reviews, except: [:index]
end
resources :users, only: [:show, :destroy]
resources :reviews, only: [:index]
root to: "albums#index"
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
【问题讨论】:
【参考方案1】:reviews
关联是 has_many,因此 fields_for 应该使用复数形式。
= f.fields_for :reviews do |ff|
这样,rails 会创建您允许的参数reviews_attributes
。
如果您对该更改仍有问题,请显示新的错误和堆栈跟踪。
编辑: 如果您想将当前用户 id 设置为评论的用户(类似于您将当前用户指定为专辑创建者的操作),您可以在保存记录之前指定它:
def create
# @user = User.find(current_user.id) you don need this, you already have the user at current_user, no need to find it again
@album = current_user.albums.build(album_params)
# @album.user = current_user you don't need this, current_user.albums.build already sets this
@album.reviews.each |r| r.user ||= current_user # I'm using ||= so you can use the same code on update without changing reviews that already have a user
if @album.save
redirect_to album_path(@album)
else
render :new
end
end
此外,从reviews_attributes
的允许参数中删除:user_id
和:album_id
,您不希望用户利用该分配添加您实际上不使用的参数
【讨论】:
在此处将review_attributes
更改为 reviews_attributes
params.require(:album).permit(:artist, :title, :avatar, :user_id, review_attributes:[:title, :date, :content, :user_id, :album_id])
我这样做了,在我的表单上显示了一个错误,上面写着“评论用户必须存在”。需要明确的是,这就是发生的事情ibb.co/jw3wgjn
你有belongs_to :user
,你允许user_id
参数,但你没有在任何地方设置用户ID。
您是否将review_attributes
更改为reviews_attributes
?显示带有请求和错误的服务器日志以及当前的album_params
方法,更新问题
我仍然在您的 album_params
方法中看到 review_attributes
。你真的改变了吗?确保它是复数以上是关于Rails 嵌套表单提交但不持久化数据的主要内容,如果未能解决你的问题,请参考以下文章
React Hook Form:提交带有嵌套组件的表单或提取嵌套组件的字段提交