I have a form that uses jQuery datepicker for picking just the month and year.
$(".date-input").datepicker({
format: 'mm-yyyy',
startView: "months",
minViewMode: "months"
});
I have postgreSQL db on the backend with datetime column storing this date which apparently needs date in full format 'dd-mm-yyyy' to save it. While POSTing the form to my controller, this is what i came across:
Experience.update(date_from: '02-2016') updates the table with nil value for date_from column
So, the question is how can i append '01-' into this date_from param that i receive from POST request on the controller as Strong parameters.
Use the ISO8601 Date format (2016-10-21).
$(".date-input").datepicker({
format: 'yyyy-mm-dd',
startView: "months",
minViewMode: "months"
});
You can parse it with Date.iso8601(str) or DateTime.iso8601(str).
I would create a model level setter which handles this:
class Experience < ActiveRecord::Base # or ApplicationRecord
def date_from=(val)
super( val.is_a?(String) ? Date.iso8601(val) : val )
end
end
This means you don't have to worry about the params on the controller level. Just whitelist it like any other scalar value and let your model do the work.
I would just store the values as a DATE type (as the first day of the month). Thats gives you the maximum flexibility when comes to DB querying and also a sane format when you pull records so that you don't have to parse a string again.
This has nothing to do with strong parameters. When you get the data from the backend using strong params, you can access the information in the controller. You can try something like this -
def create
datestr = date_params[:datestr]
date = Date.strptime("01-".concat(datestr), "%d-%m-%Y")
Experience.update(date_from: date)
end
private
def date_params
params.require(:dateinfo).permit(:datestr)
end
Related
I am trying to POST a json object to the rails app.
One of the field of this object is a a pretty much timestamp. It's saved in a 'timestamp' member in a model.
Rails handles well a lot of datetime formats represented by a string (as example I can send "December 24, 2015 at 9:46:24 PM PST" and it will work).
However, rails will reject an object if I try to send an integer (unix time) timestamp.
Is this standard behavior (or am I missing something)
We can easily do this by defining like a proxy attribute in your model
attr_accessible :integer_timestamp
def integer_timestamp
timestamp.to_time.to_i
end
def integer_timestamp=(value)
self.timestamp = value.blank? ? nil : Time.at(value.to_i)
end
You need to convert your epoch time explicitly.
It can be:
class MyModel < ActiveRecord::Base
def my_attr_name=(timestamp)
dt = begin
timestamp.to_datetime
rescue ArgumentError
DateTime.strptime(timestamp,'%s')
end
write_attribute :my_attr_name, dt
end
end
I have a table call periodo with the attribute hour. i pass my time param in this way
hour = Time.parse( splitLine[1] ) #where splitLine[1] is my time but in string
periodo = Periodo.new(:hour => hour.strftime("%H:%M"))
periodo.save
but active record save the records in this way hour: "2000-01-01 07:00:00" , I already set the format in /config/initializers/time_formats.rb
Time::DATE_FORMATS[:default] = "%H:%M"
Date::DATE_FORMATS[:default] = "%H:%M"
and in en.yml too
en:
hello: "Hello world"
time:
formats:
default: "%H:%M"
date:
formats:
default: "%H:%M"
but my records are still saving the year and month :( what i have to do to save just the hour and minutes ???
greetings
Date formats are only valid within your application, not within the database - they are only responsible for the way time objects are displayed to your users and will not affect the way data is stored.
Unfortunately, there is no such a concept like time only in the database (at least I haven't heard about any, and trust me I did search for it as I need it in my current project)
Simplest solution
However, in many cases it makes sense to store only the time of the event. In current project we decided to store it in format of integer 60 * hour + minutes. This is unfortunately where we stopped in the project. :(
One step further
You can then create a helper class (just a scaffold - more work needed like validations casting etc):
class SimpleTime
attr_accessor :hour, :minute
def initialize(hour, minute)
#hour, #minute = hour, minute
end
def self.parse(integer)
return if integer.blank?
new(integer / 60, integer % 60)
end
def to_i
60 * #hour + #minute
end
end
and then override a setter and getter:
class Model < ActiveRecord::Base
def time
#time ||= SimpleTime.parse(super)
end
def time=(value)
super(value.to_i)
end
end
Further fun
Now there are more things you could do. You can for example write extension simple_time to active_record which will automatically redefine setters and getters for a list of passed attributes. You can even wrap it in a small gem and make it real-world-proof (missing validations, string format parsers, handling _before_type_cast values etc).
You have to do nothing. That is activerecord convention on time storing. So if you want to have automatically parsed time in your model from sql database you have to store it in the way AR does. But if you really want to store only hours and minutes, you should change your scheme and use just string instead of datetime in AR migration. So you can store your time like that. and in the model class you can override the attribute getter like:
def some_time
Time.parse(#some_time)
end
Then you can get parsed time object when you call attribute. But that is a bad way actually.
my controller's action
time = Time.now.strftime("%d-%m-%Y")
# render text: time
u=SectionMst.new( :section_name => params[:section_name], :date_added => time , date_updated => time)
u.save
My modal code is
class SectionMst < ActiveRecord::Base
attr_accessible :date_added, :date_updated, :id, :section_name
end
render text:time is giving correct desired format but saving in %Y-%m-%d format
not able to get WHY??
The default db date format is: %Y-%m-%d %H:%M:%S. You can check this by executing Time::DATE_FORMATS[:db] in your rails console.
You can update this format by defining the format of your choice in an initializer file inside config/initializers/. Example:
# config/initializers/date_time_format.rb
Date::DATE_FORMATS[:db] = "%d-%m-%Y"
Time::DATE_FORMATS[:db] = "%d-%m-%Y %H:%M:%S"
This is the problem of your database, actualy DB support different format of dates, when you send date, after formatting it convert into own way so for avoid that use DB function to convert date. as date_format(date, format)
eg. SELECT ProductName, Price, FORMAT(Now(),'YYYY-MM-DD') AS PerDate
FROM Products
for making that sql query take an help from this link
Format used when converting and saving a date string to database with rails
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.
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.
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.
When using Rails date_select with :prompt => true I see some very strange behavior when submitting the form without all fields selected. Eg.
Submitting the form with January selected but the day and year fields left at the default prompt results in January 1st 0001 getting passed to the model validation. If validation fails and the form is rendered again January is still selected (correctly) but the day is set to 1 (incorrectly). If the form is submitted with just the year selected, both month and day get set to 1.
This is very strange behavior - can anyone give me a workaround?
The problem has to do with multiparameter assignment. Basically you want to store three values into one attribute (ie. written_at). The date_select sends this as { 'written_at(1)' => '2009', 'written_at(2)' => '5', 'written_at(3)' => '27' } to the controller. Active record packs these three values into a string and initializes a new Date object with it.
The problem starts with the fact that Date raises an exception when you try to instantiate it with an invalid date, Date.new(2009, 0, 1) for instance. Rails catches that error and instantiates a Time object instead. The Time class with timezone support in Rails has all kinds of magic to make it not raise with invalid data. This makes your day turn to 1.
During this process active record looses the original value hash because it packed the written_at stuff into an array and tried to create a Date or Time object out of it. This is why the form can't access it anymore using the written_at_before_time_cast method.
The workaround would be to add six methods to your model: written_at_year, written_at_year=, and written_at_year_before_type_cast (for year, month and day). A before_validation filter can reconstruct the date and write it to written_at.
class Event < ActiveRecord::Base
before_validation :reconstruct_written_at
def written_at_year=(year)
#written_at_year_before_type_cast = year
end
def written_at_year
written_at_year_before_type_cast || written_at.year
end
def written_at_year_before_type_cast
#written_at_year_before_type_cast
end
private
def reconstruct_written_at
written_at = Date.new(written_at_year, written_at_month, written_at_day)
rescue ArgumentError
written_at = nil
end
end