【问题标题】:Take an ActiveRecord model that can accept nested attributes for another model, and update it individually取一个可以接受另一个模型的嵌套属性的 ActiveRecord 模型,并单独更新它
【发布时间】:2014-06-06 03:22:43
【问题描述】:

tl;博士

你能否创建两个使用嵌套属性的 ActiveRecord 模型,但分别更新它们?

加长版

我正在开发一个 SaaS 应用程序,其中我有(除其他外)两个模型,一个帐户模型和一个用户模型。

使用 Rails 4.0.3 和 Ruby:2.1.1

帐户.rb

class Account < ActiveRecord::Base

  belongs_to :owner, class_name: 'User'

  validates :owner, presence: true

  accepts_nested_attributes_for :owner, :update_only => true

用户.rb

class User < ActiveRecord::Base

  devise :invitable, :database_authenticatable, :recoverable, :rememberable, :validatable, :registerable

  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :email, presence: true
  validates :password, presence: true

帐户接受用户的嵌套属性。当有人注册应用程序时,他们会同时创建自己的帐户和用户个人资料(具有嵌套属性的单个表单)。

虽然这是注册网站的理想用户流程,但我希望用户能够在更新其用户信息的同时更新其帐户信息。我已经设置了 Account#edit 和 Account#update 操作,以及一个编辑视图(参见下面的代码):

AccountsController

class AccountsController < ApplicationController

  skip_before_filter :authenticate_user!, only: [:new, :create]

  def edit
    @account = current_account
  end

  def update
    @account = current_account
    if @account.update_attributes(account_params)
      redirect_to settings_url(subdomain: @account.subdomain)
    else
      render action: 'edit'
    end
  end

  private
    def account_params
        params.require(:account).permit(:firm_name,
                                    :street_address,
                                    :city,
                                    :state,
                                    :zip_code,
                                    :phone,
                                    :subdomain,
                                    owner_attributes: [ :first_name, 
                                                        :last_name, 
                                                        :email, 
                                                        :password, 
                                                        :password_confirmation])
    end
end

views/accounts/edit.html.erb

<h2>Edit Account Info</h2>
  <%= simple_form_for @account do |f| %>

    <%= f.input :firm_name %>
    <%= f.input :street_address %>
    <%= f.input :city %>
    <%= f.input :state %>
    <%= f.input :zip_code %>
    <%= f.input :phone %>
    <%= f.button :submit, class: 'btn-primary' %>
  <% end %>

当用户尝试使用此表单更新其帐户时,更新失败,因为所有者(用户)密码的存在验证失败。在 AccountsController#update 中,如果您检查第 42 行之后的错误消息,您将看到:

Owner's password can't be blank

我想知道的是 - 有没有办法使用嵌套属性创建两个模型,但分别更新它们?我花了几个小时研究这个问题,但没有找到描述的类似情况,也没有找到问题的真正解决方案。明确地说,我想只更新 Account(父模型),而不是 User。

已经尝试过的一些事情:

  • 更改帐户的强参数

  • 将 AR 的 :inverse_of 用于 Account 和 User 模型

  • 在参数中包含用户的密码(在适当的嵌套中 方式,就像在帐户/用户创建期间一样)

我确实有一个部分解决方案,虽然不是一个理想的解决方案 - 它涉及绕过专门针对 AccountsController#update 操作的密码验证。

欢迎任何提示或建议!谢谢。

【问题讨论】:

  • accepts_nested_attributes_for :owner, :update_only =&gt; true 是否适合您?为什么在child 模型中这样做?

标签: ruby-on-rails ruby activerecord nested-attributes


【解决方案1】:

是的,您应该能够在不更新用户的情况下更新帐户。在我们希望找到解决方案之前有几点:

您不需要在用户中进行这些验证,因为 Devise 会自动处理它们:

class User < ActiveRecord::Base

  validates :email, presence: true
  validates :password, presence: true

如果您只想更新此控制器中的帐户,则应从参数中删除有关 owner_attributes 的任何内容。像这样:

def account_params
  params.require(:account).permit(
    :firm_name, :street_address, :city, :state, :zip_code, :phone, :subdomain
  )
end

我认为发生的事情是有空参数被传递到nested_attributes_for,它试图更新所有者,但设计阻止了这一点,因为默认情况下你不能在没有密码的情况下更新设计用户属性:

https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-edit-their-account-without-providing-a-password

【讨论】:

  • 感谢您的回复。重新定义 account_params(至少对于#update)是我们尝试过的事情之一,但奇怪的是它没有任何效果。相同的错误消息(所有者密码不能为空)被添加到 @account 对象中,即使在调用 @account.update_attributes(account_params) 时定义了您在答案中提到的 account_params。
猜你喜欢
  • 2017-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多