Czy istnieje sposób na zastąpienie wartości identyfikatora modelu podczas tworzenia? Coś jak:
Post.create(:id => 10, :title => 'Test')
byłby idealny, ale oczywiście nie zadziała.
Czy istnieje sposób na zastąpienie wartości identyfikatora modelu podczas tworzenia? Coś jak:
Post.create(:id => 10, :title => 'Test')
byłby idealny, ale oczywiście nie zadziała.
Odpowiedzi:
id jest po prostu attr_protected, dlatego nie możesz użyć przypisania masowego, aby go ustawić. Jednak przy ręcznym ustawianiu po prostu działa:
o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888
Nie jestem pewien, jaka była pierwotna motywacja, ale robię to podczas konwersji modeli ActiveHash do ActiveRecord. ActiveHash pozwala na użycie tej samej semantyki allowed_to w ActiveRecord, ale zamiast migracji i tworzenia tabeli oraz ponoszenia narzutu bazy danych przy każdym wywołaniu, po prostu przechowujesz dane w plikach yml. Klucze obce w bazie danych odwołują się do identyfikatorów w pamięci w pliku yml.
ActiveHash doskonale nadaje się do list wyboru i małych tabel, które zmieniają się rzadko i zmieniają się tylko przez programistów. Więc przechodząc z ActiveHash do ActiveRecord, najłatwiej jest po prostu zachować te same odniesienia do wszystkich kluczy obcych.
ActiveRecord::VERSION::STRING == "3.2.11"
tutaj (z adapterem sqlite3) i powyższe działa dla mnie.
Próbować
a_post = Post.new do |p|
p.id = 10
p.title = 'Test'
p.save
end
to powinno dać ci to, czego szukasz.
Możesz też użyć czegoś takiego:
Post.create({:id => 10, :title => 'Test'}, :without_protection => true)
Chociaż, jak stwierdzono w dokumentacji , ominie to zabezpieczenia przydziału masowego.
Dla szyn 4:
Post.create(:title => 'Test').update_column(:id, 10)
Inne odpowiedzi Rails 4 nie działały dla mnie. Wiele z nich pojawiła się do zmian podczas sprawdzania za pomocą konsoli Rails, ale gdy sprawdziłem wartości w bazie danych MySQL, pozostały one na niezmienionym poziomie. Inne odpowiedzi tylko czasami działały.
Przynajmniej w przypadku MySQL przypisanie id
poniżej numeru identyfikatora automatycznego zwiększania nie działa, chyba że używasz update_column
. Na przykład,
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
Jeśli zmienisz create
na new
+ save
, nadal będziesz mieć ten problem. Ręczna zmiana id
podobnych p.id = 10
również powoduje ten problem.
Ogólnie rzecz biorąc, użyłbym update_column
do zmiany, id
mimo że kosztuje to dodatkowe zapytanie do bazy danych, ponieważ będzie działać przez cały czas. Jest to błąd, który może nie pojawić się w środowisku programistycznym, ale może dyskretnie uszkodzić produkcyjną bazę danych przez cały czas, gdy mówi, że działa.
Post.new.update(id: 10, title: 'Test')
Właściwie okazuje się, że wykonanie następujących czynności:
p = Post.new(:id => 10, :title => 'Test')
p.save(false)
validate: false
, zamiast po prostu false
. Jednak nadal napotykasz problem z atrybutem chronionym - istnieje oddzielny sposób na obejście tego, który przedstawiłem w mojej odpowiedzi.
Jak zauważa Jeff, id zachowuje się tak, jakby był attr_protected. Aby temu zapobiec, musisz zastąpić listę domyślnych atrybutów chronionych. Zachowaj ostrożność, robiąc to wszędzie tam, gdzie informacje o atrybutach mogą pochodzić z zewnątrz. Pole id jest domyślnie chronione z jakiegoś powodu.
class Post < ActiveRecord::Base
private
def attributes_protected_by_default
[]
end
end
(Testowane z ActiveRecord 2.3.5)
możemy zastąpić attribute_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 }
Nie wydaje mi się to czymś, co normalnie chciałbyś zrobić, ale działa całkiem dobrze, jeśli musisz wypełnić tabelę ustalonym zestawem identyfikatorów (na przykład podczas tworzenia wartości domyślnych za pomocą zadania rake), a ty chcesz zastąpić autoinkrementację (aby za każdym razem, gdy uruchamiasz zadanie, tabela była zapełniana tymi samymi identyfikatorami):
post_types.each_with_index do |post_type|
PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end
Umieść tę funkcję create_with_id na początku pliku seeds.rb, a następnie użyj jej do utworzenia obiektu, w którym wymagane są jawne identyfikatory.
def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
obj
end
i używaj go w ten sposób
create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})
zamiast używać
Foo.create({id:1,name:"My Foo",prop:"My other property"})
W tym przypadku jest podobny problem, który należało nadpisać id
jakimś rodzajem niestandardowej daty:
# 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
I wtedy :
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">
Callback działa dobrze.
Powodzenia!.
id
oparty na Uniksie znacznik czasu. Zrobiłem to w środku before_create
. Działa w porządku.
W przypadku Rails 3 najprostszym sposobem na to jest użycie new
z without_protection
udoskonaleniem, a następnie save
:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save
W przypadku danych początkowych sensowne może być ominięcie weryfikacji, którą można zrobić w ten sposób:
Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)
W rzeczywistości dodaliśmy metodę pomocniczą do ActiveRecord :: Base, która jest deklarowana bezpośrednio przed wykonaniem plików źródłowych:
class ActiveRecord::Base
def self.seed_create(attributes)
new(attributes, without_protection: true).save(validate: false)
end
end
I teraz:
Post.seed_create(:id => 10, :title => 'Test')
W Railsach 4 powinieneś używać StrongParams zamiast chronionych atrybutów. W takim przypadku będziesz mógł po prostu przypisać i zapisać bez przekazywania żadnych flag do new
:
Post.new(id: 10, title: 'Test').save # optionally pass `{validate: false}`
{}
odpowiedzi Samuela powyżej (Rails3).
id
jest nadal 10.
id
zostało przekazane jako 10 - więc dokładnie tak powinno być. Jeśli nie tego się spodziewałeś, czy możesz wyjaśnić to na przykładzie?