6

Bir istisnayı kurtarmanın ve işlemeye devam etmenin doğru yolu nedir? Klasörler ve Öğeler içeren bir uygulamamız var. Bu tablonun yinelenen öğe/klasör kombinasyonları olmadığından emin olmak için benzersiz bir kısıtlaması vardır. Kullanıcı birkaç kez aynı klasöre bir öğe eklemeyi denerse, açıkça ek satırların eklenmesini istemiyorum; ama ben de işlemeyi durdurmak istemiyorum.Raylar 3 yok sayılır Postgres benzersiz kısıtlama istisnası

Postgres otomatik olarak benzersiz kısıtlamayı ihlal edilen bir durum atar, bu yüzden aşağıdaki gibi denetleyicisi bunu görmezden çalıştı: Bu tek eklemeler üzerinde çalışıyor

rescue PG::Error, :with => :do_nothing 

def do_nothing 

end 

. Denetleyici, render'i 200 durum koduyla yürütür. Ancak, bir döngüde yığın ekler yapan başka bir yönteme sahibim. Bu yöntemde, denetleyici, ilk yinelenen satırla karşılaştığında döngüden çıkar, istediğim bu değil. İlk başta, döngünün geri alınacak bir işlemin sarılmış olması gerektiğini düşündüm, ancak bu değil - yinelenen tüm satırlar eklensin. Kısıtlama istisnasını görmezden gelmek ve bir sonraki maddeye geçmek istiyorum. PG :: Error istisnasının bunu kesintiye uğratmasını nasıl engellerim?

cevap

11

Genel olarak, istisna durumunuz, istisna ile mantıklı bir şey yapabileceğiniz hatanın en yakın noktasında olmalıdır. senin durumda, örneğin, döngü içinde senin rescue isterdim: ilgi

stuff.each do |h| 
    begin 
    Model.create(h) 
    rescue ActiveRecord::RecordNotUnique => e 
    next if(e.message =~ /unique.*constraint.*INDEX_NAME_GOES_HERE/) 
    raise 
    end 
end 

Birkaç puan:

  1. veritabanı içinde bir kısıtlama ihlali size bir ActiveRecord::RecordNotUnique hata vermek yerine olacaktır Altta yatan PG::Error. AFAIK, ActiveRecord'a geçmek yerine doğrudan veritabanına konuşuyor olsaydınız bir PG::Error alırsınız.
  2. Benzersiz dizinin gerçek adıyla INDEX_NAME_GOES_HERE'u değiştirin.
  3. Yalnızca beklediğiniz belirli kısıtlama ihlali, yani next if(...) bit ve ardından bağımsız olarak raise (yani, görmeyi beklediğiniz şey değilse istisnayı yeniden yükseltin) ihmal etmek istiyorsunuz.
+0

Teşekkürler, mu. İşe yaramış gibi görünüyor. Rescue_with olarak adlandırılan do_nothing yöntemine 'next' yerleştirmeyi denedim, ancak bu bana bir hata verdi. Her durumda, kesinlikle bir PG :: Hatası ve doğrudan db ile konuşmuyorum. Özel bir SQL eki içermeyen sade bir eski habtm birleşim tablosu. Belki de ActiveRecord :: RecordNotUnique sadece model tablolara çağrılır? –

+0

Çok erken konuştum. Tekrar test edildi ve kesinlikle karşılaştığı ilk kopyadaki döngüyü sonlandırıyor. İlginç olan şu ki, kontrolör yönteminde başlangıç ​​/ kurtarma bloğunu ekler ve kontrol cihazından kurtarma programını kaldırırsam istisna hiç bir şekilde yakalanmaz. Yani, bir sebepten dolayı, bu görünüşte hiç kontrolör seviyesinde yakalanmadı. –

+0

Denetleyici düzeyinde yakalamak istemezsiniz, bu istisna hakkında bir şeyler yapabileceğiniz yerden çok uzaktır. Kodunuz neye benziyor? –

1

Modelinize bir Rails doğrulayıcı yerleştirirseniz, özel durum atmadan akışınızı kontrol edebilirsiniz. Bir hata varsa

class FolderItems 
    belongs_to :item 
    belongs_to :folder 
    validates_uniqueness_of :item, scope: [:folder], on: :create 
end 

Sonra, dernek oluşturulmuşsa Bu doğru dönecektir yanlış

FolderItem.create(folder: folder, item: item) 

kullanabilirsiniz. Bir istisna atmayacak. İlişki oluşturulmamışsa, FolderItem.create! kullanmak bir istisna atar.

PG hatalarını görmenizin nedeni, Rails'in modelin kaydetme için geçerli olduğunu düşündüğünden, Rails'in model sınıfının benzersiz bir kısıtlaması olmadığı için. Tabii ki, DB'de Rails'i şaşırtan ve son dakikada havaya uçmasına neden olan benzersiz bir kısıtlama var. Performans önemliyse, belki bu öneriyi göz ardı edin. Rails modelinde benzersiz bir kısıtlamaya sahip olmak, INSERT'dan önce SELECT yapmasını ve böylece Rails seviyesinde benzersiz bir doğrulama yapmasını sağlayarak, döngünüzün gerçekleştirdiği sorgu sayısını potansiyel olarak iki katına çıkarır.Sadece yaptığınız gibi veritabanı seviyesindeki hataları yakalamak, performans için makul bir zerafet ticareti olabilir.

(düzenle) TL; DR: DB'de her zaman benzersiz kısıtlama var. Ayrıca, model kısıtlaması olması, DB'nin bir hata atmadan önce ActiveRecord/ActiveModel doğrulamasına izin verecektir.

+3

A Rails benzersizliği validasyonu ** her zaman ** veritabanı içindeki bir özgünlük kısıtlaması ile desteklenmelidir. Tüm ActiveRecord doğrulamaları yarış koşullarına tabidir, bu yüzden mantık veritabanında olmalı ve kırık veri istemiyorsanız istisnalarla uğraşmanız gerekir. –

+0

Kesinlikle doğru, aksi ima etmek istemedim. Model doğrulamadan söz ederken, kesinlikle veritabanındaki benzersiz kısıtlamaya ek olarak bunu kastediyorum. Veritabanında bütünlük için kısıtlamaya ve ActiveRecord/ActiveModel doğrulaması için Rails kısıtlamasına gereksiniminiz vardır. –

İlgili konular