Làm cách nào tôi có thể đặt giá trị mặc định trong ActiveRecord?


417

Làm cách nào tôi có thể đặt giá trị mặc định trong ActiveRecord?

Tôi thấy một bài đăng từ Pratik mô tả một đoạn mã xấu xí, phức tạp: http://m.onkey.org/2007/7/24/how-to-set-default-values-in-your-model

class Item < ActiveRecord::Base  
  def initialize_with_defaults(attrs = nil, &block)
    initialize_without_defaults(attrs) do
      setter = lambda { |key, value| self.send("#{key.to_s}=", value) unless
        !attrs.nil? && attrs.keys.map(&:to_s).include?(key.to_s) }
      setter.call('scheduler_type', 'hotseat')
      yield self if block_given?
    end
  end
  alias_method_chain :initialize, :defaults
end

Tôi đã thấy các ví dụ sau đây googling xung quanh:

  def initialize 
    super
    self.status = ACTIVE unless self.status
  end

  def after_initialize 
    return unless new_record?
    self.status = ACTIVE
  end

Tôi cũng đã thấy mọi người đưa nó vào di chuyển của họ, nhưng tôi muốn thấy nó được xác định trong mã mô hình.

Có cách nào hợp quy để đặt giá trị mặc định cho các trường trong mô hình ActiveRecord không?


Có vẻ như bạn đã tự trả lời câu hỏi, trong hai biến thể khác nhau :)
Adam Byrtek

19
Lưu ý rằng thành ngữ Ruby "tiêu chuẩn" cho 'self.status = ACTIVE trừ khi self.status' là 'self.status || = ACTIVE'
Mike Woodhouse

1
Câu trả lời của Jeff Perrin tốt hơn nhiều so với câu trả lời hiện được đánh dấu là được chấp nhận. default_scope là một giải pháp không được chấp nhận để thiết lập các giá trị mặc định, bởi vì nó có HIỆU QUẢ TUYỆT VỜI cũng thay đổi hành vi của các truy vấn.
lawrence


2
cho tất cả các upvotes cho câu hỏi này, tôi sẽ nói của Ruby cần có một phương pháp setDefaultValue cho ActiveRecord
spartikus

Câu trả lời:


557

Có một số vấn đề với mỗi phương thức khả dụng, nhưng tôi tin rằng việc xác định một after_initializecuộc gọi lại là cách để thực hiện vì những lý do sau:

  1. default_scopesẽ khởi tạo các giá trị cho các mô hình mới, nhưng sau đó sẽ trở thành phạm vi mà bạn tìm thấy mô hình. Nếu bạn chỉ muốn khởi tạo một số số thành 0 thì đây không phải là điều bạn muốn.
  2. Xác định mặc định trong di chuyển của bạn cũng hoạt động một phần thời gian ... Như đã được đề cập, điều này sẽ không hoạt động khi bạn chỉ gọi Model.new.
  3. Ghi đè initializecó thể hoạt động, nhưng đừng quên gọi super!
  4. Sử dụng một plugin như phusion là một chút vô lý. Đây là ruby, chúng ta có thực sự cần một plugin chỉ để khởi tạo một số giá trị mặc định không?
  5. Ghi đè after_initialize bị phản đối kể từ Rails 3. Khi tôi ghi đè lên after_initializeđường ray 3.0.3, tôi nhận được cảnh báo sau trong bảng điều khiển:

CẢNH BÁO KHAI THÁC: Thay vào đó, cơ sở # after_initialize không được dùng nữa, vui lòng sử dụng phương thức Base.after_initialize :. (được gọi từ / Users / me / myapp / app / model / my_model: 15)

Do đó, tôi muốn viết một after_initializecuộc gọi lại, cho phép bạn mặc định các thuộc tính ngoài việc cho phép bạn đặt mặc định cho các liên kết như vậy:

  class Person < ActiveRecord::Base
    has_one :address
    after_initialize :init

    def init
      self.number  ||= 0.0           #will set the default value only if it's nil
      self.address ||= build_address #let's you set a default association
    end
  end    

Bây giờ bạn chỉmột nơi để tìm kiếm khởi tạo các mô hình của bạn. Tôi đang sử dụng phương pháp này cho đến khi ai đó nghĩ ra một phương pháp tốt hơn.

