[django] get(), 그리고 filter()

EMMA·2022년 7월 13일
1

django의 핵심 메소드 - get()filter()


django/django ORM을 다룰 때 get()filter()는 단골 손님이다.
API 작성의 핵심은 CRUD이고, 필연적으로 DB를 많이 다루게 된다. 그러다보니 당연히 get()filter() 가 가장 많이 사용하는 메소드 중 하나가 된다.

그런데 코드를 계속 보고 여러 기능들을 계속해서 작성하다보니 머리가 살짝 뒤죽박죽 되는 듯한 느낌을 받았다 (...) 아무렇지 않게 filter()를 쓰다가도, '어차피 조회하려는 queryset에는 데이터가 1개 밖에 없는데, 바로 get() 쓰면 안되나?' 하는 등.

그래서, 정리가 한번 필요하다고 느꼈다.


몇 개의 row를 호출하는가?

공식문서의 설명은 아래와 같다.

get() : returns the object matching the given lookup parameters
filter() : returns a new QuerySet containing objects that match the given lookup parameters

즉, get()은 조건에 해당하는 object, 1개의 row를 반환한다.
반면 filter()는 조건에 해당하는 여러 row를 모두 묶어 QuerySet이라는 형태로 반환한다.


그래서 get은, filter와 달리 여러 에러를 raise한다

위에서 서술한 차이 때문에, get() 은 2가지 에러를 발생시킨다.

첫 번째, DoesNotExist 에러다.
get()은 조건에 해당하는 row가 존재하지 않는 경우, 'matching query does not exist' 라며 에러를 뱉는다.

#Member 라는 테이블이 있다고 가정해보자. 여기엔 2명의 정보가 저장되어 있다. 
Member.objects.all() 
>>> <QuerySet [<Member: Member object (1)>, <Member: Member object (2)>]


#id=3인 member를 get으로 호출해보자
Member.objects.get(id=3) 
>>> Member.DoesNotExist: Member matching query does not exist.


#id=3인 member를 filter로 호출해보자
Member.objects.filter(id=3)
>>> <QuerySet []>

반면 filter()는 에러가 아닌 빈 QuerySet을 반환한다.

두 번째, MultipleObjectsReturned 에러다.
get()은 한 개의 row만 반환한다. 따라서 해당 조건에 해당하는 데이터가 여러개 일 경우 역시 에러를 뱉는다.

#Member 라는 테이블이 있다고 가정해보자. 
#모든 objects는 `sns_id` 컬럼을 갖고 있고, 모두 1로 저장되어 있다. 
Member.objects.all() 
>>> <QuerySet [<Member: Member object (1)>, <Member: Member object (2)>]

Member.objects.get(sns_id=1)
>>> Member.MultipleObjectsReturned: get() returned more than one Member -- it returned 2!

만약 filter()로 조회하면, 2개 row를 모두 QuerySet에 묶어 반환할 것이다.


filter -> get 순서로 모두 사용하는 경우

아래와 같은 상황이 있을 수 있다.

req_m = Member.objects.filter(id=10)

if not req_m:
    return JsonResponse({"message":"FAIL"}) 
else:
	req_m = req_m[0]
    return JsonResponse({"message":req_m.sns_id})

Member 테이블에 id=10인 row는 없다. 그리고 id=10인 멤버의 유무에 따라 다른 JsonResponse를 보내야 한다고 가정해보자.

처음부터 filter대신 Member.objects.get(id=10) 로 작성하면 더 편할 수 있겠으나, 이렇게 작성하면 바로 에러가 난다.

그래서 먼저 filter() 를 통해 존재 유무를 판단하고 > 있으면 해당 row의 sns_id값을 반환, 없으면 FAIL이라는 메세지를 반환하게 하는 것이다.


get과 filter를 섞어 쓰기

만약 filter() 를 먼저 사용해야 하는데, QuerySet에 1개의 row만 있는 것이 너무나도 명백한 경우가 있다.
아래와 같이 2개 메소드를 섞어서 사용할 수 있다.

Member.objects.filter(id=1).get()

filter는 조건을 AND 연산으로 묶는다

filter에 대해서도 조금 더 살펴보자. filter()는 조건들을 인자로 받는데, 모두 AND 연산으로 묶인다.

Multiple parameters are joined via AND in the underlying SQL statement. (django 공식문서)

그래서 만약 OR 연산으로 조건을 걸고 싶다면 Q객체를 활용해야 한다.

  • filter(condition1, condition2) > 조건 2개가 AND로 묶임
  • filter(Q(condition1) | Q(condition2)) > 조건 2개가 OR로 묶임
  • filter(Q(condition1) & Q(condition2)) > 조건 2개가 AND로 묶임
  • Queryset1 & Queryset2 > 조건 2개가 AND로 묶임
    • 예시는 아래와 같다
    • User.objects.filter(first_name__startswith='R') 
      & User.objects.filter(last_name__startswith='D')

참고 자료
https://docs.djangoproject.com/en/dev/ref/models/querysets/
https://django-orm-cookbook-ko.readthedocs.io/en/latest/and_query.html

profile
예비 개발자의 기술 블로그 | explore, explore and explore

1개의 댓글

comment-user-thumbnail
2022년 7월 14일

API 작성의 핵심은 CRUD이고, DB를 많이 다뤄야 한다는 것을 이제야 깨달았습니다. 물론 get() 및 filter()가 가장 많이 사용되는 방법 중 하나입니다. 그건 알아차리기 힘든 일입니다.

답글 달기