Формат даты Rails в текстовом поле
У меня есть форма рельсов, которая отображает дату в текстовом поле:
<%= form.text_field :check_in_date %>
Дата отображается как yyyy-mm-dd
Я пытаюсь понять, как его отображать как mm-dd-yyyy
Я попытался добавить эту конфигурацию, но она не сработала.
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:default => '%m/%d/%Y'
)
Ответы
Ответ 1
Простым одноразовым решением является использование опции :value
в вызове text_field
вместо добавления чего-либо к ActiveRecord::Base
или CoreExtensions
.
Например:
<%= f.text_field :some_date, value: @model_instance.some_date.strftime("%d-%m-%Y") %>
Ответ 2
Я добавил их в свой initializers/time_formats.rb
, и он отлично работает:
# Default format for displaying dates and times
Date::DATE_FORMATS[:default] = "%m/%d/%Y"
Time::DATE_FORMATS[:default] = "%m/%d/%Y"
Использование Ruby 1.9.3 и Rails 3.2.x
Ответ 3
Я потратил некоторое время на просмотр кода, и изменение DATE_FORMAT не будет работать, потому что Rails никогда не запрашивает формат даты. Решение Felix настройки: значение действительно работает, но кажется громоздким: почему мне нужно вручную устанавливать значение текстовых полей, специфичных для даты? Так что это код, и я хотел что-то лучше.
Вот мое короткое решение, но потом я объясню это немного, потому что я надеюсь, что кто-то еще напишет что-то лучше:
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
Несколько более подробное объяснение:
Когда текстовое поле задает атрибут value, он сначала рассмотрит хэш-параметры (поэтому работает решение Felix), но если он там не будет, он вызовет form.object.check_in_date_before_type_cast
, который (после некоторой магии method_missing) вызовет form.object.attributes_before_type_cast['check_in_date']
, который будет искать 'check_in_date' в хэш файле @attributes внутри form.object. Я предполагаю, что хеш @attributes получает прямое строковое представление MySQL (которое следует за формам "yyyy-mm-dd" ), прежде чем оно будет завернуто в объект Ruby, поэтому просто установка DATE_FORMAT не работает сама по себе, Таким образом, создание динамического метода выше создает методы для всех объектов дат, которые фактически выполняют typecast, что позволяет вам форматировать дату с помощью ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default]
.
Это чувствует себя немного грязным, потому что цель "_before_type_cast" заключается в том, чтобы избежать приведения типов, и это одна из тех обезьян. Но все же, из того, что я могу сказать, это похоже на лучшее из того, что вокруг. Может быть, кто-то еще может немного поработать и найти лучшее решение?
Ответ 4
Расширение Kamil немного: добавьте следующий метод в application_helper.rb:
def date_mdY(date)
if date.nil?
""
else
date.strftime("%m-%d-%Y")
end
end
Затем вы можете немного изменить ответ Камиля:
<%= f.text_field :some_date, :value => date_mdY(@model_instance.some_date) %>
Затем вы можете использовать этот вспомогательный метод в любом другом представлении.
Я использую jQuery datepicker и дату, необходимую для того, чтобы быть в определенном формате, чтобы установить дату по умолчанию правильно. Спасибо за ответ Камиль!
Ответ 5
конфиг/Инициализаторы/date_format.rb
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default] = '%m/%d/%Y'
Вид
<%= form.text_field :check_in_date, value: model.check_in_date.to_s %>
Ответ 6
Это одно место использования в вашей форме:
<%= f.text_field :date3, value: ( l @model.date3 if @model.date3? ) %>
В местах en.yml(или es.yml)
en:
date:
formats:
default: "%d/%m/%Y"
Ответ 7
Перейдите в файл environment.rb и добавьте следующее:
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
:default => '%m-%d-%Y' )
Проверьте официальную документацию, если вам нравится читать больше:)
Ответ 8
Итак, я сделал что-то подобное в модели.
def start_date
super.strftime "%Y/%m/%d %H:%M"
end
Скажем, у вас есть столбец с именем start_date
в модели и хотите отобразить другой формат в форме, просто перепишите его значение в модели.
И ничего не нужно изменять в форме.
<%= f.text_field :start_date, class: 'form-control' %>
Ответ 9
Если вы можете установить значение вручную, вы можете использовать created_at.strftime( "% m-% d-% Y" ) http://snippets.dzone.com/tag/strftime.
Ответ 10
У меня были аналогичные проблемы с атрибутами времени/полями. Таким образом, можно следовать этому:
http://railscasts.com/episodes/32-time-in-text-field
И это работает очень хорошо.
Но я вошел и нашел еще одно интересное решение. Вид уродливого monkeypatch, но в некоторых случаях это может быть более полезным, чем одно из railscasts.
Итак, у меня есть модель с столбцом с именем time
(ofc имеет тип времени). Вот мое решение:
after_initialize :init
private
def init
unless time.nil?
@attributes['time'] = I18n.l(time, format: :short)
end
end
Как вы можете видеть, я форматирую переменную, которая будет возвращена методом time_before_type_cast
. Поэтому я правильно отформатировал время в своем текстовом вводе (отображается FormBuilder
), но если пользователь помещает что-то не так, как 10;23
, у меня все еще есть это, и в следующем запросе он будет отображаться FormBuilder::text_field
. Таким образом, у пользователя есть возможность исправить свою несчастную ошибку.
Ответ 11
Есть две части для этой работы, а третья часть - для того, чтобы все работало хорошо. Если вы просто хотите скопировать пасту, продолжайте и прокрутите до конца.
Часть 1: Получение Date
для форматирования по желанию
Есть куча основных расширений для Date в ActiveSupport, но ни один из них не создает новый класс дат. Если вы работаете с Date
, это то, что возвращает Rails, когда у вас есть столбец Date
в вашей базе данных, метод преобразования этой даты в строку Date#to_formatted_s
. Расширения ActiveSupport
добавляют этот метод, и они используют метод to_s
для него, поэтому, когда вы вызываете Date#to_s
(возможно, неявно), вы получаете Date.to_formatted_s
.
Существует два способа изменить формат, используемый to_formatted_s
:
- Передайте имя формата, который вы хотели бы использовать (этот аргумент будет использоваться для поиска формата в
Date::DATE_FORMATS
).
-
Измените формат, который соответствует :default
. Поскольку to_formatted_s
устанавливает значение по умолчанию :default
, когда вы вызываете Date#to_s
без передачи аргумента, вы получаете формат, указанный Date::DATE_FORMATS[:default]
. Вы можете переопределить значение по умолчанию на основе всего приложения следующим образом:
Date::DATE_FORMATS[:default] = "%m/%d/%Y"
У меня этот набор в инициализаторе, как config/initializers/date_formats.rb
. Он груб и не поддерживает изменение с вашими локализациями, но получает Date#to_s
, чтобы вернуть желаемый формат без каких-либо попыток обезьян или явно преобразовать ваши даты в строки и передать аргумент.
Часть 2: Получение введенных пользователем дат, преобразованных в объекты Date
По умолчанию ваш экземпляр ActiveRecord
будет указать столбцы даты, используя хороший код ISO 8601, который выглядит следующим образом: yyyy-mm-dd
.
Если он точно не соответствует этому формату (например, он сокращен слэшем), он возвращается к использованию Ruby Date._parse, который немного более снисходителен, но все же ожидает увидеть дни, месяцы, затем годы: dd/mm/yyyy
.
Чтобы заставить Rails анализировать даты в формате, который вы их собираете, вам просто нужно заменить метод cast_value
на свой собственный. Вы можете monkeypatch ActiveRecord::Types::Date
, но не намного сложнее свернуть собственный тип, наследующий от него.
Мне нравится использовать Chronic для синтаксического анализа строк даты с пользовательского ввода, потому что он добавляет больше дерьма, например "Завтра", или "1 января 99", или даже бонкеров, например "5/6-99" (6 мая 1999 г.). Поскольку Chronic возвращает экземпляр Time
, и мы переопределяем поставляемый метод cast_value
, нам нужно называть его to_date
.
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
Теперь нам просто нужно указать ActiveRecord использовать EasyDate
вместо ActiveRecord::Type::Date
:
class Pony < ApplicationRecord
attribute :birth_date, EasyDate.new
end
Это работает, но если вы похожи на меня, вы хотите взять на себя больше работы прямо сейчас, чтобы вы могли сэкономить 3 секунды в будущем. Что, если бы мы могли сказать ActiveRecord, чтобы всегда использовать EasyDate для столбцов, которые являются только датами? Мы можем.
Часть 3: Сделать Rails автоматически обрабатывать
ActiveRecord предоставляет вам всякую информацию о вашей модели, которую он использует для обработки всего "волшебства" за кулисами. Один из методов дает нам attribute_types
. Если вы запустите это в своей консоли, вы получите рвоту - это страшно, и вы просто идете "о, никогда не думай". Если вы копаете в эту рвоту, как какой-то странный, это просто хеш, который выглядит так:
{ column_name: instance_of_a_type_object, ... }
Не страшно, но, поскольку мы начинаем суетиться внутри, проверенный результат этих instance_of_a_type_object
может оказаться тупым и "закрытым". Вот так:
#<ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Money:0x007ff974d62688
К счастью, мы действительно заботимся только об одном: ActiveRecord::Types::Date
, поэтому мы можем сузить его:
date_attributes = attribute_types.select { |_, type| ActiveRecord::Type::Date === type }
Это дает нам список атрибутов, которые мы хотим использовать upcycle для использования EasyDate
. Теперь нам просто нужно сказать ActiveRecord, чтобы использовать EasyDate для таких атрибутов, как мы сделали выше:
date_attributes.each do |attr_name, _type|
attribute attr_name, EasyDate.new
end
И это в основном делает это, но нам нужно склеить все это вместе. Если вы используете ApplicationRecord
, вы можете отбросить это прямо в него и выйти на гонки:
def inherited(klass)
super
klass.attribute_types.select { |_, type| ActiveRecord::Type::Date === type }.each do |name, _type|
klass.attribute name, EasyDate.new
end
end
Если вы не хотите "загрязнять" ApplicationRecord
, вы можете сделать то, что я сделал, и создать модуль и расширить его с помощью ApplicationRecord
. Если вы не используете ApplicationRecord
, вам необходимо создать модуль и расширить 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 - Что скопировать пасту
Если вы не обеспокоены тем, как и просто хотите, чтобы это работало, вы можете:
- Добавьте
gem "chronic"
в свой Gemfile и bundle install
- Убедитесь, что ваши модели наследуются от
ApplicationRecord
- Скопируйте код под файлом
config/initializers/date_formatting.rb
- Restart
- Теперь все должно быть просто Work ™
Здесь код для копирования:
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
Ответ 12
Мой предпочтительный способ справиться с этим - с виртуальными атрибутами. Там короткий RailsCast на тему (3 м), но результатом является то, что виртуальные атрибуты позволят вам использовать нестандартный формат при отображении атрибуты модели в форме или наоборот (т.е. сохранение данных формы для модели).
В принципе, вместо создания поля формы для атрибута check_in_date
, вы можете определить "виртуальный атрибут" в модели:
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
Теперь вы можете создавать поля формы, соответствующие check_in_date_string
, а не check_in_date
:
<%= f.text_field :check_in_date_string %>
Что это! Нет необходимости явно указывать опцию value
, и когда эта форма будет отправлена, модель будет обновлена соответствующим образом.