Hãy cẩn thận:

  1. Đối với các trường boolean làm:

    self.bool_field = true if self.bool_field.nil?

    Xem bình luận của Paul Russell về câu trả lời này để biết thêm chi tiết

  2. Nếu bạn chỉ chọn một tập hợp con các cột cho một mô hình (nghĩa là sử dụng selecttrong một truy vấn như Person.select(:firstname, :lastname).all), bạn sẽ nhận được MissingAttributeErrornếu initphương thức của bạn truy cập vào một cột chưa được bao gồm trong selectmệnh đề. Bạn có thể bảo vệ chống lại trường hợp này như vậy:

    self.number ||= 0.0 if self.has_attribute? :number

    và cho một cột boolean ...

    self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?

    Cũng lưu ý rằng cú pháp khác trước Rails 3.2 (xem bình luận của Cliff Darling bên dưới)


7
Đây chắc chắn là cách tốt nhất để đạt được điều này. Điều này thực sự kỳ quặc và đáng tiếc. Một phương pháp ưa thích hợp lý để thiết lập mặc định thuộc tính mô hình khi tạo dường như là thứ gì đó Rails nên được xây dựng. Cách duy nhất (đáng tin cậy) khác, ghi đè initialize, dường như thực sự khó hiểu đối với một thứ gì đó rõ ràng và được xác định rõ. Tôi đã dành hàng giờ để bò qua tài liệu trước khi tìm kiếm ở đây vì tôi cho rằng chức năng này đã có ở đâu đó và tôi chỉ không biết về nó.
seaneshbaugh

106
Một lưu ý về điều này - nếu bạn có một trường boolean mà bạn muốn mặc định, đừng làm self.bool_field ||= true, vì điều này sẽ buộc trường này thành đúng ngay cả khi bạn khởi tạo nó thành sai. Thay vào đó hãy làm self.bool_field = true if self.bool_field.nil?.
Paul Russell

2
Về điểm # 2, Model.new thực sự hoạt động (chỉ đối với tôi?) Cùng với các mặc định được xác định trong di chuyển hoặc chính xác hơn với các giá trị mặc định cho các cột trong bảng. Nhưng tôi nhận ra rằng phương pháp của Jeff dựa trên cuộc gọi lại after_initialize có lẽ là cách tốt nhất để làm. Chỉ là một câu hỏi: nó có hoạt động với các đối tượng bẩn chưa được lưu không? Trong ví dụ của bạn, Person.new.number_was sẽ trả về 0,0?
Laurent Farcy

21
Thận trọng khi sử dụng phương pháp này kết hợp với việc chọn các cột cụ thể với bản ghi hoạt động. Trong trường hợp này, chỉ các thuộc tính được chỉ định trong truy vấn sẽ được tìm thấy trong đối tượng và mã init sẽ ném a MissingAttributeError. Bạn có thể thêm một kiểm tra bổ sung như được hiển thị: self.number ||= 0.0 if self.has_attribute? :number Đối với booleans : self.bool_field = true if (self.has_attribute? :bool_value) && self.bool_field.nil?. Đây là Rails 3.2+ - để sử dụng sớm hơn self.attributes.has_key?và bạn cần một chuỗi thay vì ký hiệu.
Cliff Darling

6
Làm điều này với các hiệp hội sẽ háo hức tải các hiệp hội đó vào tra cứu. Bắt đầu initializevới return if !new_record?để tránh các vấn đề hiệu suất.
Kyle Macey

68

Đường ray 5+

Bạn có thể sử dụng phương thức thuộc tính trong các mô hình của mình, vd.:

class Account < ApplicationRecord
  attribute :locale, :string, default: 'en'
end

Bạn cũng có thể truyền lambda cho defaulttham số. Thí dụ:

attribute :uuid, UuidType.new, default: -> { SecureRandom.uuid }

3
ahhhhh đây là viên ngọc tôi đang tìm kiếm! mặc định cũng có thể mất một Proc, ví dụ: mặc định: -> {Time.cản.to_date}
schpet

