문제

는 방법을 설정할 수 있습니에서 기본값 ActiveRecord?

나는 게시물로서 그래에 대해 설명하는 추한,복잡한의 덩어리 코드: 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 's와 같은 플러그인을 사용하면 약간 말도 안됩니다. 이것은 루비입니다. 기본값을 초기화하기 위해 플러그인이 정말로 필요합니까?
  5. 우선 after_initialize 더 이상 사용되지 않습니다 Rails 3. 내가 무시할 때 after_initialize Rails 3.0.3에서는 콘솔에서 다음 경고를받습니다.

감가 상각 경고 : 기본#after_initialize가 더 이상 사용되지 않았으며, base.after_initialize : method를 대신 사용하십시오. (/사용자/me/myapp/app/models/my_model : 15)

그러므로 나는 쓴다 after_initialize 콜백, 기본 속성이 가능합니다 에 추가 SO와 같은 협회에서 기본값을 설정할 수 있습니다.

  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 당신의 경우 init 메소드 액세스에 포함되지 않은 열에 액세스합니다. select 절. 이 경우와 같이이 사건을 방지 할 수 있습니다.

    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의 의견 참조).

다른 팁

우리가 기본에 있는 값 데이터베이스를 마이그레이션(지정하여 :default 각각에 대한 옵션은 열 정의)및 활동 기록 이러한 값을 사용하여 기본값을 설정하려면 각각의 특성이 있습니다.

이럴 때 이 방법은 정렬의 원리와 AR:컨벤션 구성,건조,테이블 정의 드라이브 모델은 아닌 다른 방법으로,주위에.

참고하는 기본값은 여전히 응용 프로그램(루비)코드,하지만 모델에서 마이그레이션(s).

레일 5+

