How do you use multiple form fields to interact with a single method in rails?
I have a mo开发者_运维问答del which has start_at
and end_at
attributes. In the form for the end-user, I am displaying start_at
using the standard datetime_select
, but I'd rather not have a second datetime select presented to the user. I'm hoping I can create two fields that represent a duration; one for hours, the other for minutes. My question is, how in my view do I use form helpers to automatically fill in the fields when editing an existing entry. Furthermore, how would I connect that to the model and subsequently save the recording using the real attribute, end_at
?
Thanks in advance for any advice you can give!
I have to do this a bunch and i've been doing the following:
- Use the FormTagHelper versions of the calls for the field to be handled specially.
- In the controller, read the form_tag values out of the params object.
- delete the extra values:
params[:examplemodelname].delete :distance if params[:examplemodelname].has_key? :distance
- put the 'real' values into the params object (in your example, ends_at)
- call
ExampleModelName.new(params[:examplemodelname])
or@examplemodelname.update_attributes(params[:examplemodelname])
as per usual.
Wouldn't logic like this be better suited for the model? Fat model, skinny controller?
I think this is absolutely right. I despise using a controller for stuff like this. In my opinion, controllers are best used for a few things:
- Shuffling data between views and models, ala CRUD operations
- Access control (usually with before_filters)
- Support to fancy UI actions (multiple select, wizards, etc)
Of course everyone has to find their own balance, but I find "making an exception" for something like this tends to lead to exceptions everywhere. Not to mention the controller logic is harder to test.
To answer your question: you should simply define virtual attributes in your model that help you convert between start_at and a duration. Something like:
# Return the duration in seconds. Will look different if you want to use
# multiparameter assignment (which you definitely should consider)
def duration
(end_at - start_at).seconds
end
# Store the duration as an instance variable. Might need to use multiparameter
# assignment if you use a time_select() in the view.
def duration=(arg)
@duration = arg
end
before_save :set_end_at
def set_end_at
end_at = start_at + @duration.seconds
end
I'd usually set the actual attribute in a before_save to avoid any race conditions from the form assignment. You have no guarantee that start_at will get assigned before or after duration, and that can introduce bugs in your otherwise good logic.
精彩评论