reset_column_information은 코드 냄새입니까?

7784 단어 railsruby
Ruby on Rails로 작업한 적이 있다면 reset_column_information 메서드를 사용하는 마이그레이션을 작성한 적이 있을 것입니다. 방금 수정한 테이블이나 열 주변의 일부 데이터를 조정하려는 경우에 자주 사용됩니다.

사용하는데 어떤 문제가 있나요?



마이그레이션은 데이터베이스 구조 변경에만 관심을 가져야 합니다. 데이터를 추가하거나 제거하기 시작하면 새 개발자가 프로젝트를 설정할 때 실행할 수 없는 시나리오로 이어집니다. 대신 그들은 그들에게 제공된 데이터베이스를 강제로 사용하게 될 것이며, 종종 이것은 프로덕션의 백업이 될 것입니다(큰 데이터 누출 위험이 있음).

무엇을 조심해야 합니까?


bin/setup라는 모델을 만들고 다음과 같은 일부 데이터를 채운다고 가정해 보겠습니다.

class CreateJobLevels < ActiveRecord::Migration[6.0]
  def up
    create_table :job_levels do |t|
      t.string :name
    end

    JobLevel.reset_column_information
    JobLevel.create(name: 'executive')
    JobLevel.create(name: 'manager')
  end
end


이것의 큰 "아, 아니요"측면은 이제 모든 미래의 개발자가 데이터베이스의 rails db:setup 테이블을 채우기 위해 이 마이그레이션을 실행하도록 요구한다는 것입니다. 개발자가 마이그레이션을 실행하지 않으면 데이터를 추가할 다른 방법을 찾아야 합니다. 또한 JobLevel 필드 주변의 유효성 검사가 변경되면 개발자는 백지 상태에서 실행할 수 없습니다.

다음은 매우 일반적이지만 문제가 있는 또 다른 예입니다.

class AddEnabledToJobLevels < ActiveRecord::Migration[6.0]
  def up
    add_column :job_levels, :enabled, :boolean, default: false, null: false
    JobLevel.reset_column_information
    JobLevel.update_all(enabled: true)
  end
end


두 예에서 job_levels 모델의 이름이 바뀌면 마이그레이션을 다시 실행할 수 없습니다.

더 나은 접근 방식은 무엇입니까?



이름을 통한 SQL


JobLevel 메서드를 사용하면 마이그레이션 중에 임의의 SQL을 실행할 수 있습니다. 예:

class AddEnabledToJobLevels < ActiveRecord::Migration[6.0]
  def up
    add_column :job_levels, :enabled, :boolean, default: false, null: false
    execute('UPDATE job_levels SET enabled = TRUE')
  end
end


앱의 현재 상태가 아닌 마이그레이션 시점에서 데이터베이스의 제약 조건으로 실행되기 때문에 저는 이것을 정말 좋아합니다. 따라서 execute 모델을 삭제하더라도 나중에 이 마이그레이션을 다시 실행할 수 있습니다.

씨앗!



내가 아주 좋아하기 시작한 한 가지는 마이그레이션을 실행한 다음during a release 시드를 실행하는 것입니다. 내 개발 및 프로덕션에서 필요한 데이터를 동일한 방식으로 설정할 수 있기 때문에 좋아합니다.

# db/seeds.rb

# Seed all the job levels, if it exists it won't add a new one.
JobLevel.find_or_create_by!(name: 'manager')


데이터베이스에 변경되지 않는 데이터를 넣는 이유는 무엇입니까?



실제로 dev/prod parity을 0으로 낮추기 위해 일부 데이터 비트가 데이터베이스에 전혀 속하는지 및 Plain Old Ruby Objects와 같은 것이 더 나은지 생각하기 시작했습니다.

JobLevel = Struct.new(:name, :enabled, keyword_init: true) do
  alias enabled? enabled

  def self.find(name)
    all.find { |job_level| job_level.name == name }
  end

  def self.all
    @all ||= [
      new(name: 'executive', enabled: true),
      new(name: 'manager', enabled: true),
    ]
  end
end


이 접근 방식은 변경되지 않은 데이터로 데이터베이스를 쿼리하는 대신 메모리에 저장할 수 있기 때문에 제가 가장 좋아하는 방법입니다. 또한 모든 환경에서 동일할 강력한 피부여자와 함께 제공됩니다.

그렇다면 reset_column_information은 코드 냄새입니까?



그렇게 생각해요! 나는 이것이 "앞으로 몇 줄의 코드가 당신을 갉아먹을 것"이라는 지표라고 생각하며, 대부분의 경우 취할 수 있는 보다 안정적인 접근 방식이 있습니다.

Rubocop 경고가 적절하지 않다고 생각하지만 앞으로는 사용하지 않으려고 합니다.

좋은 웹페이지 즐겨찾기