Rails& ActiveRecord - 对on :: create :: on :: update?

时间:2017-08-24 14:05:21

标签: ruby-on-rails rails-activerecord

我想要一个具有以下属性的User模型创建/验证/更新系统:

要求1

首次创建User时,:name:email:password属性都应该存在,否则创建保存将失败。

要求2

更新User时,可以单独更新这三个属性,因此如果其中一个或两个空白,则可以继续更新保存。

简单,常见的要求对吗?我不确定。满足第一个要求的显而易见的方法是User模型validations,其中presence: true为所有三个:

user.rb

validates :name,      length: {maximum: 50},           presence: true
validates :email, uniqueness: {case_sensitive: false}, presence: true
validates :password,  length: {minimum: 6},            presence: true

所以从这里开始,我们如何满足第二个要求?

尝试1

on: :create添加到验证中:

validates :name,      length: {maximum: 50},           presence: true, on: :create
validates :email, uniqueness: {case_sensitive: false}, presence: true, on: :create
validates :password,  length: {minimum: 6},            presence: true, on: :create

这是灾难性的。如果用户尝试通过使用新的name但空白nameemail字段提交更新表单来仅更新他的password,则name确实会已更新,但emailpassword属性也会更新为空值"" - 因为验证无法运行。

尝试2

allow_nil: true添加到验证中:

validates :name,      length: {maximum: 50},           presence: true, allow_nil: true
validates :email, uniqueness: {case_sensitive: false}, presence: true, allow_nil: true
validates :password,  length: {minimum: 6},            presence: true, allow_nil: true

同样灾难性的。您可以使用空白属性创建和更新用户。

尝试3

仅验证它是新记录还是该字段不为空:

validates :email, uniqueness: {case_sensitive: false}, presence: true, if: :should_validate?

def should_validate?
  new_record? || email.present?
end

这已经被Stackoverflow上的其他地方接受为正确的解决方案但如果我错了就纠正我,是不是也是灾难性的?如果您使用空白email字段更新模型,should_validate?将返回false,则不会运行电子邮件验证,并且数据库将使用空白email进行更新。

如果我错了,请纠正我,但如果不是验证也意味着更新,这个“解决方案”会起作用,但事实并非如此吗?如果您没有运行验证,该属性仍将保存到数据库中吗?

尝试4

在控制器中,如果属性为空,请从params中删除该属性:

user.rb

validates :name,      length: {maximum: 50},           presence: true
validates :email, uniqueness: {case_sensitive: false}, presence: true
validates :password,  length: {minimum: 6},            presence: true

users_controller.rb

def update
  params[:user].delete(:name)     if params[:user][:name].blank?
  params[:user].delete(:email)    if params[:user][:email].blank?
  params[:user].delete(:password) if params[:user][:password].blank?

  if @user.update_attributes(update_user_params)
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end

def update_user_params
  params.require(:user).permit(:name, :email, :password)
end

由于验证中presence: true,这不起作用。如果params中没有任何属性,则验证失败,并且记录不会更新。

同样,这已被接受为Stackoverflow上其他地方的正确解决方案,所以我很确定我在某处遗漏了某些东西,请告诉我在哪里?!

尝试5

如果属性为空,则从params移除该属性,并从验证中删除presence: true

user.rb

validates :name,      length: {maximum: 50}
validates :email, uniqueness: {case_sensitive: false}
validates :password,  length: {minimum: 6}

users_controller.rb

def update
  params[:user].delete(:name)     if params[:user][:name].blank?
  params[:user].delete(:email)    if params[:user][:email].blank?
  params[:user].delete(:password) if params[:user][:password].blank?

  if @user.update_attributes(update_user_params)
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end

这不起作用。我们已经达到要求2但已销毁要求1.使用此“解决方案”,您确实可以更新记录的各个属性,但您也可以创建具有空白属性的新记录。

尝试6

我能看到的唯一可行解决方案是对on: :createon: :update进行不同的验证。如果您正在创建记录,则会坚持presence: true,但如果您要更新记录,请删除presence: true。您还需要在控制器中包含空白属性删除:

user.rb

validates :name,      length: {maximum: 50},           presence: true, on: :create
validates :email, uniqueness: {case_sensitive: false}, presence: true, on: :create
validates :password,  length: {minimum: 6},            presence: true, on: :create

validates :name,      length: {maximum: 50},           on: :update
validates :email, uniqueness: {case_sensitive: false}, on: :update
validates :password,  length: {minimum: 6},            on: :update

users_controller.rb

def update
  params[:user].delete(:name)     if params[:user][:name].blank?
  params[:user].delete(:email)    if params[:user][:email].blank?
  params[:user].delete(:password) if params[:user][:password].blank?

  if @user.update_attributes(update_user_params)
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end

这是允许的吗?会有用吗?我完全错过了解决这个问题的正确方法吗?

1 个答案:

答案 0 :(得分:0)

您想保存名称,电子邮件在更新时为零吗?我认为没有必要单独更新名称和电子邮件。你只需在你想要的字段或电子邮件的字段中进行更改,就不要让其他字段变得简单。

如果您需要用户名,请单独使用电子邮件更新,只需在用户模型下创建setter方法,如下所示:

def name=(val)
  val = name if val.blank?
  self.write_attribute(name: val)
end

def email=(val)
  val = email if val.blank?
  self.write_attribute(email: val)
end 

无需为更新用户编写单独的验证。

validates :name,      length: {maximum: 50},           presence: true
validates :email, uniqueness: {case_sensitive: false}, presence: true
validates :password,  length: {minimum: 6},            presence: true

更新记录时未允许用户参数。如果你使用rails 4,你可能会遇到异常。确保在创建或更新users_controller中的记录之前允许使用params:

def user_params
   params.require(:user).permit(:name, :email, :password)
end

所以你更新动作将是:

def update
  if @user.update_attributes(user_params)
    flash[:success] = "Edit Successful."
    redirect_to @user
  else
    @title = "Edit user"
    render 'edit'
  end
end