Có cách nào để ghi đè giá trị id của mô hình khi tạo không? Cái gì đó như:
Post.create(:id => 10, :title => 'Test')
sẽ là lý tưởng, nhưng rõ ràng sẽ không hoạt động.
Có cách nào để ghi đè giá trị id của mô hình khi tạo không? Cái gì đó như:
Post.create(:id => 10, :title => 'Test')
sẽ là lý tưởng, nhưng rõ ràng sẽ không hoạt động.
Câu trả lời:
id chỉ là attr_protected, đó là lý do tại sao bạn không thể sử dụng tính năng gán hàng loạt để đặt nó. Tuy nhiên, khi thiết lập thủ công, nó chỉ hoạt động:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
Tôi không chắc động cơ ban đầu là gì, nhưng tôi thực hiện điều này khi chuyển đổi các mô hình ActiveHash thành ActiveRecord. ActiveHash cho phép bạn sử dụng ngữ nghĩa tương tự thuộc về thuộc tính trong ActiveRecord, nhưng thay vì phải di chuyển và tạo bảng, đồng thời phát sinh chi phí cơ sở dữ liệu trong mỗi lần gọi, bạn chỉ cần lưu trữ dữ liệu của mình trong các tệp yml. Các khóa ngoại trong cơ sở dữ liệu tham chiếu đến id trong bộ nhớ trong yml.
ActiveHash rất phù hợp cho các danh sách chọn lọc và các bảng nhỏ thay đổi không thường xuyên và chỉ thay đổi bởi các nhà phát triển. Vì vậy, khi chuyển từ ActiveHash sang ActiveRecord, đơn giản nhất là chỉ cần giữ nguyên tất cả các tham chiếu khóa ngoại.
ActiveRecord::VERSION::STRING == "3.2.11"
ở đây (với bộ điều hợp sqlite3) và những điều trên phù hợp với tôi.
Thử
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
điều đó sẽ cung cấp cho bạn những gì bạn đang tìm kiếm.
Bạn cũng có thể sử dụng một cái gì đó như thế này:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Mặc dù như đã nêu trong tài liệu , điều này sẽ bỏ qua bảo mật chuyển nhượng hàng loạt.
Đối với Rails 4:
Post.create(:title => 'Test').update_column(:id, 10)
Câu trả lời Rails 4 khác không phù hợp với tôi. Nhiều người trong số họ dường như thay đổi khi kiểm tra bằng Rails Console, nhưng khi tôi kiểm tra các giá trị trong cơ sở dữ liệu MySQL, chúng vẫn không thay đổi. Các câu trả lời khác đôi khi chỉ hoạt động.
Đối với MySQL ít nhất, việc gán id
số id tự động bên dưới không hoạt động trừ khi bạn sử dụng update_column
. Ví dụ,
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
Nếu bạn đổi create
sang sử dụng new
+ save
bạn sẽ vẫn gặp sự cố này. Thay đổi thủ công id
tương tự p.id = 10
cũng tạo ra vấn đề này.
Nói chung, tôi sẽ sử dụng update_column
để thay đổi id
mặc dù nó tốn thêm một truy vấn cơ sở dữ liệu vì nó sẽ hoạt động mọi lúc. Đây là một lỗi có thể không hiển thị trong môi trường phát triển của bạn, nhưng có thể âm thầm làm hỏng cơ sở dữ liệu sản xuất của bạn trong khi nói rằng nó đang hoạt động.
Post.new.update(id: 10, title: 'Test')
Trên thực tế, nó chỉ ra rằng làm những việc sau đây:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, thay vì đơn giản false
. Tuy nhiên, bạn vẫn gặp phải vấn đề thuộc tính được bảo vệ - có một cách riêng để giải quyết vấn đề mà tôi đã nêu trong câu trả lời của mình.
Như Jeff đã chỉ ra, id hoạt động như thể được bảo vệ bởi attr_protected. Để ngăn chặn điều đó, bạn cần ghi đè danh sách các thuộc tính được bảo vệ mặc định. Hãy cẩn thận làm điều này ở bất kỳ nơi nào mà thông tin thuộc tính có thể đến từ bên ngoài. Trường id được bảo vệ mặc định vì một lý do.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Đã kiểm tra với ActiveRecord 2.3.5)
chúng ta có thể ghi đè thuộc tính_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 }
Đây không phải là điều mà bạn thường muốn làm, nhưng nó hoạt động khá tốt nếu bạn cần điền vào một bảng với một tập hợp id cố định (ví dụ: khi tạo giá trị mặc định bằng tác vụ cào) và bạn muốn ghi đè tính năng tự động tăng dần (để mỗi khi bạn chạy tác vụ, bảng sẽ được điền với các id giống nhau):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Đặt hàm create_with_id này ở trên cùng của seed.rb và sau đó sử dụng nó để tạo đối tượng ở những nơi bạn muốn có id rõ ràng.
def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
obj
end
và sử dụng nó như thế này
create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})
thay vì sử dụng
Foo.create({id:1,name:"My Foo",prop:"My other property"})
Trường hợp này là một vấn đề tương tự cần thiết phải ghi đè lên id
bằng một loại ngày tùy chỉnh:
# 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
Và sau đó :
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">
Gọi lại hoạt động tốt.
Chúc may mắn!.
id
thời gian dựa trên Unix. Tôi đã làm điều đó bên trong before_create
. Hoạt động tốt.
Đối với Rails 3, cách đơn giản nhất để làm điều này là sử dụng new
với sự without_protection
tinh chỉnh, sau đó save
:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
Đối với dữ liệu hạt giống, bạn có thể bỏ qua xác thực mà bạn có thể làm như sau:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
Chúng tôi thực sự đã thêm một phương thức trợ giúp vào ActiveRecord :: Base được khai báo ngay trước khi thực thi các tệp hạt giống:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
Và bây giờ:
Post.seed_create(:id => 10, :title => 'Test')
Đối với Rails 4, bạn nên sử dụng StrongParams thay vì các thuộc tính được bảo vệ. Nếu đúng như vậy, bạn chỉ cần gán và lưu mà không cần chuyển bất kỳ cờ nào cho new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
{}
như câu trả lời của Samuel ở trên (Rails3).
id
vẫn là 10 tuổi
id
được chuyển là 10 - vì vậy đó chính xác là những gì nó phải như vậy. Nếu đó không phải là điều bạn mong đợi, bạn có thể làm rõ bằng một ví dụ không?