Как правильно отобразить ошибку проверки, если мой формат даты неверен в Rails?

Я использую Rails 4.2.7. Я хотел бы выдать ошибку проверки, если пользователь не вводит свое поле даты рождения в правильном формате, поэтому у меня есть

  def update
    @user = current_user
    begin
      @user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
    rescue ArgumentError => ex
    end
    if @user.update_attributes(user_params)

и у меня есть это на мой взгляд

      <%= f.text_field :dob, :value => (f.object.dob.strftime('%m/%d/%Y') if f.object.dob), :size => "20", :class => 'textField', placeholder: 'MM/DD/YYYY' %>
      <% if @user.errors[:dob] %><%= @user.errors[:dob] %><% end %>

Однако, даже если кто-то вводит дату, например "01-01/1985", вышеприведенное не возвращает ошибку проверки представлению. Что мне нужно сделать, чтобы получить правильную ошибку проверки?

Редактировать: в одном из ответов, я пытался

@user = current_user
begin
  @user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
rescue ArgumentError => ex
  puts "Setting error." 
  @user.errors.add(:dob, 'The birth date is not in the right format.')
end
if @user.update_attributes(user_params)
  last_page_visited = session[:last_page_visited]
  if !last_page_visited.nil?
    session.delete(:last_page_visited)
  else
    flash[:success] = "Profile updated"
  end
  redirect_to !last_page_visited.nil? ? last_page_visited : url_for(:controller => 'races', :action => 'index') and return
else
  render 'edit'
end

И даже несмотря на то, что я вижу вызываемую ветвь "спасения", я не обращаюсь к своему блоку "render 'edit".

5 ответов

Решение

Призвание update_attributes очищает ошибки, которые вы установили в rescue, Вы должны проверить на наличие ошибок, а если их нет, то продолжить, примерно так:

@user = current_user
begin
  @user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
rescue ArgumentError => ex
  puts "Setting error." 
  @user.errors.add(:dob, 'The birth date is not in the right format.')
end
if !@user.errors.any? && @user.update_attributes(user_params)
  last_page_visited = session[:last_page_visited]
  if !last_page_visited.nil?
    session.delete(:last_page_visited)
  else
    flash[:success] = "Profile updated"
  end
  redirect_to !last_page_visited.nil? ? last_page_visited : url_for(:controller => 'races', :action => 'index') and return
end

render 'edit'

С вами redirect_to ... and return Вы можете закрыть условное выражение и, если вы сделаете это так далеко, просто отобразить страницу редактирования.

Вы также можете добавить простую проверку вашей пользовательской модели:

validates :dob, presence: true

Это всегда будет неудачным, если dob не может быть установлен по какой-то другой, непредвиденной причине.

Чтобы получить введенную пользователем строку для заполнения поля при повторной загрузке, вы можете добавить средство доступа к пользовательской модели для:dob_string

attr_accessor :dob_string

def dob_string
  dob.to_s
  @dob_string || dob.strftime('%m/%d/%Y')
end

def dob_string=(dob_s)
  @dob_string = dob_s
  date = Date.strptime(dob_s, '%m/%d/%Y')
  self.dob = date
rescue ArgumentError
  puts "DOB format error"
  errors.add(:dob, 'The birth date is not in the correct format')
end

Затем измените форму, чтобы установить:dob_string

<%= form_for @user do |f| %>
  <%= f.text_field :dob_string, :value => f.object.dob_string , :size => "20", :class => 'textField', placeholder: 'MM/DD/YYYY' %>
  <% if @user.errors[:dob] %><%= @user.errors[:dob] %><% end %>
  <%= f.submit %>
<% end %>

И обновите контроллер, чтобы установить dob_string:

def update
  @user = User.first
  begin
    #@user.dob = Date.strptime(params[:user][:dob], '%m/%d/%Y')
    @user.dob_string = user_params[:dob_string]
  end
  if ! @user.errors.any? && @user.update_attributes(user_params)
    redirect_to url_for(:controller => 'users', :action => 'show') and return
  end
  render 'edit'
end

def user_params
  params.require(:user).permit(:name, :dob_string)
end

Запуск исключения ничего не добавляет к errors список. Если вы просто хотите немного подправить этот код, вы сможете позвонить errors.add внутри rescue блок. Что-то вроде @user.errors.add(:dob, 'some message here'),

