/ PYTHON

Python(4) - Query 만들기 [object 검색]

Python 관련 포스팅

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)

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 메서드]

Limiting QuerySets[한정 queryset]

  • 특정한 수 만큼의 결과로 queryset을 한정하기 위해 python의 array-slicing 문법의 부분 집합을 사용하라
  • 이는 SQL의 limitoffset 절과 동일함
    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 참조)