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 참조)