Aaron Van Bokhoven / aaronvb

I am a Software Developer and a Portrait Film Photographer.
  • I love coding with Ruby and Ruby on Rails, and I love shooting with film.
  • I currently live in Honolulu, Hawaii, but frequent Chicago and California.
  • I believe that programming is a form of art, like painting and photography, where you can express your ideas and logic and see it transform into something real.
Aaron Van Bokhoven
photo: kipkeston
Email me at bokhoven@gmail.com. View my Photography Portfolio. Follow me on Twitter and on Tumblr.

Mar 06, 2010
You're viewing an article, go back to the home page to see more.

Here's a little trick I found when handling multiple nested models in a form that require validations for each model.

Spec time. For example, we have a model, Author, which has_many Books and has_many Magazines. Book and Magazine has validations. On a single page, we create a three forms for Book, Magazine, and Author. There is also an option select that a user can choose if the Author has a Book or a Magazine.

Pretty straight forward. Let's do some code.


#models/author.rb
class Author < ActiveRecord::Base
has_many :books
has_many :magazines

accepts_nested_attributes_for :books
accepts_nested_attributes_for :magazines

validates_presence_of :name
end


#models/book.rb
class Book < ActiveRecord::Base
belongs_to :author
validates_presence_of :title,
:genre
end


#models/magazine.rb
class Magazine < ActiveRecord::Base
belongs_to :author
validates_presence_of :title,
:genre
end

Basic model setup with validations and accepts_nested_attributes_for. More info on that can be found in the Ruby on Rails API - Nested Attributes.

Moving on to the form setup..


#views/authors/new.html.erb
<% form_for @author do |f| %>
<%= f.error_messages %>


<%= f.label :name %>

<%= f.text_field :name %>



<%= f.radio_button("media_type", "book" %><%= f.label :media_type_book, 'Book' %> [?]
<%= f.radio_button("media_type", "magazine" %><%= f.label :media_type_magazine, 'Magazine' %> [?]


<% f.fields_for :books do |book| %>


<%= book.label :title %>

<%= book.text_field :title %>



<%= book.label :genre %>

<%= book.text_field :genre %>


<% end %>

<% f.fields_for :magazines do |magazine| %>


<%= magazine.label :title %>

<%= magazine.text_field :title %>



<%= magazine.label :genre %>

<%= magazine.text_field :genre %>


<% end %>
<%= f.submit 'Submit' %>
<% end %>

That sets up the form with the nested model, now for the controller code.


#controllers/authors_controller.rb
class AuthorsController < ApplicationController
def new
@author = Author.new
books = @author.books.build #this builds the nested form in the view
magazines = @author.magazines.build #this builds the nested form in the view
end

def create
param_hash = params[:author]
if params[:author][:media_type] == "book"
param_hash.delete("books_attributes")
elsif params[:author][:media_type] == "magazine"
param_hash.delete("magazines_attributes")
end
@author = Author.new(param_hash)
@author.save
end
end

Instead of passing the params straight to Author.new, it's put into a hash variable. Then the params get checked for a title and genre, if empty delete the key from the hash and pass to Author.new. Activerecord wont see the book param and will skip the validations for it.



© Aaron Van Bokhoven