Формат даты в Rails в текстовом поле - PullRequest
43 голосов
/ 20 сентября 2009

У меня есть форма рельсов, которая отображает дату в текстовом поле:

<%= form.text_field :check_in_date  %>

Дата отображается как yyyy-mm-dd

Я пытаюсь выяснить, как сделать так, чтобы оно отображалось как mm-dd-yyyy

Я пытался добавить этот конфиг, но он не работал.

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
  :default => '%m/%d/%Y'
)

Ответы [ 14 ]

63 голосов
/ 29 апреля 2011

Простое одноразовое решение состоит в использовании опции :value в вызове text_field вместо добавления чего-либо к ActiveRecord::Base или CoreExtensions.

Например:

<%= f.text_field :some_date, value: @model_instance.some_date.strftime("%d-%m-%Y") %>
38 голосов
/ 13 сентября 2012

Я добавил их к своему 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

11 голосов
/ 04 ноября 2010

Я потратил некоторое время на просмотр кода, и изменение DATE_FORMAT не сработает, потому что Rails никогда не запрашивает формат даты. Решение Феликса о настройке: значение работает, но кажется обременительным: зачем мне вручную устанавливать значение текстовых полей для конкретной даты? Так что это кодовый запах, и я хотел что-то лучше.

Вот мое короткое решение, но потом я объясню это немного, потому что я надеюсь, что кто-то еще напишет что-то лучше:

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, он сначала просматривает хэш параметров (именно поэтому решение Феликса работает), но если его там нет, он вызовет 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 не работает само по себе. Таким образом, создание динамического метода, описанного выше, создает методы для всех объектов даты, которые фактически выполняют приведение типов, позволяя вам форматировать дату, используя ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS[:default].

Это выглядит немного грязно, потому что цель '_before_type_cast' состоит в том, чтобы избежать трансляции типов, и это одна из тех обезьян, которые могут быть "драконами". Но все же, из того, что я могу сказать, это похоже на лучшее из того, что вокруг. Может быть, кто-то еще может немного покопаться и найти лучшее решение?

9 голосов
/ 13 сентября 2012

Немного расширив ответ Камиля: добавьте следующий метод в 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, и дата должна быть в определенном формате, чтобы дата по умолчанию была установлена ​​правильно. Спасибо за ответ, Камил!

7 голосов
/ 16 декабря 2009

конфиг / Инициализаторы / 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 %>
5 голосов
/ 09 июля 2015

Это одно место использования, в вашей форме:

<%= f.text_field :date3, value: ( l @model.date3 if @model.date3? ) %>

В местах en.yml (или es.yml)

en:
  date:
    formats:
      default: "%d/%m/%Y"
2 голосов
/ 20 июля 2017

Есть две части, чтобы заставить это работать, и третья часть, чтобы заставить все это работать хорошо. Если вы просто хотите скопировать вставку, продолжайте и прокрутите до конца.

Часть 1: Получение Date для форматирования по желанию

В ActiveSupport имеется базовых расширений для Date на , но ни одно из них не создает новый класс дат. Если вы работаете с Date, то есть то, что Rails возвращает, когда в вашей базе данных есть столбец date, метод преобразования этой даты в строку - Date#to_formatted_s. Расширения ActiveSupport добавляют этот метод, и они псевдоним метода to_s, поэтому, когда вы вызываете Date#to_s (вероятно, неявно), вы получаете Date.to_formatted_s.

Существует два способа изменить формат, используемый to_formatted_s:

  1. Передайте имя формата, который вы хотели бы использовать (этот аргумент будет использоваться для поиска формата в Date::DATE_FORMATS).
  2. Изменить формат, на который отображается :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.

Если он не точно соответствует этому формату (например, он разделен косой чертой), он прибегает к использованию Date._parse в Ruby, который немного мягче, но все еще ожидает увидеть дни, месяцы, затем годы: dd/mm/yyyy.

Чтобы Rails анализировал даты в формате, который вы собираете, вам просто нужно заменить метод cast_value на ваш собственный. Вы можете monkeypatch ActiveRecord::Types::Date, но не намного сложнее бросить свой собственный тип, унаследованный от него.

Мне нравится использовать Chronic для разбора строк дат из пользовательского ввода, потому что он терпит больше дерьма, например, "Tomorrow" или "Jan 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 }

Это дает нам список атрибутов, которые мы хотим использовать для использования 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 - Что копировать вставить

Если вас не интересует как и вы просто хотите, чтобы это работало, вы можете:

  1. Добавьте gem "chronic" к вашему Gemfile и bundle install
  2. Убедитесь, что ваши модели наследуются от ApplicationRecord
  3. Скопируйте код ниже файла в config/initializers/date_formatting.rb
  4. Перезапуск
  5. Все должно теперь просто работать ™

Вот код для копирования:

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
1 голос
/ 25 декабря 2017

Мой предпочтительный способ обработки это с виртуальными атрибутами . Существует короткий 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, и при отправке этой формы модель будет соответствующим образом обновлена.

1 голос
/ 23 октября 2016

Итак, я сделал что-то подобное в модели.

def start_date
  super.strftime "%Y/%m/%d %H:%M"
end

Скажем, у вас есть столбец с именем start_date в модели, и вы хотите отобразить другой формат в форме, просто перезапишите его значение в модели.

И ничего не нужно менять в форме.

<%= f.text_field :start_date, class: 'form-control' %>
1 голос
/ 20 сентября 2009

Перейдите в файл environment.rb и добавьте следующее:

ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(
  :default => '%m-%d-%Y' ) 

Проверьте официальную документацию, если вам хочется больше читать :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...