Varsa nesneyi nasıl alabilirim yoksa yoksa Yok'u nasıl alabilirim?


223

Model yöneticisinden bir nesne almasını istediğimde, DoesNotExisteşleşen bir nesne olmadığında yükselir .

go = Content.objects.get(name="baby")

Yerine DoesNotExist, ben nasıl goolması Noneyerine?

Yanıtlar:


332

Bunu yapmanın 'yerleşik' yolu yoktur. Django, her seferinde DoesNotExist istisnasını yükseltir. Bunu python'da işlemenin deyimsel yolu onu bir deneme yakalamaya sarmaktır:

try:
    go = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
    go = None

Yaptığım şey, alt sınıfları safe_getmodellemektir. Yönetici, yukarıdaki kodu benzer bir şekilde oluşturun ve modellerim için bu yöneticiyi kullanın. Yazabilirsiniz Bu şekilde: SomeModel.objects.safe_get(foo='bar').


7
İstisna almak yerine, SomeModel.DoesNotExist'in güzel kullanımı.
14'te süper

196
Bu çözüm dört satır uzunluğundadır. Benim için bu çok fazla. Django 1.6 ile SomeModel.objects.filter(foo='bar').first()bu ilk eşleşmeyi veya Hiçbiri'ni kullanabilirsiniz. queryset.get()
Guettli

9
Varsayılan davaları ele almak için istisnaları aşırı kullanmanın kötü bir stil olduğunu düşünüyorum. Evet, "af dilemek izin almaktan daha kolaydır". Ancak yine de, istisnalar için, gözümde bir istisna kullanılmalıdır.
Konstantin Schubert

8
Açık, örtük olmaktan iyidir. Kullanılacak bir performans nedeni olmadığı sürece filter().first(), istisnanın gidilecek yol olduğunu düşünüyorum.
christianbundy

6
First () öğesini kullanmak, yalnızca katlar olduğunda umursamıyorsanız iyi bir fikirdir. Aksi takdirde, bu çözüm üstündür, çünkü beklenmedik bir şekilde birden fazla nesne bulursanız yine de bir İstisna atar, bu normalde bu durumda olmasını istediğiniz şeydir.
rossdavidh

182

Django 1.6'dan beri first () yöntemini şu şekilde kullanabilirsiniz :

Content.objects.filter(name="baby").first()

26
Bu durumda, birden fazla eşleşme varsa Hata oluşmaz.
Konstantin Schubert

7
'FeroxTL', bu yanıt için @guettli'ye kredi vermeniz gerekiyor.
colm.anseo

7
@colminator Guettli'nin, yığın akışı saygınlığını artırmak istiyorsa yeni bir cevabın yorum olarak ait olmadığını öğrenmesini tercih ederim :) FeroxTL, yorum olarak gizli bir şeyi bir cevap olarak daha net hale getirmek için puan almalıdır. Yorumunuz sanırım guettli için yeterince kredidir ve öneriniz buysa cevaba eklenmemelidir.
Joakim

3
@Joakim Yeni bir "cevap" göndermekle ilgili sorunum yok - sadece vadesi geldiğinde kredi vermek :-)
colm.anseo

3
Bu yaklaşımın kabul edilen cevaba kıyasla performansı ne olacak?
MaxCore

36

Django dokümanlarından

get()DoesNotExistverilen parametreler için bir nesne bulunamazsa bir istisna oluşturur. Bu istisna aynı zamanda model sınıfının bir özelliğidir. DoesNotExist Dan istisna devralırdjango.core.exceptions.ObjectDoesNotExist

İstisnayı yakalayabilir ve Nonegitmek için atayabilirsiniz .

from django.core.exceptions import ObjectDoesNotExist
try:
    go  = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    go = None

33

Bunun için genel bir fonksiyon oluşturabilirsiniz.

def get_or_none(classmodel, **kwargs):
    try:
        return classmodel.objects.get(**kwargs)
    except classmodel.DoesNotExist:
        return None

Bunu aşağıdaki gibi kullanın:

go = get_or_none(Content,name="baby")

İçerik girişi eşleşmeyen başka bir girdi döndürülmezse Git Yok olur.

Not: name = "baby" için birden fazla giriş döndürüldüğünde, ÇokluObjectsReturned istisnasını yükseltir



14

İşleri kolaylaştırmak için, burada harika yanıtlardan gelen girdilere dayanarak yazdığım kodun bir parçası:

class MyManager(models.Manager):

    def get_or_none(self, **kwargs):
        try:
            return self.get(**kwargs)
        except ObjectDoesNotExist:
            return None

Ve sonra modelinizde:

class MyModel(models.Model):
    objects = MyManager()

Bu kadar. Şimdi MyModel.objects.get () ve MyModel.objetcs.get_or_none ()


7
Ayrıca, almayı unutmayın: django.core.exceptions ithalat ObjectDoesNotExist
Moti Radomski

13

existsbir filtreyle kullanabilirsiniz :

Content.objects.filter(name="baby").exists()
#returns False or True depending on if there is anything in the QS

