无法为嵌套模型保存数据

时间:2020-01-04 19:32:15

标签: ruby-on-rails model nested

我是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

以下是摘要/我对我要做的事情的理解。

  1. 在Controller的显示页面上呈现“图片”表单

  2. 将表格日期过帐到图片模型中,同时还将 控制器的控制器对象ID,以便保留图片与控制器的关系

  3. 使用发送的控制器ID调用Controller对象 参数

  4. 使用.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):

1 个答案:

答案 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在路径中,所以我们不需要使用隐藏的输入来传递它的垃圾垃圾。这也将表单绑定到模型实例,以便在输入无效时不会丢失用户输入。

相关问题