为什么 Rails 5 不保存嵌套属性,因为父模型不首先保存

The*_*aru 1 activerecord ruby-on-rails nested-forms nested-attributes ruby-on-rails-5

我正在使用 Rails 5 及其最新稳定版本的所有内容。所以我得到以下信息:

\n\n
\n

您已将关联设置为必需,但它丢失了。\n 在 Rails 5 中,默认情况下将关联设置为必需,因此,如果您想要\n 将其保留为空,则需要在\n 模式下对关联设置可选项:true

\n
\n\n

这太棒了,我明白发生了什么,但是在我的一生中,我无法弄清楚如何让父模型首先保存,因此 user_id 被转换为嵌套模型记录。我在上面到处都看到了相同的答案,但是除了将初始化程序中的默认值从 true 更改为 false 之外,没有人解释解决方法。这并不能解决问题,因为记录确实保存了,但不包含 user_id。

\n\n

下面是我的代码库,我想问而不是用上面的引用来回应,有人可以启发我如何在保存时将 USER_ID 字段放入嵌套属性中。我拒绝禁用验证并手动处理插入,因为这不是 ruby​​ 方式并且违反了标准!\n提前感谢任何可以直接回答这个问题并且没有偏离 ruby​​ 方式的模糊解释的人!

\n\n
###Models\n#Users\nclass User < ApplicationRecord\n  has_one :profile, inverse_of: :user\n  accepts_nested_attributes_for :profile, allow_destroy: true\nend\n\n#Profiles\nclass Profile < ApplicationRecord\n  belongs_to :user, inverse_of: :profile\nend\n\n###Controller\nclass UsersController < ApplicationController\n  before_action :set_user, only: [:show, :edit, :update, :destroy]\n\n  # GET /users\n  # GET /users.json\n  def index\n    @users = User.all\n  end\n\n  # GET /users/1\n  # GET /users/1.json\n  def show\n  end\n\n  # GET /users/new\n  def new\n    @user = User.new\n    @user.build_profile\n  end\n\n  # GET /users/1/edit\n  def edit\n    @user.build_profile\n  end\n\n  # POST /users\n  # POST /users.json\n  def create\n    @user = User.new(user_params)\n\n    respond_to do |format|\n      if @user.save\n        format.html { redirect_to @user, notice: \'User was successfully created.\' }\n        format.json { render :show, status: :created, location: @user }\n      else\n        format.html { render :new }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # PATCH/PUT /users/1\n  # PATCH/PUT /users/1.json\n  def update\n    respond_to do |format|\n      if @user.update(user_params)\n        format.html { redirect_to @user, notice: \'User was successfully updated.\' }\n        format.json { render :show, status: :ok, location: @user }\n      else\n        format.html { render :edit }\n        format.json { render json: @user.errors, status: :unprocessable_entity }\n      end\n    end\n  end\n\n  # DELETE /users/1\n  # DELETE /users/1.json\n  def destroy\n    @user.destroy\n    respond_to do |format|\n      format.html { redirect_to users_url, notice: \'User was successfully destroyed.\' }\n      format.json { head :no_content }\n    end\n  end\n\n  private\n    # Use callbacks to share common setup or constraints between actions.\n    def set_user\n      @user = User.find(params[:id])\n    end\n\n    # Never trust parameters from the scary internet, only allow the white list through.\n    def user_params\n      params.require(:user).permit(:username, :password, :user_type_id, profile_attributes: [:user_id, :first_name, :middle_name, :last_name, :phone_number, :cell_number, :email])\n    end\nend\n\n##View\n<%= form_for(@user) do |f| %>\n  <% if user.errors.any? %>\n    <div id="error_explanation">\n      <h2><%= pluralize(user.errors.count, "error") %> prohibited this user from being saved:</h2>\n\n      <ul>\n      <% user.errors.full_messages.each do |message| %>\n        <li><%= message %></li>\n      <% end %>\n        <!--<li><%= debug f %></li>-->\n      </ul>\n    </div>\n  <% end %>\n\n  <div class="field">\n    <%= f.label :username %>\n    <%= f.text_field :username %>\n  </div>\n\n  <div class="field">\n    <%= f.label :password %>\n    <%= f.text_field :password %>\n  </div>\n\n  <div class="field">\n    <% if params[:trainer] == "true" %>\n      <%= f.label :user_type_id %>\n      <%= f.text_field :user_type_id, :readonly => true, :value => \'2\' %>\n    <% else %>\n      <%= f.label :user_type_id %>\n      <%= f.text_field :user_type_id, :readonly => true, :value => \'1\'  %>\n    <% end %>\n  </div>\n    <h2>Account Profile</h2>\n    <%= f.fields_for :profile do |profile| %>\n      <%#= profile.inspect %>\n        <div>\n          <%= profile.label :first_name %>\n          <%= profile.text_field :first_name %>\n        </div>\n        <div>\n          <%= profile.label :middle_name %>\n          <%= profile.text_field :middle_name %>\n        </div>\n        <div>\n          <%= profile.label :last_name %>\n          <%= profile.text_field :last_name %>\n        </div>\n        <div>\n          <%= profile.label :email %>\n          <%= profile.text_field :email %>\n        </div>\n        <div>\n          <%= profile.label :phone_number %>\n          <%= profile.telephone_field :phone_number %>\n        </div>\n        <div>\n          <%= profile.label :cell_phone %>\n          <%= profile.telephone_field :cell_number %>\n        </div>\n    <% end %>\n  <div class="actions">\n    <%= f.submit %>\n  </div>\n    <%= debug params %>\n    <%= debug user %>\n    <%= debug user.profile %>\n<% end %>\n
Run Code Online (Sandbox Code Playgroud)\n\n