sadece var olup olmadığını bilmek istiyorsanız


4
Bu, mevcut olduğunda ekstra bir veritabanı çağrısına neden olur. İyi bir fikir değil
Christoffer

@Christoffer neden bu ekstra bir db çağrı olurdu emin değilim. Gereğince docs :Note: If you only want to determine if at least one result exists (and don’t need the actual objects), it’s more efficient to use exists().
Anupam

2
@Christoffer Haklı olduğunu düşünüyorum. Şimdi soruyu tekrar okudum ve OP aslında asıl nesnenin iade edilmesini istiyor. Bu nedenle nesne getirilmeden önce yan tümce exists()ile birlikte kullanılacaktır, ifböylece db'ye iki kez çarpılır. Başka birine yardım etmesi durumunda yorumu hala saklayacağım.
Anupam

7

Görünümlerinizdeki farklı noktalardaki istisnaları işlemek gerçekten zahmetli olabilir .. models.py dosyasında özel bir Model Yöneticisi tanımlamaya ne dersiniz?

class ContentManager(model.Manager):
    def get_nicely(self, **kwargs):
        try:
            return self.get(kwargs)
        except(KeyError, Content.DoesNotExist):
            return None

ve sonra bunu içerik Model sınıfına dahil etme

class Content(model.Model):
    ...
    objects = ContentManager()

Bu şekilde görüşlerde kolayca ele alınabilir yani

post = Content.objects.get_nicely(pk = 1)
if post:
    # Do something
else:
    # This post doesn't exist

1
Bu çözümü gerçekten seviyorum, ancak python 3.6 kullanırken olduğu gibi çalışmayı başaramadım. ContentManager'daki getiriyi değiştirerek return self.get(**kwargs)benim için çalışmasını sağlayacak bir not bırakmak istedim. Cevabı yanlış olan bir şey söylememek, sadece daha sonraki sürümlerle (veya benim için çalışmasını engelleyen herhangi bir şeyle) kullanmaya çalışan herkes için bir ipucu.
skagzilla

7

Yeniden uygulamak istemeyebileceğiniz can sıkıcı işlevlerden biridir :

from annoying.functions import get_object_or_None
#...
user = get_object_or_None(Content, name="baby")

Kodunu kontrol ettim get_object_or_Noneama MultipleObjectsReturnedbirden fazla nesne varsa hala yükseldiğini buldum . Bu nedenle, kullanıcı bir çevreyle try-except(işlevin kendisinin try-exceptzaten sahip olduğu) düşünebilir .
John Pang

4

İstisna işleme, koşullu ifadeler veya Django 1.6+ gereksinimi içermeyen basit bir tek satırlık çözüm istiyorsanız, bunun yerine şunları yapın:

x = next(iter(SomeModel.objects.filter(foo='bar')), None)

4

Bence kullanmak kötü bir fikir değil 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)

Bu örnek şuna eşdeğerdir:

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.")

Django çevrimiçi belgelerinde get_object_or_404 () hakkında daha fazla bilgi edinebilirsiniz .


2

Django 1.7'den itibaren şunları yapabilirsiniz:

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 ()" avantajı, aşağıdakilerin her ikisinin de çalışmasıdır:

MyModel.objects.filter(...).get_or_none()
MyModel.objects.get_or_none()

1

Burada QuerySet, benzersiz nesneyi (varsa), modelin allnesneleri sorgulama kümesinden (örneğin, üst örnek):

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

Bu iki şekilde kullanılabilir, örneğin:

  1. obj = get_unique_or_none(Model, **kwargs) daha önce tartışıldığı gibi
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)

1

İstisnasız:

if SomeModel.objects.filter(foo='bar').exists():
    x = SomeModel.objects.get(foo='bar')
else:
    x = None

İstisna kullanma:

try:
   x = SomeModel.objects.get(foo='bar')
except SomeModel.DoesNotExist:
   x = None

Python'da ne zaman istisna kullanılması gerektiğine dair biraz tartışma var. Bir yandan, "bağış istemek izin vermekten daha kolaydır". Buna katılıyorum, ancak bir istisnanın kalması gerektiğine inanıyorum, istisna ve "ideal dava" bir tanesine çarpmadan çalışmalıdır.


1

Olarak adlandırılan modellere eklenen Django yerleşik istisnasını kullanabiliriz .DoesNotExist. Yani, ObjectDoesNotExististisna ithal etmek zorunda değiliz .

Bunun yerine:

from django.core.exceptions import ObjectDoesNotExist

try:
    content = Content.objects.get(name="baby")
except ObjectDoesNotExist:
    content = None

Bunu yapabiliriz:

try:
    content = Content.objects.get(name="baby")
except Content.DoesNotExist:
    content = None

0

Bu, yöntemin Yok değerini döndürmesi dışında, Django'nun get_object_or_404 ürününden bir taklitçi. Bu, only()yalnızca belirli alanları almak için sorgu kullanmamız gerektiğinde son derece yararlıdır . Bu yöntem bir modeli veya bir sorgu kümesini kabul edebilir.

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.