मैं रेल को नष्ट करने पर कैसे 'मान्य' करता हूं


81

एक विनाशकारी संसाधन के विनाश पर, मैं कुछ चीजों की गारंटी देना चाहता हूं इससे पहले कि मैं एक नष्ट ऑपरेशन को जारी रखने की अनुमति दूं? मूल रूप से, मैं नष्ट ऑपरेशन को रोकने की क्षमता चाहता हूं यदि मैं ध्यान देता हूं कि ऐसा करने से डेटाबेस को अमान्य स्थिति में रखा जाएगा? एक नष्ट ऑपरेशन पर कोई सत्यापन कॉलबैक नहीं हैं, इसलिए एक "सत्यापन" कैसे करता है कि क्या एक नष्ट ऑपरेशन को स्वीकार किया जाना चाहिए?


जवाबों:


70

आप एक अपवाद उठा सकते हैं जिसे आप तब पकड़ते हैं। रेल रैप एक लेनदेन में हटा देता है, जो मामलों में मदद करता है।

उदाहरण के लिए:

class Booking < ActiveRecord::Base
  has_many   :booking_payments
  ....
  def destroy
    raise "Cannot delete booking with payments" unless booking_payments.count == 0
    # ... ok, go ahead and destroy
    super
  end
end

वैकल्पिक रूप से आप पहले_डस्ट्रो कॉलबैक का उपयोग कर सकते हैं। यह कॉलबैक आमतौर पर निर्भर रिकॉर्ड को नष्ट करने के लिए उपयोग किया जाता है, लेकिन आप इसके बजाय एक अपवाद फेंक सकते हैं या एक त्रुटि जोड़ सकते हैं।

def before_destroy
  return true if booking_payments.count == 0
  errors.add :base, "Cannot delete booking with payments"
  # or errors.add_to_base in Rails 2
  false
  # Rails 5
  throw(:abort)
end

myBooking.destroyअब झूठी वापसी करेगा, और myBooking.errorsवापसी पर आबाद होगा।


3
ध्यान दें कि जहां यह अब कहता है "... ठीक है, आगे बढ़ो और नष्ट करें", आपको "सुपर" लगाने की आवश्यकता है, इसलिए मूल नष्ट विधि वास्तव में कहा जाता है।
अलेक्जेंडर मलफिट

3
त्रुटियों .add_to_base को रेल 3 में पदावनत किया गया है। इसके बजाय आपको त्रुटियां करनी चाहिए। आधार (: आधार, "संदेश")।
रयान

9
रेल नष्ट होने से पहले मान्य नहीं होती है, इसलिए इससे पहले कि यह नष्ट हो जाए रद्द करने के लिए इससे पहले कि गलत तरीके से वापस लौटना पड़े। सिर्फ त्रुटियां जोड़ना बेकार है।
ग्रेव

24
रेल 5 के साथ, के falseअंत में before_destroyबेकार है। अब से आपको throw(:abort)(@see: weblog.rubyonrails.org/2015/1/10/This-week-in-Rails/… ) का उपयोग करना चाहिए ।
romainsalles

1
अनाथ रिकॉर्ड के खिलाफ बचाव करने के आपके उदाहरण को बहुत आसानी से हल किया जा सकता हैhas_many :booking_payments, dependent: :restrict_with_error
Thisismydesign

48

सिर्फ एक नोट:

रेल के लिए ३

class Booking < ActiveRecord::Base

before_destroy :booking_with_payments?

private

def booking_with_payments?
        errors.add(:base, "Cannot delete booking with payments") unless booking_payments.count == 0

        errors.blank? #return false, to not destroy the element, otherwise, it will delete.
end

2
इस दृष्टिकोण के साथ एक समस्या यह है कि पहले बुकिंग के बाद कॉलिंग को नष्ट कर दिया गया लगता है।
सनस्क्रीनिटी

4
संबंधित टिकट: github.com/rails/rails/issues/3458 @sunkencity आप अस्थायी रूप से इससे बचने के लिए एसोसिएशन की घोषणा से पहले d_destroy घोषित कर सकते हैं।
लूलालाल

