티스토리 뷰

🐍/Django

[Django] 쿼리문 (retrieve)

eungding 2021. 3. 21. 01:55
반응형

장고의 Making queries 문서를 정리합니다 ✏️ 

[Django] 쿼리문 (create, update, delete) 에서 이어지는 글 입니다! 

 

 

[ Retrieve 원리 ] 

장고는 Manager라는 객체를 모든 Model class에 디폴트로 추가합니다.

objects 라는 이름으로 추가되는데, 원한다면 objects 대신 다른 이름을 쓰도록 바꿀 수 있습니다.

(하지만 바꾸는 사례를 본 적이 없습니다,,,,)

 

모델 클래스에 있는 이 Manager를 통해 QuerySet을 얻어오는 방식으로 

database에서 objects를 retrieve 합니다. 

 

 

[ QuerySet ]

QuerySet 은 database에 있는 objects의 모음(collection)을 말합니다. 

 

예를들어 어떻게 생겼는 지(?) 살펴볼게요

이런 모델이 있다고 할 때, 

class FamilyQuestion(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    family = models.ForeignKey(Family, on_delete=models.CASCADE)
    create_date = models.DateTimeField()

 

모든 objects를 가져오면

이렇게 생긴 QuerySet이 리턴되는 것을 볼 수 있습니다.

 

 

 

QuerySet에서 index로 원하는 object를 가져올 수 도 있고

 

 

QuerySet을 list로 만들 수 있습니다. 

 

 

또한 QuerySet에 하나 또는 여러개의 필터도 적용할 수 있습니다. (참고: QuerySet API)

이것은 아래에서 더 살펴보겠습니다!

 

[1] all

 

all() 메소드는 모든 objects를 담고 있는 QuerySet을 리턴합니다. 

이 메소드를 이용해서 특정 table에 있는 모든 record를 얻어 올 수 있습니다. 

 

 

[2] Field lookups 

 

이제 원하는 objects만 retrieve할 수 있는

QuerySet 의 세가지 메소드 filter()exclude() ,get() 을 살펴볼 것입니다.

그 전에 위의 메소드에 들어가는 Keyword Arguments 를 먼저 알야합니다..!!

 

keyword arguments의 형식은  'field__lookuptype=value' 입니다.

풀어쓰면(?)

'필드네임 ➕ 더블 언더스코어  field lookup type =  값'  입니다. 

 

예를 들어 2006-01-01 이전에 발행된 블로그 글을 retrieve 하고 싶다! 라고 할때

 

field lookup type 중 lte를 선택하고 

 

 

'필드네임(pub_date) ➕ 더블 언더스코어(__)  filed lookup type(lte)  = ➕ 값 (2006-01-01)'

이렇게 작성할 수 있는 것입니다. 

 

 

이는 SQL의 WHERE문과 같다고 볼 수 있습니다. 

 


대표적인 field lookup type 몇개만 더 살펴보겠습니다.

(field lookup type 문서 에서는 대응되는 SQL문도 친절하게 같이 적혀있어요! 읽어보시길 강력추천)

 

 

1) exact

 

exact 는 말그대로 정확히 match 되는 것을 의미합니다. 

예를들어 headline필드가 "Cat bites dog"인 object를 가져오고 싶을 때 이렇게 사용할 수 있습니다. 

 

 

만약 keyword argument를 작성할 때 lookup type을 명시하지 않으면 exact로 인식합니다. 

그래서 두개의 예시는 똑같은 결과를 리턴합니다. 

 

 

2) inexact

 

iexact 는 case-insensitive match를 의미합니다.

(exact 앞의 i는 case-insensitive의 i를 의미하는 걸까요...?! )

 

예를들어 nickname이 f인 object가 있다고 할 때, 

exact를 쓰면 결과가 empty인 반면

iexact는 해당 object를 리턴해줍니다. 

 

 

이 예제도 참고하세요!

 

3) contains

 

contains 는 case-sensitive containment test를 의미합니다.

예를들어 headline필드가 Lennon을 포함하는 글들을 가져오고 싶을 때 

이렇게 해줄 수 있습니다.

 

case-sensitive 하기 때문에 

headline이 'Today Lennon honored' 인 object는 가져오지만

'today lennon honored' 인 object는 가져오지 않습니다.

 

(case-insensitive 버전을 쓰고 싶다면 icontains 를 쓰면 됩니다)

 


 

만약 related field의 속성으로 조건을 만들고 싶으면 어떻게 해야할까요?

예를 들어 이런 모델이 있을 때, 

blog name이  'Beatles Blog'인 모든 entry들을 가져오고 싶은 경우에는?!?

단순히 더블 언더스코어로 참조하는 필드(여기서는 blog)를 명시해주면 됩니다.!

 

 

 “reverse” relationship에도 동일하게 사용됩니다. 

 

예를들어 headine이 'Lennon'을 포함하는 글을 하나이상 가진 블로그들을 가져오고 싶을 때

아래처럼 해주면 됩니다 (정말 간편,,,🥺)

 

 

그리고 여러 조건 해주고 싶으면 콤마로 연결해주면 됩니다. 

 

 

[3] filter / exclude

 

1. filter

 

filter() 메소드는 조건에 해당하는 objects만 담긴 QuerySet을 리턴합니다. 

 

 

두개 모두 같은 결과를 리턴하는데 보통 전자가 더 많이 쓰입니다. 

 

 

2. exclude

 

반대로 exclude() 메소드는 조건에 해당하는 objects를 제외한 QuerySet을 리턴합니다. 

 

 

3. Chaining filters

 

두 메소드가 QuerySet를 리턴하기 때문에 이런식으로 필터를 체이닝할 수 있습니다. 

 

 exclude, exclude 체이닝
exclue, filter 체이닝

 

체이닝하기 싫으면 이렇게 해도 됩니다. 

 

 

당연히 새로운 값을 반환하는 것이기 때문에

이 세개의 QuerySet은 separate 합니다. 

(예를들어 q1은 q2, q3에 의해 영향을 받지 않습니다. )

 

 

[4] get

 

filter()는 objects로 구성된 QuerySet을 리턴합니다. 

반면 get() 은 sinlge object를 리턴합니다. 

 

get() 은 

조건에 해당하는 object가 없으면 ObjectDoesNotExist exception을 발생시킵니다. 

조건에 해당하는 object가 한개보다 더 많으면  MultipleObjectsReturned exception을 발생시킵니다. 

 

 

[5] Limiting QuerySets

 

Python의 array-slicing을 사용해서 QuerySet을 특정 갯수로 제한(limit)할 수 있습니다. 

이는 SQL의 LIMIT과 OFFSET과 같습니다. 

 

예를들어  first 5 objects만 리턴받고 싶다면 (Limit 5)

 

 

sixth through tenth objects만 리턴받고 싶다면  (OFFSET 5 LIMIT 5)

 

 

하지만 Negative indexing은 지원되지 않습니다. 

Entry.objects.all()[-1]

 

 

만약 Single Object를 retrieve하고 싶다면 (SELECT foo FROM bar LIMIT 1)

index를 사용하면 됩니다. 

 

예를들어 headline을 기준으로 알파벳 순으로 정렬한 다음 

첫번째 Entry를 DB에서 retrieve하고 싶다면 아래처럼 하면 됩니다.

 

 

두개의 결과는 똑같으나

만약 index에 해당하는 object가 존재하지 않았을 경우

전자는 IndexError

후자는 DoesNotExist 를 raise 한다는 점이 다릅니다. 

 

 

반응형
댓글