I ran into a bit of strangeness when trying to get RESTful forms working with non-ActiveRecord models — in this case, models that I was using to represent data stored in Redis. It turned out to not be straightforward. I wanted to create a simple form for creating a new object. Because I created things using the generators, I had a skeleton page already (even if I’m not using ActiveRecord it’s still a bit of a time-saver):
mymodels/new.html.erb: <h1>New Thing</h1> <% form_for(@mymodel) do |f| %> <%= f.error_messages %> <p> <%= f.label :name %><br/> <%= f.text_field :name %> </p> <p> <%= f.label :a_link %><br/> <%= f.text_field :a_link %> </p> <p> <%= f.submit 'Create' %> </p> <% end %>
Pretty ordinary. What happens with this, though, is that when you load the page, it calls the controller’s
new() method first. The page expects that method to pass in an instance of the model,
form_for() call then ends up introspecting that instance to determine what to do with it. When the form is submitted, then the controller’s
create() method is called…or is it? Actually, if
form_for() determines that the instance is brand-new, then yes,
create() is called. Otherwise, the controller’s
update() method is invoked.
This is what was throwing me off. I put things together, and discovered that
update() was being invoked instead of
create. Why? Well, I ended up having to dig into the Rails source code to find out, specifically in the
apply_form_for_options() method. The trick turns out that it really wants to call a method on the model called
new_record?() to determine whether or not the instance is brand new. If the model doesn’t respond to that method, or if it returns false, then
form_for() decides that this must be an existing instance, so it sets up the form to invoke the
Once figured out, it was easy enough. I added to my model:
def new_record? (id.nil? || (id == 0)) end
And that fixed the problem. If my instance has a nil or zero id, then it’s new. I do have to point out that while this
form_for() behavior is perhaps convenient for coding, it’s not a very efficient thing to do. The side effect is that every time the form is loaded a model instance is created, which is then thrown away after the page is delivered; clearly, that’s a waste. It would make more logical sense to allow
form_for() to operate on a nil object, treating it as obviously new.
In any case, hopefully this info will help save someone else some time.