views:

925

answers:

4

I'm sure this is a relatively simple question, and there must be a good sensible rails way of doing it, but I'm not sure what it is.

Basically I'm adding books to a database, and I want to store the Author in a separate table. So I have a table called authors which is referenced by the table books.

I want to create a rails form for adding a book, and I'd like it to be just a simple form for Author, Title, Publisher etc, and if it finds the author already in the authors table then it should just reference that record, and if it isn't in the authors table then it should add a new record and reference it.

I'm sure there's an easy way of doing this in rails - but I can't seem to find it.

Cheers,

Robin

+5  A: 

Assuming that your form passes an author_name and a book hash with the book attributes, you could do the following in your controller:

@author = Author.find_or_create_by_name(params[:author_name])
@book = Book.new(params[:book])
@book.author = @author
@book.save

This will find an author by that name, or create one, then assign that author to the book that's created.

Note: This also assumes that authors can be uniquely identified by their name, and this usually isn't the case. There could be multiple guys namned "Steven King" out there, for instance, and only one of them wrote The Shining

Micah
Thanks for the quick response! This looks like a good way to do it - although I am surprised in some ways that Rails doesn't do it a bit more automatically.
robintw
+4  A: 

I suggest you watch the Complex Forms Series of Railscasts here.

Honza
+1  A: 

robintw's comment to Micah's answer:

This looks like a good way to do it - although I am surprised in some ways that Rails doesn't do it a bit more automatically

It's four lines! How much more automatically can Rails make it? It's a framework not a human being. :)

And here it is in two:

@author = Author.find_or_create_by_name(params[:author_name])
@book = Book.create params[:book].merge({:author_id => @author.id})

And Honza's answer has a lot of merit:

class Book < ActiveRecord::Base
  ...
  def author_name=(author_name)
    self.author = Author.find_or_create_by_name author_name
  end
end

With this example, and assuming your form has a parameter like "book[author_name]", your code is just:

@book = Book.create params[:book]

Wowzers.

Ian Terrell
A: 

This will eventually be automatic (according to this post, in Rails 2.2): http://ryandaigle.com/articles/2008/7/19/what-s-new-in-edge-rails-nested-models

Bryan Stearns