以一种形式创建深度嵌套 Rails 关联

Create Deeply Nested Rails Associations in One Form

我的欢迎页面上有一个用户表单。提交用户表单后,我想创建一个属于该用户的网站记录以及一个属于该网站的页面记录。

我正在尝试使用 fields_for,但我不确定自己的操作是否正确。

# welcome.html.erb

<%= form_for @user do |f| %>
    <div>
        <%= f.text_field :name' %>
     </div>
     <div>
         <%= f.email_field :email %>
     </div>
     <div>
         <%= f.password_field :password %>
     </div>
     <div>
         <%= f.fields_for :website do |website_fields| %>
             <%= website_fields.text_field :name, value: 'Untitled website' %>
         <% end %>
     </div>
     <div>
         <%= f.fields_for :page do |page_fields| %>
             <%= page_fields.text_field :name, value: 'Untitled page' %>
             <%= page_fields.text_field :content, class: 'js-PageContentHiddenField', value: 'Page content' %>
         <% end %>
     </div>
     <div>
         <%= f.submit 'Create account' %>
     </div>
 <% end %>

页面控制器看起来像这样...

# pages_controller.rb

def welcome
    @user = User.new
    @website = @user.websites.new
    @page = @user.websites.pages.new
end

我的路线如下...

# routes.rb

resources :users do
    resources :websites do
        resources :pages
    end
end

模型看起来像这样...

# User model

has_many :websites, :dependent => :destroy
has_many :pages, through: :websites
accepts_nested_attributes_for :websites
accepts_nested_attributes_for :pages

# Website model

belongs_to :user
has_many :pages, :dependent => :destroy

# Page model

belongs_to :website

最后是我的用户控制器...

# User controller

def create
    @user = User.new(shared_params)
    @website = @user.websites.new(website_params)
    @page = @website.pages.new(page_params)

    if @user.save
        auto_login(@user)
        @redirect_to user_website_page_path
    else
        # redirect to wherever
    end
end

private

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

    def website_params
        params.require(:user).permit(:name, :user_id)
    end

    def page_params
        params.require(:user).permit(:name, :website_id)
    end

我现在遇到的问题是用户名被保存为页面和网站名称等。我认为这是我的参数问题。另外,我不确定如何设置重定向以在创建页面后重定向到该页面。

几周来我一直在尝试各种配置,但我无法破解它。我不能强调我对我在这里做什么知之甚少,真的很想得到一些帮助!

回调 是在对象生命周期的特定时刻调用的方法。通过回调,可以编写代码,在创建、保存、更新、删除、验证或从数据库加载 Active Record 对象时 运行。

因此,当创建 user 时需要创建 websitepage,因此您可以使用 after_createbefore_create打回来。 after_create 在对象创建后执行,before_create 回调在对象创建前执行。

因此,在您的 User 模型中,您可以做的是:

after_create :create_website_and_page

def create_website_and_page
  website = websites.build(name: 'Untitled Website')
  page = website.pages.build(name: 'Untitled Page', content: 'Page Content')
  website.save # This will automatically save the associated page too
end

因此,一旦您的用户被创建,websitepage 也将被创建。

要从表单中获取 pagecontent,您可以执行以下操作:

User模型中:

attr_accessor :page_content

这将在数据库中不存在的 user 对象上创建一个虚拟属性。现在在你的 form:

<%= f.hidden_field :page_content, class: 'js-PageContentHiddenField' %>

因此在此字段中追加来自 javascript 的内容。现在在你的控制器中也允许这个属性:

params.require(:user).permit(:email, :password, :name, :page_content)

然后最后在我们上面写的方法中 create_website_and_page 将行更改为:

page = website.pages.build(name: 'Untitled Page', content: page_content)

这应该满足要求。

如果您出于任何原因仍希望使用 nested_form,那么您犯的错误就是 params。只需在创建操作的顶部放置一个 debugger 并选中 params。因此,要允许网站和页面的参数,您必须执行以下操作:

def user_params
  params.require(:user).permit(:email, :password, :name, website_attributes: [:name], page_attributes: [:name, :website_id, :content])
end

但是您永远不会在 params 中获得 website_id,因为您也是从同一个 form 创建的。而且 user_id 也不需要被允许,因为它会在嵌套表单的情况下自动关联。