Python(4) - Query 만들기 [object 검색]
Python 관련 포스팅
- Python(1) - Django 설치
 - Python(2) - MySQL 연결
 - Python(3) - PostgreSQL 연결
 - Python(4) - Query 만들기
 - Python(5) - Django 가상 환경과 settings.py
 
https://docs.djangoproject.com/en/4.0/topics/db/queries/ 해석
Retrieving objects [object 검색]
- DB에서 object를 검색하기 위해서는 model class에서 Manager를 통해 query set을 만들어야 함
 - query set은 db의 object 집합을 의미함
 - 0개 이상의 filter를 가지는데, filter는 주어진 parameter에 기반해 query 결과를 좁혀줌
 - SQL에서는 query set은 select문과 동일하며, filter는 where나 limit절과 같음
 - model의 manager를 사용해 query set을 설정함
 - 각 medel은 하나 이상의 manager를 가지며, 이는 기본적으로 objects라고 부름
(manager는 ‘table-level’연산자와 ‘record-level’연산자를 분리시키기 위해 model instance보다는 model class를 통해 접근함 ) - model을 통해 직접적으로 접근하려면, 아래와 같이 입력하면 됨
    
Blog.objects # <django.db.models.manager.Manager object at ...> b = Blog(name='Foo', tagline='Bar') b.objects - manager는 model의 queryset의 main source임
예를들어,Blog.objects.all()은 DB에 있는 모든 Blog object를 포함하는 queryset을 반환함 
1. Retrieving all objects (모든 object 검색)
- table의 모든 object를 검색하기 위한 가장 간단한 방법
 - manager에 대해 
all()메서드를 사용하면 됨
all_entries = Entry.objects.all() 
2. Retrieving specific objects with filters (filter를 이용한 특정 object 검색)
all()에 의해 반환된 queryset은 db table의 모든 object를 서술함- 
    
하지만, 대게, 완전한 object의 특정 부분만 선택적으로 원할 것임
 - 이러한 특정 부분을 생성하기 위해서는, filter 조건을 추가해 최초의 queryset을 정제해야 함
 - queryset을 정제하기 위한 가장 흔한 두 가지 방법,
    
- filter(**kwargs)
주어진 검색 parameter와 일치하는 object가 포함된 새로운 queryset을 반환 함 - exclude(**kwargs)
주어진 검색 parameter와 일치하지 않는 object가 포함된 새로운 queryset을 반환 함 - 검색 parameters(위의 함수 정의에 있는 **kwargs)는 아래의 Field lookups에 서술된 형태로 있어야 함
예를 들어, 2006년부터의 blog entry들의 queryset을 얻으려면 filter()를 사용함
Entry.objects.filter(pub_date__year=2006)기본적인 manager class에서도 동일함
Entry.objects.all().filter(pub_date__year=2006) 
 - filter(**kwargs)
 
Chaining filters(연쇄 필터)
Entry.objects.filter(
    headline__startswith='What'
).exclude(
    pub_date__gte=datetime.date.today()
).filter(
    pub_date__gte=datetime.date(2005, 1, 30)
)
- db의 모든 entry의 초기 queryset을 가져와, filter를 추가한 뒤, 또 다른 filter인 예외를 추가한 것임
 - 최종 결과는 “What”으로 시작하는 헤드라인을 가지며, 2005년 1월 30일부터 현재사이에 발행된 모든 entry를 포함한 queryset임
 
Filtered QuerySets are unique (걸러진 queryset은 고유함)
- queryset을 정제할 때마다, 이전의 queryset과 bound되지 않은 새로운 queryset을 받게 됨
 - 각각의 정제는 독립된 별개의 queryset을 만들며, 저장, 사용, 재사용될 수 있음
    
q1 = Entry.objects.filter(headline__startswith="What") q2 = q1.exclude(pub_date__gte=datetime.date.today()) q3 = q1.filter(pub_date__gte=datetime.date.today()) - 세 queryset은 독립되어있음
    
- 첫 번째는 “What”으로 시작하는 헤드라인을 포함하는 모든 entry를 가지는 기본 queryset
 - 두 번째는 pub_date가 오늘의 record를 제외하는 추가적인 기준을 가진 첫 번째의 부분 집합
 - 세 번째는 pub_date가 오늘인 record만 선택하는 추가적인 기준을 가진 첫 번째의 부분 집합
 - 초기 queryset(q1)은 정제 과정에 영향 받지 않음
 
 
