I’ve been fighting for a couple of days to learn how to best use Rails’ form validation on forms that don’t map cleanly to database tables. I think I’ve got it. The trick appears to be to use non-persistent model classes, that nonetheless inherit from ActiveRecord::Base. Here’s how.
1 2 3 4 5 6 7 |
# Code based on code found at http://www.railsweenie.com/forums/2/topics/724 class EphemeralModel < ActiveRecord::Base def self.columns() @columns ||= []; end def self.column(name, sql_type = nil, default = nil, null = true) columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null) end end |
So now you can do this in a subclass:
1 2 3 4 |
class SomeForm < EphemeralModel column :somefield, :string validates_format_of :somefield, :with => /^\w+$/ end |
And you can now use the form_for helper, standard error messages, validation, etc. as you would with a form that maps directly to a table. Just don’t call save, because it will fail, badly.
Instead what I did was to add a to_foobar method on the non-persistent model class that creates the model object (which is persistent) behind the form, so the knowledge of how the form fields map onto the model remains in this class, and the controller and persistent model classes remain simple. Obviously if the form wraps several model objects or if the construction is tricky this method would be complex, but that’s OK, because that’s just about all the logic (aside from any manual validation code) that resides in this class.
So far, it works well. It’s a lot better than lower-level xxx_tag Rails constructs and the gross-ugly controller code that resulted from manually assembling and disassembling a mixed set of model properties and model-less fields and validating them separately. That was a mess; this is tidy.