개체가 존재하는 경우 어떻게 얻거나 존재하지 않는 경우 없음을 어떻게 얻습니까?


223

모델 관리자에게 객체를 요청하면 DoesNotExist일치하는 객체가 없을 때 발생합니다.

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

대신에 DoesNotExist, 내가 가지고 할 수있는 방법 go이 될 None대신?

답변:


332

이를위한 '내장'방법은 없습니다. 장고는 매번 DoesNotExist 예외를 발생시킵니다. 파이썬에서 이것을 처리하는 관용적 인 방법은 그것을 시도 캐치로 감싸는 것입니다.

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

내가 한 것은 모델을 서브 클래스하는 것입니다 .Manager, safe_get위의 코드와 같은 코드를 만들고 해당 관리자를 사용하십시오. 그렇게하면 다음과 같이 쓸 수 있습니다 : SomeModel.objects.safe_get(foo='bar').


7
예외를 가져 오는 대신 SomeModel.DoesNotExist를 사용하는 것도 좋습니다.
supermitch

196
이 솔루션의 길이는 4 줄입니다. 나에게 이것은 너무 많다. django 1.6 SomeModel.objects.filter(foo='bar').first()에서는 첫 번째 일치를 반환하거나 None을 사용할 수 있습니다 . 여러 인스턴스가 같이있는 경우는 실패하지 않습니다queryset.get()
guettli

9
기본 사례를 처리하기 위해 예외를 과도하게 사용하는 것은 나쁜 스타일이라고 생각합니다. 그렇습니다. "허가보다는 용서를 구하는 것이 더 쉽습니다". 그러나 예외는 여전히 내 눈에 사용되어야합니다.
Konstantin Schubert

8
암시적인 것보다 명시적인 것이 좋습니다. 성능상의 이유가 없다면 filter().first()예외는 갈 길이라고 생각합니다.
christianbundy

6
배수가있을 때 신경 쓰지 않으면 first ()를 사용하는 것이 좋습니다. 그렇지 않으면이 솔루션은 우수합니다. 예를 들어 예기치 않은 여러 객체를 발견하면 여전히 예외가 발생하므로 일반적 으로이 경우에 발생합니다.
rossdavidh

182

django 1.6부터 first () 메소드를 다음과 같이 사용할 수 있습니다 .

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

26
이 경우 일치하는 항목이 두 개 이상이면 오류가 발생하지 않습니다.
Konstantin Schubert

7
'FeroxTL'은 게시물 1 년 전에 수락 된 답변에 대해 댓글을 달았으므로이 답변에 대해 @guettli의 크레딧을 제공해야합니다.
colm.anseo

7
@colminator 나는 guettli가 stackoverflow reputaiton을 높이고 싶다면 새로운 답변이 주석이 아니라는 것을 알아야한다고 말하고 싶습니다. 귀하의 의견은 guettli의 의견입니다. 그것이 귀하의 제안 인 경우 대답에 추가되어서는 안됩니다.
Joakim

3
@Joakim 새로운 "답변"을 게시하는 데 아무런 문제가 없습니다. 마감일을 신용장으로
보내려면

3
허용 된 답변과 비교하여이 방법의 성능은 어떻습니까?
MaxCore

36

장고 문서에서

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

33

이를위한 일반 함수를 만들 수 있습니다.

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이되며 Content 항목을 반환합니다.

참고 : name = "baby"에 대해 둘 이상의 항목이 반환되면 MultipleObjectsReturned 예외가 발생합니다.



14

일을 쉽게하기 위해 여기에 멋진 답변의 입력을 기반으로 작성한 코드 스 니펫이 있습니다.

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 ()이 있습니다.


7
또한, 수입하는 것을 잊지 마세요 : django.core.exceptions이 ObjectDoesNotExist 가져올에서
모티 라돔스키

13

exists필터와 함께 사용할 수 있습니다 .

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

존재하는지 알고 싶은 경우에 대한 대안


4
존재하는 경우 추가 데이터베이스 호출이 발생합니다. 좋은 생각이 아닙니다
Christoffer

@Christoffer는 왜 추가 DB 호출인지 확실하지 않습니다. 당으로 문서 :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 당신이 옳다고 생각합니다. 이제 질문을 다시 읽고 OP는 실제로 실제 개체를 반환하기를 원합니다. 따라서 객체를 가져 오기 전에 절 exists()과 함께 사용 if되므로 db에 이중 히트가 발생합니다. 다른 사람에게 도움이 될 수 있도록 의견을 계속 유지합니다.
Anupam

7

뷰의 다른 지점에서 예외를 처리하는 것은 정말 번거로울 수 있습니다. 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

1
나는이 솔루션을 정말로 좋아하지만 python 3.6을 사용할 때와 같이 작동하지 못했습니다. ContentManager에서 수익을 수정하면 효과가 있다는 메모를 남기고 싶었 return self.get(**kwargs)습니다. 대답에 문제가 있다고 말하지 말고, 이후 버전 (또는 다른 것이 나에게 도움이되지 않는 것)으로 사용하려고하는 사람을위한 팁.
skagzilla

7

다시 구현하고 싶지 않은 성가신 기능 중 하나입니다 .

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이미있는) 주변을 고려할 수 있습니다 .
John Pang

4

예외 처리, 조건문 또는 Django 1.6+의 요구 사항을 포함하지 않는 간단한 한 줄 솔루션을 원한다면 다음을 수행하십시오.

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

4

사용하는 것이 나쁘지 않다고 생각합니다 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.")

django 온라인 문서에서 get_object_or_404 () 에 대한 자세한 내용을 읽을 수 있습니다 .


2

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()

1

다음 QuerySet은 모델의 all객체 queryset 이외의 queryset에서 (예를 들어 부모 인스턴스) :

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

다음과 같은 두 가지 방법으로 사용할 수 있습니다.

  1. obj = get_unique_or_none(Model, **kwargs) 미리 논의한대로
  2. obj = get_unique_or_none(Model, parent.children, **kwargs)

1

예외없이:

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

파이썬에서 예외를 사용해야 할 때에 대한 약간의 논쟁이 있습니다. 한편으로, "허가보다는 용서를 구하는 것이 더 쉽다". 이에 동의하는 동안 예외는 그대로 유지되어야하며 예외는 "이상적인 경우"가 발생하지 않아야합니다.


1

로 명명 된 모델에 첨부 된 Django 내장 예외를 사용할 수 있습니다 .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

0

이것은 메소드가 None을 반환하는 것을 제외하고는 Django의 get_object_or_404에서 복사 한 것입니다. 이는 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

0

어쩌면 사용하는 것이 좋습니다.

User.objects.filter(username=admin_username).exists()
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.