1
अनाथ रिकॉर्ड के खिलाफ बचाव करने के आपके उदाहरण को बहुत आसानी से हल किया जा सकता हैhas_many :booking_payments, dependent: :restrict_with_error
Thisismydesign

रेल गाइड से पहले_डिस्ट्रो कॉलबैक्स निर्भर हो सकते हैं और आश्रित_डेस्ट्रो के साथ जुड़ाव से पहले रखा जाना चाहिए; इससे संबंधित विध्वंस
grouchomc

20

यह वही है जो मैंने रेल 5 के साथ किया था:

before_destroy do
  cannot_delete_with_qrcodes
  throw(:abort) if errors.present?
end

def cannot_delete_with_qrcodes
  errors.add(:base, 'Cannot delete shop with qrcodes') if qrcodes.any?
end

3
यह एक अच्छा लेख है जो इस व्यवहार को रेल 5 में बताता है: blog.bigbinary.com/2016/02/13/…
Yaro Holodiuk

1
अनाथ रिकॉर्ड के खिलाफ बचाव करने के आपके उदाहरण को बहुत आसानी से हल किया जा सकता हैhas_many :qrcodes, dependent: :restrict_with_error
Thisismydesign

6

ActiveRecord संघों के पास has_many और has_one एक आश्रित विकल्प के लिए अनुमति देता है जो यह सुनिश्चित करेगा कि संबंधित संबंधित तालिका पंक्तियाँ हटा दी गई हैं, लेकिन यह आमतौर पर आपके डेटाबेस को अमान्य होने से रोकने के बजाय उसे साफ़ रखने के लिए है।


1
अंडरस्कोर की देखभाल करने का एक और तरीका, यदि वे एक फ़ंक्शन नाम या समान का हिस्सा हैं, तो उन्हें बैकटिक्स में लपेटना है। फिर कोड के रूप में प्रदर्शित होगा like_so
रिचर्ड जोन्स

धन्यवाद। आपके उत्तर ने मुझे उस प्रकार के आश्रित विकल्प के बारे में एक और खोज करने के लिए प्रेरित किया जो यहाँ उत्तर दिया गया था: stackoverflow.com/a/25962390/3681793
bonafernando

वहाँ भी dependentविकल्प हैं जो एक इकाई को हटाने की अनुमति नहीं देते हैं अगर यह अनाथ रिकॉर्ड बनाएगा (यह सवाल के लिए अधिक प्रासंगिक है)। जैसेdependent: :restrict_with_error
यहवादवाद

5

आप नियंत्रक में "यदि" कथन में नष्ट कार्रवाई को लपेट सकते हैं:

def destroy # in controller context
  if (model.valid_destroy?)
    model.destroy # if in model context, use `super`
  end
end

कहां मान्य_डेस्ट्रो? आपके मॉडल वर्ग की एक विधि है जो एक रिकॉर्ड को नष्ट करने की शर्तों को पूरा करने पर सही है।

इस तरह एक विधि होने से आप उपयोगकर्ता को हटाए गए विकल्प के प्रदर्शन को भी रोक सकते हैं - जिससे उपयोगकर्ता अनुभव में सुधार होगा क्योंकि उपयोगकर्ता एक अवैध संचालन करने में सक्षम नहीं होगा।


1
अच्छी पकड़ है, लेकिन मैं मान रहा था कि यह विधि नियंत्रक में है, मॉडल की ओर इशारा करते हुए। यदि यह मॉडल में होता तो निश्चित रूप से मुद्दों का कारण बनता
टोबी हैड

हे, सॉरी बाउट दैट ... मैं देख रहा हूं कि आपका क्या मतलब है, मैंने बस "आपके मॉडल वर्ग पर विधि" देखी और जल्दी से सोचा "उह ओह", लेकिन आप सही हैं - कंट्रोलर पर नष्ट करें, यह ठीक काम करेगा। :)
jenjenut233

सभी अच्छे, वास्तव में बेहतर होने के बजाय बहुत स्पष्ट होने के बजाय कुछ गरीब शुरुआती जीवन को खराब स्पष्टता के साथ कठिन बनाते हैं
टोबी हैड

