2009-11-22 16 views
5

Tôi đã có mô hình này mà đã làm việc tốt:validates_presence_of gây after_initialize được gọi với một tự lạ

class Weight < ActiveRecord::Base 
    belongs_to :user 
    validates_presence_of :weight, :measured_on 
    attr_accessible :weight, :measured_on 

    def after_initialize 
    self.measured_on ||= Date.today 
    end 
end 

tôi đã thêm nó dòng

validates_uniqueness_of :measured_on, :scope => :user_id 

này và nó bắt đầu ném một lỗi trên xác thực. Không phải lỗi xác thực nhưng có lỗi của Ruby:

>> w.valid? 
ActiveRecord::MissingAttributeError: missing attribute: measured_on 
    from /Users/pupeno/Projects/sano/app/models/weight.rb:8:in `after_initialize' 

Tôi đã đặt một tuyên bố gỡ lỗi sau after_initialize và tôi đã nhận thấy điều gì đó không mong muốn. Khi tôi tạo một trọng lượng mới, nó hoạt động như mong đợi và đối tượng tự trên after_initialize là trọng lượng mong đợi:

>> w = Weight.new 
/Users/pupeno/Projects/sano/app/models/weight.rb:9 
self.measured_on ||= Date.today 
(rdb:1) p self 
#<Weight id: nil, user_id: nil, weight: nil, measured_on: nil, created_at: nil, updated_at: nil> 
(rdb:1) c 
=> #<Weight id: nil, user_id: nil, weight: nil, measured_on: "2009-11-22", created_at: nil, updated_at: nil> 

Khi tôi chạy w.valid? nó trở nên kỳ lạ. after_initialize được gọi là một lần nữa, tôi không chắc chắn lý do tại sao, và tự đối tượng là gì tôi mong đợi:

>> w.valid? 
/Users/pupeno/Projects/sano/app/models/weight.rb:9 
self.measured_on ||= Date.today 
(rdb:1) p self 
#<Weight id: 1> 
(rdb:1) p self.inspect 
"#<Weight id: 1>" 
(rdb:1) p self.class 
Weight(id: integer, user_id: integer, weight: float, measured_on: date, created_at: datetime, updated_at: datetime) 
(rdb:1) p self.measured_on 
ActiveRecord::MissingAttributeError Exception: missing attribute: measured_on 
(rdb:1) 

Nó có vẻ như một đối tượng Trọng lượng được tạo ra mà không cần bất kỳ thuộc tính nhưng các thiết lập id. Bất kỳ ý tưởng tại sao? Đây có phải là lỗi hoặc hành vi dự kiến ​​không? Tôi có làm điều gì đó sai bằng cách thiết lập các measur_on trên after_initialize?

workaround hiện tại của tôi, trong trường hợp bất cứ ai đang có cùng một vấn đề, là

class Weight < ActiveRecord::Base 
    belongs_to :user 
    validates_presence_of :weight, :measured_on 
    validates_uniqueness_of :measured_on, :scope => :user_id 
    attr_accessible :weight, :measured_on 

    def after_initialize 
    if self.has_attribute? :measured_on 
     self.measured_on ||= Date.today 
    end 
    end 
end 

nhưng tôi muốn có một giải pháp thích hợp.

Trả lời

6

Tôi nghĩ rằng bạn đang đánh một lỗi đường ray Gần đây tôi đã chiến đấu với. Xem This blog entry liên kết đến lỗi ngọn hải đăng có liên quan.

Sự hiểu biết của tôi là những gì đang xảy ra là một số đoạn mã ray trước đó thực hiện "id chọn từ tablename" để xem mục nhập có tồn tại hoặc khớp hay không. Đối tượng sau đó lưu trữ rằng trường duy nhất tồn tại cho bảng là "id". Mã của bạn sau đó chạy và giá trị "thuộc tính" sau đó không chính xác, chỉ phản ánh trường id.

Từ những gì tôi có thể tìm thấy, điều này chỉ xảy ra khi đường dẫn mã cụ thể này được truy cập và không làm mọi thứ khó chịu, trừ khi thực hiện xác thực.

Những gì tôi đã làm để có được xung quanh nó đã được bọc mã after_initialise trong một khối khởi động/giải cứu ActiveRecord :: MissingAttributeError. Sau đó, tôi đã viết một lưu ý lớn trong các ứng dụng và trên mỗi mục cho thấy khi một phiên bản mới của đường ray được phát hành, chúng tôi có thể loại bỏ điều này.

Vâng, tôi chắc chắn có nhiều giải pháp thanh lịch hơn.

def after_initialize 
    begin 
    # ... updates here 
    # self.unique_reference = UUIDTools::UUID.random_create.to_s 
    rescue ActiveRecord::MissingAttributeError 
    end 
end 

Hoặc bạn cũng có thể làm:

def after_initialize 
    if self.has_attribute? :measured_on 
    self.measured_on ||= Date.today 
    end 
end 
+0

OP đã chỉnh sửa phản hồi của tôi và tạo phần "has_attribute?: Measured_on" - Tôi không chắc chắn 100% rằng tôi đồng ý với nó sau khi xem nguồn đường ray - tôi nghi ngờ nó hoạt động do tác dụng phụ, thay vì - thiết kế). Tôi không quay trở lại phần đó của câu trả lời mặc dù, như những người hiểu biết, nó có thể giúp một ai đó. – oskarpearson

+0

Tôi bị cắn vào ngày hôm nay. Bài đăng này và câu trả lời của bạn đã lưu thịt xông khói của tôi. Tôi đã quên rằng một trong những nhược điểm của ngôn ngữ động là bạn không phải lúc nào cũng có cùng thuộc tính trong một đối tượng ... có thể tốt nhưng cũng xấu :) –

+0

Bị cắn bởi điều này ngày hôm nay. Wow, điều này đã được đăng ở đây trong năm 2009? Tôi rất ấn tượng rằng nó đã không còn tồn tại quá lâu. – Trejkaz

0

validates_uniqueness_of cần phải quét cơ sở dữ liệu của bạn cho các mục nhập. ActiveRecord đang tải tất cả các mục nhập khác đó làm trường hợp của mô hình của bạn. Nhưng để cắt giảm bộ xử lý/bộ nhớ, nó không thêm các phương thức cho các thuộc tính, vì nó không cần chúng để kiểm tra sự tồn tại nhanh chóng. Tuy nhiên nó vẫn instantiating tất cả các hồ sơ hiện có như là trường hợp của mô hình của bạn để after_initialize được gọi là.

Công việc xung quanh là để thay đổi @attributes băm trực tiếp thay vì dựa vào accessors:

class Weight < ActiveRecord::Base 
    belongs_to :user 
    validates_presence_of :weight, :measured_on 
    validates_uniqueness_of :measured_on, :scope => :user_id 
    attr_accessible :weight, :measured_on 

    def after_initialize 
    @attributes["measured_on"] ||= Date.today 
    end 
end 
+0

Tôi chắc chắn validates_uniqueness chỉ đơn giản là tạo ra một câu lệnh sql để kiểm tra sự tồn tại. Nó không gây ra tất cả các mục trong bảng được khởi tạo. – Mike

+0

Đó là cách nó trông, nhưng tồn tại? ở cuối đuôi của khối validates \ _each trong nguồn hợp lệ \ _uniqueness \ _of vẫn tạo ra một bản ghi hoạt động. – EmFi

2

Vấn đề này có liên quan đến đường ray vé # 3165. Đọc lên đó, nếu bạn quan tâm đến cách thức và lý do tại sao điều này xảy ra

Tôi đã dành nửa ngày cho việc này, trước khi tôi tìm thấy vé đó. Trong khi tôi buồn mà nó được gần như chính xác một năm kể từ này đã được báo cáo, và nó đã không được cố định nào, đây là một công việc đơn giản của tôi xung quanh cho kịch bản của tôi:

validates_uniqueness_of :email, :scope => :library_id 

def after_initialize 
    self.status ||= "Invited" 
end 

này sẽ gây ra một 'MissingAttributeError' để được ném, nếu có các bản ghi được trả về bởi truy vấn validates_uniqueness_of. Giải pháp đơn giản của tôi là:

def after_initialize 
    self.status ||= "Invited" if new_record? 
end 

Khi người khác gặp sự cố phức tạp hơn, điều này sẽ giải quyết trường hợp đơn giản, cho đến khi giải pháp thực tế được cam kết vào đường ray.

+0

Lưu ý rằng nếu bạn cũng cần điều này cho các hồ sơ không phải là mới, cách giải quyết này là không tốt (trong trường hợp của tôi, nó đã phá vỡ các bài kiểm tra.) – Trejkaz

Các vấn đề liên quan