“1 个错误禁止保存:用户不能为空”- Hartl Ruby Rails 教程

"1 error prohibiting this from being saved: User can't be blank" - Hartl Ruby on Rails Tutorial

我正在学习 Hartl Rails 教程,到目前为止已经创建了 2 个模型:一个 'User' 模型和一个 'Listing' 模型(本质上是 'Microposts' 模型,除了自定义为更像 Craigslist 类型的 post/advertisement 而不是 Twitter 类型的状态更新)。

我已经使用“belongs_to :user”设置了“listing.rb”模型,并且还有带有“has_many :listings, dependent: destroy”的“user.rb”模型,甚至给出 'listings' a user_id index and and created_at index.

但是,每当我尝试创建新列表并提交时,我都会收到以下错误:

“1 个错误,禁止保存此列表:用户不能为空”。

没有出现其他错误,这让我假设 'listing' 的其他列是必需的(名称、描述、价格和可选图片),但由于某种原因 User_id 没有附加到创建的列表?

这是什么原因?

这是我的 user.rb、listing.rb 和 schema.rb 以及控制器:

schema.rb:

ActiveRecord::Schema.define(version: 20150223175136) do

  create_table "categories", force: true do |t|
    t.string   "name"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "listings", force: true do |t|
    t.string   "name"
    t.text     "description"
    t.decimal  "price"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
    t.string   "image"
    t.integer  "user_id"
    t.integer  "category_id"
    t.string   "username"
  end

  add_index "listings", ["user_id", "created_at"], name:     "index_listings_on_user_id_and_created_at"
  add_index "listings", ["user_id"], name: "index_listings_on_user_id"
  add_index "listings", ["username"], name: "index_listings_on_username"

  create_table "users", force: true do |t|
    t.string   "email"
    t.datetime "created_at",                        null: false
    t.datetime "updated_at",                        null: false
    t.string   "password_digest"
    t.string   "remember_digest"
    t.boolean  "admin",             default: false
    t.string   "activation_digest"
    t.boolean  "activated",         default: false
    t.datetime "activated_at"
    t.string   "reset_digest"
    t.datetime "reset_sent_at"
    t.string   "username"
    t.string   "first_name"
    t.string   "last_name"
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true

end

user.rb:

class User < ActiveRecord::Base
    has_many :listings, dependent: :destroy
    attr_accessor :remember_token, :activation_token, :reset_token
    before_save   :downcase_email
    before_create :create_activation_digest
    validates :first_name,  presence: true, length: { maximum: 25 }
    validates :last_name, presence: true, length: { maximum: 50 }
    validates :username, presence: true, uniqueness: true, length: {maximum: 50 }
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
    validates :email, presence: true, length: { maximum: 255 },
                                        format: { with: VALID_EMAIL_REGEX },
                                        uniqueness: { case_sensitive: false }
    has_secure_password
    validates :password, length: { minimum: 6 }, allow_blank: true


    class << self
        # Returns the hash digest of the given string.
        def 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 new_token
        SecureRandom.urlsafe_base64
      end
    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?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end

  # Forgets a user.
  def forget
    update_attribute(:remember_digest, nil)
  end

  # Activates an account.
  def activate
    update_attribute(:activated,  true)
    update_attribute(:activated_at, Time.zone.now)
  end

  # Sends activation email.
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end

  # Sets the password reset attributes.
  def create_reset_digest
    self.reset_token = User.new_token
    update_attribute(:reset_digest, User.digest(reset_token))
    update_attribute(:reset_sent_at, Time.zone.now)
  end

  # Sends password reset email.
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
  end

  # Returns true if a password reset has expired.
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end


  private

    # Converts email to all lower-case.
    def downcase_email
        self.email = email.downcase
    end

    # Creates and assigns the activation token and digest.
    def create_activation_digest
        self.activation_token  = User.new_token
        self.activation_digest = User.digest(activation_token)
    end
end

listing.rb:

class Listing < ActiveRecord::Base
  belongs_to :user
  default_scope -> { order('created_at DESC') }
  validates :name, presence: true
  validates :description, presence: true
  validates :price, presence: true
  validates :user_id, presence: true
  mount_uploader :image, ImageUploader
end

这是“列表”控制器:

class ListingsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]
  before_action :correct_user,   only: :destroy

  def index
    @listings = Listing.all
  end

  def show
  end

  def new
    @listing = Listing.new
  end

  def edit
  end

  def create
  @listing = Listing.new(listing_params)
    if @listing.save
      redirect_to @listing
      flash[:success] = "Listing was successfully created."
    else
      render 'new'
    end
  end

  def update
    if @listing.update(listing_params)
      flash[:success] = "Listing was successfully updated."
      redirect_to @listing
    else
      render 'edit'
    end
  end

  def destroy
    @listing.destroy
    flash[:success] = "Listing deleted."
    redirect_to request.referrer || root_url
  end

  private

    def listing_params
      params.require(:listing).permit(:name, :description, :price, :image)
    end

    def correct_user
      @listing = current_user.listings.find_by(id: params[:id])
      redirect_to root_url if @listing.nil?
    end
end

和'Users'控制器:

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
  before_action :correct_user,   only: [:edit, :update]
  before_action :admin_user,     only: :destroy

  def show
    @user = User.find(params[:id])
  end

  def index
    @users = User.paginate(page: params[:page])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      @user.send_activation_email
      flash[:info] = "Please check your email to activate your account."
      redirect_to root_url
    else
      render 'new'
    end
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      flash[:success] = "Profile updated"
      redirect_to @user
    else
      render 'edit'
    end
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "User deleted"
    redirect_to users_url
  end


  private

    def user_params
      params.require(:user).permit(:first_name, :last_name, :username, :email, 
                                   :password, :password_confirmation)
    end

    # Before filters

    # Confirms the correct user.
    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

    # Confirms an admin user.
    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end
end

我不熟悉您正在学习的教程,但用户不会自动附加到新创建的列表,您需要明确地执行此操作。在 ListingsControllercreate 操作中,将第一行替换为以下内容:

@listing = current_user.listings.build(listing_params)

在这种情况下,Listing 通过 listings 关联获得构建(尚未保存),并且 current_user 自动设置为 user。你也可以这样做:

@listing = Listing.new(listing_params)
@listing.user = current_user

但更喜欢较短的版本。