【问题标题】:Why does my rails rollback when I try to user.save?当我尝试 user.save 时,为什么我的 rails 会回滚?
【发布时间】:2013-09-26 20:08:08
【问题描述】:

我已经安装了 RailsTutorial 示例应用程序(类似 twitter 的应用程序),并试图了解为什么当我尝试更新用户数据库时,以下控制台代码不会更新数据库。一旦我使用user.save,我希望用户信息会得到更新。但是,这会回滚到未编辑的数据。这是由于基于用户的限制吗?

用户控制器:

class UsersController < ApplicationController

#before_filter :signed_in_user, only: [:index, :edit, :update, :destroy, :following, :followers]
# By default before filters apply to all actions
#before_filter :correct_user, only: [:edit, :update]
  def edit
    @user = User.find(params[:id])
  end

  def update

    @user = User.find params[:id]

    respond_to do |format|

    if @user.update_attributes(params[:user])
      flash[:notice] = "Profile updated"
      format.html { redirect_to(@user, :notice => 'User was successfully updated.') }
      format.json { respond_with_bip(@user) }
    else

      format.html { render :action => "edit" }
      format.json { respond_with_bip(@user) }
    end
    end
 end

  private


  def correct_user
    @user = User.find(params[:id])
    redirect_to(root_path) unless current_user?(@user)
  end

  def admin_user
    redirect_to(root_path) unless current_user.admin?
  end


end

Rails 控制台:

1.9.3-p392 :001 > user = User.find(109)


User Load (8.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 109]]
 => #<User id: 109, name: "laurie", email: "auriepage@gmail.com", created_at: "2013-09-26 18:10:12", updated_at: "2013-09-26 18:10:12", password_digest: "$2a$10$aXEtun8Z2Deqy2wNxvFRNOjPczKQkYc1vDezP5OduJuF...", remember_token: "YhIUhgFm9iMewxdNOHJ45A", admin: false> 

1.9.3-p392 :002 > user.name = "larry"
 => "larry" 

1.9.3-p392 :003 > user.save
   (0.2ms)  begin transaction
  User Exists (0.6ms)  SELECT 1 AS one FROM "users" WHERE (LOWER("users"."email") = LOWER('auriepage@gmail.com') AND "users"."id" != 109) LIMIT 1
   (0.1ms)  rollback transaction
 => false 

用户模型:

class User < ActiveRecord::Base

# Declaration of public variables   
  attr_accessible :email, :name, :password, :password_confirmation
  has_secure_password
  has_many :microposts, dependent: :destroy
  has_many :relationships, foreign_key: "follower_id", dependent: :destroy
  has_many :followed_users, through: :relationships, source: :followed
  has_many :reverse_relationships, foreign_key: "followed_id", class_name: "Relationship", dependent: :destroy
  has_many :followers, through: :reverse_relationships, source: :follower

  before_save {email.downcase!}
  before_save :create_remember_token

  validates :name, presence: true, length: {maximum: 50}
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, format: {with: VALID_EMAIL_REGEX}, uniqueness: {case_sensitive: false}
  validates :password, presence: true, length: {minimum: 6}
  validates :password_confirmation, presence: true
  after_validation {self.errors.messages.delete(:password_digest)}

 private
    def create_remember_token
        self.remember_token = SecureRandom.urlsafe_base64
    end
end

