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.
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.
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
Si vous voulez Rails 3 ou Ruby 1.9 1.9, essayez le date_validator gem.].
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.
É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
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
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
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