1
मैंने इसे नियंत्रक के रूप में भी करने के बारे में सोचा था, लेकिन यह वास्तव में मॉडल पर है ताकि वस्तुओं को कंसोल या किसी अन्य नियंत्रक से नष्ट नहीं किया जा सके, जिन्हें उन वस्तुओं को नष्ट करने की आवश्यकता हो सकती है। इसे रखो DRY। :)
जोशुआ पिंटर

यह कहा जा रहा है, आप अभी भी अपने नियंत्रक ifकी destroyकार्रवाई में अपने बयान का उपयोग कर सकते हैं , कॉल करने के बजाय if model.valid_destroy?, बस कॉल करें if model.destroyऔर मॉडल को संभालने दें कि क्या विनाश सफल था, आदि
जोशुआ पिंटर

5

रेल राज्य के नियम 6:

यह काम:

before_destroy :ensure_something, prepend: true do
  throw(:abort) if errors.present?
end

private

def ensure_something
  errors.add(:field, "This isn't a good idea..") if something_bad
end

validate :validate_test, on: :destroyकाम नहीं करता है: https://github.com/rails/rails/issues/32376

चूँकि रेल throw(:abort)को निष्पादन को रद्द करने के लिए 5 की आवश्यकता होती है: https://makandracards.com/makandra/20301-cancelling-the-activerecord-callback-chain

prepend: trueआवश्यक है ताकि dependent: :destroyसत्यापन निष्पादित होने से पहले न चले: https://github.com/rails/rails/issues/3458

आप अन्य उत्तरों और टिप्पणियों से इसे एक साथ रख सकते हैं, लेकिन मुझे उनमें से कोई भी पूरा नहीं मिला।

एक विचार के रूप में, कई ने एक has_manyउदाहरण के रूप में एक संबंध का उपयोग किया, जहां वे यह सुनिश्चित करना चाहते हैं कि अगर यह अनाथ रिकॉर्ड बनाएगा तो किसी भी रिकॉर्ड को न हटाएं। इसे बहुत आसानी से हल किया जा सकता है:

has_many :entities, dependent: :restrict_with_error


एक छोटा सा सुधार: before_destroy :handle_destroy, prepend: true; before_destroy { throw(:abort) if errors.present? }तुरंत नष्ट होने की प्रक्रिया को समाप्त करने के बजाय अन्य पहले_डिस्ट्रॉय सत्यापन से त्रुटियों को पारित करने की अनुमति देगा
पॉल

4

मैंने एक्टिवरकॉर्ड पर can_destroy ओवरराइड बनाने के लिए यहां से कोड का उपयोग करके समाप्त किया: https://gist.github.com/andhapp/1761098

class ActiveRecord::Base
  def can_destroy?
    self.class.reflect_on_all_associations.all? do |assoc|
      assoc.options[:dependent] != :restrict || (assoc.macro == :has_one && self.send(assoc.name).nil?) || (assoc.macro == :has_many && self.send(assoc.name).empty?)
    end
  end
end

यह ui पर एक डिलीट बटन को छिपाने / दिखाने के लिए इसे तुच्छ बनाने का अतिरिक्त लाभ है



2

मेरे पास ये कक्षाएं या मॉडल हैं

class Enterprise < AR::Base
   has_many :products
   before_destroy :enterprise_with_products?

   private

   def empresas_with_portafolios?
      self.portafolios.empty?  
   end
end

class Product < AR::Base
   belongs_to :enterprises
end

अब जब आप किसी उद्यम को हटाते हैं तो यह प्रक्रिया मान्य होती है यदि उद्यम से जुड़े उत्पाद हैं नोट: आपको इसे पहले मान्य करने के लिए कक्षा के शीर्ष में लिखना होगा।


1

ActiveRecord संदर्भ सत्यापन का उपयोग रेल 5 में करें।

class ApplicationRecord < ActiveRecord::Base
  before_destroy do
    throw :abort if invalid?(:destroy)
  end
end
class Ticket < ApplicationRecord
  validate :validate_expires_on, on: :destroy

  def validate_expires_on
    errors.add :expires_on if expires_on > Time.now
  end
end

आप मान्य नहीं कर सकते on: :destroy, इस मुद्दे को
thesecretmaster

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.