【问题讨论】:

  • 控制台中的.save 方法不使用控制器代码,因此发布它是无关紧要的。查看您的模型并尝试找出为什么发出奇怪的SELECT 1 AS one FROM... SQL 语句,并考虑发布您的模型而不是控制器的代码。另外,您是否偶然在模型中定义了自己的 save 方法,或者您在该模型上是否有任何自定义回调?也许答案就在某个地方。
  • 这可能是因为用户在电子邮件上有唯一性索引,并且电子邮件auriepage@gmail.com的用户不止一个。这将解释该查询试图查找电子邮件为该电子邮件但 ID 不是 109 的用户
  • 您的User 课程有问题,请务必包含您的User 课程源代码。
  • 验证也可能导致此问题,请在问题中包含模型。
  • 我已经包含了用户模型。我从验证中收到以下错误消息: 1.9.3-p392 :005 > user.errors.full_messages => [“密码不能为空”、“密码太短(最少 6 个字符)”、“密码确认不能为空"] 这些是验证错误,但是,当我已经以用户身份登录时,我不想每次更改名称时都必须更改密码。我希望编辑工作独立于密码验证(但应该验证当前用户是正在编辑的用户)

标签: ruby-on-rails ruby railstutorial.org


【解决方案1】:

您的用户模型可能有不满意的验证。由于您尚未发布这些内容,因此我无法真正解决您的问题。为了让生活更轻松,您可以调试用户不愿意保存的原因。

尝试运行

user.errors.full_messages

这应该可以提示您出了什么问题。

【讨论】:

  • 我从验证中得到以下错误信息: 1.9.3-p392 :005 > user.errors.full_messages => ["密码不能为空", "密码太短(最小是 6 个字符)”、“密码确认不能为空”] 这些是验证错误,但是,当我已经以用户身份登录时,我不想每次更改密码时都必须更改密码名字。我希望编辑工作独立于密码验证(但应该验证当前用户是正在编辑的用户)
  • @Venomoustoad 验证可以有条件,例如:(代码)验证 :password,presence:true,长度:{minimum: 6},if: :password_changed? (/code) password_changed 应该是你的用户模型中的一个方法。您可以在此处设置何时验证密码的条件。
  • 在控制台中显示错误有助于解决我的问题;必须先设置 user.password 才能让我保存。
  • @jewilmeer:谢谢兄弟,你的回答拯救了我一整天:)
  • 我的密码太短也有问题 - 但由于最初的错误消息,我看错了地方。为什么会出现User Exists (0.5ms) SELECT 1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER($1) LIMIT $2 [["email", "mail@mail.com"], ["LIMIT", 1]] 错误?
【解决方案2】:

我知道这是一篇旧帖子,但希望这可以帮助将来学习本教程的人。

正如接受的答案所表达的那样,这是由于验证未得到满足。我也遇到了这个问题,发现另一个解决方法是在user 对象上使用update_attribute 方法。例如,如果您想更新 user 对象的 name 字段并将其自动保存到数据库中,而无需触及虚拟的 passwordpassword_confirmation 字段,请使用以下命令:

user.update_attribute(:name, "larry")

这将仅更新name 字段并将其保存到数据库中(无需调用save 方法),而无需触摸passwordpassword_confirmation 字段。

【讨论】:

  • 即使模型验证失败,user.update_attribute 也会更新数据库而不管有效性如何,因为此方法会跳过验证。如果我们想跳过验证并知道我们在做什么,也可以使用save(validate: false)
  • 这个答案是金。谢谢@Richard Gieg
【解决方案3】:

在您尝试保存或验证活动记录模型实例后,您可以使用一些有用的命令查看有关所发生情况的更多信息。

user = User.find(108)
user.name = "Larry"
user.valid? # returns false
user.errors.messages # returns something like {email: "Cant be blank"}

显然我犯了这个错误,因为我不知道您的模型文件是什么样的,但如果它通常由于两个原因之一而起作用。首先是您的验证失败。如果它们没有错误消息,则可能是因为您的过滤器链中的某些内容返回了错误。例如

class User << ActiveRecord::Base
  before_save :accidentally_return_false

  def accidentally_return_false
    self.some_boolean = (condition == value)
  end
end

user = User.new( params )
user.save # where condition does not equal value
user.valid? # false because of the before save method

希望有帮助

【讨论】:

    【解决方案4】:

    保存回滚时,使用 save!而是打印错误日志。

    【讨论】:

      【解决方案5】:

      您的验证没有通过。你可以这样做:

      user.errors.full_messages
      

      保存失败后在控制台中查看原因。

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题,并且没有存储任何错误。原来我的before_save 函数返回 false 导致保存被取消(我猜?我是 Rails 新手)。

        我试图设置一个布尔值,该值只能在文件上传后设置。

        这就是我正在做的事情:

        before_save :set_orientation
        
        def set_orientation
          # ...do stuff...
          self[:is_landscape] = ratio > 1  # stores AND returns the boolean value!!
        end
        

        函数的最后一行也是一个隐式返回,我不是故意的。我的解决方案是明确返回:

        before_save :set_orientation
        
        def set_orientation
          # ...do stuff...
          self[:is_landscape] = ratio > 1  # store the boolean value
          return                           # now return
        end
        

        【讨论】:

        • 如果你自己挖那个傻东西。那你就是个天才伙伴:P
        【解决方案7】:

        当我尝试更新用户的管理属性时,我遇到了数据库在控制台中回滚我的事务的相同问题。如果您正在学习 Hartl rails 教程,问题是如果您在控制台中输入 user.errors.messages,它会告诉您密码太短。这是因为在模型中,在保存密码并将密码散列到 password_digest 之前会进行密码验证。

        解决此问题的方法是在控制台中执行您的正常活动,例如设置 user.admin = true,然后在完成后输入 user.password = "foobar",然后输入 user.password_confirmation = "foobar",然后当您执行 user.save 时,它​​将提交您的所有更改。

        【讨论】:

          【解决方案8】:

          尝试更新数据库:

          rails db:migrate
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-01-26
            • 1970-01-01
            • 1970-01-01
            • 2014-08-31
            • 1970-01-01
            • 1970-01-01
            • 2010-10-19
            • 2022-01-24
            相关资源
            最近更新 更多