QuerySets are lazy (queryset은 게으름)
- queryset을 생성하는 것은 어떠한 db 활동과도 연관되지 않음
 - 온종일 filter를 쌓을 수 있고 django는 queryset이 평가될 때까지 실제로 query를 실행하지 않을 것임
    
q = Entry.objects.filter(headline__startswith="What") q = q.filter(pub_date__lte=datetime.date.today()) q = q.exclude(body_text__icontains="food") print(q) - 세 번의 db 접근으로 보이지만, 실제로는 마지막 줄의 print(q) 단 한 번만 db에 접근함
 - 일반적으로 queryset의 결과는 “요청”할 때까지 db로부터 가져오지 않음
 - 요청할 때, queryset은 db에 접근해 평가됨 (자세한 내용은 https://docs.djangoproject.com/en/4.0/ref/models/querysets/#when-querysets-are-evaluated 참조)
 
Retrieving a single object with get() (get()으로 단일 object 검색)
filter()는 단 하나의 object가 query와 일치할지라도 하나의 요소가 포함된 queryset을 줌- query와 일치하는 단 하나의 object가 있다고 할 때, 직적적으로 object를 반환하는 
get()메서드를 manager에 사용할 수 있음
one_entry = Entry.objects.get(pk=1) filter()처럼get()으로 어떤 query문이든 사용할 수 있음(https://docs.djangoproject.com/en/4.0/topics/db/queries/#field-lookups 참조)- [0]의 부분에 대해, 
get()을 사용하는 것과filter()를 사용하는 것 사이에는 차이가 있음을 명심할 것 - query와 일치하는 결과가 없다면, 
get()은 DoesNotExist 예외를 발생시킴 - 이 예외는 query가 수행되는 model class 속성임
 - pk=1인 entry object가 없다면 django는 Entry.DoesNotExist 예외를 발생시킬 것 임
 - 비슷하게, 하나 이상의 item과 get() query가 일치한다면, django는 항의할 것임
 - 이 경우, model class 속성인 MultipleObjectsReturned를 발생키길 것임
 
Other QuerySet methods[다른 queryset 메서드]
- db의 object를 찾아보아야 할 때, 대부분 
all(),get(),filter(),exclude()를 사용할 것임 - 모든 다양한 queryset 메서드의 완전한 리스트를 위해서는 https://docs.djangoproject.com/en/4.0/ref/models/querysets/#queryset-api를 참조할 것
 
Limiting QuerySets[한정 queryset]
- 특정한 수 만큼의 결과로 queryset을 한정하기 위해 python의 array-slicing 문법의 부분 집합을 사용하라
 - 이는 SQL의 
limit와offset절과 동일함
Entry.objects.all()[:5]→ 첫 5개의 object를 반환(LIMIT 5)
Entry.objects.all()[5:10]→ 여섯~열 번째 object를 반환(OFFSET 5 LIMIT 5) - 음수 indexing은 지원되지 않음(예를 들면, 
Entry.objects.all()[-1]) - 일반적으로 queryset을 자르는 것은 새로운 queryset을 반환하며, 이는 query를 평가하지 않음
 - 
    
python의 slice 문법의 ‘step’ parameter를 사용하는 것은 예외임
Entry.objects.all()[:10:2]
→ 첫 10개의 두 번째마다의 object를 list로 반환하는 query 실행 - 잘려진 queryset를 추가적으로 filtering하거나 ordering하는 것은 금지됨
 - 
    
단일 object를 정제하기 위해 index를 slice하는 대신에 list를 사용함(
SELECT foo FROM bar LIMIT 1)
Entry.objects.order_by('headline')[0]
Entry.objects.order_by('headline')[0:1].get()
→ 헤드라인을 알파벳 순서에 따라 정렬한 뒤, db의 첫 Entry를 반환 - 주어진 기준과 일치하는 object가 없을 시, 첫 번째는 
IndexError를, 두 번째는DoesNotExist를 발생시킴 (get()에 대한 자세한 사항은 https://docs.djangoproject.com/en/4.0/ref/models/querysets/#django.db.models.query.QuerySet.get 참조)