كيف يمكنك منع تغييرات قاعدة البيانات داخل ActivereCord قبل _create من التراجع عندما تعود كاذبة؟

StackOverflow https://stackoverflow.com/questions/1597434

سؤال

لقد أضفت مرشحًا قبل _create إلى أحد طرازات Rails ActivereCord وداخل هذا المرشح أقوم ببعض تحديثات قاعدة البيانات.

أحيانًا أعود خطأ من المرشح لمنع إنشاء النموذج المستهدف ، لكن هذا يسبب كل آخر تغييرات قاعدة البيانات التي أجريتها (أثناء داخل المرشح) لتتخلف عن التراجع.

كيف يمكنني منع ذلك؟

تحديث #1: إليك بعض الكود الزائف الذي يشرح مشكلتي:

class Widget < ActiveRecord::Base
  before_create :update_instead

  def update_instead
    if some_condition?
      update_some_record_in_same_model # this is getting rolled back
      return false # don't create a new record
    else
      return true # let it continue
    end
  end
end

تحديث رقم 2: بعض الإجابات الجيدة أدناه ولكن كان لكل منها أوجه القصور. انتهى بي الأمر تجاوز طريقة إنشاء مثل SO:

def create
  super unless update_instead? # yes I reversed the return values from above
end
هل كانت مفيدة؟

المحلول

كان علي فقط القيام بذلك مؤخرًا. تحتاج إلى طلب اتصال آخر على وجه التحديد من AR. ثم قم بتنفيذ التغييرات الخاصة بك على هذا الاتصال. وبهذه الطريقة ، إذا فشل الخلق وتراجع المعاملة ، فقد تم بالفعل ارتكاب تغييرات رد الاتصال في معاملة مختلفة.

تجاهل إجابتي أعلاه. رمز المثال الذي قدمته للتو أشياء موضحة حقًا.

class Foo < ActiveRecord::Base
  before_create :update_instead

  def update_instead
    dbconn = self.class.connection_pool.checkout
    dbconn.transaction do
      dbconn.execute("update foos set name = 'updated'")
    end
    self.class.connection_pool.checkin(dbconn)
    false
  end
end


>> Foo.create(:name => 'sam')
=> #<Foo id: nil, name: "sam", created_at: nil, updated_at: nil>
>> Foo.all
=> [#<Foo id: 2, name: "updated", created_at: "2009-10-21 15:12:55", updated_at: "2009-10-21 15:12:55">]

نصائح أخرى

استخدم معاملة في المرشح.

هل حاولت الكتابة فوق إنشاء/حفظ وإصداراتها المدمرة؟ ActivereCord :: base.create ، ActivereCord :: base.save وإصداراتها المدمرة ملفوفة في معاملة ، فهي أيضًا ما هي عمليات الاسترجاعات والتحققات. إذا كنت تتجاوز ذلك ، فإن الأشياء التي تقوم بها Super فقط ستكون جزءًا من المعاملة. إذا كنت بحاجة إلى عمليات التحقق من تشغيلك من قبل ، فيمكنك الاتصال الصحيح بشكل صريح لتشغيلها جميعًا.

مثال:

before_create :before_create_actions_that_can_be_rolled_back

def create
  if valid? && before_create_actions_that_wont_be_rolled_back
    super
  end
end

def before_create_actions_that_wont_be_rolled_back
 # exactly what it sounds like
end

def before_create_actions_that_can_be_rolled_back
 # exactly what it sounds like
end

التحذير: مع هذه التعديلات ، سيتم استدعاء الأساليب بهذا الترتيب:

  1. قبل التحقق من الصحة (ON_CREATE)
  2. التحقق
  3. بعد التحقق من الصحة (ON_CREATE)
  4. قبل _create_actions_that_wont_be_rolled_back
  5. قبل التحقق من الصحة (ON_CREATE)
  6. التحقق
  7. بعد التحقق من الصحة (ON_CREATE)
  8. قبل حفظ عمليات الاستدعاء
  9. قبل إنشاء عمليات الاسترجاعات
  10. يتم إنشاء السجل
  11. بعد إنشاء عمليات الاسترجاعات
  12. بعد حفظ عمليات الاستدعاء

إذا فشلت أي عمليات التحقق من صحة أو إذا كان أي رد فعل يعيد خطأ في الخطوات 5-12 ، فسيتم إعادة قاعدة البيانات إلى الحالة التي كانت فيها قبل الخطوة 5.

إذا كان صالحا؟ يفشل ، أو قبل تفشل _create_actions_that_wont_be_rolled_back من توقف السلسلة بأكملها.

هل يمكن إجراء هذه التغييرات في after_create في حين أن؟

هذا يستخدم نفس مفهوم إجابة ريتش كافانو. أضفت نموذجًا للأصل حتى يكون أكثر وضوحًا ما يفعله المرشح. المفتاح هو استخدام مؤشر ترابط + تسجيل الوصول التلقائي/تسجيل الوصول لاتصال منفصل. ملاحظة: يجب عليك التأكد من ضبط قيمة البلياردو على 2 على الأقل في مواصفات الاتصال الخاصة بك ، اعتمادًا على عدد المواضيع المتزامنة التي ستشغلها. أعتقد أنه افتراضي إلى 5.

class Gadget < ActiveRecord::Base
  has_many :widgets
end

class Widget < ActiveRecord::Base
  belongs_to :gadget
  before_create :update_instead

  def update_some_record_in_same_model
    # the thread forces a new connection to be checked out
    Thread.new do
      ActiveRecord::Base.connection_pool.with_connection do |conn|
        # try this part without the 2 surrounding blocks and it will be rolled back
        gadget.touch_count += 1
        gadget.save!
      end
    end.join
  end
  def some_condition?
    true
  end

  def update_instead
    if some_condition?
      update_some_record_in_same_model # this is getting rolled back
      p [:touch_count_in_filter, gadget.reload.touch_count]
      return false # don't create a new record
    else
      return true # let it continue
    end
  end
end

اختبار:

  g = Gadget.create(:name => 'g1')
  puts "before:"
  p [:touch_count, g.reload.touch_count]
  p [:widget_count, Widget.count]

  g.widgets.create(:name => 'w1')
  puts "after:"
  # Success means the count stays incremented
  p [:touch_count, g.reload.touch_count]
  p [:widget_count, Widget.count]

قراءة متعمقة: http://bibwild.wordpress.com/2011/11/14/multi-threading-in-trails-activerecord-3-0-3-1/

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top