Rails 4 - Convert datetime into separate date and

2020-04-02 09:17发布

问题:

How can you convert a mysql datetime field into two form fields (1) date only, (2) time only, and combine both fields back into datetime format on form submit?

This would allow the use of the following gems, but store the dates in a single datetime field:

gem 'bootstrap-datepicker-rails'
gem 'bootstrap-timepicker-rails'

Thanks in advance!

回答1:

Found the solution with help from @Althaf

Added virtual attributes to model.rb Used before_save callback to convert back to datetime.

before_save :convert_to_datetime

def sched_date_field
  sched_date.strftime("%d/%m/%Y") if sched_date.present?
end 

def sched_time_field
  sched_time.strftime("%I:%M%p") if sched_time.present?
end

def sched_date_field=(date)
  # Change back to datetime friendly format
  @sched_date_field = Date.parse(date).strftime("%Y-%m-%d")
end

def sched_time_field=(time)
  # Change back to datetime friendly format
  @sched_time_field = Time.parse(time).strftime("%H:%M:%S")
end

def convert_to_datetime
  self.sched_time = DateTime.parse("#{@sched_date_field} #{@sched_time_field}")
end

Using Rails 4, needed to add sched_date_field and sched_time_field to strong params in controller.rb

Here are the fields in _form.html.erb

<%= f.label :sched_date_field, "Scheduled Date" %>
<%= f.text_field :sched_date_field, :class => "datepicker" %>

<%= f.label :sched_time_field, "Scheduled Time" %>
<%= f.text_field :sched_time_field, :class => "timepicker" %>


回答2:

You can use date_time_attribute gem:

class MyModel < ActiveRecord::Base
  include DateTimeAttribute

  date_time_attribute :scheduled_at
end

It will allow you to set schedule_at_date and scheduled_at_time separately. Once attributes are set, values will be combined into schedule_at.



回答3:

You could use virtual attributes See this Railscast and if you have a pro subscription the revised one.

Basically in the view you would the following

<%= f.label :date_field %>
<%= f.text :date_field %>
<%= f.label :time_field %>
<%= f.text :time_field %>

Your database would still keep a field which I'll call full_date

Now in your model you would have to define the above 2 fields as follows.

def date_field  # What this returns will be what is shown in the field
  full_date.strftime("%m-%d'%y") if full_date.present?
end 

def time_field
  full_date.strftime("%I:%M%p") if full_date.present?
end

def time_field=(time)
  full_date = DateTime.parse("#{date_field} #{time_field})
end

Since it looks like you are using Rails 4, you'll have to permit date_field and time_field in your strong parameters.



回答4:

Alternatively, I set up a solution in the controller that does all the datetime conversions before the object gets created, because changing the data in the model impacted all my tests and validations. "Event" is the object I'm creating here with the datetime values being assigned to it.

#In the controller:
def convert_to_datetime_and_assign(event, params)
  date_field = Date.parse(params[:date_field]).strftime("%Y-%m-%d")
  start_time_field = Time.parse(params[:start_time_field]).strftime("%H:%M:%S")
  end_time_field = Time.parse(params[:end_time_field]).strftime("%H:%M:%S")
  event.start_time = DateTime.parse("#{date_field} #{start_time_field}")
  event.end_time = DateTime.parse("#{date_field} #{end_time_field}")
  event
rescue ArgumentError
  event.errors.add(:start_time, :invalid, message: "Date or time was invalid")
  event
end

in the create and update controller methods I called the method above:

@event = convert_to_datetime_and_assign(@event, event_params)

I added fields for date_field, start_time_field and end_time_field in my forms for creating/updating "events". And in the model I added an accessor to be able to access those values.

attr_accessor :date_field, :start_time_field, :end_time_field