1
Đảm bảo chỉ định loại là đối số thứ hai, nếu không loại sẽ là Valuevà sẽ không có kiểu chữ nào được thực hiện.
null

với sự hài lòng của tôi, điều này cũng hoạt động với store_accessor, ví dụ như store_accessor :my_jsonb_column, :localebạn đã có thể xác địnhattribute :locale, :string, default: 'en'
Ryan Romanchuk

Ôi thật tuyệt vời, tôi cần mặc định để thể hiện dưới hình thức và điều này hoạt động rất tốt. Cảm ơn Lucas.
Paul Watson

Người ta vẫn có thể đặt những thứ này thành nil. Nếu chúng không thể là nilDB not null+ DB mặc định + github.com/sshaw/keep_defaults là cách để đi từ trải nghiệm của tôi
sshaw

47

Chúng tôi đặt các giá trị mặc định trong cơ sở dữ liệu thông qua việc di chuyển (bằng cách chỉ định :defaulttùy chọn trên mỗi định nghĩa cột) và để Active Record sử dụng các giá trị này để đặt mặc định cho từng thuộc tính.

IMHO, cách tiếp cận này phù hợp với các nguyên tắc của AR: quy ước về cấu hình, DRY, định nghĩa bảng điều khiển mô hình, không phải theo cách khác.

Lưu ý rằng mặc định vẫn nằm trong mã ứng dụng (Ruby), mặc dù không phải trong mô hình mà trong (các) di chuyển.


3
Một vấn đề khác là khi bạn muốn một giá trị mặc định cho khóa ngoại. Bạn không thể mã hóa giá trị ID vào trường khóa ngoại vì trên các DB khác nhau, ID có thể khác nhau.
shmichael

2
còn một vấn đề nữa là, theo cách này bạn không thể khởi tạo các bộ truy cập không liên tục (thuộc tính không phải là cột db).
Viktor Trón

2
một vấn đề khác là bạn không nhất thiết phải thấy tất cả các giá trị mặc định ở một nơi. chúng có thể được phân tán thông qua các cuộc di cư khác nhau.
tuyên bố

8
Decan, có db / lược đồ.rb
Benjamin Atkin

6
Tôi muốn đề cập đến những độc giả tương lai: ít nhất là từ những gì tôi đã đọc, điều này trái với các nguyên tắc của AR. Logic cho các mô hình nên nằm trong các lớp mô hình và cơ sở dữ liệu nên càng không biết gì càng tốt. Các giá trị mặc định đối với tôi cấu thành logic cụ thể về một mô hình.
darethas

40

Một số trường hợp đơn giản có thể được xử lý bằng cách xác định mặc định trong lược đồ cơ sở dữ liệu nhưng điều đó không xử lý một số trường hợp phức tạp hơn bao gồm các giá trị được tính toán và khóa của các mô hình khác. Đối với những trường hợp này, tôi làm điều này:

after_initialize :defaults

def defaults
   unless persisted?
    self.extras||={}
    self.other_stuff||="This stuff"
    self.assoc = [OtherModel.find_by_name('special')]
  end
end

Tôi đã quyết định sử dụng after_initialize nhưng tôi không muốn nó được áp dụng cho các đối tượng chỉ tìm thấy những cái mới hoặc được tạo. Tôi nghĩ rằng gần như gây sốc khi một cuộc gọi lại after_new không được cung cấp cho trường hợp sử dụng rõ ràng này nhưng tôi đã thực hiện bằng cách xác nhận xem đối tượng đã được duy trì chưa cho biết rằng nó không phải là mới.

Nhìn thấy câu trả lời của Brad Murray, điều này thậm chí còn sạch hơn nếu điều kiện được chuyển sang yêu cầu gọi lại:

after_initialize :defaults, unless: :persisted?
              # ":if => :new_record?" is equivalent in this context

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
  self.assoc = [OtherModel.find_by_name('special')]
end

4
Đây là một điểm thực sự quan trọng. Tôi phải tưởng tượng rằng trong hầu hết các trường hợp, việc thiết lập mặc định trên một bản ghi chỉ được thực hiện trước khi duy trì một bản ghi mới, chứ không phải khi tải một bản ghi liên tục.
Russell Silva

