[ruby-on-rails] ActiveRecord에서 기본값을 어떻게 설정합니까?

ActiveRecord에서 기본값을 어떻게 설정합니까?

나는 추악하고 복잡한 코드 덩어리를 설명하는 Pratik의 게시물을 참조하십시오 : 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

나는 다음과 같은 예제가 인터넷을 통해 보았습니다.

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

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

또한 사람들이 마이그레이션에 넣는 것을 보았지만 모델 코드에 정의 된 것을 보았습니다.

ActiveRecord 모델에서 필드의 기본값을 설정하는 정식 방법이 있습니까?



답변

사용 가능한 각 방법에는 몇 가지 문제가 있지만 after_initialize콜백 을 정의 하면 다음과 같은 이유로 갈 수 있습니다.

  1. default_scope새 모델의 값을 초기화하지만 모델을 찾는 범위가됩니다. 일부 숫자를 0으로 초기화 하려면 원하는 것이 아닙니다 .
  2. 마이그레이션에서 기본값을 정의하는 것도 시간의 일부로 작동합니다. 이미 언급했듯이 Model.new를 호출하면 작동 하지 않습니다 .
  3. 재정의 initialize는 효과가 있지만 전화하는 것을 잊지 마십시오 super!
  4. phusion과 같은 플러그인을 사용하는 것은 약간 어리 석습니다. 이것은 루비입니다. 기본값을 초기화하기 위해 플러그인이 정말로 필요합니까?
  5. Rails 3에서는 재정의 after_initialize 가 더 이상 사용되지 않습니다. Rails after_initialize3.0.3에서 재정의 하면 콘솔에 다음 경고가 표시됩니다.

DEPRECATION 경고 : Base # after_initialize가 사용되지 않으므로 대신 Base.after_initialize : method를 사용하십시오. (/ Users / me / myapp / app / models / my_model : 15에서 호출)

따라서 after_initialize콜백을 작성하면 다음 과 같이 연결에서 기본값을 설정할 수 있을뿐만 아니라 기본 속성 을 사용할 수 있습니다.

  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    

이제 모델의 초기화를 찾을 수있는 곳이 뿐입니다 . 누군가가 더 나은 방법을 찾을 때 까지이 방법을 사용하고 있습니다.

주의 사항 :

  1. 부울 필드의 경우 다음을 수행하십시오.

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

    자세한 내용은이 답변에 대한 Paul Russell의 의견을 참조하십시오.

  2. 모델에 대한 열의 하위 집합 만 선택하는 경우 (예 : select와 같은 쿼리에서 사용 Person.select(:firstname, :lastname).all) 메서드가 절 에 포함되지 않은 열에 액세스하는 MissingAttributeError경우 얻을 수 있습니다. 다음과 같이이 경우를 막을 수 있습니다.initselect

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

    부울 열의 경우 …

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

    또한 구문은 Rails 3.2 이전과 다릅니다 (아래 Cliff Darling의 의견 참조).


답변

레일 5+

모델 내 에서 속성 방법을 사용할 수 있습니다 ( 예 :

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

default매개 변수에 람다를 전달할 수도 있습니다 . 예:

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


답변

마이그레이션을 통해 데이터베이스에 기본값을 넣습니다 ( :default각 열 정의에 옵션을 에 기본값을 설정하고 Active Record에서 이러한 값을 사용하여 각 속성에 대한 기본값을 설정하도록합니다.

IMHO,이 접근법은 AR의 원칙과 일치합니다 : 컨벤션에 대한 컨벤션, DRY, 테이블 정의는 다른 방식이 아니라 모델을 주도합니다.

모델이 아니라 마이그레이션에서는 기본값이 여전히 애플리케이션 (Ruby) 코드에 있습니다.


답변

데이터베이스 스키마에서 기본값을 정의하여 일부 간단한 경우를 처리 할 수 ​​있지만 계산 된 값과 다른 모델의 키를 포함하여 여러 까다로운 경우는 처리하지 않습니다. 이 경우 나는 이것을한다 :

after_initialize :defaults

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

after_initialize를 사용하기로 결정했지만 새로운 객체 또는 생성 된 객체에만 적용되는 것을 원하지 않습니다. after_new 콜백 이이 명백한 유스 케이스에 제공되지 않는 것이 거의 충격적이지만 객체가 이미 지속되어 객체가 새로운 것이 아님을 확인하여 확인했습니다.

Brad Murray의 답변을 본 후 조건이 콜백 요청으로 이동하면 더 깨끗합니다.

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


답변

after_initialize 콜백 패턴은 다음을 수행하여 간단히 개선 할 수 있습니다.

after_initialize :some_method_goes_here, :if => :new_record?

다음 코드는 관련 코드를 포함하지 않고 초기 레코드를 읽는 경우 미묘한 n + 1을 트리거하기 때문에 초기화 코드가 연관을 처리해야하는 경우 이는 사소한 이점이 있습니다.

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end


답변

Phusion 녀석은 이것에 대한 멋진 플러그인 을 가지고 있습니다 .


답변

제안 된 답변보다 훨씬 더 좋고 깨끗한 잠재적 방법은 다음과 같이 접근자를 덮어 쓰는 것입니다.

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

ActiveRecord :: Base 설명서의 “기본 접근 자 덮어 쓰기” 및 self 사용에 대한 자세한 내용은 StackOverflow를 참조하십시오 .