ruby on rails - tag - Formato de fecha de carriles en un text_field
rails select_tag (13)
Tengo un formulario de rieles que muestra una fecha en un text_field:
<%= form.text_field :check_in_date %>
La fecha se representa como yyyy-mm-dd
Estoy intentando descubrir cómo mostrarlo como mm-dd-yyyy
Intenté agregar esta configuración, pero no funcionó.
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:default => ''%m/%d/%Y''
)
config / initializers / date_format.rb
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default] = ''%m/%d/%Y''
ver
<%= form.text_field :check_in_date, value: model.check_in_date.to_s %>
Entonces hice algo como esto en el modelo.
def start_date
super.strftime "%Y/%m/%d %H:%M"
end
Supongamos que tiene una columna llamada start_date
en el modelo y desea mostrar otro formato en el formulario, simplemente sobrescriba su valor en el modelo.
Y nada necesita ser cambiado en la forma.
<%= f.text_field :start_date, class: ''form-control'' %>
Esta ubicación de un solo uso, en tu forma:
<%= f.text_field :date3, value: ( l @model.date3 if @model.date3? ) %>
En ubicaciones en.yml (o es.yml)
en:
date:
formats:
default: "%d/%m/%Y"
Expandiendo la respuesta de Kamil un poco: agregue el siguiente método a application_helper.rb:
def date_mdY(date)
if date.nil?
""
else
date.strftime("%m-%d-%Y")
end
end
Entonces puedes modificar la respuesta de Kamil levemente:
<%= f.text_field :some_date, :value => date_mdY(@model_instance.some_date) %>
Entonces puedes usar este método de ayuda en cualquier otra vista.
Estoy usando jQuery datepicker y la fecha necesaria para estar en un formato particular para que la fecha predeterminada se establezca correctamente. Gracias por la respuesta Kamil!
Hay dos partes para hacer que esto funcione, y una tercera parte para que todo funcione bien. Si solo quiere copiar pegar, continúe y avance hasta el final.
Parte 1: Cómo obtener la Date
para formatearla como desee
Hay un montón de extensiones principales para Fecha en ActiveSupport , pero ninguna de ellas crea una nueva clase de fecha. Si está trabajando con Date
, que es lo que Rails devuelve cuando tiene una columna de date
en su base de datos, el método para convertir esa fecha en una cadena es Date#to_formatted_s
. Las extensiones de ActiveSupport
agregan este método y alias el método to_s
, por lo que cuando llama a Date#to_s
(probablemente implícitamente), obtiene Date.to_formatted_s
.
Hay dos formas de cambiar el formato utilizado por to_formatted_s
:
- Pase el nombre del formato que desea utilizar (esta arg se usará para buscar el formato en
Date::DATE_FORMATS
). Cambie el formato al que se asigna
:default
mapa:default
. Comoto_formatted_s
establece un valor predeterminado de:default
, cuando llama aDate#to_s
sin pasar un argumento, obtiene el formato especificado porDate::DATE_FORMATS[:default]
. Puede anular el valor predeterminado para toda la aplicación de esta manera:Date::DATE_FORMATS[:default] = "%m/%d/%Y"
Tengo este conjunto en un inicializador como config/initializers/date_formats.rb
. Es tosco y no admite cambios con sus localizaciones, pero obtiene la Date#to_s
para devolver el formato deseado sin ningún parche mono o convirtiendo explícitamente sus fechas a cadenas y pasando un argumento.
Parte 2: Obtener las fechas ingresadas por el usuario convertidas en objetos Date
Por defecto, su instancia de ActiveRecord
lanzará columnas de fecha usando el viejo ISO 8601 que se ve así: yyyy-mm-dd
.
Si no coincide exactamente con ese formato (por ejemplo, está delimitado por Date._parse ), se recurre al uso de Ruby''s Date._parse , que es un poco más indulgente, pero aún espera ver días, meses y años: dd/mm/yyyy
Para que Rails analice las fechas en el formato que las está recopilando, solo necesita reemplazar el método cast_value
por uno propio. Puede parchear mono ActiveRecord::Types::Date
, pero no es mucho más difícil lanzar su propio tipo heredando de él.
Me gusta usar Chronic para analizar las cadenas de fecha de la entrada del usuario porque aguanta más mierda, como "Mañana" o "Jan 1 99", o incluso comentarios como "5 / 6-99" (6 de mayo de 1999). Como Chronic devuelve una instancia de Time
y estamos anulando el método cast_value
suministrado, necesitamos llamar a to_date
en él.
class EasyDate < ActiveRecord::Type::Date
def cast_value(value)
default = super
parsed = Chronic.parse(value)&.to_date if value.is_a?(String)
parsed || default
end
end
Ahora solo tenemos que decirle a ActiveRecord que use EasyDate
lugar de ActiveRecord::Type::Date
:
class Pony < ApplicationRecord
attribute :birth_date, EasyDate.new
end
Eso funciona, pero si eres como yo, quieres tomar un montón más de trabajo en este momento para que puedas ahorrar 3 segundos en el futuro. ¿Qué sucede si podemos decirle a ActiveRecord que siempre use EasyDate para las columnas que son solo fechas? Podemos.
Parte 3: Haz que los Rails manejen esto automáticamente
ActiveRecord le brinda todo tipo de información sobre su modelo que utiliza para manejar toda la "magia" detrás de las escenas. Uno de los métodos nos da attribute_types
. Si lo ejecuta en su consola, recupera el vómito; es un poco aterrador, y simplemente dice "oh, no sé". Si cavas en ese vómito como una especie de bicho raro, es solo un hash que se ve así:
{ column_name: instance_of_a_type_object, ... }
No es algo que da miedo, pero como estamos comenzando a pinchar en las partes internas, la salida inspeccionada de estos instance_of_a_type_object
puede terminar siendo obtusa y "cerrada". Me gusta esto:
#<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Money:0x007ff974d62688
Afortunadamente, realmente solo nos importa uno: ActiveRecord::Types::Date
, por lo que podemos restringirlo:
date_attributes = attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
Eso nos da una lista de los atributos que queremos reciclar para usar EasyDate
. Ahora solo tenemos que decirle a ActiveRecord que use EasyDate en esos atributos como hicimos anteriormente:
date_attributes.each do |attr_name, _type|
attribute attr_name, EasyDate.new
end
Y eso básicamente lo hace, pero tenemos que unirlo todo. Si está utilizando ApplicationRecord
, puede colocarlo en este y dirigirse a las carreras:
def inherited(klass)
super
klass.attribute_types.select { |_, type| ActiveRecord::Type::Date === type }.each do |name, _type|
klass.attribute name, EasyDate.new
end
end
Si no quiere "contaminar" ApplicationRecord
, puede hacer lo que hice y crear un módulo y extender ApplicationRecord
con él. Si no está utilizando ApplicationRecord
, deberá crear el módulo y extender ActiveRecord::Base
.
module FixDateFormatting
# the above `inherited` method here
end
# Either this...
class ApplicationRecord < ActiveRecord::Base
extend FixDateFormatting
end
# ...or this:
ActiveRecord::Base.extend(FixDateFormatting)
TL; DR - Qué copiar pegar
Si no le preocupa el cómo y solo quiere que esto funcione, puede:
- Agrega
gem "chronic"
a tu Gemfile ybundle install
- Asegúrese de que sus modelos hereden de
ApplicationRecord
- Copie el código debajo de un archivo en
config/initializers/date_formatting.rb
- Reiniciar
- Todo debería ahora Just Work ™
Aquí está el código para copiar:
Date::DATE_FORMATS[:default] = "%m/%d/%Y"
module FixDateFormatting
class EasyDate < ActiveRecord::Type::Date
def cast_value(value)
default = super
parsed = Chronic.parse(value)&.to_date if value.is_a?(String)
parsed || default
end
end
def inherited(subclass)
super
date_attributes = subclass.attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
date_attributes.each do |name, _type|
subclass.attribute name, EasyDate.new
end
end
end
Rails.application.config.to_prepare do
ApplicationRecord.extend(FixDateFormatting)
end
La solución simple de una sola vez es usar :value
opción de :value
dentro de la llamada text_field
lugar de agregar algo a ActiveRecord::Base
o CoreExtensions
.
Por ejemplo:
<%= f.text_field :some_date, value: @model_instance.some_date.strftime("%d-%m-%Y") %>
Los agregué a mis initializers/time_formats.rb
y funciona muy bien:
# Default format for displaying dates and times
Date::DATE_FORMATS[:default] = "%m/%d/%Y"
Time::DATE_FORMATS[:default] = "%m/%d/%Y"
Usando Ruby 1.9.3 y Rails 3.2.x
Mi forma preferida de manejar esto es con atributos virtuales . Hay un RailsCast corto sobre el tema (3m), pero el resultado es que los atributos virtuales le permitirán usar un formato no estándar al mostrar los atributos del modelo en un formulario, o viceversa ( es decir, guardar datos del formulario en un modelo).
Básicamente, en lugar de crear un campo de formulario para el atributo check_in_date
, puede definir un "atributo virtual" en el modelo:
class Reservation
def check_in_date_string
check_in_date.strftime(''%m-%d-%Y'')
end
def check_in_date_string=(date_string)
self.check_in_date = Date.strptime(date_string, ''%m-%d-%Y'')
end
end
Ahora puede crear campos de formulario que corresponden a check_in_date_string
lugar de check_in_date
:
<%= f.text_field :check_in_date_string %>
¡Eso es! No es necesario especificar explícitamente una opción de value
, y cuando se envía este formulario, el modelo se actualizará adecuadamente.
Para las personas interesadas en formatear en una ubicación específica y no en todo el proyecto, se recomiendan varias respuestas para comprobar si son nulas y quiero agregar que usar try with strftime ayudará a acortarlo .
<%= f.fields_for :assets_depreciations do |asset| %> <tr> <td> <%= asset.text_field :depreciation_date, value: asset.object.depreciation_date.try(:strftime, "%d-%m-%Y") %> </td> </tr> <% end %>
Pasé un tiempo mirando el código, y cambiar el DATE_FORMAT no funcionará porque Rails nunca solicita el formato de fecha. La solución de ajuste de Felix: el valor funciona, pero se siente incómodo: ¿por qué debería tener que establecer manualmente el valor de los campos de texto con fecha específica? Así que este es un código, y yo quería algo mejor.
Esta es mi pequeña solución, pero luego la explicaré un poco porque espero que alguien escriba algo mejor:
MyModel < ActiveRecord::Base
# ...
self.columns.each do |column|
if column.type == :date
define_method "#{column.name}_before_type_cast" do
self.send(column.name).to_s
end
end
end
end
Una explicación algo más larga: cuando un campo de texto está configurando el atributo de valor, primero se verá en el hash de opciones (que es la razón por la solución de Felix), pero si no está allí, llamará a form.object.check_in_date_before_type_cast
, que (después de alguna magia method_missing) llamará a form.object.attributes_before_type_cast[''check_in_date'']
, que buscará ''check_in_date'' en el hash @attributes dentro de form.object. Supongo que el hash @attributes obtiene la representación de cadena directa de MySQL (que sigue el formato ''aaaa-mm-dd'') antes de que quede envuelto en un objeto Ruby, por lo que simplemente establecer el DATE_FORMAT no funciona por sí mismo . Por lo tanto, la creación de métodos dinámicos anterior crea métodos para todos los objetos de fecha que realmente realizan el encasillado, lo que le permite formatear la fecha usando ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default]
.
Esto se siente un poco sucio porque el propósito de ''_forefore_type_cast'' es evitar un encasillamiento, y este es uno de esos monopatches de "ser dragones". Pero aún así, por lo que puedo decir, se siente como lo mejor de lo que hay alrededor. ¿Tal vez alguien más puede hacer un poco más de excavación y encontrar una mejor solución?
Si puede establecer el valor manualmente, puede usar created_at.strftime ("% m-% d-% Y") http://snippets.dzone.com/tag/strftime .
Tuve problemas similares con los atributos / campos de tiempo. Entonces uno puede seguir esto:
http://railscasts.com/episodes/32-time-in-text-field
Y funciona bastante bien.
Pero busqué y encontré otra solución interesante. Tipo de monopata feo, pero en algunos casos podría ser más útil que el de Railscasts.
Entonces tengo un modelo con una columna llamada time
(ofc tiene un tipo de hora). Aquí está mi solución:
after_initialize :init
private
def init
unless time.nil?
@attributes[''time''] = I18n.l(time, format: :short)
end
end
Como puede ver, formato la variable que va a devolver el método time_before_type_cast
. Así que he formateado correctamente el tiempo en mi entrada de texto (representada por FormBuilder
), pero si el usuario pone algo incorrecto como 10;23
todavía tengo esto y en la próxima solicitud será procesado por FormBuilder::text_field
. Entonces el usuario tiene la oportunidad de arreglar su miserable error.
Vaya a su archivo environment.rb y agregue lo siguiente:
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:default => ''%m-%d-%Y'' )
Consulte la documentación official si desea leer más :)