Thx anh bạn, bạn đã cứu ngày của tôi.
stephanfriedrich

2
Thế còn :before_create?
Franklin Yu

Làm thế nào: before_create xử lý các cuộc gọi mới và lưu riêng biệt? Tôi muốn kiểm tra và thực sự hiểu trước khi chuyển sang nó.
Joseph Lord

17

Mẫu gọi lại after_initialize có thể được cải thiện bằng cách thực hiện như sau

after_initialize :some_method_goes_here, :if => :new_record?

Điều này có một lợi ích không hề nhỏ nếu mã init của bạn cần xử lý các liên kết, vì đoạn mã sau kích hoạt n + 1 tinh tế nếu bạn đọc bản ghi ban đầu mà không bao gồm liên kết.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

16

Các anh chàng Phusion có một số plugin tốt cho việc này.


Lưu ý, plugin này cho phép các :defaultgiá trị trong di chuyển lược đồ 'chỉ hoạt động' với Model.new.
jchook

Tôi có thể nhận được các :defaultgiá trị trong việc di chuyển để 'chỉ hoạt động' Model.new, trái với những gì Jeff nói trong bài đăng của mình. Đã xác minh làm việc trong Rails 4.1.16.
Magne

8

Một cách tiềm năng thậm chí tốt hơn / sạch hơn các câu trả lời được đề xuất là ghi đè lên trình truy cập, như thế này:

def status
  self['status'] || ACTIVE
end

Xem "Ghi đè người truy cập mặc định" trong tài liệu ActiveRecord :: Basenhiều thông tin khác từ StackOverflow về cách sử dụng tự .


Trạng thái vẫn sẽ là con số không trong hàm băm được trả về attributes. Đã thử nghiệm trong đường ray 5.2.0.
spyle

8

tôi sử dụng attribute-defaults đá quý

Từ tài liệu: chạy sudo gem install attribute-defaultsvà thêm require 'attribute_defaults'vào ứng dụng của bạn.

class Foo < ActiveRecord::Base
  attr_default :age, 18
  attr_default :last_seen do
    Time.now
  end
end

Foo.new()           # => age: 18, last_seen => "2014-10-17 09:44:27"
Foo.new(:age => 25) # => age: 25, last_seen => "2014-10-17 09:44:28"

7

Các câu hỏi tương tự, nhưng tất cả đều có ngữ cảnh hơi khác nhau: - Làm cách nào để tạo giá trị mặc định cho các thuộc tính trong mô hình của Rails activerecord?

Câu trả lời hay nhất: Phụ thuộc vào những gì bạn muốn!

Nếu bạn muốn mọi đối tượng bắt đầu bằng một giá trị: hãy sử dụngafter_initialize :init

Bạn muốn new.htmlbiểu mẫu có giá trị mặc định khi mở trang? sử dụng https://stackoverflow.com/a/5127684/1536309

class Person < ActiveRecord::Base
  has_one :address
  after_initialize :init

  def init
    self.number  ||= 0.0           #will set the default value only if it's nil
    self.address ||= build_address #let's you set a default association
  end
  ...
end 

Nếu bạn muốn mọi đối tượng có một giá trị được tính từ đầu vào của người dùng: sử dụngbefore_save :default_values Bạn muốn người dùng nhậpXvà sau đóY = X+'foo'? sử dụng:

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P'
  end
end

4

Đây là những gì các nhà xây dựng dành cho! Ghi đè initializephương thức của mô hình .

Sử dụng after_initializephương pháp.


2
Thông thường bạn sẽ đúng nhưng bạn không bao giờ nên ghi đè khởi tạo trong mô hình ActiveRecord vì nó không phải lúc nào cũng được gọi. Bạn nên sử dụng after_initializephương pháp thay thế.
Luke Redpath

Sử dụng default_scope chỉ để đặt mặc định là sai CERTAINLY. after_initialize là câu trả lời đúng.
joaomilho

4

Sup guys, tôi đã kết thúc như sau:

def after_initialize 
 self.extras||={}
 self.other_stuff||="This stuff"
end

Hoạt động như một lá bùa!


3

