我是Rails的新手,他完成了Michael Hartl教程并在本书的基本应用程序上创建了一些变体。我想做的一件事是创建一组三个深度的模型关联(用户-> colleciotn->图片->)。我还试图将我的图片表单放在视图中以显示收藏集。我尝试遵循本书中用于“用户与收藏”关系的模式,但是遇到了几个问题。首先,我无法使用Form_with标记( form_with(model:@picture,local:true))并最终写出路径( form_with(url:“ / pictures / create” ,方法:“发布”))。另外,我使用了一个隐藏字段标记将collection_id传递给“ create”方法。
我现在的问题似乎是它没有将@picture数据保存在Picture Controller中。这是我认为值得怀疑的那一行:
@picture= @collection.pictures.build
以下是摘要/我对我要做的事情的理解。
在Controller的显示页面上呈现“图片”表单
将表格日期过帐到图片模型中,同时还将 控制器的控制器对象ID,以便保留图片与控制器的关系
使用发送的控制器ID调用Controller对象 参数
使用.build将Picture参数保存到Picture模型,并显示一条成功消息
从日志中,我认为问题出在我使用.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
Picure Controller
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):
答案 0 :(得分:1)
这里有很多事情。第一个斧头是这个垃圾:
post '/pictures/create', to: 'pictures#create' # never do this again please.
此处创建图片的路线为POST /collections/:collection_id/pictures
。 REST充分地描述了我们正在创建属于集合的图片。您已经通过以下方式设置了路由:
resources :collections do
resources :pictures
end
仅在/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方法中那样将用户发送到何处,该方法应该重定向到索引或用户供稿。如果您确实想可靠地重定向回save the location in the session。
该表格应显示为:
<%= form_with(model: [@collection, @picture]) do |f| %>
<div class="field">
<%= f.text_field :picture_title, placeholder: "Picture Title" %>
</div>
<%= f.submit %>
<% end %>
因为集合ID在路径中,所以我们不需要使用隐藏的输入来传递它的垃圾垃圾。这也将表单绑定到模型实例,以便在输入无效时不会丢失用户输入。