Имейте в виду, что это будет проверять только дату рождения при использовании этого метода контроллера. Если вы хотите проверять дату рождения при каждом сохранении пользователя, вам нужно явно добавить проверку в модель. Вы можете написать свой собственный класс или метод проверки, а также есть некоторые гемы, которые добавляют проверку даты.

Я бы добавил правило проверки в модель. Подобно:

validates_format_of :my_date, with: /\A\d{2}\/\d{2}\/\d{4}\z/, message: 'Invalid format'

Попробуйте добавить правило проверки в модель.

  validate :validate_date

  def validate_date
    begin
      self.dob = Date.parse(self.dob)
    rescue
      errors.add(:dob, 'Date does not exists. Please insert valid date')
    end
  end

и в вашем контроллере обновите свой код

...
@user.update_attributes(user_params)
if @user.save
....

Я думаю, что это тот случай, когда активная модель сияет. Мне нравится использовать его для реализации объектов формы без дополнительных зависимостей. Я не знаю точных деталей вашей ситуации, но ниже я вставил небольшую демонстрацию, которую вы сможете адаптировать к вашему делу.

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

Шаг 1: Магазин dob в users

Ваш users таблица должна иметь столбец dob типа date, Например:

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name, null: false
      t.date :dob, null: false
    end
  end
end

Не добавляйте в свою модель ничего необычного:

class User < ActiveRecord::Base
end

Шаг 2: Добавить Profile

Поместите следующее в app/models/profile.rb, Смотрите комментарии для объяснений.

class Profile
  # This is an ActiveModel model.
  include ActiveModel::Model

  # Define accessors for fields you want to use in your HTML form.
  attr_accessor :dob_string

  # Use the validatiors API to define the validators you want.
  validates :dob_string, presence: true
  validate :dob_format

  # We store the format in a constant to keep the code DRY.
  DOB_FORMAT = '%m/%d/%Y'

  # We store the user this form pertains to and initialize the DOB string
  # to the one based on the DOB of the user.
  def initialize(user)
    # We *require* the user to be persisted to the database.
    fail unless user.persisted?

    @user = user
    @dob_string = user.dob.strftime(DOB_FORMAT)
  end

  # This method triggers validations and updates the user if validations are
  # good.
  def update(params)
    # First, update the model fields based on the params.
    @dob_string = params[:dob_string]

    # Second, trigger validations and quit if they fail.
    return nil if invalid?

    # Third, update the model if validations are good.
    @user.update!(dob: dob)
  end

  # #id and #persisted? are required to make form_for submit the form to
  # #update instead of #create.
  def id
    @user.id
  end

  def persisted?
    true
  end

  private

  # Parse dob_string and store the result in @dob.
  def dob
    @dob ||= Date.strptime(dob_string, DOB_FORMAT)
  end

  # This is our custom validator that calls the method above to parse dob_string
  # provided via the params to #update.
  def dob_format
    dob
  rescue ArgumentError
    errors[:dob] << "is not a valid date of the form mm/dd/yyyy"
  end
end

Шаг 3: Используйте форму в контроллере

использование Profile в ProfilesController:

class ProfilesController < ApplicationController
  def edit
    # Ensure @profile is set.
    profile
  end

  def update
    # Update the profile with data sent via params[:profile].
    unless profile.update(params[:profile])
      # If the update isn't successful display the edit form again.
      render 'edit'
      return
    end

    # If the update is successful redirect anywhere you want (I chose the
    # profile form for demonstration purposes).
    redirect_to edit_profile_path(profile)
  end

  private

  def profile
    @profile ||= Profile.new(user)
  end

  def user
    @user ||= User.find(params[:id])
  end
end

Шаг 4: визуализируйте форму с form_for

В app/views/profiles/edit.html.erb использование form_for для отображения формы:

<%= form_for(@form) do |f| %>
  <%= f.label :dob_string, 'Date of birth:' %>
  <%= f.text_field :dob_string %>
  <%= f.submit 'Update' %>
<% end %>

Шаг 5: Добавьте маршрутизацию

Имейте в виду, чтобы добавить маршрутизацию к config/routes.rb:

Rails.application.routes.draw do
  resources :profiles
end

Это оно!

Другие вопросы по тегам