Điều này đã được trả lời trong một thời gian dài, nhưng tôi cần các giá trị mặc định thường xuyên và không muốn đưa chúng vào cơ sở dữ liệu. Tôi tạo ra một DefaultValuesmối quan tâm:

module DefaultValues
  extend ActiveSupport::Concern

  class_methods do
    def defaults(attr, to: nil, on: :initialize)
      method_name = "set_default_#{attr}"
      send "after_#{on}", method_name.to_sym

      define_method(method_name) do
        if send(attr)
          send(attr)
        else
          value = to.is_a?(Proc) ? to.call : to
          send("#{attr}=", value)
        end
      end

      private method_name
    end
  end
end

Và sau đó sử dụng nó trong các mô hình của tôi như vậy:

class Widget < ApplicationRecord
  include DefaultValues

  defaults :category, to: 'uncategorized'
  defaults :token, to: -> { SecureRandom.uuid }
end

3

Tôi cũng đã thấy mọi người đưa nó vào di chuyển của họ, nhưng tôi muốn thấy nó được xác định trong mã mô hình.

Có cách nào hợp quy để đặt giá trị mặc định cho các trường trong mô hình ActiveRecord không?

Cách Rails chính tắc, trước Rails 5, thực sự là để thiết lập nó trong quá trình di chuyển, và chỉ cần nhìn vào db/schema.rb bất cứ khi nào muốn xem giá trị mặc định nào được DB đặt cho bất kỳ mô hình nào.

Trái với những gì @Jeff Perrin trả lời trạng thái (hơi cũ), phương pháp di chuyển thậm chí sẽ áp dụng mặc định khi sử dụng Model.new , do một số phép thuật Rails. Đã xác minh làm việc trong Rails 4.1.16.

Điều đơn giản nhất thường là tốt nhất. Nợ kiến ​​thức ít hơn và các điểm tiềm ẩn của sự nhầm lẫn trong codebase. Và nó 'chỉ hoạt động'.

class AddStatusToItem < ActiveRecord::Migration
  def change
    add_column :items, :scheduler_type, :string, { null: false, default: "hotseat" }
  end
end

Hoặc, để thay đổi cột mà không tạo một cột mới, hãy thực hiện:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column_default :items, :scheduler_type, "hotseat"
  end
end

Hoặc có lẽ tốt hơn nữa:

class AddStatusToItem < ActiveRecord::Migration
  def change
    change_column :items, :scheduler_type, :string, default: "hotseat"
  end
end

Kiểm tra hướng dẫn RoR chính thức để biết các tùy chọn trong phương pháp thay đổi cột.

Các null: falsegiá trị NULL không cho phép trong DB và, như là một lợi ích bổ sung, nó cũng cập nhật để tất cả các bản ghi DB tồn tại trước đó là null được đặt cùng với giá trị mặc định cho trường này. Bạn có thể loại trừ tham số này trong quá trình di chuyển nếu bạn muốn, nhưng tôi thấy nó rất tiện dụng!

Cách thức kinh điển trong Rails 5+ là, như @Lucas Caton nói:

class Item < ActiveRecord::Base
  attribute :scheduler_type, :string, default: 'hotseat'
end

1

Vấn đề với các giải pháp after_initialize là bạn phải thêm after_initialize vào mọi đối tượng mà bạn tìm kiếm từ DB, bất kể bạn có truy cập thuộc tính này hay không. Tôi đề nghị một cách tiếp cận lười biếng.

Các phương thức thuộc tính (getters) tất nhiên là các phương thức, vì vậy bạn có thể ghi đè chúng và cung cấp một mặc định. Cái gì đó như:

Class Foo < ActiveRecord::Base
  # has a DB column/field atttribute called 'status'
  def status
    (val = read_attribute(:status)).nil? ? 'ACTIVE' : val
  end
end

Trừ khi, như ai đó đã chỉ ra, bạn cần thực hiện Foo.find_by_status ('HOẠT ĐỘNG'). Trong trường hợp đó, tôi nghĩ rằng bạn thực sự cần phải đặt mặc định trong các ràng buộc cơ sở dữ liệu của mình, nếu DB hỗ trợ nó.


