ActiveRecord'da oluşturmada kimliği geçersiz kılma


104

Oluşturma sırasında bir modelin kimlik değerini geçersiz kılmanın bir yolu var mı? Gibi bir şey:

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

ideal olurdu, ama belli ki işe yaramayacak.


1
Bu yanıtların çoğu, Rails 4 ile ara sıra başarısız olacak ve işe yaradığını söyleyecektir. Bkz Cevabımı bir açıklamada,.
Rick Smith

Yanıtlar:


116

id sadece attr_protected, bu yüzden onu ayarlamak için toplu atamayı kullanamazsınız. Bununla birlikte, manuel olarak ayarlarken, sadece çalışır:

o = SomeObject.new
o.id = 8888
o.save!
o.reload.id # => 8888

Orijinal motivasyonun ne olduğundan emin değilim, ancak bunu ActiveHash modellerini ActiveRecord'a dönüştürürken yapıyorum. ActiveHash, ActiveRecord'da aynı names_to semantiğini kullanmanıza izin verir, ancak bir geçiş yapmak ve bir tablo oluşturmak ve her aramada veritabanının ek yükünü üstlenmek yerine, verilerinizi yml dosyalarında depolamanız yeterlidir. Veritabanındaki yabancı anahtarlar yml'deki bellek içi kimliklere başvurur.

ActiveHash, seyrek olarak değişen ve yalnızca geliştiriciler tarafından değişen seçim listeleri ve küçük tablolar için mükemmeldir. Bu nedenle, ActiveHash'tan ActiveRecord'a geçerken, tüm yabancı anahtar referanslarını aynı tutmak en kolayıdır.


@jkndrkn - Ne demek istediğini bilmiyorum. Buraya geldim ActiveRecord::VERSION::STRING == "3.2.11"(sqlite3 adaptörüyle) ve yukarıdakiler benim için çalışıyor.
Felix Rabe

Belki de yaşadıklarım sadece mysql driver ile ifade edilmiştir.
jkndrkn

@jkndrkn Rails için MySQL sürücüsünü kullanarak benim için çalışıyor 3.2.18
lulalala

benim için çalıştı. hayatımı kurtardı. raylarda kullanılır 4.0.0 / postgres 9.3.5
allenwlee

Bunun nasıl yapılacağına dair cevabımı Rails 4'te görün.
Rick Smith

30

Deneyin

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

bu size aradığınız şeyi vermelidir.


2
değil emin downvoted alıyoruz neden, bu benim için harika çalışıyor
semanticart

Activerecord 3.2.11'den itibaren çalışmaya devam ediyor. Jeff Dean tarafından 2 Ekim 2009'da gönderilen cevap artık işe yaramıyor.
jkndrkn

çalışmıyor, sadece çalışıyor gibi görünüyor çünkü p.save çağrısı, muhtemelen yanlış döndürüyor. p.save! çalıştırırsanız bir ActiveRecord :: RecordNotFound alacaktır!
Alan

29

Bunun gibi bir şey de kullanabilirsiniz:

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

Belgelerde belirtildiği gibi , bu toplu atama güvenliğini atlayacaktır.


Güzel keşif @ Samuel Heaney, bunun activerecord 3.2.13 ile mükemmel çalıştığını doğrulayabilirim.
mkralla11

Benim için de çalıştı; ayrı bir alan eklemek gerekli değildi.
shalott

2
Ne yazık ki, bu artık Rails 4'te çalışmıyor, seçenekler karmasını kaldırdılar
Jorge Sampayo 16'14

@JorgeSampayo - Rails 4'te, korumalı özellikler StrongParams lehine kaldırılabildiğinden buna ihtiyacınız olmamalıdır.
PinnyM

21

Raylar 4 için:

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

Diğer Raylar 4 cevaplar vermedi değil benim için çalış. Birçoğu Rails Konsolunu kontrol ederken değişiyor gibi görünüyordu , ancak MySQL veritabanındaki değerleri kontrol ettiğimde değişmeden kaldılar. Diğer cevaplar sadece bazen işe yaradı.

MySQL için en azından atanırken idotomatik artış id numarasının altında yok değil kullandığınız sürece işe update_column. Örneğin,

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

Eğer değiştirirseniz createkullanmak new+ savehala bu sorun olacaktır. El ile değişen idgibi p.id = 10de bu sorunu üretir.

Genel olarak, fazladan bir veritabanı sorgusuna mal olsa bile, her zaman çalışacağı update_columniçin değiştirmek için kullanırdım id. Bu, geliştirme ortamınızda görünmeyebilecek bir hatadır, ancak çalıştığını söylerken üretim veritabanınızı sessizce bozabilir.


3
Konsolda 3.2.22 raylar durumunda çalışmasını sağlamamın tek yolu da buydu. 'Kaydet'e ilişkin varyasyonları kullanan cevapların hiçbir etkisi olmadı.
JosephK

2
4+ raylar için kullanıyorum:Post.new.update(id: 10, title: 'Test')
Spencer

6

Aslında şu işe yaradığı ortaya çıktı:

p = Post.new(:id => 10, :title => 'Test')
p.save(false)

Çalışabilir olsa da, tüm doğrulamaları da kapatır, bu da istenildiği gibi olmayabilir.
Jordan Moncharmont

