web-dev-qa-db-fra.com

Comment valider une date dans les rails?

Je souhaite valider une date dans mon modèle dans Ruby sur Rails, toutefois, les valeurs de jour, de mois et d’année sont déjà converties en une date incorrecte au moment où elles atteignent mon modèle.

Par exemple, si j'entre le 31 février 2009 à mon avis, lorsque j'utilise Model.new(params[:model]) dans mon contrôleur, il le convertit en "3 mars 2009", ce que mon modèle considère alors comme une date valide, à savoir: , mais c'est incorrect.

J'aimerais pouvoir faire cette validation dans mon modèle. Y a-t-il un moyen que je peux ou est-ce que je m'y prend totalement mal?

J'ai trouvé ce " Date validation " qui traite du problème mais il n'a jamais été résolu.

89
Megamug

Je suppose que vous utilisez le date_select assistant pour générer les balises pour la date. Une autre façon de procéder consiste à utiliser l’assistant de sélection de formulaire pour les champs jour, mois et année. Comme ceci (l'exemple que j'ai utilisé est le champ de date created_at):

<%= f.select :month, (1..12).to_a, selected: @user.created_at.month %>
<%= f.select :day, (1..31).to_a, selected: @user.created_at.day %>
<%= f.select :year, ((Time.now.year - 20)..Time.now.year).to_a, selected: @user.created_at.year %>

Et dans le modèle, vous validez la date:

attr_accessor :month, :day, :year
validate :validate_created_at

private

def convert_created_at
  begin
    self.created_at = Date.civil(self.year.to_i, self.month.to_i, self.day.to_i)
  rescue ArgumentError
    false
  end
end

def validate_created_at
  errors.add("Created at date", "is invalid.") unless convert_created_at
end

Si vous cherchez une solution de plugin, je vérifierai le plugin validates_timeliness . Cela fonctionne comme ceci (à partir de la page github):

class Person < ActiveRecord::Base
  validates_date :date_of_birth, on_or_before: lambda { Date.current }
  # or
  validates :date_of_birth, timeliness: { on_or_before: lambda { Date.current }, type: :date }
end 

La liste des méthodes de validation disponibles est la suivante:

validates_date     - validate value as date
validates_time     - validate value as time only i.e. '12:20pm'
validates_datetime - validate value as a full date and time
validates          - use the :timeliness key and set the type in the hash.
58
Jack Chu

En utilisant la gemme chronique:

class MyModel < ActiveRecord::Base
  validate :valid_date?

  def valid_date?
    unless Chronic.parse(from_date)
      errors.add(:from_date, "is missing or invalid")
    end
  end

end
20
Roger

Si vous voulez Rails 3 ou Ruby 1.9 1.9, essayez le date_validator gem.].

18
Txus

Active Record vous donne _before_type_cast attributs qui contiennent les données d'attribut brutes avant la conversion de type. Cela peut être utile pour renvoyer des messages d'erreur avec des valeurs de pré-transtypage ou simplement pour effectuer des validations impossibles après le transtypage.

Je ne voudrais surtout pas que Daniel Von Fange suggère de remplacer l'accesseur, car la validation dans un accesseur modifie légèrement le contrat de l'accesseur. Active Record a une fonctionnalité explicite pour cette situation. Utilise le.

10
joshuacronemeyer

Étant donné que vous devez gérer la chaîne de date avant qu'elle ne soit convertie en date dans votre modèle, je remplacerais l'accesseur pour ce champ.

Disons que votre champ de date est published_date. Ajoutez ceci à votre objet de modèle:

def published_date=(value)
    # do sanity checking here
    # then hand it back to Rails to convert and store
    self.write_attribute(:published_date, value) 
end
3
Daniel Von Fange

Un peu tard ici, mais grâce à " Comment valider une date dans les rails? " J'ai réussi à écrire ce validateur, l'espoir est utile à quelqu'un:

À l'intérieur de votre model.rb

validate :date_field_must_be_a_date_or_blank

# If your field is called :date_field, use :date_field_before_type_cast
def date_field_must_be_a_date_or_blank
  date_field_before_type_cast.to_date
rescue ArgumentError
  errors.add(:birthday, :invalid)
end
2
unmultimedio

Voici une réponse non chronique ..

class Pimping < ActiveRecord::Base

validate :valid_date?

def valid_date?
  if scheduled_on.present?
    unless scheduled_on.is_a?(Time)
      errors.add(:scheduled_on, "Is an invalid date.")
    end
  end
end
1
Trip

Vous pouvez valider la date et l'heure ainsi (dans une méthode quelque part dans votre contrôleur avec un accès à vos paramètres si vous utilisez des sélections personnalisées) ...

# Set parameters
year = params[:date][:year].to_i
month = params[:date][:month].to_i
mday = params[:date][:mday].to_i
hour = params[:date][:hour].to_i
minute = params[:date][:minute].to_i

# Validate date, time and hour
valid_date    = Date.valid_date? year, month, mday
valid_hour    = (0..23).to_a.include? hour
valid_minute  = (0..59).to_a.include? minute
valid_time    = valid_hour && valid_minute

# Check if parameters are valid and generate appropriate date
if valid_date && valid_time
  second = 0
  offset = '0'
  DateTime.civil(year, month, mday, hour, minute, second, offset)
else
  # Some fallback if you want like ...
  DateTime.current.utc
end
0
King'ori Maina