ActiveRecord에서 생성시 ID 재정의


104

생성시 모델의 ID 값을 재정의하는 방법이 있습니까? 다음과 같은 것 :

Post.create(:id => 10, :title => 'Test')

이상적이지만 분명히 작동하지 않습니다.


1
이러한 답변 중 대부분은 Rails 4에서 간헐적으로 실패하며 작동 중이라고 말합니다. 설명은 내 대답 을 참조하십시오 .
Rick Smith

답변:


116

id는 attr_protected 뿐이므로 대량 할당을 사용하여 설정할 수 없습니다. 그러나 수동으로 설정하면 다음과 같이 작동합니다.

o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888

원래 동기가 뭔지는 모르겠지만 ActiveHash 모델을 ActiveRecord로 변환 할 때 이렇게합니다. ActiveHash를 사용하면 ActiveRecord에서 동일한 belongs_to 시맨틱을 사용할 수 있지만 마이그레이션하고 테이블을 생성하고 모든 호출에서 데이터베이스 오버 헤드를 발생시키는 대신 yml 파일에 데이터를 저장하기 만하면됩니다. 데이터베이스의 외래 키는 yml의 메모리 내 ID를 참조합니다.

ActiveHash는 자주 변경되지 않고 개발자 만 변경하는 선택 목록 및 작은 테이블에 적합합니다. 따라서 ActiveHash에서 ActiveRecord로 이동할 때 모든 외래 키 참조를 동일하게 유지하는 것이 가장 쉽습니다.


@jkndrkn-무슨 말인지 모르겠네요. 나는 ActiveRecord::VERSION::STRING == "3.2.11"여기에 (sqlite3 어댑터 사용) 위의 내용이 나를 위해 작동합니다.
Felix Rabe 2013

아마도 내가 경험 한 것은 mysql 드라이버로만 표현되었을 것입니다.
jkndrkn 2013

레일 3.2.18에 대한 MySQL의 드라이버를 사용하여 나에게 작품 @jkndrkn
lulalala

나를 위해 일했습니다. 내 생명을 구했습니다. 레일 4.0.0 / postgres 9.3.5에서 사용
allenwlee

보기 내 대답 레일 4.이 작업을 수행하는 방법에 대한
릭 스미스

30

시험

a_post = Post.new do |p| 
  p.id = 10
  p.title = 'Test'
  p.save
end

그것은 당신이 찾고있는 것을 당신에게 줄 것입니다.


2
왜 당신이 반대표를 받는지 잘 모르겠습니다. 이것은 저에게 효과적입니다
semanticart

이것은 activerecord 3.2.11부터 계속 작동합니다. Jeff Dean이 2009 년 10 월 2 일에 게시 한 답변은 더 이상 작동하지 않습니다.
jkndrkn

작동하지 않고 p.save를 호출하기 때문에 작동하는 것 같습니다. 아마도 false를 반환합니다. p.save!를 실행하면 ActiveRecord :: RecordNotFound가 표시됩니다.
Alan

29

다음과 같이 사용할 수도 있습니다.

Post.create({:id => 10, :title => 'Test'}, :without_protection => true)

문서에 명시되어 있지만 이는 대량 할당 보안을 우회합니다.


@Samuel Heaney, 이것이 activerecord 3.2.13에서 완벽하게 작동하는지 확인할 수 있습니다.
mkralla11 2013 년

나를 위해 일했습니다. 별도의 필드를 포함 할 필요가 없습니다.
shalott

2
아아,이 더 이상 레일 4 일, 그들은 옵션 해시 제거되지
호르헤 Sampayo

@JorgeSampayo-Rails 4에서는 StrongParams를 위해 보호 된 속성을 제거 할 수 있으므로이 기능이 필요하지 않습니다.
PinnyM

21

Rails 4 :

Post.create(:title => 'Test').update_column(:id, 10)

다른 Rails 4 답변은 저에게 효과적 이지 않았습니다 . Rails Console을 사용하여 확인할 때 대부분 변경된 것처럼 보였지만 MySQL 데이터베이스에서 값을 확인했을 때 변경되지 않았습니다. 다른 답변은 때때로 효과가있었습니다.

최소한 MySQL의 경우를 사용하지 않는 한 id자동 증가 ID 번호 아래에 할당하면 작동 하지 않습니다update_column . 예를 들면

p = Post.create(:title => 'Test')
p.id
=> 20 # 20 was the id the auto increment gave it

p2 = Post.create(:id => 40, :title => 'Test')
p2.id
=> 40 # 40 > the next auto increment id (21) so allow it

p3 = Post.create(:id => 10, :title => 'Test')
p3.id
=> 10 # Go check your database, it may say 41.
# Assigning an id to a number below the next auto generated id will not update the db

+ create를 사용하도록 변경 하면 여전히이 문제가 발생합니다. 유사 항목을 수동으로 변경해도이 문제가 발생합니다.newsaveidp.id = 10

일반적으로 항상 작동하므로 추가 데이터베이스 쿼리가 필요하더라도 update_column변경하는 데 사용 합니다 id. 이것은 개발 환경에 표시되지 않을 수있는 오류이지만 작동 중이라고 말하면서 프로덕션 데이터베이스를 조용히 손상시킬 수 있습니다.


3
이것은 또한 콘솔의 레일 3.2.22 상황에서 작동하도록 한 유일한 방법이었습니다. '저장'에 대한 변형을 사용한 답변은 효과가 없었습니다.
JosephK

2
레일 4+의 경우 다음을 사용합니다.Post.new.update(id: 10, title: 'Test')
Spencer

6

실제로 다음 작업을 수행하는 것으로 나타났습니다.

p = Post.new(:id => 10, :title => 'Test')
p.save(false)

