Làm cách nào để xác thực một ngày trong đường ray?


92

Tôi muốn xác thực một ngày trong mô hình của mình trong Ruby on Rails, tuy nhiên, các giá trị ngày, tháng và năm đã được chuyển đổi thành một ngày không chính xác vào thời điểm chúng tiếp cận với mô hình của tôi.

Ví dụ: nếu tôi nhập ngày 31 tháng 2 năm 2009 trong chế độ xem của mình, khi tôi sử dụng Model.new(params[:model])trong bộ điều khiển của mình, nó sẽ chuyển nó thành "ngày 3 tháng 3 năm 2009", mà sau đó mô hình của tôi coi là ngày hợp lệ, nhưng nó không chính xác.

Tôi muốn có thể thực hiện xác nhận này trong mô hình của mình. Có cách nào mà tôi có thể, hoặc tôi đang đi về điều này hoàn toàn sai?

Tôi đã tìm thấy " Xác thực ngày " thảo luận về sự cố nhưng nó chưa bao giờ được giải quyết.


4
Câu hỏi này hiện là một trong những kết quả hàng đầu của Google về xác thực ngày Rails. Bạn có thể bỏ lỡ nó trong lần vượt qua đầu tiên giống như tôi đã làm: phần quan trọng của câu trả lời được chọn là đá quý validates_timeliness. Tôi thậm chí còn không đọc phần còn lại của câu trả lời bởi vì tôi đã tìm hiểu về validates_timeliness ở một nơi khác và viên ngọc đó thực hiện mọi thứ tôi cần mà tôi không cần phải viết bất kỳ mã tùy chỉnh nào.
Jason Swett

Câu trả lời:


61

Tôi đoán bạn đang sử dụng trình date_selecttrợ giúp để tạo các thẻ cho ngày tháng. Một cách khác mà bạn có thể làm là sử dụng trình trợ giúp biểu mẫu chọn lọc cho các trường ngày, tháng, năm. Như thế này (ví dụ tôi đã sử dụng là trường ngày tạo_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 %>

Và trong mô hình, bạn xác thực ngày:

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

Nếu bạn đang tìm kiếm một giải pháp plugin, tôi sẽ kiểm tra plugin validates_timeliness . Nó hoạt động như thế này (từ trang 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 

Danh sách các phương pháp xác thực có sẵn như sau:

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.

Giải pháp được đề xuất không hoạt động với tôi và tôi đang sử dụng Rails 2.3.5
Roger

Tôi đã viết lại nó để sạch hơn và đã thử nghiệm nó trên Rails 2.3.5 và điều này thực sự hiệu quả với tôi.
Jack Chu

Xin chào Jack, tôi đã thử giải pháp của bạn, giải pháp này phù hợp với tôi khi thêm attr_accessible: day,: month,: year. Điều đó không có ý nghĩa đối với tôi ... Cảm ơn nếu bạn có bất kỳ ý tưởng nào!
benoitr

Trong Rails 3, tôi đang sử dụng github.com/adzap/validates_timeliness và nó thật tuyệt vời.
Jason Swett

Các validates_timeliness plugin / đá quý thực sự tốt đẹp để sử dụng. Hoạt động một cách quyến rũ và giữ cho mã của tôi nhỏ hơn. Cảm ơn vì tiền hỗ trợ.
mjnissim

20

Sử dụng đá quý mãn tính:

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

3
Trong ví dụ cụ thể này, tôi phải sử dụng Chronic.parse(from_date_before_type_cast)để nhận đầu vào giá trị chuỗi. Nếu không, Rails sẽ nhập chuỗi với to_datevì trường là một datetimetrường.
Calvin

18

Nếu bạn muốn tương thích với Rails 3 hoặc Ruby 1.9, hãy thử đá quý date_validator .


Tôi vừa thử cái này. Mất tất cả 2 phút để cài đặt và thêm xác thực!
jflores 29/10/12

11

Active Record cung cấp cho bạn _before_type_castcác thuộc tính chứa dữ liệu thuộc tính thô trước khi đánh máy. Điều này có thể hữu ích cho việc trả về các thông báo lỗi với các giá trị trước khi đánh máy hoặc chỉ thực hiện các xác nhận không thể thực hiện được sau khi đánh máy.

Tôi sẽ né tránh đề xuất của Daniel Von Fange về việc ghi đè trình truy cập, bởi vì việc xác thực trong trình truy cập sẽ thay đổi hợp đồng trình truy cập một chút. Active Record có một tính năng rõ ràng cho tình huống này. Sử dụng nó.


4

Vì bạn cần xử lý chuỗi ngày trước khi nó được chuyển đổi thành ngày trong mô hình của bạn, tôi sẽ ghi đè trình truy cập cho trường đó

Giả sử trường ngày của bạn là published_date. Thêm cái này vào đối tượng mô hình của bạn:

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

Tôi không biết liệu tôi có sử dụng nó cho mục đích này hay không, nhưng đó là một gợi ý tuyệt vời.
Dan Rosenstark

3

Ở đây hơi muộn, nhưng nhờ " Làm cách nào để xác thực ngày trong đường ray? " Tôi đã viết được trình xác thực này, hy vọng sẽ hữu ích cho ai đó:

Bên trong của bạn 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

1

Đây là một câu trả lời không kinh niên ..

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
Điều này luôn trả về false: "1/1/2013".is_a?(Date)- Ngày đến từ đầu vào của người dùng, vì vậy nó được cung cấp dưới dạng một chuỗi. Tôi phải phân tích cú pháp nó như một ngày đầu tiên?
Mohamad

Chính xác. Date.parse("1/1/2013").is_a?(Date), nhưng bạn có thể thấy rằng nếu nó không phân tích cú pháp, nó cũng có thể không phải là ngày.
Chuyến đi

1
Cần giải quyết lỗi đối số ... ahh, sẽ thật tuyệt nếu nó chỉ phân tích cú pháp chuỗi tự động ... ôi thôi, có những viên ngọc cho điều đó.
Mohamad

0

Bạn có thể xác thực ngày và giờ như vậy (trong một phương pháp ở đâu đó trong bộ điều khiển của bạn có quyền truy cập vào các thông số của bạn nếu bạn đang sử dụng các lựa chọn tùy chỉnh) ...

# 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

-3

Bạn đã thử validates_date_timeplug-in chưa?


Mặc dù liên kết này có thể trả lời câu hỏi, nhưng tốt hơn hết bạn nên đưa các phần thiết yếu của câu trả lời vào đây và cung cấp liên kết để tham khảo. Các câu trả lời chỉ có liên kết có thể trở nên không hợp lệ nếu trang được liên kết thay đổi.
Pinal

Tôi thích câu trả lời của tôi hơn. Hai người đồng ý.
Ryan Duffield

4
@Pinal Liên kết thực sự đã chết.
Wayne Conrad
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.