rails:嵌套表单属性如何避免在数据库中创建新条目?

时间:2017-02-08 19:30:30

标签: ruby-on-rails

我有一个模型书籍和一个模型作者。

添加书籍的表单,包含允许添加作者的嵌套。这样可行。但是,我在authors字段上有一个自动完成功能,所以当表单发布到控制器时,作者(几乎)肯定存在于数据库中。

我应该以某种方式对嵌套的属性进行find_or_initialize_by

我可能正在寻找错误的地方,但我在导轨指南中找不到这个。我试过这个(在SO上找到):

def create

    @book = Book.new(params_book)
    small_name = params[:book][:authors_attributes]["0"]["name"].downcase
    aut_id = Author.where("\"authors\".\"name\" = :name",{name: small_name}).pluck(:id).join
    @book.authors = Author.find_or_initialize_by(id: aut_id)

    if @book.save
        redirect_to see_book_url(Book.last)
    else
       render 'new'
    end
end

这会产生错误:

undefined method `each' for #<Author:0x007fac59c7e1a8>

引用第@book.authors = Author.find_or_initialize_by(id: aut_id)

修改

在对这个问题的评论之后,我将代码更新为:

def create

    book_params = params_book
    small_name = params[:book][:authors_attributes]["0"]["name"].downcase
    id = Author.where("\"authors\".\"name\" = :name",{name: small_name}).pluck(:id).join
    book_params["authors_attributes"]["0"]["id"] = id

    @book = Book.new(book_params)

    if @book.save
          redirect_to see_book_url(Biblio.last)
    else
        ....

书籍参数如下:

<ActionController::Parameters {"title"=>"Testus Testa",
 "authors_attributes"=><ActionController::Parameters {
    "0"=><ActionController::Parameters {"name"=>"Vabien", "id"=>"22"}
         permitted: true>} permitted: true>} permitted: true>

这对我来说很好,但是,我收到了这个错误:

ActiveRecord::RecordNotFound in Administration::BooksController#create
Couldn't find Author with ID=22 for Book with ID=

2 个答案:

答案 0 :(得分:1)

好的,获得所需内容的最简单方法是更改​​表单中的自动填充,例如:['author 1 name', 'author 2 name']将其更改为包含作者姓名和ID的对象数组:{ {1}}所以只要那个表单字段现在是“id”而不是“name”,那么在你的控制器中,你所要做的就是:

[{label: 'author 1 name', value: 0}, {label: 'author 2 name', value: 1}]

因为只有没有ID的属性才会被创建为新对象。 只需确保在图书模型中设置 def create @book = Book.new(params_book) if @book.save redirect_to see_book_url(Book.last) else render 'new' end end

您获得的错误是因为accepts_nested_attributes_for :authors是一个很多关系,因此当您将其设置为单个作者时,它会期望一个集合。要将单个作者添加到集合中,您执行@book.authors而不是@book.authors << Author.find_or_initialize_by(id: aut_id),尽管其冗余用于使用名称来获取ID,只是用id初始化。 id将自动创建。请改用@book.authors = Author.find_or_initialize_by(id: aut_id)

在您当前的代码中,您创建了多个作者,这不仅是因为缺少“id”而是因为Author.find_or_initialize_by(name: small_name)将嵌套属性传递给对象初始值设定项,然后在您访问嵌套属性参数之后并再次添加作者。此外,如果您有多个具有相同名称的作者,那么@book = Book.new(params_book)实际上会从具有该名称的所有作者的组合ID中创建一个ID。

如果您想手动执行此操作,请在“params_book”方法中从许可中删除Author.where("\"authors\".\"name\" = :name",{name: small_name}).pluck(:id).join,这样就不会将其传递给:authors_attributes,然后执行以下操作:

Book.new

如果您遇到麻烦,请告诉我!

海报回复后

从“params_book”方法中删除许可证中的def create @book = Book.new(params_book) params[:book][:author_attributes].each{|k,v| @book.authors << Author.find_or_initialize_by(name: v['name'])} if @book.save redirect_to see_book_url(Book.last) else render 'new' end end 并尝试以下操作:

:authors_attributes

答案 1 :(得分:0)

解决了,非常感谢Jose Castellanos和这篇文章:

Adding existing has_many records to new record with accepts_nested_attributes_for

现在的代码是:

<requestfocus />