IT story

Rails activerecord의 모델에서 속성의 기본값을 어떻게 생성합니까?

hot-time 2020. 5. 13. 08:08
반응형

Rails activerecord의 모델에서 속성의 기본값을 어떻게 생성합니까? [복제]


이 질문에는 이미 답변이 있습니다.

ActiveRecord에서 속성을 정의하여 속성의 기본값을 만들고 싶습니다. 기본적으로 레코드를 만들 때마다 attribute의 기본값을 원합니다 :status. 나는 이것을 시도했다 :

class Task < ActiveRecord::Base
  def status=(status)
    status = 'P'
    write_attribute(:status, status)
  end
end

그러나 생성시 여전히 데이터베이스 에서이 오류를 검색합니다.

ActiveRecord::StatementInvalid: Mysql::Error: Column 'status' cannot be null

따라서 값이 속성에 적용되지 않은 것으로 가정합니다.

Rails에서이를 수행하는 우아한 방법은 무엇입니까?

많은 감사합니다.


마이그레이션에서 열의 기본 옵션을 설정할 수 있습니다

....
add_column :status, :string, :default => "P"
....

또는

콜백을 사용할 수 있습니다. before_save

class Task < ActiveRecord::Base
  before_save :default_values
  def default_values
    self.status ||= 'P' # note self.status = 'P' if self.status.nil? might be safer (per @frontendbeauty)
  end
end

얼마 전에이 문제가 발생했고 Rails 3.0의 옵션이 약간 다르기 때문에이 질문에 대한 또 다른 답변을 제공 할 것입니다.

Rails 3.0에서는 다음과 같이하고 싶습니다 :

class MyModel < ActiveRecord::Base
  after_initialize :default_values

  private
    def default_values
      self.name ||= "default value"
    end
end

기본값이 필요한 경우 일반적으로 새 조치의보기가 렌더링되기 전에 새 레코드에 대해 기본값이 설정됩니다. 다음 방법은 양식을 렌더링 할 때 사용할 수 있도록 새 레코드에만 기본값을 설정합니다. before_save그리고 before_create 너무 늦게 작동하지 않습니다 당신이 기본 값이 입력 필드에 표시하려는 경우 .

after_initialize do
  if self.new_record?
    # values will be available for new record forms.
    self.status = 'P'
    self.featured = true
  end
end

코드를 전혀 쓰지 않고 할 수 있습니다. :) 데이터베이스의 열 기본값을 설정하기 만하면됩니다. 마이그레이션에서이 작업을 수행 할 수 있습니다. 예를 들면 다음과 같습니다.

create_table :projects do |t|
  t.string :status, :null => false, :default => 'P'
  ...
  t.timestamps
end

희망이 도움이됩니다.


해결책은 몇 가지에 달려 있습니다.

Is the default value dependent on other information available at creation time? Can you wipe the database with minimal consequences?

If you answered the first question yes, then you want to use Jim's solution

If you answered the second question yes, then you want to use Daniel's solution

If you answered no to both questions, you're probably better off adding and running a new migration.

class AddDefaultMigration < ActiveRecord::Migration
  def self.up
     change_column :tasks, :status, :string, :default => default_value, :null => false
  end
end

:string can be replaced with any type that ActiveRecord::Migration recognizes.

CPU is cheap so the redefinition of Task in Jim's solution isn't going to cause many problems. Especially in a production environment. This migration is proper way of doing it as it is loaded it and called much less often.


I would consider using the attr_defaults found here. Your wildest dreams will come true.


Just strengthening Jim's answer

Using presence one can do

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

For column types Rails supports out of the box - like the string in this question - the best approach is to set the column default in the database itself as Daniel Kristensen indicates. Rails will introspect on the DB and initialize the object accordingly. Plus, that makes your DB safe from somebody adding a row outside of your Rails app and forgetting to initialize that column.

For column types Rails doesn't support out of the box - e.g. ENUM columns - Rails won't be able to introspect the column default. For these cases you do not want to use after_initialize (it is called every time an object is loaded from the DB as well as every time an object is created using .new), before_create (because it occurs after validation), or before_save (because it occurs upon update too, which is usually not what you want).

Rather, you want to set the attribute in a before_validation on: create, like so:

before_validation :set_status_because_rails_cannot, on: :create

def set_status_because_rails_cannot
  self.status ||= 'P'
end

As I see it, there are two problems that need addressing when needing a default value.

  1. You need the value present when a new object is initialized. Using after_initialize is not suitable because, as stated, it will be called during calls to #find which will lead to a performance hit.
  2. You need to persist the default value when saved

Here is my solution:

# the reader providers a default if nil
# but this wont work when saved
def status
  read_attribute(:status) || "P"
end

# so, define a before_validation callback
before_validation :set_defaults
protected
def set_defaults
  # if a non-default status has been assigned, it will remain
  # if no value has been assigned, the reader will return the default and assign it
  # this keeps the default logic DRY
  status = status
end

I'd love to know why people think of this approach.


I found a better way to do it now:

def status=(value) 
  self[:status] = 'P' 
end 

In Ruby a method call is allowed to have no parentheses, therefore I should name the local variable into something else, otherwise Ruby will recognize it as a method call.

참고URL : https://stackoverflow.com/questions/1550688/how-do-i-create-a-default-value-for-attributes-in-rails-activerecords-model

반응형