ActiveRecordでの作成時のIDのオーバーライド


104

作成時にモデルのID値をオーバーライドする方法はありますか?何かのようなもの:

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

理想的ですが、明らかに機能しません。


1
これらの回答の多くは、断続的にRails 4で失敗し、機能していると言われます。説明については私の回答を参照しください。
リックスミス

回答:


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

おそらく、私が経験したことは、mysqlドライバーでのみ表現されました。
jkndrkn 2013

@jkndrknは、Rails 3.2.18のMySQLドライバーを使用して私のために動作します
lulalala

私のために働いた。私を助けた。レール4.0.0 / postgres 9.3.5で使用
allenwlee 2014年

参照してください私の答えのRails 4でこれを行う方法について
リック・スミスの

30

試す

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

それはあなたが探しているものをあなたに与えるはずです。


2
なぜあなたが反対票を投じているのかわからない、これは私にとってはうまくいく
semanticart

これは、activerecord 3.2.11から引き続き機能します。2009年10月2日にジェフ・ディーンが投稿した回答は機能しなくなりました。
jkndrkn 2013年

動作しません。おそらくfalseを返すp.saveを呼び出すため、動作するようです。あなたがp.saveを実行した場合、ActiveRecord :: RecordNotFoundを取得します!
2014年

29

次のようなものを使用することもできます。

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

docsで述べられているように、これは大量割り当てセキュリティをバイパスします。


素敵な発見、@ Samuel Heaney、これがactiverecord 3.2.13で完全に機能することを確認できます。
mkralla11 2013

私のためにも働いた。別のフィールドを含める必要はありませんでした。
shalott 2014年

2
悲しいかな、これはもはやRails 4では機能しません。オプションハッシュを削除しました
Jorge Sampayo 2014年

@JorgeSampayo-Rails 4では、StrongParamsを優先して保護された属性を削除できるため、これは必要ありません。
PinnyM 2015

21

Rails 4の場合:

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

その他のRails 4つの答えはなかったではない私のために働きます。それらの多くはRailsコンソールを使用して確認すると変更されたように見えましたが、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 2016年

2
4歳以上のレールの場合、次を使用します。– 2017Post.new.update(id: 10, title: 'Test')
スペンサー

6

実際、次の作業を行うことがわかりました。

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

それは機能するかもしれませんが、すべての検証もオフにします。これは、質問が意図したものではない可能性があります。
ジョーダンモンチャーモント2012

これはまさに、IDが重要なシードデータに必要なものです。ありがとうございました。
JD。

@JDが指摘したように、元々これはシードデータで機能すると考えて賛成していましたが、次にactiverecord 3.2.13で試してみましたが、「保護された属性を割り当てられません」というエラーが表示されます。したがって、:(
mkralla11 2013

1
残念ながら、これはRails 4では機能しません。NoMethodError:undefined method `[] 'for false:FalseClass
Jorge Sampayo

@JorgeSampayoこれvalidate: falseは、単に渡すのではなく、渡す場合でも機能しますfalse。ただし、保護された属性の問題は引き続き発生します-私が私の回答で概説した別の方法があります。
PinnyM 2015

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の固定セットをテーブルに入力する必要がある場合(たとえば、rakeタスクを使用してデフォルトを作成する場合)、自動インクリメントを上書きしたい(タスクを実行するたびにテーブルに同じIDが入力されるようにする):

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

2

このcreate_with_id関数をseeds.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}`

{}上記のサミュエルの答え(Rails3)のように属性を入れないと私にとってはうまくいきません。
Christopher Oezbek

@PinnyM Your Ra​​ils 4の答えは私にはうまくいきません。 idまだ10です
リック・スミス

与えられた例では@RickSmithはid10として渡されました-それはまさにそれがそうあるべきものです。それがあなたが期待していたものではない場合、例で明確にできますか?
PinnyM

申し訳ありませんが、私はまだ言うことを意味するものではない 10。説明については私の回答を参照してください。
リックスミス

@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.