gpt4 book ai didi

ruby-on-rails - 如何使用单个表单在 ActiveRecord 中保存嵌套资源(Ruby on Rails 5)

转载 作者:数据小太阳 更新时间:2023-10-29 08:59:38 25 4
gpt4 key购买 nike

我有两个具有多对一关系的实体。用户有很多地址。创建用户时,我希望表单也创建一个地址。实体是嵌套的。
方法一:
下面的代码有效,但只保存用户,没有关联的地址。
环顾四周,我认为accepts_nested_attributes_for会自动保存地址。我不确定,但这可能是行不通的,因为我进入 Controller 的参数实际上并没有嵌套,即。他们看着像是:

"user"=>{"name"=>"test"}, "address"=>{"address"=>"test"}
而不是像这样嵌套:
"user"=>{"name"=>"test", "address"=>{"address"=>"test"} }
我认为这可能是由于我的形式有问题,但我不知道问题是什么......
方法二:
我也尝试过更改 Controller - 实现第二个私有(private)方法, address_params ,看起来像 params.require(:address).permit(:address) ,然后使用 @user.address.build(address_params) 显式创建地址在 create方法。
当使用调试器跟踪这种方法时,地址实体确实被成功创建,但是 respond_to do由于我不明白的原因引发了一个 ArgumentError(“respond_to 接受类型或 block ,从不接受两者”),这会在点击保存方法之前回滚所有内容......
[编辑] - respond_to do引发错误是一个红鲱鱼 - 我误解了调试器。但是,由于我不明白的原因,事务被回滚了。
问题:
  • 对于 Rails 来说,一种或另一种方法是否更标准? (或者两者都不是,我从根本上误解了某些东西)
  • 我在这两种方法中的任何一种/两种方法中做错了什么,以及如何修复它们以便保存用户和地址?

  • 以下相关代码 (它实现了上面的方法 1,并生成了如上所述的非嵌套参数):
    user.rb
    class User < ApplicationRecord
    has_many :address
    accepts_nested_attributes_for :address
    end
    地址.rb
    class Address < ApplicationRecord
    belongs_to :user
    end
    users_controller.rb
    class UsersController < ApplicationController
    # GET /users/new
    def new
    @user = User.new
    end

    # POST /users
    # POST /users.json
    def create
    @user = User.new(user_params)

    respond_to do |format|
    if @user.save
    format.html { redirect_to @user, notice: 'User was successfully created.' }
    format.json { render :show, status: :created, location: @user}
    else
    format.html { render :new }
    format.json { render json: @user.errors, status: :unprocessable_entity }
    end
    end
    end

    private
    def user_params
    params.require(:user).permit(:name, address_attributes: [:address])
    end

    end
    _form.html.erb
    <%= form_for(user) do |f| %>
    <div class="field">
    <%= f.label :name %>
    <%= f.text_field :name %>
    </div>

    <%= fields_for(user.address.build) do |u| %>
    <div class="field">
    <%= u.label :address %>
    <%= u.text_field :address %>
    </div>
    <% end %>

    <div class="actions">
    <%= f.submit %>
    </div>
    <% end %>
    更新 1:
    在进行@Ren 建议的更改后,我可以看到参数看起来更像我对嵌套资源的预期:
    "user"=>{"name"=>"test", "addresses_attributes"=>{"0"=>{"address"=>"test"}}}
    但是,在尝试保存用户时,由于我不明白的原因,事务仍然回滚。我从 users.new 页面得到的输出是:

    2 error prohibited this user from being saved:

    Addresses user must exist

    Addresses user can't be blank


    但是,使用byebug,在 @user = User.new(user_params)之后打电话,事情看起来像我期望的那样:
    (byebug) @user
    #<User id: nil, name: "test", created_at: nil, updated_at: nil>
    (byebug) @user.addresses
    #<ActiveRecord::Associations::CollectionProxy [#<Address id: nil, user_id: nil, address: "test", created_at: nil, updated_at: nil>]>
    显然 user.id 字段在记录写入数据库之前不会设置,因此同样在用户被保存之前不能设置 address.user_id 字段,所以这可能是由于 ActiveRecord 保存到数据库时的某种不正确的排序引起的数据库?我将继续尝试通过使用 byebug 进行调试来了解发生了什么......
    更新 2:
    使用 rails console进行测试,首先保存用户,然后添加地址工作(两条记录都写入数据库,尽管显然是在 2 个单独的事务中):
    > user = User.new(name: "consoleTest")
    > user.save
    > user.addresses.build(address: "consoleTest")
    > user.save
    最后只保存一次会导致我在运行程序时看到的相同问题,即。由于某种原因,事务被回滚:
    > user = User.new(name: "consoleTest")
    > user.addresses.build(address: "consoleTest")
    > user.save
    据我所知,使用 rails console 进行调试, user.addresses状态的唯一区别在这两种方法中,第一个 address.user_id已经设置,因为 user.id已经知道了,而在第二个中,它不是。所以这可能是问题所在,但据我所知, save方法应该确保实体以正确的顺序保存,这样这不是问题。理想情况下,能够看到哪些实体会很好 save正在尝试写入数据库并按顺序写入,但是使用 byebug 调试它会使我陷入 ActiveRecord 的陷阱,我完全不明白!

    最佳答案

    更新 :与以前的版本相反,Rails 5 现在要求在父子中 belongs_to关系,在保存子级时,默认情况下必须存在父级的关联 ID。否则会出现验证错误。显然它不允许你一步保存父子节点......所以为了让下面的解决方案起作用,修复方法是添加 optional: truebelongs_to地址模型中的关联:

    class Address < ApplicationRecord
    belongs_to :user, optional: true
    end

    请参阅我在一个从这个问题分支出来的问题中的回答:

    https://stackoverflow.com/a/39688720/5531936

    在我看来,您将 address 的单数和复数混淆了。对象的方式不符合 Rails。如果用户 有很多地址,那么您的模型应该显示 has_many :addressesaccepts_nested_attributes_for应该有 addresses :
    class User < ApplicationRecord
    has_many :addresses
    accepts_nested_attributes_for :addresses
    end

    并且您的 Controller 中的强参数应该具有 addresses_attributes :
    def user_params
    params.require(:user).permit(:name, addresses_attributes: [:id, :address])
    end

    现在,如果您希望用户只保存一个地址,那么在您的表单中,您应该只有一个嵌套地址实例可用:
    def new
    @user = User.new
    @user.addresses.build
    end

    顺便说一下,您的表单似乎有 fields_for什么时候应该是 f.fields_for :
    <%= f.fields_for :addresses do |u| %>
    <div class="field">
    <%= u.label :address %>
    <%= u.text_field :address %>
    </div>
    <% end %>

    我强烈建议您查看 Rails guide关于嵌套表单的文档,第 9.2 节。它有一个类似的例子,其中一个人 has_many地址。引用该来源:

    When an association accepts nested attributes fields_for renders its block once for every element of the association. In particular, if a person has no addresses it renders nothing. A common pattern is for the controller to build one or more empty children so that at least one set of fields is shown to the user. The example below would result in 2 sets of address fields being rendered on the new person form.

    def new
    @person = Person.new
    2.times { @person.addresses.build}
    end

    关于ruby-on-rails - 如何使用单个表单在 ActiveRecord 中保存嵌套资源(Ruby on Rails 5),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39682727/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com