仅更新Rails模型中的某些属性

时间:2018-11-28 05:34:47

标签: ruby-on-rails

我正在尝试创建一个帐户管理页面,您可以在其中更新您的显示名称,电子邮件,密码和其他内容。我想允许用户仅更新其部分信息,而如果未更改的信息无效,则模型拒绝更改。我只需要更新模型的一部分,特别是无需输入密码和确认即可更新帐户详细信息。

使用单个表格要求用户在更新详细信息时更改其密码。我尝试将帐户详细信息分为“个人资料”和“安全”表格,希望在更新某人的姓名时不会发送空密码,但是该模型拒绝更改,并指出密码丢失。但是,使用两种形式的设置仅更新密码似乎可以正常工作。

我正在寻找一个通用的解决方案,该解决方案可以让我将来扩展以仅更新特定的字段子集(将2FA添加到安全性部分将要求用户更改其密码以按原样更新其2FA密钥)。

我正在使用的唯一外部宝石是Have I Been Pwned gem和更新的email_check gem

我当前的代码是:
routes.rb

Rails.application.routes.draw do
  # ...
  get '/account', to: 'account#index'
  post '/account', to: 'account#update'

  root 'application#home'
end

帐户模型

class Account < ApplicationRecord
  self.primary_key = :id

  before_validation do
    self.id.to_s.downcase! # lowercase the id
    self.email.to_s.downcase! # lowercase the email
  end

  validates :id,
            presence: true,
            length: { minimum: 5, maximum: 32 },
            uniqueness: true,
            format: { with: /\A[a-z0-9-]*\z/, message: 'Only lowercase alphabet, numbers and dash allowed.' }
  validates :name,
            presence: true,
            length: { minimum: 2, maximum: 50 }
  validates_email_strictness :email,
                  message: 'Something about that email doesn\'t look right... Make sure the spelling is right or try another?'

  has_secure_password
  validates :password,
            presence: true,
            not_pwned: true,
            format: { with: /\d/ },
            length: { minimum: 8 }
  attr_accessor :password_confirmation

  attr_accessor :remember_token
end

account_controller.rb

class AccountController < ApplicationController
  def index
    if logged_in?
      @account = current_account
    else
      flash[:danger] = "You must be logged in to do that!"
      redirect_to '/account/login'
    end
  end
  def update
    @account = current_account
    # TODO security concerns or otherwise when receiving only profile, security, etc fields
    if @account.update_attributes(account_params)
      flash.now[:success] = 'Update success!'
    else
      flash.now[:danger] = 'Something went wrong!'
    end
    render 'index'
  end

  private def account_params
    params.require(:account).permit(:id,:name,:email,:password,:password_confirmation)
  end
end

和account / index.html.erb

<% provide(:title,'Manage Account') %>

<h1>Manage Account</h1>

<%= render 'shared/error_messages' %>

<h3>Profile & Contact Info</h3>
<div>
  <p>Account ID</p>
  <input disabled value="<%= @account.id %>">
  <form action="/account" method="post">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <%= hidden_field_tag :authenticity_token, form_authenticity_token %>

    <p>Name</p>
    <input name="account[name]" value="<%= @account.name %>">
    <p>Email</p>
    <input name="account[email]" value="<%= @account.email %>" type="email">

    <button type="submit">Submit</button>
  </form>
</div>
<h3>Password & Security</h3>
<form action="/account" method="post">
  <input name="utf8" type="hidden" value="&#x2713;" />
  <%= hidden_field_tag :authenticity_token, form_authenticity_token %>

  <p>New Password</p>
  <input name="account[password]" type="password">
  <p>Confirm New Password</p>
  <input name="account[password_confirmation]" type="password">

  <button type="submit">Submit</button>
</form>

1 个答案:

答案 0 :(得分:2)

如果仅在输入密码后才想验证密码字段,则按条件使用proc对象,如下所示。这将允许其他字段更新,而与密码字段无关

  validates :password,
            presence: true,
            not_pwned: true,
            format: { with: /\d/ },
            length: { minimum: 8 }, unless: Proc.new { |account| account.password.blank? }