การลบล้าง id ในการสร้างใน ActiveRecord


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 แต่แทนที่จะมีการโยกย้ายและสร้างตารางและทำให้เกิดค่าใช้จ่ายของฐานข้อมูลในทุกการโทรคุณเพียงแค่จัดเก็บข้อมูลของคุณในไฟล์ yml คีย์นอกในฐานข้อมูลอ้างอิงรหัสในหน่วยความจำใน yml

ActiveHash เหมาะสำหรับรายการเลือกและตารางขนาดเล็กที่มีการเปลี่ยนแปลงไม่บ่อยนักและมีการเปลี่ยนแปลงโดยนักพัฒนาเท่านั้น ดังนั้นเมื่อเปลี่ยนจาก ActiveHash เป็น ActiveRecord วิธีที่ง่ายที่สุดก็คือให้การอ้างอิงคีย์ต่างประเทศทั้งหมดเหมือนกัน


@jkndrkn - ฉันไม่รู้ว่าคุณหมายถึงอะไร ฉันมาถึงActiveRecord::VERSION::STRING == "3.2.11"ที่นี่แล้ว (ด้วยอะแดปเตอร์ sqlite3) และข้างต้นใช้ได้กับฉัน
Felix Rabe

บางทีสิ่งที่ฉันพบก็แสดงออกมาด้วยไดรเวอร์ mysql เท่านั้น
jkndrkn

@jkndrkn ใช้งานได้สำหรับฉันโดยใช้ไดรเวอร์ MySQL สำหรับ Rails 3.2.18
lulalala

ทำงานให้ฉัน ช่วยชีวิตฉันไว้. ใช้กับราง 4.0.0 / postgres 9.3.5
allenwlee

ดูคำตอบของฉันสำหรับวิธีการทำใน Rails 4
Rick Smith

30

ลอง

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

ที่ควรให้สิ่งที่คุณกำลังมองหา


2
ไม่แน่ใจว่าทำไมคุณถึงถูกลดคะแนนซึ่งมันใช้ได้ดีสำหรับฉัน
semanticart

สิ่งนี้ยังคงใช้งานได้เหมือน Activerecord 3.2.11 คำตอบที่โพสต์โดย Jeff Dean เมื่อวันที่ 2 ตุลาคม 2009 ใช้ไม่ได้อีกต่อไป
jkndrkn

ใช้งานไม่ได้ดูเหมือนจะใช้งานได้เพราะเรียก p.save ซึ่งอาจส่งคืนเท็จ จะได้รับ ActiveRecord :: RecordNotFound ถ้าคุณวิ่ง p.save!
Alan

29

คุณยังสามารถใช้สิ่งนี้:

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

แม้ว่าตามที่ระบุไว้ในเอกสารนี้จะข้ามการรักษาความปลอดภัยการมอบหมายงานจำนวนมาก


ยินดีด้วย @Samuel Heaney ฉันสามารถตรวจสอบได้ว่าสิ่งนี้ทำงานได้อย่างสมบูรณ์ด้วย activerecord 3.2.13
mkralla11

ทำงานให้ฉันเช่นกัน; ไม่จำเป็นต้องรวมฟิลด์แยกต่างหาก
shalott

2
อนิจจาสิ่งนี้ใช้ไม่ได้กับ Rails 4 อีกต่อไปพวกเขาลบแฮชตัวเลือก
Jorge Sampayo

@JorgeSampayo - ใน Rails 4 คุณไม่จำเป็นต้องใช้สิ่งนี้เนื่องจากแอตทริบิวต์ที่ได้รับการป้องกันสามารถลบออกได้โดยใช้ StrongParams
PinnyM

21

สำหรับ Rails 4:

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

คำตอบอื่น ๆ Rails 4 ไม่ได้ผลสำหรับฉัน หลายคนดูเหมือนจะเปลี่ยนไปเมื่อตรวจสอบโดยใช้ Rails Console แต่เมื่อฉันตรวจสอบค่าในฐานข้อมูล MySQL ค่านั้นก็ยังไม่เปลี่ยนแปลง คำตอบอื่น ๆ ใช้ได้ผลในบางครั้งเท่านั้น

