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

Ruby on rails rails:嵌套表单属性如何避免在数据库中创建新条目?,ruby-on-rails,Ruby On Rails,我有一本模范书和一本模范作家 用于添加书籍的表单包含用于添加作者的嵌套。这很有效。但是,我在authors字段上有一个autocomplete函数,因此当表单发布到控制器时,几乎可以肯定数据库中存在作者 我应该在嵌套属性上进行查找或初始化 我可能找错地方了,但我在rails指南中找不到这个。我试过这个,所以: def create @book = Book.new(params_book) small_name = params[:book][:authors_attribut

我有一本模范书和一本模范作家

用于添加书籍的表单包含用于添加作者的嵌套。这很有效。但是,我在authors字段上有一个autocomplete函数,因此当表单发布到控制器时,几乎可以肯定数据库中存在作者

我应该在嵌套属性上进行查找或初始化

我可能找错地方了,但我在rails指南中找不到这个。我试过这个,所以:

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>
书中的参数如下所示:

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

好的,那么获得所需内容的最简单方法是将表单中的autocomplete从一个名称数组更改为:['author 1 name','author 2 name']将其更改为一个包含作者名称和id的对象数组,如:[{label:'author 1 name',value:0},{label:'author 2 name',value:1}]因此,只要表单字段现在是id而不是name,那么在控制器中,您所要做的就是:

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


您收到的错误是因为@book.authors是一个多关系,因此当您将其设置为非单个作者时,它需要一个集合。要在收藏中添加个人作者,请@book.authorsSolved,非常感谢Jose Castellanos和本文:

现在的代码是:

# the strong params isn't a Hash, so this is necessary 
# to manipulate data in params :
book_params = params_book

# All registrations in the DB are small case
small_name = params[:book][:authors_attributes]["0"]["name"].downcase

# the form sends the author's name, but I need to test against the id:
id = Author.where("\"authors\".\"name\" = :name",{name: small_name}).pluck(:id).join
book_params["authors_attributes"]["0"]["name"] = params[:book][:authors_attributes]["0"]["name"].downcase

# this author_ids is the line that I was missing! necessary to 
# test whether the author already exists and avoids adding a 
# new identical author to the DB.
book_params["author_ids"] = id
book_params["authors_attributes"]["0"]["id"] = id

# the rest is pretty standard:
@book = Book.new(book_params)

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

哇!谢谢!不过我确实有点问题。我在我的书中有一个非常类似的方法,我没有这样做的原因是因为我无法验证author参数。按照您的建议执行,我如何验证,即如果未选择作者,则将表单发回,即生成与其他缺失字段(如未填写标题)同时出现的错误消息?@thiebo我建议放在顶部的方法应该验证作者属性。如果你想按照我在底部显示的方式来做,看看这是否有效。。代替params[:book][:author_attributes]。每个{k,v |@book.authors我明天会检查这个。非常感谢这个伟大的答案。我会发布if问题,如果一切正常;@thiebo当然,让我知道进展如何!您建议的第二次尝试在db中创建了两个authors条目:@book.authors_attributes=params[:book][:author_attributes].injection{}{{hash,k,v|hash[k]=author.find_或_initialize_byname:v['name'].attributes.mergev和hash}。正如您建议只创建没有id的条目一样,我将id添加到参数中。我编辑了问题以显示我现在所做的操作。很高兴我能提供帮助!抱歉,我不知道您没有在模型中设置嵌套属性,否则我会这样做。但是我仍然对您的代码感到有点困惑。看起来您是仅查找第一作者的id,且仅查找第一作者。此外,如果您有两位同名作者,则代码将无法工作,这可能是您之前遇到的错误。假设一位作者的名字是bob,他的id为1。另一位也被称为bob的人的id为2。代码id=author.where\authors\。\name\=:name,{name:'bob'}.pluck:id.join返回'12',它不是任何bob的id,也可能不是任何其他bob的id。考虑到作者,我更改了代码,以便自动完成获取给定的作者名称和名称,而获取id的查询将名称和给定的名称作为参数。
def create
    @book = Book.new(params_book)
    if @book.save
        redirect_to see_book_url(Book.last)
    else
       render 'new'
    end
end
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
def create
    @book = Book.new(params_book)
    @book.authors_attributes = params[:book][:author_attributes].inject({}){|hash,(k,v)| hash[k] = Author.find_or_initialize_by(name: v['name']).attributes.merge(v) and hash}

    if @book.save
        redirect_to see_book_url(Book.last)
    else
       render 'new'
    end
end
# the strong params isn't a Hash, so this is necessary 
# to manipulate data in params :
book_params = params_book

# All registrations in the DB are small case
small_name = params[:book][:authors_attributes]["0"]["name"].downcase

# the form sends the author's name, but I need to test against the id:
id = Author.where("\"authors\".\"name\" = :name",{name: small_name}).pluck(:id).join
book_params["authors_attributes"]["0"]["name"] = params[:book][:authors_attributes]["0"]["name"].downcase

# this author_ids is the line that I was missing! necessary to 
# test whether the author already exists and avoids adding a 
# new identical author to the DB.
book_params["author_ids"] = id
book_params["authors_attributes"]["0"]["id"] = id

# the rest is pretty standard:
@book = Book.new(book_params)

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