2
votes

I have a form field that posts a date range in the format "05/14/2013 - 05/22/2013". My model has two separate date fields, begin and end for the respective beginning and end dates from the form field. What is the best MVC way to approach getting the date range into the correct fields?

I've been trying to manually deconstruct the date range in the controller's create method, but it looks like the updated params aren't properly seen in the model before the record is created.

EDIT: The date range is coming in that format because I'm using Keith Wood's datepick, and it outputs the dates in a single input field.

What I've been trying to do currently is this (contract is the name of my model, and dates is the input date range:
beginDate = params[:dates].split("-")[0].strip()
endDate = params[:dates].split("-")[1].strip()
params.delete :dates
params[:contract][:begin] = Date.strptime(beginDate, '%m/%d/%Y')
params[:contract][:end] = Date.strptime(endDate, '%m/%d/%Y')
@contract = Contract.new(params[:contract])

... but these changes to params don't show up by the time the record is created and validated.

2
"but it looks like the updated params aren't properly seen in the model before the record is created" - can you expand on that? If you're in a controller's update method, and you pass the params along to a model method, it will show the params as given by the user. My suggestion would be in the create and update you call a method on your model like safe_update or something where you do the extra work and try to update the model. - MrDanA
Actually another question is, why is the date range coming in in that format? If you have two text fields for entering dates, you'd get params like params[:begin] and params[:end] and then you wouldn't have to worry about manipulating a String. - MrDanA
I made edits to my question with more information. - Jonathan Hwa

2 Answers

5
votes

Define a setter on your model which takes the field, splits it, and puts each part into the appropriate field.

def date_range=(val)
  begin_str, end_str = val.split(' - ')
  self.begin_at = Date.parse(begin_str)
  self.end_at = Date.parse(end_str)
end

This will work when called directly, or from a mass assignment method such as update_attributes or create. Make sure you add date_range (or the relevant param name) to attr_accessible if you already have this defined in your model.

I've changed the field names in my example, asbegin and end should be avoided as field names, since one is a method and the other is part of the ruby syntax.

1
votes

You can use virtual attributes to make the conversion from the date range text into individual dates at the model level. Add a setter and getter as below in the model,

def date_range_text
   return "#{start_date.to_s} - #{end_date.to_s}"
end
def date_range_text= val
  start_date_text,end_date_text = val.split[" - "]
  start_date = Time.zone.parse(start_date_text) unless start_date_text.nil?
  end_date = Time.zone.parse(end_date_text) unless end_date_text.nil?
end

And use data_range_text in your forms. For me information, check out the railscast below.

http://railscasts.com/episodes/16-virtual-attributes-revised

This is the best way to handle the difference between database structure and the user input forms.