get()
과 filter()
django/django ORM을 다룰 때 get()
과 filter()
는 단골 손님이다.
API 작성의 핵심은 CRUD이고, 필연적으로 DB를 많이 다루게 된다. 그러다보니 당연히 get()
과 filter()
가 가장 많이 사용하는 메소드 중 하나가 된다.
그런데 코드를 계속 보고 여러 기능들을 계속해서 작성하다보니 머리가 살짝 뒤죽박죽 되는 듯한 느낌을 받았다 (...) 아무렇지 않게 filter()
를 쓰다가도, '어차피 조회하려는 queryset에는 데이터가 1개 밖에 없는데, 바로 get()
쓰면 안되나?' 하는 등.
그래서, 정리가 한번 필요하다고 느꼈다.
공식문서의 설명은 아래와 같다.
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()
은 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에 묶어 반환할 것이다.
아래와 같은 상황이 있을 수 있다.
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
이라는 메세지를 반환하게 하는 것이다.
만약 filter()
를 먼저 사용해야 하는데, QuerySet에 1개의 row만 있는 것이 너무나도 명백한 경우가 있다.
아래와 같이 2개 메소드를 섞어서 사용할 수 있다.
Member.objects.filter(id=1).get()
filter에 대해서도 조금 더 살펴보자. filter()
는 조건들을 인자로 받는데, 모두 AND 연산으로 묶인다.
Multiple parameters are joined via AND in the underlying SQL statement. (django 공식문서)
그래서 만약 OR 연산으로 조건을 걸고 싶다면 Q객체
를 활용해야 한다.
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
API 작성의 핵심은 CRUD이고, DB를 많이 다뤄야 한다는 것을 이제야 깨달았습니다. 물론 get() 및 filter()가 가장 많이 사용되는 방법 중 하나입니다. 그건 알아차리기 힘든 일입니다.