Giải pháp này và giải pháp thay thế được đề xuất không hoạt động trong trường hợp của tôi: Tôi có hệ thống phân cấp lớp STI trong đó chỉ có một lớp có thuộc tính đó và màu tương ứng sẽ được sử dụng trong điều kiện truy vấn DB.
cmoran92

1

Tôi gặp vấn đề với after_initializeviệc đưa ra ActiveModel::MissingAttributeErrorlỗi khi thực hiện các phát hiện phức tạp:

ví dụ:

@bottles = Bottle.includes(:supplier, :substance).where(search).order("suppliers.name ASC").paginate(:page => page_no)

"tìm kiếm" trong .wherehàm băm là điều kiện

Vì vậy, tôi đã kết thúc việc đó bằng cách ghi đè khởi tạo theo cách này:

def initialize
  super
  default_values
end

private
 def default_values
     self.date_received ||= Date.current
 end

Cuộc supergọi là cần thiết để đảm bảo đối tượng khởi tạo chính xác từ ActiveRecord::Basetrước khi thực hiện mã tùy chỉnh của tôi, tức là: default_values


Tôi thích nó. Tôi cần phải làm def initialize(*); super; default_values; endtrong Rails 5.2.0. Thêm vào đó, giá trị mặc định có sẵn, ngay cả trong .attributeshàm băm.
spyle

1
class Item < ActiveRecord::Base
  def status
    self[:status] or ACTIVE
  end

  before_save{ self.status ||= ACTIVE }
end

2
Mmmhh ... ban đầu có vẻ khéo léo, nhưng sau khi suy nghĩ một chút, tôi thấy một vài vấn đề. Đầu tiên, tất cả các giá trị mặc định không nằm trong một điểm duy nhất, nhưng nằm rải rác trong lớp (tưởng tượng tìm kiếm chúng hoặc thay đổi chúng). Thứ hai và tồi tệ nhất, yo không thể đặt, sau này, một giá trị null (hoặc thậm chí là sai!).
paradoja

Tại sao bạn cần đặt giá trị null làm mặc định? bạn lấy cái đó ra khỏi hộp với AR mà không làm gì cả. Đối với sai khi sử dụng cột boolean thì bạn đúng, đây không phải là cách tiếp cận tốt nhất.
Mike Breen

Tôi không thể nói cho người khác thói quen mã hóa Tôi chưa gặp vấn đề gì vì tôi không phân tán getters / setters của mình xung quanh một tệp lớp. Ngoài ra, bất kỳ trình soạn thảo văn bản hiện đại nào cũng giúp bạn dễ dàng điều hướng đến một phương thức (shift-cmd-t trong textmate).
Mike Breen

@paradoja - Tôi lấy lại nó, bây giờ tôi thấy nó bị hỏng khi sử dụng null. Không nhất thiết phải sử dụng null làm mặc định nhưng nếu bạn thực sự muốn thay đổi giá trị thành null tại một số điểm. Bắt tốt @paradoja, cảm ơn.
Mike Breen

Tôi sử dụng phương pháp này vì nó hoạt động tốt với các thuộc tính được tạo động.
Dale Campbell

0

Mặc dù làm điều đó để thiết lập các giá trị mặc định là khó hiểu và lúng túng trong hầu hết các trường hợp, bạn cũng có thể sử dụng :default_scope. Kiểm tra bình luận của squil ở đây .


0

Phương thức after_initialize không được dùng nữa, thay vào đó hãy sử dụng hàm gọi lại.

after_initialize :defaults

def defaults
  self.extras||={}
  self.other_stuff||="This stuff"
end

tuy nhiên, sử dụng : mặc định trong di chuyển của bạn vẫn là cách sạch nhất.


4
Trong Rails 3: after_initializePhương pháp được không phản đối . Trong thực tế, cuộc gọi lại kiểu macro mà bạn đưa ra một ví dụ về IS không được chấp nhận . Chi tiết: hướng dẫn.rubyonrails.org / từ
Zabba

0

Tôi đã thấy rằng sử dụng phương thức xác thực cung cấp nhiều quyền kiểm soát đối với cài đặt mặc định. Bạn thậm chí có thể đặt mặc định (hoặc không xác thực) cho các bản cập nhật. Bạn thậm chí còn đặt một giá trị mặc định khác cho chèn so với cập nhật nếu bạn thực sự muốn. Lưu ý rằng mặc định sẽ không được đặt cho đến khi #valid? được gọi là.