당신은 사용할 수 있습니다 기인하다 모델 내의 메소드 (예 : 예 :

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

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

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

몇 가지 간단한 케이스에 의해 처리될 수 있을 정의하는 기본값이 데이터베이스에서 스키마에만 있는 처리하지 않 수의 난이도가 경우를 포함하여 계산한 값을 키의 다른 모델이다.이러한 경우 나는 이것:

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 콜백이 제공되지 않 이를 위해 명백한 사용할 경우 그러나 내가 만들어지 의 여부를 확인한 개체에 이미 지속했음을 나타내는 새로운되지 않습니다.

을 보 브래드 머레이의 대답이 이어도 청소기 조건이 있다면 이동을 콜백을 요청은:

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

후 _initialize 콜백 패턴은 다음을 수행하면 개선 할 수 있습니다.

after_initialize :some_method_goes_here, :if => :new_record?

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

class Account

  has_one :config
  after_initialize :init_config

  def init_config
    self.config ||= build_config
  end

end

Phusion Guys는 약간의 좋은 것을 가지고 있습니다 플러그인 이것을 위해.

제안 된 답변보다 훨씬 나은/더 깨끗한 잠재적 방법은 다음과 같이 액세서를 덮어 쓰는 것입니다.

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

"기본 액세서를 덮어 쓰기"를 참조하십시오 ActivereCord :: 기본 문서 그리고 Self 사용에 대한 StackoverFlow에서 더 많은 것입니다.

나는 그것을 사용한다 attribute-defaults 보석

문서에서 : 실행 sudo gem install attribute-defaults 그리고 추가 require 'attribute_defaults' 앱에.

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"

비슷한 질문이지만 모두 약간 다른 맥락을 가지고 있습니다. Rails Activerecord의 모델에서 속성에 대한 기본값을 어떻게 작성합니까?

베스트 답변 : 당신이 원하는 것에 달려 있습니다!

모든 개체를 원한다면 값으로 시작하십시오 : 사용 after_initialize :init

당신은 원합니다 new.html 페이지를 열 때 기본값을 갖기 위해 양식? 사용 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 

모든 개체를 원한다면 사용자 입력에서 계산 된 값이 있습니다. 사용 before_save :default_values사용자가 입력하기를 원합니다 X 그리고 Y = X+'foo'? 사용:

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

이것이 생성자를위한 것입니다! 모델을 무시하십시오 initialize 방법.

사용 after_initialize 방법.

Sup Guys, 나는 다음을 끝냈다.

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

매력처럼 작동합니다!

가장 먼저 : 나는 Jeff의 대답에 동의하지 않습니다. 앱이 작고 논리가 간단한 경우에 의미가 있습니다. 나는 더 큰 응용 프로그램을 구축하고 유지할 때 그것이 문제가 될 수있는 방법에 대한 통찰력을 제공하려고 노력하고 있습니다. 작은 것을 구축 할 때는 먼저이 접근법을 사용하는 것이 아니라 대안 적 접근 방식으로 명심하는 것이 좋습니다.


여기서 질문은 레코드의 기본값이 비즈니스 로직인지 여부입니다. 그렇다면, 나는 그것을 ORM 모델에 넣는 것이 신중합니다. RYW가 언급 한 필드이기 때문에 활동적인, 이것은 비즈니스 로직처럼 들립니다. 예를 들어 사용자가 활성화되어 있습니다.

ORM 모델에 비즈니스 문제를 제기하는 이유는 무엇입니까?

  1. 그것은 깨집니다 SRP. ActiveRecord :: Base에서 나온 모든 수업은 많은 다른 것들 중에서, 그들 중 가장 중요한 것은 데이터 일관성 (검증)과 지속성 (저장)입니다. 비즈니스 논리를 넣지 만 작지만 AR :: BASE BREAKS SRP.

  2. 테스트가 느립니다. ORM 모델에서 발생하는 모든 형태의 논리를 테스트하려면 테스트가 실행하려면 레일을 초기화해야합니다. 이것은 응용 프로그램의 시작 부분에서 너무 많은 문제가되지 않지만 단위 테스트를 실행하는 데 시간이 오래 걸릴 때까지 축적됩니다.

  3. 그것은 SRP를 더욱 줄을 서서 구체적인 방식으로 깨뜨릴 것입니다. 이제 우리의 비즈니스가 항목이 활성화 될 때 사용자에게 이메일을 보내야한다고 가정 해 봅시다. 이제 우리는 항목 ORM 모델에 이메일 로직을 추가하고 있으며, 그의 주요 책임은 항목을 모델링하는 것입니다. 이메일 로직에 신경 쓰지 않아야합니다. 이것은의 경우입니다 비즈니스 부작용. 이것들은 ORM 모델에 속하지 않습니다.

  4. 다각화하기가 어렵습니다. 데이터베이스 뒷받침 init_type : 문자열 필드와 같은 것들이있는 성숙한 레일 앱을 보았습니다. 이것은 구조적 문제를 해결하기 위해 데이터베이스를 오염시키는 것입니다. 더 좋은 방법이 있습니다.

Poro Way : 이것은 조금 더 많은 코드이지만 ORM 모델과 비즈니스 로직을 별도로 유지할 수 있습니다. 여기의 코드는 단순화되었지만 아이디어를 보여 주어야합니다.

class SellableItemFactory
  def self.new(attributes = {})
    record = Item.new(attributes)
    record.active = true if record.active.nil?
    record
  end
end

그런 다음 이것을 제자리에 사용하면 새 항목을 만드는 방법은

SellableItemFactory.new

그리고 내 테스트는 이제 값이없는 경우 항목에서 항목에 활성화 된 것을 단순히 확인할 수 있습니다. Rails 초기화가 필요하지 않으며 SRP 파손이 없습니다. 항목 초기화가 더 발전되면 (예 : 상태 필드, 기본 유형 등) 항목이 추가 될 수 있습니다. 두 가지 유형의 기본값으로 끝나면이를 수행하기 위해 새로운 BusinescaseItemFactory를 만들 수 있습니다.

참고 : 여기에서 의존성 주입을 사용하여 공장이 많은 활동적인 물건을 구축 할 수 있도록하는 것도 도움이 될 수 있지만 단순성을 위해 남겨 두었습니다. 여기있어: self.new (klass = 항목, 속성 = {})

이것은 오랫동안 답변되었지만 기본값이 자주 필요하며 데이터베이스에 넣지 않는 것이 좋습니다. 나는 a를 만든다 DefaultValues 우려:

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

그런 다음 다음과 같은 내 모델에서 사용하십시오.

class Widget < ApplicationRecord
  include DefaultValues

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

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

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

레일 5 이전의 정식 레일 방식은 실제로 마이그레이션에 설정하고 db/schema.rb 모든 모델에 대해 DB가 어떤 기본값을 설정하고 있는지 확인하고 싶을 때마다.

@Jeff Perrin이 답변 한 것과는 달리 (약간 오래된) 마이그레이션 접근법은 사용시 기본값을 적용합니다. Model.new, 일부 레일 마법으로 인해. Rails 4.1.16에서의 작업 확인.

가장 간단한 것은 종종 최고입니다. 코드베이스에서 지식 부채와 혼란의 잠재적 지점이 적습니다. 그리고 그것은 '그냥 작동합니다'.

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

그만큼 null: false DB의 NULL 값을 해제하고 추가 된 이점으로 기존 DB 레코드가 모두 기본값으로 설정되어 있습니다. 원하는 경우 마이그레이션 에서이 매개 변수를 제외시킬 수 있지만 매우 편리하다는 것을 알았습니다!

@Lucas Caton이 말했듯이 Rails 5+의 표준 방식은 다음과 같습니다.

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

After_Initialize 솔루션의 문제점은이 속성에 액세스하든 아니든 DB에서 찾는 모든 단일 객체에 After_initialize를 추가해야한다는 것입니다. 나는 게으른로드 된 접근법을 제안합니다.

속성 메소드 (getters)는 물론 메소드 자체이므로이를 무시하고 기본값을 제공 할 수 있습니다. 같은 것 :

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

누군가가 지적한 것처럼 Foo.find_by_status ( 'Active')를해야합니다. 이 경우 DB가 지원하는 경우 데이터베이스 제약 조건에서 기본값을 설정해야한다고 생각합니다.

나는 문제를 해결했다 after_initialize 기부 ActiveModel::MissingAttributeError 복잡한 찾기를 수행 할 때 오류 :

예 :

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

"검색" .where 조건의 해시입니다

그래서 나는 이런 식으로 초기화를 재정의함으로써 그것을하게되었습니다.

def initialize
  super
  default_values
end

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

그만큼 super 객체가 올바르게 초기화하는지 확인하려면 호출이 필요합니다. ActiveRecord::Base 내 사용자 정의 코드를 수행하기 전에, 즉 : default_values

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

  before_save{ self.status ||= ACTIVE }
end

"default_value_for"보석을 사용하는 것이 좋습니다. https://github.com/foobarwidget/default_value_for

GEM이하는 초기화 방법을 무시 해야하는 몇 가지 까다로운 시나리오가 있습니다.

예 :

DB 기본값은 null이고 모델/루비 정의 기본값은 "일부 문자열"이지만 실제로는 원하다 어떤 이유로 든 값을 nil로 설정합니다. MyModel.new(my_attr: nil)

여기서 대부분의 솔루션은 값을 nil로 설정하지 못하고 대신 기본값으로 설정합니다.

좋아, 그럼 ||= 접근, 당신은 전환합니다 my_attr_changed?...

하지만 이제 DB 기본값이 "일부 문자열"이라고 상상해보십시오. 모델/루비 정의 기본값은 "일부 다른 문자열"이지만 특정 시나리오에서는 귀하가 원하다 값을 "일부 문자열"(DB 기본값)으로 설정하려면 : MyModel.new(my_attr: 'some_string')

결과가 발생합니다 my_attr_changed? 존재 거짓 값이 DB 기본값과 일치하기 때문에 루비 정의 된 기본 코드를 실행하고 값을 원하는 것이 아니라 "일부 다른 문자열"으로 설정합니다.


이러한 이유로 나는 이것이 후 _initialize 후크로 올바르게 달성 될 수 있다고 생각하지 않습니다.

다시 한 번, "default_value_for"보석이 올바른 접근 방식을 취하고 있다고 생각합니다. https://github.com/foobarwidget/default_value_for

대부분의 경우 기본값을 설정하기 위해이를 수행하지만 대부분의 경우 혼란스럽고 어색하지만 사용할 수 있습니다. :default_scope 또한. 체크 아웃 여기 스 콰이의 의견.

After_Initialize 메소드는 더 이상 사용되지 않습니다. 대신 콜백을 사용하십시오.

after_initialize :defaults

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

그러나 사용 :기본 마이그레이션에서 여전히 가장 깨끗한 방법입니다.

유효성 검사 방법을 사용하면 기본 설정 설정에 대한 많은 제어 기능이 있음을 발견했습니다. 업데이트에 대한 기본값 (또는 실패 검증)을 설정할 수도 있습니다. 실제로 원하는 경우 Inserts 대 업데이트에 대해 다른 기본값을 설정합니다. #Valid까지 기본값이 설정되지 않습니까? 호출됩니다.

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

After_Initialize 메소드를 정의하는 것과 관련하여 다음에 의해 반환 된 각 객체에서 After_initialize가 호출되기 때문에 성능 문제가있을 수 있습니다. 찾기 : 찾기 :http://guides.rubyonrails.org/active_record_validations_callbacks.html#after_initialize-and-after_find

열이 '상태'유형 열이되고 모델이 상태 머신 사용에 적합한 경우 AASM 보석, 그 후에는 단순히 할 수 있습니다

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

여전히 구원받지 않은 레코드의 값을 초기화하지는 않지만 자신의 init 또는 무엇이든, 그리고 당신은 모든 지위에 대한 범위와 같은 AAM의 다른 이점을 거두게됩니다.

https://github.com/keithrowell/rails_default_value

class Task < ActiveRecord::Base
  default :status => 'active'
end

여기에 내가 조금 놀랐다는 솔루션이 아직 추가되지 않았다는 솔루션이 있습니다.

두 부분이 있습니다. 첫 번째 부분은 실제 마이그레이션에서 기본값을 설정하는 것입니다. 두 번째 부분은 모델에 유효성 검사를 추가하여 존재가 참을 수 있도록하는 것입니다.

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

따라서 기본값이 이미 설정되어 있음을 알 수 있습니다. 이제 유효성 검사에서 문자열에 항상 값이 있는지 확인하고 싶습니다.

 validates :new_team_signature, presence: true

이것이 할 일은 기본값을 설정하는 것입니다. (저에게는 "팀에 오신 것을 환영합니다"), 그리고 한 단계 더 나아가서 그 객체에 항상 존재하는 값이 있는지 확인합니다.

도움이되기를 바랍니다!

레일 3에서 default_scope를 사용하십시오

API DOC

ActiveRecord는 데이터베이스 (스키마)에 정의 된 기본값과 응용 프로그램 (모델)에서 수행 된 기본값 간의 차이를 가리 킵니다. 초기화 중에 데이터베이스 스키마를 구문 분석하고 지정된 기본값을 기록합니다. 나중에 객체를 만들 때 데이터베이스를 터치하지 않고도 스키마 지정 기본값을 할당합니다.

논의

API 문서에서 http://api.rubyonrails.org/classes/activerecord/callbacks.html사용 before_validation 메소드 모델에서 CREATE and UPDATE CALL에 대한 특정 초기화를 작성하는 옵션을 제공합니다 (이 예제에서 API 문서 예제에서 가져온 코드)는 신용 카드에 대한 번호 필드가 초기화됩니다. 원하는 값을 설정하도록 쉽게 조정할 수 있습니다.

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

그의 제안은 여기서 제안되지 않았다는 것에 놀랐다

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top