سؤال

هنا مثال بسيط من جانغو عرض مع شرط سباق محتملة:

# myapp/views.py
from django.contrib.auth.models import User
from my_libs import calculate_points

def add_points(request):
    user = request.user
    user.points += calculate_points(user)
    user.save()

حالة السباق يجب أن تكون واضحة إلى حد ما:يمكن للمستخدم جعل هذا الطلب مرتين ، وتطبيق يحتمل أن تنفيذ user = request.user في وقت واحد, مما تسبب في واحدة من طلبات تجاوز البعض.

لنفترض وظيفة calculate_points معقدة نسبيا ، ويجعل الحسابات على أساس كل أنواع الأشياء الغريبة التي لا يمكن وضعها في واحد update و سيكون من الصعب وضع في إجراء مخزن.

حتى هنا هو بلدي السؤال:أي نوع من تأمين الآليات المتاحة جانغو ، للتعامل مع حالات مماثلة إلى هذا ؟

هل كانت مفيدة؟

المحلول

وجانغو 1.4+ الدعم <وأ href = "https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.select_for_update" يختلط = "noreferrer" > select_for_update ، في الإصدارات السابقة قد تنفيذ SQL الخام يستعلم على سبيل المثال select ... for update التي تبعا لDB الأساسي سوف تغلق الصف من أية تحديثات، يمكنك أن تفعل ما تريد مع هذا الصف حتى نهاية المعاملة. منها مثلا.

from django.db import transaction

@transaction.commit_manually()
def add_points(request):
    user = User.objects.select_for_update().get(id=request.user.id)
    # you can go back at this point if something is not right 
    if user.points > 1000:
        # too many points
        return
    user.points += calculate_points(user)
    user.save()
    transaction.commit()

نصائح أخرى

واعتبارا من جانغو 1.1 يمكنك استخدام F () تعبيرات ORM على حل هذه المشكلة بالتحديد.

from django.db.models import F

user = request.user
user.points  = F('points') + calculate_points(user)
user.save()

لمزيد من التفاصيل انظر وثائق:

الشبكي: / /docs.djangoproject.com/en/1.8/ref/models/instances/#updating-attributes-based-on-existing-fields

الشبكي: //docs.djangoproject كوم / EN / 1.8 / المرجع / نماذج / عبارات / # django.db.models.F

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


ويدعم

وتأمين متشائم الآن من قبل ORM جانغو 1.4 عندما وDB الأساسية (مثل بوستجرس) يعتمد عليه. انظر جانغو ملاحظات الإصدار 1.4a1 .

لديك العديد من الطرق واحد موضوع هذا النوع من الشيء.

معيار واحد النهج التحديث الأول.يمكنك أن تفعل التحديث والتي سوف الاستيلاء على تأمين للاستخدام خاص على التوالي.ثم هل العمل الخاص بك ؛ وأخيرا تنفيذ التغيير.لهذا العمل تحتاج إلى تجاوز ORM هو التخزين المؤقت.

معيار آخر هو نهج منفصلة ، واحدة مترابطة تطبيق الخادم أن يعزل شبكة الإنترنت معاملات حسابية معقدة.

  • تطبيق الويب الخاص بك يمكن إنشاء طابور من سجل الطلبات ، تفرخ عملية منفصلة ، ومن ثم كتابة سجل الطلبات على قائمة الانتظار هذه.تفرخ يمكن وضعها في جانغو urls.py لذلك يحدث على شبكة الإنترنت التطبيق بدء التشغيل.أو أنها يمكن أن تكون وضعت في فصل manage.py مشرف السيناريو.أو أنه يمكن القيام به "حسب الحاجة" عندما سجل أول طلب هو محاولة.

  • يمكنك أيضا إنشاء منفصلة WSGI بنكهة ملقم ويب باستخدام Werkzeug الذي يقبل WS الطلبات عبر urllib2.إذا كان لديك واحد رقم المنفذ لهذا الملقم الطلبات في قائمة الانتظار عن طريق TCP/IP.إذا كان الخاص بك WSGI معالج لديه موضوع واحد, ثم كنت قد حققت تسلسل واحد-threading.هذا هو أكثر قليلا تحجيم منذ التهديف المحرك WS الطلب يمكن تشغيلها في أي مكان.

نهج آخر هو أن يكون بعض الموارد الأخرى التي يمكن الحصول عليها وعقد القيام به الحساب.

  • المفرد كائن في قاعدة البيانات.صف واحد في طاولة فريدة من نوعها يمكن تحديثها مع معرف جلسة للاستيلاء على السيطرة ؛ تحديث مع الدورة معرف None إلى الإفراج عن السيطرة.الأساسية التحديث لتشمل WHERE SESSION_ID IS NONE مرشح أن أؤكد أن التحديث يفشل عند قفل يقام من قبل شخص آخر.هذا أمر مثير للاهتمام لأنه بطبيعته سباق مجانا-انها واحدة التحديث من غير تحديد-تسلسل التحديث.

  • حديقة متنوعة إشارة يمكن استخدامها خارج قاعدة البيانات.طوابير (عموما) هي أسهل للعمل مع من مستوى منخفض سيمافور.

وهذا يمكن تبسيط الوضع الخاص بك، ولكن ماذا عن مجرد استبدال رابط جافا سكريبت؟ وبعبارة أخرى عندما يقوم المستخدم بالنقر فوق ارتباط أو زر التفاف على طلب في وظيفة جافا سكريبت مما يؤدي إلى تعطيل فورا / "رمادية" الارتباط ويحل محل النص مع "تحميل ..." أو "تقديم طلب ..." معلومات أو شيء مماثل. سيكون هذا العمل بالنسبة لك؟

والآن، يجب عليك استخدام:

Model.objects.select_for_update().get(foo=bar)
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top