更新\n对于初学者,我发现您需要包含autosave: true关系,如下所示

\n\n
class User < ApplicationRecord\n  has_one :profile, inverse_of: :user, autosave: true\n  accepts_nested_attributes_for :profile, allow_destroy: true\nend\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后父记录会在子记录之前保存。现在出现了另一个问题,我只是不确定,并且很奇怪,当提交表单时,您会在我粘贴在下面的控制台输出中注意到INSERT INTOprofiles语句包含user_id列和1的值。它通过了验证,并且从输出来看它运行正常,但是配置文件表中的user_id列仍然为空。我将继续挖掘,希望我的一位红宝石同胞能够看到这一点,并就如何完成修复这个问题有一些想法。到目前为止,我喜欢 Rails 5 的改进,但如果没有一些有趣的小陷阱,它就不会是 ROR!再次提前致谢!

\n\n
Started POST "/users" for 192.168.0.31 at 2017-03-12 22:28:14 -0400\nCannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255\nProcessing by UsersController#create as HTML\n  Parameters: {"utf8"=>"\xe2\x9c\x93", "authenticity_token"=>"YA7kQnScvlIBy5OiT+BmOQ2bR7J00ANXId38FqNwX37Cejd+6faUyD3rMF4y0qJNKBUYGaxrRZqcLrXonL6ymA==", "user"=>{"username"=>"john", "password"=>"[FILTERED]", "user_type_id"=>"1", "profile_attributes"=>{"first_name"=>"john", "middle_name"=>"r", "last_name"=>"tung", "email"=>"thegugaru@gmail.com", "phone_number"=>"8033207677", "cell_number"=>"8033207677"}}, "commit"=>"Create User"}\n   (0.1ms)  BEGIN\n  SQL (0.3ms)  INSERT INTO `users` (`username`, `password`, `user_type_id`, `created_at`, `updated_at`) VALUES (\'john\', \'0000\', 1, \'2017-03-13 02:28:14\', \'2017-03-13 02:28:14\')\n  SQL (0.4ms)  INSERT INTO `profiles` (`user_id`, `email`, `first_name`, `middle_name`, `last_name`, `phone_number`, `cell_number`, `created_at`, `updated_at`) VALUES (1, \'thegu@gmail.com\', \'john\', \'r\', \'tung\', \'8033207677\', \'8033207677\', \'2017-03-13 02:28:14\', \'2017-03-13 02:28:14\')\n   (10.8ms)  COMMIT\nRedirected to http://192.168.0.51:3000/users/1\nCompleted 302 Found in 24ms (ActiveRecord: 11.5ms)\n
Run Code Online (Sandbox Code Playgroud)\n

The*_*aru 5

好吧,我正在回答我自己的问题,因为我知道很多人都在为此苦苦挣扎,而我实际上有答案,而不是对文档的模糊回应。

首先,我们将在此示例中使用一对一关系。创建关系时,您需要确保父模型具有以下内容

  1. 逆:
  2. 自动保存:真
  3. Accept_nested_attributes_for:模型,allow_destroy:true

这是用户模型,然后我将解释,

class User < ApplicationRecord
  has_one :profile, inverse_of: :user, autosave: true
  accepts_nested_attributes_for :profile, allow_destroy: true
end
Run Code Online (Sandbox Code Playgroud)

在 Rails 5 中,您需要 inverse_of: 因为这告诉 Rails 通过外键存在关系,并且在保存表单数据时需要在嵌套模型上设置它。

现在,如果您要从关系行中关闭autosave: true ,您将留下 user_id保存到配置文件表而仅保存到其他列,除非您关闭了验证,然后它不会出错,它只会保存它没有user_id

这里发生的是autosave: true确保首先保存用户记录,以便将 user_id存储在配置文件模型的嵌套属性中。

简而言之,这就是为什么user_id没有遍历到子进程并且它正在回滚而不是提交。

最后一个问题是,有一些帖子告诉您在控制器中的编辑路线中,您应该添加@user.build_profile,就像我在帖子中一样。不要这样做,他们是完全错误的,在评估控制台输出后,它会导致

Started GET "/users/1/edit" for 192.168.0.31 at 2017-03-12 22:38:17 -0400
Cannot render console from 192.168.0.31! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by UsersController#edit as HTML
  Parameters: {"id"=>"1"}
  User Load (0.4ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
  Profile Load (0.5ms)  SELECT  `profiles`.* FROM `profiles` WHERE `profiles`.`user_id` = 1 LIMIT 1
   (0.1ms)  BEGIN
  SQL (0.5ms)  UPDATE `profiles` SET `user_id` = NULL, `updated_at` = '2017-03-13 02:38:17' WHERE `profiles`.`id` = 1
   (59.5ms)  COMMIT
  Rendering users/edit.html.erb within layouts/application
  Rendered users/_form.html.erb (44.8ms)
  Rendered users/edit.html.erb within layouts/application (50.2ms)
Completed 200 OK in 174ms (Views: 98.6ms | ActiveRecord: 61.1ms)
Run Code Online (Sandbox Code Playgroud)

如果您看到它正在从头开始重建配置文件,并将与您正在编辑的当前用户匹配的记录的 user_id 重置为 null。

因此,请务必小心,因为我已经看到了大量提出此建议的帖子,并且我花了几天的时间研究才找到解决方案!