All threads / Using find_or_create_by with accepts_nested_attributes_for
Ask A Question


You’re not receiving notifications from this thread.

Using find_or_create_by with accepts_nested_attributes_for

Nino Rosella asked in Rails

Hi gang...

I have a Book which has_many :authors, through: :book_authors

Instead of creating a new Author each time I create a book I'd like to use find_or_create_by on the author's name attribute. Really struggling to work out where this code should go. Using cocoon gem for the form.

Here's what I currently have that works.

class Book < ApplicationRecord

has_many :book_authors
has_many :authors, through: :book_authors
belongs_to :user

accepts_nested_attributes_for :authors, allow_destroy: true

class Author < ApplicationRecord
has_many :book_authors
has_many :books, through: :book_authors

class BookAuthor < ApplicationRecord
belongs_to :book
belongs_to :author

class BooksController < ApplicationController
before_action :authenticate_user!

def new
@book =

def create
@book = current_user.books.create(book_params)
@book.authors.each {|author| author.user_id =}

    redirect_to book_path(@book)
    render :new


def book_params
params.require(:book).permit(:title, authors_attributes: [:id, :name, :_destroy])

<%= simple_form_for @book do |f| %>
<%= f.input :title %>
<div id='authors'>
<%= f.simple_fields_for :authors do |author| %>
<%= render 'author_fields', :f => author %>
<% end %>
<div class='links'>
<%= link_to_add_association 'Add another author', f, :authors %>
<% end %>

<div class="nested-fields">
<%= f.input :name, label: "Author(s)", collection: @authors, value_method: :name, input_html: {value: @authors, class: 'new-author'} %>
<%= link_to_remove_association "Remove this author", f %>
Don't suppose anyone could lend a hand before this drives me insane..? :s
Hey Nino, 

I haven't tested this, but I believe this should work for you on your create method:

@book = current_user.books.create(book_params)

author = Author.find_or_create_by(name: "foo") do |author|
  # do stuff if author is new


find_or_create_by will either create and return a new object or return the found object. So once you have the author object, just create the @book.book_authors record directly.
Hi Jacob,

That's been a massive help, so thanks. I just have one small problem now.

When I save the Book and then do


I have two records saved. One is the correct record that was retrieved by the find_or_create_by method. The second record is new and I believe this is being created by the first line of code:

@book = current_user.books.create(book_params)

Is there a way to skip the saving of the new record?
Oh, duh I didn't pay attention to your book_params, it includes authors_attirbutes.

If this were my project, right or wrong, I'd make two sets of params, one for author and one for book. So something like:

def book_params

def author_params
  params.require(:book).permit(authors_attributes: [:id, :name, :_destroy])

This way you can create your book without the rails magic also creating the association when you create the book when passing book_params.

There could be a better way to handle this scenario that rails provides, but I haven't found it so I usually resort to this sort of setup to get the job done.
Jacob, you nailed it.

Thanks so much for your help!
Woohoo, glad that worked for you!

Good luck!
Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 68,099+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more. Icons by Icons8

    © 2022 GoRails, LLC. All rights reserved.