작동 할 수 있지만 모든 유효성 검사도 해제되며 요청한 내용이 아닐 수 있습니다.
Jordan Moncharmont

이것이 바로 ID가 중요한 시드 데이터에 필요한 것입니다. 감사합니다.
JD.

나는 원래 @JD가 지적했듯이 이것이 seed 데이터에 대해 작동 할 것이라고 생각하면서 upvoted했지만 activerecord 3.2.13으로 시도했지만 여전히 "Ca n't assign protected attributes"오류가 발생합니다. 그래서, downvoted :(
mkralla11 2013-09-11

1
불행히도, 이것은 rails 4에서 작동하지 않습니다. 당신은 NoMethodError : undefined method`[] 'for false : FalseClass
Jorge

@JorgeSampayo 이것은 validate: false단순히 통과하는 것이 아니라 통과해도 여전히 작동합니다 false. 그러나 여전히 보호 된 속성 문제가 발생합니다. 제 답변에서 설명한 것과는 별도의 방법이 있습니다.
PinnyM

6

Jeff가 지적했듯이 id는 attr_protected 인 것처럼 동작합니다. 이를 방지하려면 기본 보호 속성 목록을 재정의해야합니다. 속성 정보가 외부에서 올 수있는 모든 곳에서이 작업을 수행하도록주의하십시오. id 필드는 기본적으로 보호됩니다.

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(ActiveRecord 2.3.5로 테스트)


6

attributes_protected_by_default를 재정의 할 수 있습니다.

class Example < ActiveRecord::Base
    def self.attributes_protected_by_default
        # default is ["id", "type"]
        ["type"]
    end
end

e = Example.new(:id => 10000)

5
Post.create!(:title => "Test") { |t| t.id = 10 }

이것은 당신이 일반적으로하고 싶은 일이라고 생각하지 않지만 고정 된 ID 세트로 테이블을 채워야하는 경우 (예를 들어 레이크 태스크를 사용하여 기본값을 생성 할 때) 꽤 잘 작동합니다. 자동 증가를 재정의하려는 경우 (작업을 실행할 때마다 테이블이 동일한 ID로 채워지도록) :

post_types.each_with_index do |post_type|
  PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end

2

create_with_id 함수를 seed.rb 상단에 배치 한 다음이를 사용하여 명시 적 ID가 필요한 객체 생성을 수행하십시오.

def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
    obj
end

이렇게 사용하세요

create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})

사용하는 대신

Foo.create({id:1,name:"My Foo",prop:"My other property"})


2

이 경우는 id일종의 맞춤 날짜로 덮어 쓰는 데 필요한 유사한 문제입니다 .

# in app/models/calendar_block_group.rb
class CalendarBlockGroup < ActiveRecord::Base
...
 before_validation :parse_id

 def parse_id
    self.id = self.date.strftime('%d%m%Y')
 end
...
end

그리고 :

CalendarBlockGroup.create!(:date => Date.today)
# => #<CalendarBlockGroup id: 27072014, date: "2014-07-27", created_at: "2014-07-27 20:41:49", updated_at: "2014-07-27 20:41:49">

콜백이 제대로 작동합니다.

행운을 빕니다!.


idUnix 타임 스탬프를 기반으로 생성해야했습니다 . 나는 그것을 내부에서했다 before_create. 잘 작동합니다.
WM

0

Rails 3의 경우이를 수행하는 가장 간단한 방법 newwithout_protection세분화 와 함께 사용 하고 다음을 수행하는 것입니다 save.

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save

시드 데이터의 경우 다음과 같이 수행 할 수있는 유효성 검사를 우회하는 것이 좋습니다.

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)

실제로 시드 파일을 실행하기 직전에 선언 된 ActiveRecord :: Base에 도우미 메서드를 추가했습니다.

class ActiveRecord::Base
  def self.seed_create(attributes)
    new(attributes, without_protection: true).save(validate: false)
  end
end

그리고 지금:

Post.seed_create(:id => 10, :title => 'Test')

Rails 4의 경우 보호 된 속성 대신 StrongParams를 사용해야합니다. 이 경우 플래그를 전달하지 않고 간단히 할당하고 저장할 수 있습니다 new.

Post.new(id: 10, title: 'Test').save      # optionally pass `{validate: false}`

{}위의 Samuel의 대답 (Rails3) 에 속성을 넣지 않고는 나를 위해 작동하지 않습니다 .
Christopher Oezbek 2015

@PinnyM 귀하의 Rails 4 답변이 작동하지 않습니다. id아직 10입니다.
Rick Smith

주어진 예에서 @RickSmith는 id10으로 전달되었으므로 정확히 그래야합니다. 예상했던 것이 아니라면 예를 들어 설명해 주시겠습니까?
PinnyM

죄송합니다 . 아직 10이 아닙니다 . 설명은 내 대답을 참조하십시오.
Rick Smith

@RickSmith-흥미 롭습니다.이 문제가 MySQL에 고유합니까? 어쨌든 기본 키를 직접 할당하는 일반적인 용도는 시드 데이터입니다. 그렇다면 일반적으로 자동 증가 임계 값 미만의 값을 입력하지 않거나 해당 명령 세트에 대해 자동 증가를 해제해야합니다.
PinnyM 2015 년

0

Postgresql 9.5.3이있는 Rails 4.2.1에서는 Post.create(:id => 10, :title => 'Test')id = 10 인 행이 이미없는 한 작동합니다.


0

SQL로 ID를 삽입 할 수 있습니다.

  arr = record_line.strip.split(",")
  sql = "insert into records(id, created_at, updated_at, count, type_id, cycle, date) values(#{arr[0]},#{arr[1]},#{arr[2]},#{arr[3]},#{arr[4]},#{arr[5]},#{arr[6]})"
  ActiveRecord::Base.connection.execute sql
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.