class MyModel
  validate :init_defaults

  private
  def init_defaults
    if new_record?
      self.some_int ||= 1
    elsif some_int.nil?
      errors.add(:some_int, "can't be blank on update")
    end
  end
end

Về việc xác định phương thức after_initialize, có thể có các vấn đề về hiệu năng vì after_initialize cũng được gọi bởi mỗi đối tượng được trả về bởi: find: http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find


không xác nhận chỉ xảy ra trước khi lưu? Điều gì nếu bạn muốn hiển thị mặc định trước khi lưu?
Nurettin

@nurettin Đó là một điểm tốt và tôi có thể thấy tại sao đôi khi bạn muốn điều đó, nhưng OP đã không đề cập đến điều này như một yêu cầu. Bạn phải tự quyết định xem bạn có muốn chi phí mặc định cho mọi trường hợp hay không, ngay cả khi nó không được lưu. Cách khác là giữ một vật thể giả xung quanh để newhành động sử dụng lại.
Kelvin

0

Nếu cột xảy ra là cột loại 'trạng thái' và mô hình của bạn cho vay để sử dụng các máy trạng thái, hãy xem xét sử dụng đá quý aasm , sau đó bạn có thể đơn giản làm

  aasm column: "status" do
    state :available, initial: true
    state :used
    # transitions
  end

Nó vẫn không khởi tạo giá trị cho các bản ghi chưa được lưu, nhưng nó sẽ sạch hơn một chút so với việc tự tạo bằng inithoặc bất cứ thứ gì, và bạn gặt hái những lợi ích khác của sự co thắt như phạm vi cho tất cả các trạng thái của bạn.



0

Tôi thực sự khuyên bạn nên sử dụng đá quý "default_value_for": https://github.com/FooBarWidget/default_value_for

Có một số kịch bản khó mà khá nhiều yêu cầu ghi đè phương thức khởi tạo, mà gem đó làm.

Ví dụ:

Mặc định db của bạn là NULL, mặc định mô hình / ruby ​​của bạn là "một số chuỗi", nhưng bạn thực sự muốn đặt giá trị thành không vì bất kỳ lý do gì:MyModel.new(my_attr: nil)

Hầu hết các giải pháp ở đây sẽ không đặt giá trị thành 0 và thay vào đó sẽ đặt nó thành mặc định.

OK, vì vậy thay vì thực hiện ||=phương pháp này, bạn chuyển sang my_attr_changed?...

NHƯNG bây giờ hãy tưởng tượng mặc định db của bạn là "một số chuỗi", mặc định mô hình / ruby ​​của bạn là "một số chuỗi khác", nhưng theo một kịch bản nhất định, bạn muốn đặt giá trị thành "một số chuỗi" (mặc định db):MyModel.new(my_attr: 'some_string')

Điều này sẽ dẫn đến my_attr_changed?việc sai vì giá trị phù hợp với mặc định db, mà lần lượt sẽ cháy mã mặc định ruby-xác định của bạn và thiết lập giá trị cho "một số chuỗi khác" - một lần nữa, không phải những gì bạn mong muốn.


Vì những lý do đó, tôi không nghĩ rằng điều này có thể được thực hiện đúng chỉ với một cái móc sau_initialize.

Một lần nữa, tôi nghĩ rằng viên ngọc "default_value_for" đang sử dụng đúng phương pháp: https://github.com/FooBarWidget/default_value_for


0

Đây là một giải pháp tôi đã sử dụng mà tôi hơi ngạc nhiên chưa được thêm vào.

Có hai phần với nó. Phần đầu tiên là thiết lập mặc định trong di chuyển thực tế và phần thứ hai là thêm xác thực trong mô hình để đảm bảo rằng sự hiện diện là đúng.

add_column :teams, :new_team_signature, :string, default: 'Welcome to the Team'

Vì vậy, bạn sẽ thấy ở đây rằng mặc định đã được đặt. Bây giờ trong xác thực bạn muốn đảm bảo rằng luôn có một giá trị cho chuỗi, vì vậy chỉ cần làm

 validates :new_team_signature, presence: true

