作成時にモデルのid値をオーバーライドする方法はありますか?何かのようなもの:
Post.create(:id => 10, :title => 'Test')
理想的ですが、明らかに動作しません。
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に移行する場合、すべての外部キー参照を同じに保つのが最も簡単です。
次のようなものも使用できます。
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
docs で述べられているように、これは一括割り当てセキュリティをバイパスします。
試して
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
それはあなたが探しているものを与えるはずです。
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
create
をnew
+ save
を使用するように変更しても、この問題は引き続き発生します。 p.id = 10
のようなid
を手動で変更すると、この問題も発生します。
一般に、update_column
を使用してid
を変更しますが、常に機能するため、余分なデータベースクエリが必要になります。これは開発環境には表示されない可能性のあるエラーですが、動作していると言っている間、本番データベースを静かに破損する可能性があります。
Jeffが指摘しているように、idはattr_protectedのように動作します。これを防ぐには、デフォルトの保護属性のリストをオーバーライドする必要があります。これは、属性情報が外部から取得される可能性がある場所で注意してください。 idフィールドは、理由によりデフォルトで保護されています。
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(ActiveRecord 2.3.5でテスト済み)
実際、次のように動作することがわかりました。
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
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)
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
これを入れて 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"})
このケースは、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">
コールバックは正常に機能します。
がんばろう!。
Rails 4.2.1とPostgresql 9.5.3では、Post.create(:id => 10, :title => 'Test')
は、id = 10の行が存在しない限り機能します。
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}`
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