Why don't form helpers read custom model attributes?

Hi!

I’m using custom read and write accessors in my models like :

class Order < ActiveRecord::Base

def date
read_attribute(:date).strftime(’%d/%m/%Y’)
end

end

My problem is that Form helpers like text_field, text_area, etc, don’t
seem to work with these custom accessors, is that normal ?

Thanks!

Nicolas.

Nicolas B. wrote:

Hi!

I’m using custom read and write accessors in my models like :

class Order < ActiveRecord::Base

def date
read_attribute(:date).strftime(’%d/%m/%Y’)
end

end

My problem is that Form helpers like text_field, text_area, etc, don’t
seem to work with these custom accessors, is that normal ?

Thanks!

Nicolas.

The form helpers should be able to read all of the methods on the model.
My first guess was that the helper was grabbing the date attribute
instead of your date method, but that doesn’t sound possible to me since
your method should be triggered first. My second guess is that perhaps
it won’t use an accessor unless it also has a setter ( ex. a method
named date=(new_date) ). Those are just guesses though… if you tried
creating a differently named method, or adding a setter, it would be
easy enough to test out.

Tom

It always seems to go right to the backend, skipping your code. I run
into
this often.

My solution is this:

attr_writer :date_for_form

def date_for_form
read_attribute(:date).strftime(’%d/%m/%Y’)
end

before_validate :copy_form_fields

def copy_form_fields
self.attributes(:date) = self.date_for_form
end

Warning: untested. Write a unit test for this before you implement it :slight_smile:

This should allow you to use the form helpers on your new fields
(date_for_form) Kinda kludgy but it will get you going.

Nicolas B. wrote:

end

My problem is that Form helpers like text_field, text_area, etc, don’t
seem to work with these custom accessors, is that normal ?

In order to preserve both database field formats and redisplayed
user input, AR form helpers get their display strings from
_before_type_cast methods.

So you could write:

def date_before_type_cast
date.strftime(’%d/%m/%Y’)
end

But if you want a common date display format throughout your app,
instead add

ActiveSupport::CoreExtensions::date::Conversions::DATE_FORMATS[:default]
= ‘%d/%m/%Y’

to environment.rb, and write in your model

def date_before_type_cast
date.to_s
end

You’ll also need a setter

def date=(val)
self[:date] = val.is_a?(String) ? val.to_date : val
end

plus, since Rails does not currently support non-US formats for
string-to-date conversions, you’ll have to add this to environment.rb

class String
def to_date
Date.strptime(self, ‘%d/%m/%Y’)
end
end


We develop, watch us RoR, in numbers too big to ignore.

Mark Reginald J. wrote:

end
user input, AR form helpers get their display strings from

end
end


We develop, watch us RoR, in numbers too big to ignore.

I strongly recommend that you DO NOT CHANGE THE DEFAULT DATE FORMAT.
This will lead to unending pain and suffering, and will highly
displease the gods of MySQL.

If you change the default date format, rails will use this format to
try and write dates to the db. Mostl likely this will result in a
silent failure (at least in MySQL). Your dates will just get recorded
as ‘0000-00-00’, and you will lose fistfuls of hair trying to figure
out why.

See tickets #6019 and #6363

_Kevin

Nicolas B. wrote:

My problem is that Form helpers like text_field, text_area, etc, don’t
seem to work with these custom accessors, is that normal ?

Thanks!

Nicolas.

Nicolas,

Yes, this is normal. And for whatever it’s worth, I’ve learned and
forgotten this at least 3 times - since I’m thinking in pure OO mode
most of the time and I expect overridden methods to get called.

Because you can do complex transformations in a custom attribute reader,
Rails gets the attribute value using _before_type_cast.

One way to think about the reason for this is because Rails has no
built-in notion of a model of the form’s state (what in the J2EE world
might be called a “form bean”). Because the default behavior of the
ActiveRecord/controller/view interaction is to read/write attributes
directly from the DB to/from the view, there’s no facility to keep track
of what’s going on the form during unsuccessful attempts to save a
particular attribute (e.g. what was typed into the form field when it’s
not valid).

In effect, you end up using a custom attribute reader/writer pair to
manage this form state. If you take the set of all such methods that
you end up writing on your ActiveRecord object, you would see that they
basically represent a “form state” or “form model” object, even though
they are methods on the model.

I’ve been thinking about this a lot. Take a look at the ActiveForm
plugin for one way to formalize this “form model” notion. In cases
where the form state can be complex, a separate form model would keep
your form state straight, but then would imply a copying step from the
form model attributes into the model attributes and the opposite.

If most of your model attributes don’t need tweaking to be displayed,
then just adding a couple of custom attribute reader/writer pairs is
probably sufficient.

But I think it’s helpful to keep the distinction between form data
concerns and model data concerns in mind.

Wes