Kimliklerin önemli olduğu tohum verileri için ihtiyacım olan şey tam olarak buydu. Teşekkür ederim.
JD.

Başlangıçta, @JD'nin belirttiği gibi, tohum verileri için işe yarayacağını düşünerek oy verdim, ancak daha sonra bunu activerecord 3.2.13 ile denedim ve hala "Korunan öznitelikleri atayamıyorum" hatası alıyorum. Yani, olumsuz oy verildi :(
mkralla11

1
Ne yazık ki, bu raylar 4'te çalışmıyor, NoMethodError: undefined method “[] 'karşılığını alıyorsunuz: FalseClass
Jorge

@JorgeSampayo Bu validate: false, basitçe yerine geçerseniz yine de çalışır false. Ancak yine de korumalı öznitelik sorunuyla karşılaşıyorsunuz - cevabımda ana hatlarıyla açıkladığım ayrı bir yol var.
PinnyM

6

Jeff'in işaret ettiği gibi, id attr_protectedmış gibi davranır. Bunu önlemek için, varsayılan korumalı öznitelikler listesini geçersiz kılmanız gerekir. Öznitelik bilgilerinin dışarıdan gelebileceği her yerde bunu yaparken dikkatli olun. Kimlik alanı bir nedenle varsayılan olarak korunmaktadır.

class Post < ActiveRecord::Base

  private

  def attributes_protected_by_default
    []
  end
end

(ActiveRecord 2.3.5 ile test edilmiştir)


6

nitelikleri geçersiz kılabiliriz_protected_by_varsayılan

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 }

Bu bana normalde yapmak isteyeceğiniz türden bir şey olarak gelmiyor, ancak bir tabloyu sabit bir kimlikle doldurmanız gerekiyorsa (örneğin, bir rake görevi kullanarak varsayılanlar oluştururken) ve siz otomatik artırmayı geçersiz kılmak istiyorsanız (böylece görevi her çalıştırdığınızda tablo aynı kimliklerle doldurulur):

post_types.each_with_index do |post_type|
  PostType.create!(:name => post_type) { |t| t.id = i + 1 }
end

2

Bu create_with_id işlevini seeds.rb'nizin en üstüne koyun ve sonra bunu, açık kimliklerin istendiği nesne oluşturma işleminizi yapmak için kullanın.

def create_with_id(clazz, params)
obj = clazz.send(:new, params)
obj.id = params[:id]
obj.save!
    obj
end

ve böyle kullan

create_with_id( Foo, {id:1,name:"My Foo",prop:"My other property"})

kullanmak yerine

Foo.create({id:1,name:"My Foo",prop:"My other property"})


2

Bu vaka, bir idtür özel tarih ile üzerine yazılması gereken benzer bir sorundur :

# 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

Ve sonra :

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

Geri aramalar iyi çalışıyor.

İyi şanslar!.


idUnix'e dayalı bir zaman damgası oluşturmam gerekiyordu . Bunu içeride yaptım before_create. İyi çalışıyor.
WM

0

Rails 3 için bunu yapmanın en basit yolu new, without_protectioniyileştirme ile kullanmak ve ardından save:

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

Tohum verileri için, şu şekilde yapabileceğiniz doğrulamayı atlamak mantıklı olabilir:

Post.new({:id => 10, :title => 'Test'}, :without_protection => true).save(validate: false)

Aslında ActiveRecord :: Base'e çekirdek dosyaları çalıştırmadan hemen önce bildirilen bir yardımcı yöntem ekledik:

class ActiveRecord::Base
  def self.seed_create(attributes)
    new(attributes, without_protection: true).save(validate: false)
  end
end

Ve şimdi:

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

Rails 4 için, korumalı öznitelikler yerine StrongParams kullanmalısınız. Bu durumda, şunlara herhangi bir bayrak iletmeden kolayca atayabilir ve kaydedebilirsiniz new:

Post.new(id: 10, title: 'Test').save      # optionally pass `{validate: false}`

{}Yukarıdaki Samuel'in cevabı olarak nitelikler koymadan benim için işe yaramıyor (Rails3).
Christopher Oezbek

@PinnyM Your Rails 4 cevabı benim için çalışmıyor. idhala 10
Rick Smith

Verilen örnekte @RickSmith, id10 olarak geçti - yani tam olarak olması gereken bu. Beklediğiniz bu değilse, bir örnekle açıklayabilir misiniz?
PinnyM

Üzgünüm, hala 10 değil demek istedim . Açıklama için cevabıma bakın.
Rick Smith

@RickSmith - ilginç, bu problem MySQL'e özgü mü? Her durumda, birincil anahtarları doğrudan atamanın genel kullanımı çekirdek verileri içindir. Öyleyse, genellikle otomatik artış eşiğinin altındaki değerleri girmeye ÇALIŞMAMALISINIZ veya bu komut seti için otomatik artırmayı kapatmalısınız.
PinnyM

0

Postgresql 9.5.3 ile Rails 4.2.1'de, Post.create(:id => 10, :title => 'Test')id = 10 olan bir satır olmadığı sürece çalışır.


0

kimliği sql ile ekleyebilirsiniz:

  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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.