2009-02-28 39 views
82

Tôi muốn xác thực ngày tháng trong mô hình của mình trong Ruby on Rails, tuy nhiên, giá trị ngày, tháng và năm đã được chuyển đổi thành ngày không chính xác vào thời điểm chúng đến được mô hình của tôi. Ví dụ: nếu tôi nhập vào 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, nó sẽ chuyển đổi thành "Ngày 3 tháng 3 năm 2009", sau đó mô hình của tôi sẽ là ngày hợp lệ, nhưng nó không chính xác.Làm cách nào để xác thực ngày trong đường ray?

Tôi muốn có thể thực hiện xác thực 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 điều này "Date validation" thảo luận vấn đề nhưng chưa bao giờ được giải quyết.

+1

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

Trả lời

55

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

Và trong mô hình, bạn xác nhận 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 muốn 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 nhận 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. 
+0

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

+0

Tôi viết lại nó để làm sạch hơn và kiểm tra nó chống lại Rails 2.3.5 và điều này không hiệu quả đối với tôi. –

+0

Hi Jack, tôi đã thử giải pháp của bạn, nó hoạt động cho tôi thêm attr_accessible: ngày,: tháng,: năm. Mà không có ý nghĩa đối với tôi ... Cảm ơn nếu bạn có bất kỳ ý tưởng! – benoitr

2

Vì bạn cần phải xử lý chuỗi ngày trước khi nó được chuyển đổi sang một ngày trong mô hình của bạn, tôi muốn ghi đè lên các accessor cho lĩnh vực

rằng Hãy nói rằng trường ngày của bạn là published_date. Thêm mục 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 
+0

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

-3

Bạn đã thử plugin validates_date_time chưa?

+0

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

+0

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

+3

@Pinal Liên kết thực sự đã chết. –

19

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 
+2

Trong ví dụ cụ thể này, tôi đã phải sử dụng 'Chronic.parse (from_date_before_type_cast)' để lấy giá trị chuỗi đầu vào. Nếu không, Rails sẽ định kiểu chuỗi với 'to_date' vì trường này là trường' datetime'. – Calvin

17

Nếu bạn muốn Tương thích Rails 3 hoặc Ruby 1.9 thử đá quý date_validator.

+0

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

9

Bản ghi hoạt động cung cấp cho bạn _before_type_cast thuộc tính chứa dữ liệu thuộc tính thô trước khi nhập. Đ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ị được định sẵn hoặc chỉ thực hiện các xác nhận hợp lệ mà không thể thực hiện được sau khi đã định kiểu.

Tôi sẽ né tránh đề xuất của Daniel Von Fange về việc ghi đè người truy cập, vì việc xác thực trong một người truy cập sẽ thay đổi hợp đồng người 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ó.

1

Dưới đây là một câu trả lời không mãn tính ..

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? (Ngày)' - Ngày xuất phát từ đầu vào của người dùng, do đó nó được nạp dưới dạng chuỗi. Tôi phải phân tích nó như là một ngày đầu tiên? – Mohamad

+0

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 ở tất cả, nó cũng có thể không phải là một ngày tháng. – Trip

+1

Cần phải giải quyết lỗi đối số ... ahh, nó sẽ rất tuyệt nếu nó chỉ phân tích cú pháp chuỗi tự động ... ồ, có đá quý cho điều đó. – Mohamad

0

Bạn có thể xác nhận ngày tháng và thời gian như vậy (trong một phương pháp nào đó trong điều khiển của bạn có thể tiếp cận params của bạn nếu bạn đang sử dụng tùy chỉnh chọn) .. .

# 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

Một hơi muộn ở đây, nhưng nhờ "How do I validate a date in rails?" Tôi quản lý để viết validator này, hy vọng là hữu ích cho ai đó:

Bên trong 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 
Các vấn đề liên quan