Чи є якийсь спосіб змінити значення id моделі при створенні? Щось на зразок:
Post.create(:id => 10, :title => 'Test')
було б ідеально, але, очевидно, не вийде.
Чи є якийсь спосіб змінити значення 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 дозволяє використовувати ту саму семантику last_to в ActiveRecord, але замість міграції та створення таблиці та здійснення накладних витрат на базу даних при кожному виклику ви просто зберігаєте свої дані у файлах yml. Іноземні ключі в базі даних посилаються на ідентифікатори в пам'яті в yml.
ActiveHash чудово підходить для списків вибору та невеликих таблиць, які змінюються нечасто і змінюються лише розробниками. Тож, переходячи з ActiveHash до ActiveRecord, найпростіше просто зберегти всі посилання іноземних ключів однаковими.
ActiveRecord::VERSION::STRING == "3.2.11"
сюди (з адаптером sqlite3) і вищезгадане працює для мене.
Спробуйте
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
це повинно дати вам те, що ви шукаєте.
Ви також можете використовувати щось подібне:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Хоча, як зазначено в документах , це обійде безпеку масового призначення.
Для рейок 4:
Post.create(:title => 'Test').update_column(:id, 10)
Інші відповіді Rails 4 для мене не спрацювали. Багато з них з’явилося змінювався під час перевірки за допомогою консолі Rails, але коли я перевіряв значення в базі даних 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
хоча це коштує додатковий запит до бази даних, оскільки він буде працювати весь час. Це помилка, яка може не з’являтися у вашому середовищі розробки, але може спокійно пошкодити вашу виробничу базу даних, говорячи, що вона працює.
Post.new.update(id: 10, title: 'Test')
Власне, виявляється, що виконуючи такі роботи:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, а не просто false
. Однак ви все ще стикаєтесь із проблемою захищеного атрибуту - існує окремий шлях, який я окреслив у своїй відповіді.
Як зазначає Джефф, id поводиться так, ніби захищеноtrtr_protected. Щоб цього не допустити, вам потрібно змінити список захищених за замовчуванням атрибутів. Будьте обережні, роблячи це де завгодно, щоб інформація про атрибути могла надходити ззовні. Поле id за замовчуванням захищено з причини.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Тестовано з ActiveRecord 2.3.5)
ми можемо замінити атрибути_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 }
Це не сприймає мене як те, що ви зазвичай хотіли б зробити, але це працює досить добре, якщо вам потрібно заповнити таблицю з фіксованим набором ідентифікаторів (наприклад, коли створюєте параметри за замовчуванням за допомогою завдання граблі), і ви хочете змінити автоматичне збільшення (щоб кожен раз, коли ви виконуєте завдання, таблиця заповнювалася однаковими ідентифікаторами):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Поставте цю функцію 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"})
Цей випадок є подібною проблемою, яку потрібно було перезаписати 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
. Добре працює.
Для 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, який оголошується безпосередньо перед виконанням початкових файлів:
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).
id
ще 10.
id
було позначено як 10 - тож саме це має бути. Якщо це не те, чого ви очікували, чи можете ви уточнити на прикладі?