เมื่อฉันขอให้ตัวจัดการโมเดลรับวัตถุมันจะเพิ่มขึ้นDoesNotExist
เมื่อไม่มีวัตถุที่ตรงกัน
go = Content.objects.get(name="baby")
แทนที่จะเป็นDoesNotExist
ฉันgo
จะNone
เป็นอย่างไร
เมื่อฉันขอให้ตัวจัดการโมเดลรับวัตถุมันจะเพิ่มขึ้นDoesNotExist
เมื่อไม่มีวัตถุที่ตรงกัน
go = Content.objects.get(name="baby")
แทนที่จะเป็นDoesNotExist
ฉันgo
จะNone
เป็นอย่างไร
คำตอบ:
ไม่มีวิธีการ 'ภายใน' ในการทำเช่นนี้ Django จะเพิ่มข้อยกเว้น DoesNotExist ทุกครั้ง วิธีที่ใช้สำนวนในการจัดการสิ่งนี้ในไพ ธ อนคือการห่อมันด้วยการลองจับ:
try:
go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
go = None
สิ่งที่ฉันทำคือ subclass models.Manager สร้างsafe_get
โค้ดที่เหมือนด้านบนและใช้ตัวจัดการนั้นสำหรับโมเดลของฉัน SomeModel.objects.safe_get(foo='bar')
วิธีการที่คุณสามารถเขียน:
SomeModel.objects.filter(foo='bar').first()
สิ่งนี้คืนค่าการจับคู่แรกหรือไม่มี มันจะไม่ล้มเหลวหากมีหลายกรณีเช่นqueryset.get()
filter().first()
ฉันคิดว่าข้อยกเว้นเป็นวิธีที่จะดำเนินการ
ตั้งแต่ django 1.6 คุณสามารถใช้วิธีแรก ()ดังนี้:
Content.objects.filter(name="baby").first()
get()
เพิ่มDoesNotExist
ข้อยกเว้นหากไม่พบวัตถุสำหรับพารามิเตอร์ที่กำหนด ข้อยกเว้นนี้ยังเป็นคุณลักษณะของคลาสโมเดลDoesNotExist
สืบทอดยกเว้นจากdjango.core.exceptions.ObjectDoesNotExist
คุณสามารถตรวจจับข้อยกเว้นและกำหนดNone
ให้ไปได้
from django.core.exceptions import ObjectDoesNotExist
try:
go = Content.objects.get(name="baby")
except ObjectDoesNotExist:
go = None
คุณสามารถสร้างฟังก์ชั่นทั่วไปสำหรับสิ่งนี้
def get_or_none(classmodel, **kwargs):
try:
return classmodel.objects.get(**kwargs)
except classmodel.DoesNotExist:
return None
ใช้สิ่งนี้เหมือนด้านล่าง:
go = get_or_none(Content,name="baby")
go จะเป็น None หากไม่มีรายการที่ตรงกับที่อื่นจะส่งคืนรายการเนื้อหา
หมายเหตุ: มันจะเพิ่มข้อยกเว้น MultipleObjectsReturned ถ้ามีการส่งคืนมากกว่าหนึ่งรายการสำหรับ name = "baby"
คุณสามารถทำได้ด้วยวิธีนี้:
go = Content.objects.filter(name="baby").first()
ตอนนี้ตัวแปรไปอาจเป็นวัตถุที่คุณต้องการหรือไม่มี
Ref: https://docs.djangoproject.com/en/1.8/ref/models/querysets/#django.db.models.query.QuerySet.first
เพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้นนี่คือตัวอย่างโค้ดที่ฉันเขียนโดยอิงจากอินพุตจากคำตอบที่ยอดเยี่ยมที่นี่:
class MyManager(models.Manager):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except ObjectDoesNotExist:
return None
และจากนั้นในรูปแบบของคุณ:
class MyModel(models.Model):
objects = MyManager()
แค่นั้นแหละ. ตอนนี้คุณมี MyModel.objects.get () รวมถึง MyModel.objetcs.get_or_none ()
คุณสามารถใช้exists
กับตัวกรอง:
Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS
ทางเลือกสำหรับถ้าคุณเพียงต้องการทราบว่ามีอยู่
exists()
จะใช้ร่วมกับif
ข้อก่อนดึงวัตถุจึงก่อให้เกิดการตีสองครั้งเพื่อ db ฉันจะยังคงแสดงความคิดเห็นในกรณีที่มันช่วยคนอื่น
การจัดการข้อยกเว้นที่จุดต่าง ๆ ในมุมมองของคุณอาจยุ่งยากจริง ๆ แล้วเรื่องการกำหนด Model Manager แบบกำหนดเองในไฟล์ models.py เช่น
class ContentManager(model.Manager):
def get_nicely(self, **kwargs):
try:
return self.get(kwargs)
except(KeyError, Content.DoesNotExist):
return None
และรวมไว้ในคลาสโมเดลเนื้อหา
class Content(model.Model):
...
objects = ContentManager()
ด้วยวิธีนี้สามารถจัดการได้อย่างง่ายดายในมุมมองเช่น
post = Content.objects.get_nicely(pk = 1)
if post:
# Do something
else:
# This post doesn't exist
return self.get(**kwargs)
มันทำงานได้สำหรับฉัน อย่าบอกว่ามีอะไรผิดปกติกับคำตอบเพียงแค่เคล็ดลับสำหรับทุกคนที่พยายามใช้มันกับรุ่นที่ใหม่กว่า
เป็นหนึ่งในฟังก์ชั่นที่น่ารำคาญที่คุณอาจไม่ต้องการนำมาใช้ใหม่:
from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")
get_object_or_None
แต่พบว่ายังคงเพิ่มขึ้นMultipleObjectsReturned
หากมีวัตถุมากกว่าหนึ่งรายการ ดังนั้นผู้ใช้อาจพิจารณาล้อมรอบด้วยtry-except
(ซึ่งฟังก์ชั่นของตัวเองมีtry-except
อยู่แล้ว)
หากคุณต้องการโซลูชันแบบบรรทัดเดียวที่ไม่เกี่ยวข้องกับการจัดการข้อยกเว้นคำสั่งแบบมีเงื่อนไขหรือข้อกำหนดของ Django 1.6+ ให้ทำสิ่งนี้แทน:
x = next(iter(SomeModel.objects.filter(foo='bar')), None)
ฉันคิดว่ามันไม่ควรใช้ get_object_or_404()
from django.shortcuts import get_object_or_404
def my_view(request):
my_object = get_object_or_404(MyModel, pk=1)
ตัวอย่างนี้เทียบเท่ากับ:
from django.http import Http404
def my_view(request):
try:
my_object = MyModel.objects.get(pk=1)
except MyModel.DoesNotExist:
raise Http404("No MyModel matches the given query.")
คุณสามารถอ่านเพิ่มเติมเกี่ยวกับget_object_or_404 ()ในเอกสารออนไลน์ django
จาก django 1.7 เป็นต้นไปคุณสามารถทำสิ่งต่อไปนี้:
class MyQuerySet(models.QuerySet):
def get_or_none(self, **kwargs):
try:
return self.get(**kwargs)
except self.model.DoesNotExist:
return None
class MyBaseModel(models.Model):
objects = MyQuerySet.as_manager()
class MyModel(MyBaseModel):
...
class AnotherMyModel(MyBaseModel):
...
ข้อดีของ "MyQuerySet.as_manager ()" คือทั้งสองอย่างต่อไปนี้จะทำงานได้:
MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()
นี่คือความแตกต่างของฟังก์ชั่นตัวช่วยที่อนุญาตให้คุณเลือกส่งผ่านQuerySet
ตัวอย่างในกรณีที่คุณต้องการรับวัตถุที่ไม่ซ้ำกัน (ถ้ามี) จากชุดแบบสอบถามนอกเหนือจากall
ชุดแบบสอบถามวัตถุของโมเดล(เช่นจากชุดย่อยของรายการเด็กที่เป็นของ ตัวอย่างผู้ปกครอง):
def get_unique_or_none(model, queryset=None, **kwargs):
"""
Performs the query on the specified `queryset`
(defaulting to the `all` queryset of the `model`'s default manager)
and returns the unique object matching the given
keyword arguments. Returns `None` if no match is found.
Throws a `model.MultipleObjectsReturned` exception
if more than one match is found.
"""
if queryset is None:
queryset = model.objects.all()
try:
return queryset.get(**kwargs)
except model.DoesNotExist:
return None
สามารถใช้สองวิธีเช่น:
obj = get_unique_or_none(Model, **kwargs)
ดังที่ได้กล่าวไว้ก่อนหน้าobj = get_unique_or_none(Model, parent.children, **kwargs)
โดยไม่มีข้อยกเว้น:
if SomeModel.objects.filter(foo='bar').exists():
x = SomeModel.objects.get(foo='bar')
else:
x = None
ใช้ข้อยกเว้น:
try:
x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
x = None
มีข้อโต้แย้งเล็กน้อยเกี่ยวกับเมื่อหนึ่งควรใช้ข้อยกเว้นในหลาม ในอีกด้านหนึ่ง "การขอการอภัยได้ง่ายกว่าการขออนุญาต" ในขณะที่ฉันเห็นด้วยกับสิ่งนี้ฉันเชื่อว่าข้อยกเว้นควรจะยังคงอยู่ข้อยกเว้นและ "กรณีอุดมคติ" ควรรันโดยไม่ต้องกดปุ่มใดปุ่มหนึ่ง
เราสามารถใช้ Django builtin .DoesNotExist
ยกเว้นที่แนบมากับรุ่นที่มีชื่อเป็น ดังนั้นเราไม่ต้องนำเข้าObjectDoesNotExist
ข้อยกเว้น
แทนที่จะทำ:
from django.core.exceptions import ObjectDoesNotExist
try:
content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
content = None
พวกเราสามารถทำได้:
try:
content = Content.objects.get(name="baby")
except Content.DoesNotExist:
content = None
นี่เป็นตัวเลียนแบบจาก get_object_or_404 ของ Django ยกเว้นว่าเมธอดจะคืนค่า None สิ่งนี้มีประโยชน์มากเมื่อเราต้องใช้only()
แบบสอบถามเพื่อเรียกคืนบางฟิลด์เท่านั้น วิธีนี้สามารถยอมรับรูปแบบหรือชุดแบบสอบถาม
from django.shortcuts import _get_queryset
def get_object_or_none(klass, *args, **kwargs):
"""
Use get() to return an object, or return None if object
does not exist.
klass may be a Model, Manager, or QuerySet object. All other passed
arguments and keyword arguments are used in the get() query.
Like with QuerySet.get(), MultipleObjectsReturned is raised if more than
one object is found.
"""
queryset = _get_queryset(klass)
if not hasattr(queryset, 'get'):
klass__name = klass.__name__ if isinstance(klass, type) else klass.__class__.__name__
raise ValueError(
"First argument to get_object_or_none() must be a Model, Manager, "
"or QuerySet, not '%s'." % klass__name
)
try:
return queryset.get(*args, **kwargs)
except queryset.model.DoesNotExist:
return None
อาจจะดีกว่าที่คุณใช้:
User.objects.filter(username=admin_username).exists()