web-dev-qa-db-ja.com

ActiveRecordの作成時にIDをオーバーライドする

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

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

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

103
Codebeef

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に移行する場合、すべての外部キー参照を同じに保つのが最も簡単です。

117
Jeff Dean

次のようなものも使用できます。

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

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

29
Samuel Heaney

試して

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

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

29
PJ Davis

Rails 4:

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

その他Rails 4つの答えはnot私のために働いた。それらの多くが現れた Rails Consoleを使用してチェックするとき、MySQLデータベースの値をチェックするとき、それらは変更されないままでした。他の答えは時々しか働きませんでした。

少なくともMySQLでは、idを自動インクリメントID番号の下に割り当てると、update_columnを使用しない限り、notが機能します。例えば、

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

createnew + saveを使用するように変更しても、この問題は引き続き発生します。 p.id = 10のようなidを手動で変更すると、この問題も発生します。

一般に、update_columnを使用してidを変更しますが、常に機能するため、余分なデータベースクエリが必要になります。これは開発環境には表示されない可能性のあるエラーですが、動作していると言っている間、本番データベースを静かに破損する可能性があります。

20
Rick Smith

Jeffが指摘しているように、idはattr_protectedのように動作します。これを防ぐには、デフォルトの保護属性のリストをオーバーライドする必要があります。これは、属性情報が外部から取得される可能性がある場所で注意してください。 idフィールドは、理由によりデフォルトで保護されています。

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(ActiveRecord 2.3.5でテスト済み)

6
Nic Benders

実際、次のように動作することがわかりました。

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

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)
6
user510319
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
5
Sean Cameron

これを入れて 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
Darren Hicks

このケースは、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">

コールバックは正常に機能します。

がんばろう!。

1

Rails 4.2.1とPostgresql 9.5.3では、Post.create(:id => 10, :title => 'Test')は、id = 10の行が存在しない限り機能します。

0
Nikola

Rails 3)の場合、これを行う最も簡単な方法は、without_protectionリファインメントでnewを使用し、次に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}`
0
PinnyM

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
0
wxianfeng