Điều này sẽ làm là đặt giá trị mặc định cho bạn. (đối với tôi, tôi có "Chào mừng đến với Đội"), và sau đó nó sẽ tiến thêm một bước nữa để đảm bảo rằng luôn có một giá trị hiện tại cho đối tượng đó.

Mong rằng sẽ giúp!


0
# db/schema.rb
create_table :store_listings, force: true do |t|
  t.string :my_string, default: "original default"
end

StoreListing.new.my_string # => "original default"

# app/models/store_listing.rb
class StoreListing < ActiveRecord::Base
  attribute :my_string, :string, default: "new default"
end

StoreListing.new.my_string # => "new default"

class Product < ActiveRecord::Base
  attribute :my_default_proc, :datetime, default: -> { Time.now }
end

Product.new.my_default_proc # => 2015-05-30 11:04:48 -0600
sleep 1
Product.new.my_default_proc # => 2015-05-30 11:04:49 -0600

-2

sử dụng default_scope trong rails 3

tài liệu api

ActiveRecord che khuất sự khác biệt giữa mặc định được xác định trong cơ sở dữ liệu (lược đồ) và mặc định được thực hiện trong ứng dụng (mô hình). Trong quá trình khởi tạo, nó phân tích lược đồ cơ sở dữ liệu và ghi chú mọi giá trị mặc định được chỉ định ở đó. Sau này, khi tạo các đối tượng, nó gán các giá trị mặc định do lược đồ chỉ định mà không cần chạm vào cơ sở dữ liệu.

thảo luận


nếu bạn sử dụng meta_where, default_scope có thể không hoạt động để gán mặc định cho các đối tượng AR mới do lỗi.
Viktor Trón

vấn đề meta_where này hiện đã được khắc phục [ metautonomous.lighthousản.com / projects / 53011 / tickets / trộm
Viktor Trón

3
KHÔNG sử dụng default_scope. Điều này sẽ làm cho tất cả các truy vấn của bạn thêm điều kiện này vào trường bạn đã đặt. Nó gần như KHÔNG BAO GIỜ là những gì bạn muốn.
brad

@brad, buồn cười bạn đề cập, tôi hoàn toàn đồng ý, đó là xấu xa :). xem bình luận của tôi trong stackoverflow.com/questions/10680845/ .
Viktor Trón

-3

Từ tài liệu api http://api.rubyonrails.org/groupes/ActiveRecord/Callbacks.html Sử dụng before_validationphương thức trong mô hình của bạn, nó cung cấp cho bạn các tùy chọn tạo khởi tạo cụ thể để tạo và cập nhật các cuộc gọi, ví dụ: trong mã này từ ví dụ tài liệu api) trường số được khởi tạo cho thẻ tín dụng. Bạn có thể dễ dàng điều chỉnh điều này để đặt bất kỳ giá trị nào bạn muốn

class CreditCard < ActiveRecord::Base
  # Strip everything but digits, so the user can specify "555 234 34" or
  # "5552-3434" or both will mean "55523434"
  before_validation(:on => :create) do
    self.number = number.gsub(%r[^0-9]/, "") if attribute_present?("number")
  end
end

class Subscription < ActiveRecord::Base
  before_create :record_signup

  private
    def record_signup
      self.signed_up_on = Date.today
    end
end

class Firm < ActiveRecord::Base
  # Destroys the associated clients and people when the firm is destroyed
  before_destroy { |record| Person.destroy_all "firm_id = #{record.id}"   }
  before_destroy { |record| Client.destroy_all "client_of = #{record.id}" }
end

Ngạc nhiên vì mình chưa được gợi ý ở đây


before_validation sẽ không đặt mặc định cho đến khi đối tượng sẵn sàng được duy trì. Nếu quy trình cần đọc mặc định trước khi tiếp tục thì các giá trị sẽ không sẵn sàng.
mmell

Bạn không bao giờ đặt giá trị mặc định trong khi kiểm tra xác nhận. Nó thậm chí không phải là Hack. Làm điều đó trong quá trình khởi tạo
Sachin
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.