سؤال

وكنت أعاني من مناقشة هذا مع بعض الزملاء. هل هناك طريقة مفضلة لاسترداد كائن في جانغو عندما كنت تتوقع واحد فقط؟

وطرق واضحة هما:

try:
    obj = MyModel.objects.get(id=1)
except MyModel.DoesNotExist:
    # We have no object! Do something...
    pass

و:

objs = MyModel.objects.filter(id=1)

if len(objs) == 1:
    obj = objs[0]
else:
    # We have no object! Do something...
    pass

والطريقة الأولى يبدو سلوكيا الأصح، ولكن يستخدم الاستثناءات في التحكم في التدفق الذي قد يعرض بعض النفقات العامة. والثاني هو أكثر ملتوية ولكن لن تثير أي وقت مضى استثناء.

وأية أفكار حول أي من هذه هو الأفضل؟ التي هي أكثر كفاءة؟

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

المحلول

ويتم توفير get() خصيصا لهذه الحالة. استخدامها.

والخيار 2 هو بالضبط تقريبا كيفية تنفيذ طريقة get() الواقع في جانغو، لذلك ينبغي أن يكون هناك أي "أداء" الفرق (وحقيقة كنت افكر في هذا الامر يدل على أنك تنتهك واحدة من القواعد الأساسية للبرمجة وهي محاولة لتحسين التعليمات البرمجية قبل أن يتم حتى تم مكتوب ولمحة - حتى يكون لديك رمز ويمكن تشغيله، وكنت لا أعرف كيف سيكون أداء، ومحاولة لتحسين قبل ذلك هو الطريق من الألم)

نصائح أخرى

ويمكنك تثبيت وحدة نمطية تسمى جانغو-مزعج ومن ثم قيام بذلك:

from annoying.functions import get_object_or_None

obj = get_object_or_None(MyModel, id=1)

if not obj:
    #omg the object was not found do some error stuff

1 هو الصحيح. في بيثون استثناء ديه النفقات العامة على قدم المساواة إلى العودة. لدليل مبسط يمكنك أن تبحث في <لأ href = "http://paltman.com/2008/jan/18/try-except-performance-in-python-a-simple-test/" يختلط = "نوفولو noreferrer" > هذا .

و2 وهذا هو ما تقوم به جانغو في الخلفية. get يدعو filter ويثير استثناء إذا وجدت أي بند أو إذا تم العثور على أكثر من كائن واحد.

وأنا في وقت متأخر قليلا إلى طرف، ولكن مع جانغو 1.6 هناك طريقة first() على مجموعات طلبات البحث.

الشبكي: / /docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.first


<اقتباس فقرة>   

وإرجاع الكائن الأول من قبل مجموعة طلبات البحث، أو لا يوجد تطابق ما إذا كان هناك أي كائن مطابقة. إذا كانت مجموعة طلبات البحث لا تحتوي على طلب تعريف، ثم مجموعة طلبات البحث هو أمر تلقائيا باستخدام المفتاح الأساسي.

مثال:

p = Article.objects.order_by('title', 'pub_date').first()
Note that first() is a convenience method, the following code sample is equivalent to the above example:

try:
    p = Article.objects.order_by('title', 'pub_date')[0]
except IndexError:
    p = None

لا أستطيع أن أتكلم مع أي تجربة جانغو لكن الخيار رقم 1 يقول بوضوح النظام الذي تسألون عن 1 الكائن، في حين أن الخيار الثاني لا. وهذا يعني أن الخيار رقم 1 يمكن بسهولة أكبر للاستفادة من ذاكرة التخزين المؤقت أو قاعدة بيانات الأرقام القياسية، وخصوصا حيث لم يتم ضمان السمة كنت التصفية على أن تكون فريدة من نوعها.

وأيضا (مرة أخرى، المضاربة) قد يكون الخيار الثاني لخلق نوع من جمع النتائج أو كائن مكرر منذ الدعوة فلتر () يمكن عادة العودة العديد من الصفوف. وكنت تجاوز هذه مع الحصول على ().

