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


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.