ruby-on-rails - 无法保存嵌套模型的数据
问题描述
我是 Rails 新手,完成了 Michael Hartl 教程,并在书中的基本应用程序上创建了一些变体。我想做的一件事是创建一组深度为三个的模型关联(用户-> 收藏-> 图片->)。我还试图将我的图片表单放在视图中以显示收藏。我尝试按照书中使用的用户到集合关系的模式,但遇到了几个问题。首先,我无法使用 Form_with 标签(form_with(model: @picture, local: true))并最终写出路径(form_with(url:"/pictures/create", method: "post"))。此外,我使用隐藏字段标记将 collection_id 传递给“create”方法。
我现在的问题似乎是它没有将@picture 数据保存在图片控制器中。这是我认为可疑的行:
@picture= @collection.pictures.build
这是一个总结/我对我正在尝试做的事情的理解。
在控制器显示页面上呈现图片表单
将表单日期发布到图片模型,同时将控制器对象 ID 传递给控制器,以保留图片到控制器的关系
使用在 params 中发送的控制器 ID 调用控制器对象
使用 .build 将图片参数保存到图片模型并闪烁成功消息
从日志中,我认为问题出在我对 .build 的使用上(在下面的代码中突出显示)。
我将为应用程序的所有元素以及日志提供下面的代码。我真的可以使用一些帮助来弄清楚我做错了什么。如果还有什么我应该分享的,请告诉我。
楷模
图片模型
class Picture < ApplicationRecord
belongs_to :collection
validates :collection_id, presence: true
validates :picture_title, presence: true, length: { maximum: 30}
end
收集模型
class Collection < ApplicationRecord
belongs_to :user
has_many :pictures, dependent: :destroy
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
validates :collection_title, presence: true, length: { maximum: 30 }
end
用户模型
class User < ApplicationRecord
has_many :collections, dependent: :destroy
has_many :pictures, through: :collections
attr_accessor :remember_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: true
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
def forget
update_attribute(:remember_digest, nil)
end
def feed
Collection.where("user_id = ?", id)
end
end
路线
Rails.application.routes.draw do
get 'sessions/new'
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
post '/pictures/create', to: 'pictures#create'
resources :users
resources :collections
resources :pictures
resources :users do
resources :collection
end
resources :collections do
resources :pictures
end
end
图片控制器
def create
@collection = Collection.find(params[:collection_id])
@picture= @collection.pictures.build
if @picture.save!
flash[:notice] = "Picture was successfully added."
redirect_to request.referrer
else
flash[:alert] = "Picture could not be saved."
redirect_to request.referrer
end
end
private
def correct_user
@collection = current_user.collections.find_by(id: params[:id])
redirect_to root_url if @collection.nil?
end
def picture_params
params.require(:picture).permit(:picture_title)
end
end
集合控制器
class CollectionsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy, :show, :index]
before_action :correct_user, only: [:destroy, :show]
def show
@collection = Collection.find(params[:id])
@picture= Picture.new
end
def create
@collection = current_user.collections.build(collection_params)
if @collection.save
flash[:success] = "Image collection created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
def destroy
@collection.destroy
flash[:success] = "Collection deleted"
redirect_to request.referrer || root_url
end
private
def collection_params
params.require(:collection).permit(:collection_title)
end
def correct_user
@collection = current_user.collections.find_by(id: params[:id])
redirect_to root_url if @collection.nil?
end
end
**图片形式**
<%= form_with(url:"/pictures/create", method: "post") do |f| %>
<div class="field">
<%= f.text_field :picture_title, placeholder: "Picture Title" %>
</div>
<%= f.submit "Create Collection", class: "btn btn-primary" %>
<%= hidden_field_tag :collection_id, @collection.id %>
<% end %>
日志
Started POST "/pictures/create" for 99.150.231.55 at 2020-01-04 19:29:08 +0000
Cannot render console from 99.150.231.55! Allowed networks: 127.0.0.0/127.255.255.255, ::1
Processing by PicturesController#create as JS
Parameters: {"authenticity_token"=>"GNDEKiGPVP7EHRtgphGDMIJxbKgnXn2MFSmgTJMIoEo2Owan5THjMIx9N8pKLkS7hmaqJMdwhjqvuOBR/3JaHg==", "picture_title"=>"TEST", "collection_id"=>"10", "commit"=>"Create Collection"}
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
↳ app/helpers/sessions_helper.rb:18:in `current_user'
Collection Load (0.1ms) SELECT "collections".* FROM "collections" WHERE "collections"."id" = ? ORDER BY "collections"."created_at" DESC LIMIT ? [["id", 10], ["LIMIT", 1]]
↳ app/controllers/pictures_controller.rb:12:in `create'
Completed 500 Internal Server Error in 6ms (ActiveRecord: 0.2ms | Allocations: 2423)
NoMethodError (undefined method `picture_title=' for nil:NilClass):
app/controllers/pictures_controller.rb:14:in `create'
编辑 我实现了 Max 的代码更正,但现在出现以下错误:
Started POST "/collections/10/pictures" for 99.150.231.55 at 2020-01-05 17:57:57 +0000
Cannot render console from 99.150.231.55! Allowed networks: 127.0.0.0/127.255.255.255, ::1
(0.1ms) SELECT sqlite_version(*)
NoMethodError (undefined method `make_response!' for PicturesController:Class):
解决方案
很多事情都在这里。首先砍掉这个垃圾:
post '/pictures/create', to: 'pictures#create' # never do this again please.
这里创建图片的路线将是POST /collections/:collection_id/pictures
。RESTful 描述了我们正在创建一个属于集合的图片。您已经通过以下方式设置了该路线:
resources :collections do
resources :pictures
end
在 Rails 中,动作只在 和 的路径/edit
中/new
。所有其他操作都由 HTTP 动词定义。
class PicturesController < ApplicationController
before_action :set_collection, only: [:new, :create, :index]
# POST /collections/1/pictures
def create
@picture = @collection.pictures.new(picture_params)
if @picture.save
flash[:notice] = "Picture was successfully added."
redirect_to @collection
else
flash.now[:alert] = "Picture could not be saved."
render 'collections/show'
end
end
# ...
private
def set_collection
@collection = Collection.find(params[:collection_id])
end
def picture_params
params.require(:picture).permit(:picture_title)
end
end
redirect_to request.referrer
当记录无效(或根本无效)时不要这样做。许多客户端不发送 HTTP 引用标头,这将导致非常糟糕的用户体验,因为任何用户输入和验证消息都会丢失。大多数时候,您实际上知道应该将用户发送到哪里,就像在 Collections#destroy 方法中一样,该方法可能应该重定向到索引或用户提要。如果你真的想要重定向回来可靠地保存在会话中的位置。
表格应为:
<%= form_with(model: [@collection, @picture]) do |f| %>
<div class="field">
<%= f.text_field :picture_title, placeholder: "Picture Title" %>
</div>
<%= f.submit %>
<% end %>
由于集合 id 在路径中,我们不需要做那些使用隐藏输入来传递它的 hacky 垃圾。这还将表单绑定到模型实例,以便在输入无效时不会丢失用户输入。
推荐阅读
- c# - 从另一个 .NET Core 应用程序运行 .NET Core dll
- javascript - Angular Material:“在'./material/material.module'中找不到导出'MaterialComponents'
- sql - 找到至少两行对应于具有不同值的相同 id
- javascript - 更改数据表中特定列的颜色
- python - 将 Selenium 与 Chrome 一起使用不会加载网页
- python - 使用 Python 处理 ctypes 函数中的无限循环
- java - raspbian 上的 flyway 问题:无法执行二进制文件:执行格式错误
- ansible - 使用 Ansible 变量填充 Jinja2 模板?
- apache - 如何在 AEM 中向公众隐藏内容/站点结构?
- java - 身份证查char方法?