وأخيرا، فإن الخيار الأول على حد سواء أقصر ويغفل متغير مؤقت إضافي - فقط اختلاف بسيط ولكنه يساعد كل قليلا

.

لماذا كل هذا العمل؟ استبدال خطوط 4 مع 1 اختصار مدمج. (هذا لا محاولة الخاصة / باستثناء).

from django.shortcuts import get_object_or_404

obj = get_object_or_404(MyModel, id=1)

وبعض مزيد من المعلومات حول الاستثناءات. إذا لم يتم رفعها، فإنها لا تكلف شيئا تقريبا. وبالتالي إذا كنت تعرف أنك ربما ستكون لدينا نتيجة لذلك، استخدم استثناء، منذ باستخدام التعبير الشرطي عليك دفع تكلفة فحص في كل مرة، مهما كانت. من ناحية أخرى، فهي تكلف أكثر قليلا من التعبير الشرطي يبعثون، لذلك إذا كنت تتوقع ليس لديهم نتيجة لذلك مع بعض التردد (مثلا، 30٪ من الوقت، إذا أسعفتني الذاكرة)، والاختيار مشروط تبين أن تكون أرخص قليلا.

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

ولقد لعبت مع هذه المشكلة قليلا، واكتشفت أن الخيار 2 تنفيذ اثنين من الاستفسارات SQL، والتي لمثل هذه المهمة بسيطة غير مفرطة. أرى الشرح:

objs = MyModel.objects.filter(id=1) # This does not execute any SQL
if len(objs) == 1: # This executes SELECT COUNT(*) FROM XXX WHERE filter
    obj = objs[0]  # This executes SELECT x, y, z, .. FROM XXX WHERE filter
else: 
    # we have no object!  do something
    pass

ونسخة أي ما يعادل الذي ينفذ استعلام واحد هو:

items = [item for item in MyModel.objects.filter(id=1)] # executes SELECT x, y, z FROM XXX WHERE filter
count = len(items) # Does not execute any query, items is a standard list.
if count == 0:
   return None
return items[0]

وعن طريق التحول إلى هذا النهج، وكنت قادرا على الحد بشكل كبير من عدد من الاستفسارات تنفيذ طلبي.

والسؤال المثير للاهتمام، ولكن بالنسبة لي الخيار رقم 2 يفوح برائحة الأمثل سابق لأوانه. لست متأكدا التي هي أكثر performant لل، ولكن الخيار رقم 1 بالتأكيد يبدو ويشعر أكثر pythonic لي.

وأقترح تصميم مختلف.

إذا كنت تريد إجراء الدالة على نتيجة ممكنة، هل يمكن أن تجنيها من مجموعة طلبات البحث، مثل هذا: HTTP: //djangosnippets.org/snippets/734/

وكانت النتيجة رهيبة جدا، هل يمكن على سبيل المثال:

MyModel.objects.filter(id=1).yourFunction()

وهنا، مرشح يعود إما مجموعة طلبات البحث فارغة أو مجموعة طلبات البحث مع عنصر واحد. وظائف مجموعة طلبات البحث المخصص الخاص بك هي أيضا chainable وقابلة لإعادة الاستخدام. إذا كنت ترغب في تنفيذ ذلك لجميع المداخل الخاصة بك: MyModel.objects.all().yourFunction()

وكما أنها مثالية لاستخدامها الإجراءات في واجهة مشرف:

def yourAction(self, request, queryset):
    queryset.yourFunction()

والخيار 1 هو أكثر أناقة، ولكن يجب التأكد من استخدام try..except.

ومن تجربتي الخاصة أستطيع أن أقول لك أنك في بعض الأحيان على يقين لا يمكن أن يكون هناك كائن مطابقة أكثر من واحد في قاعدة البيانات، وبعد سوف يكون هناك اثنان ... (ما عدا بالطبع عندما يحصل الكائن في الابتدائي مفتاح).

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