สำหรับ MySQL อย่างน้อยการกำหนด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ไปใช้new+ saveคุณจะยังคงมีปัญหานี้ การเปลี่ยนสิ่งที่idชอบด้วยตนเองp.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 ชี้ให้เห็น แต่จากนั้นฉันก็ลองใช้ activerecord 3.2.13 และฉันยังคงได้รับข้อผิดพลาด "ไม่สามารถกำหนดแอตทริบิวต์ที่มีการป้องกัน" ได้ ดังนั้น
โหวตลง

1
น่าเสียดายที่สิ่งนี้ใช้ไม่ได้ในราง 4 คุณได้รับ NoMethodError: วิธีการที่ไม่ได้กำหนด `` [] 'สำหรับเท็จ: FalseClass
Jorge Sampayo

@JorgeSampayo นี้ยังคงทำงานถ้าคุณผ่านแทนที่จะvalidate: false falseอย่างไรก็ตามคุณยังคงพบปัญหาแอตทริบิวต์ที่มีการป้องกัน - มีวิธีแยกต่างหากซึ่งฉันได้ระบุไว้ในคำตอบของฉัน
PinnyM

6

ดังที่เจฟฟ์ชี้ให้เห็น 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 }

สิ่งนี้ไม่ได้ตีฉันว่าเป็นสิ่งที่คุณต้องการทำตามปกติ แต่จะทำงานได้ดีถ้าคุณต้องการเติมข้อมูลในตารางด้วยชุดรหัสคงที่ (เช่นเมื่อสร้างค่าเริ่มต้นโดยใช้งานคราด) และคุณ ต้องการลบล้างการเพิ่มขึ้นอัตโนมัติ (เพื่อให้ทุกครั้งที่คุณรันงานตารางจะถูกเติมด้วยรหัสเดียวกัน):

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 ของคุณจากนั้นใช้เพื่อสร้างอ็อบเจกต์ของคุณในที่ที่ต้องการรหัสที่ชัดเจน

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">

การโทรกลับทำงานได้ดี

โชคดี!.


ฉันต้องการสร้างการidประทับเวลาตาม Unix ฉันได้ทำภายในbefore_createแล้ว ใช้งานได้ดี
WM

0

สำหรับ Rails 3 วิธีที่ง่ายที่สุดคือใช้newกับการwithout_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 ที่ประกาศทันทีก่อนที่จะเรียกใช้ไฟล์ seed:

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 Rails 4 คำตอบไม่ได้ผลสำหรับฉัน idยังคงเป็น 10
Rick Smith

@RickSmith ในตัวอย่างที่ระบุidถูกส่งผ่านเป็น 10 - นั่นคือสิ่งที่ควรจะเป็น หากนั่นไม่ใช่สิ่งที่คุณคาดหวังคุณสามารถอธิบายด้วยตัวอย่างได้หรือไม่?
PinnyM

ขอโทษนะฉันตั้งใจจะบอกว่ายังไม่ใช่ 10 ดูคำตอบของฉันสำหรับคำอธิบาย
Rick Smith

@RickSmith - น่าสนใจปัญหานี้เฉพาะกับ MySQL หรือไม่? ไม่ว่าในกรณีใดการใช้งานทั่วไปสำหรับการกำหนดคีย์หลักโดยตรงคือสำหรับข้อมูลเมล็ดพันธุ์ ในกรณีนี้คุณไม่ควรพยายามป้อนค่าที่ต่ำกว่าเกณฑ์การเพิ่มอัตโนมัติหรือคุณควรปิดการเพิ่มอัตโนมัติสำหรับชุดคำสั่งนั้น
PinnyM

0

ใน Rails 4.2.1 พร้อม Postgresql 9.5.3 Post.create(:id => 10, :title => 'Test')ทำงานได้ตราบเท่าที่ยังไม่มีแถวที่มี id = 10 อยู่แล้ว


0

คุณสามารถแทรก id โดย sql:

  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.