Django and BackEnd

장고의 모델 쿼리셋에서 더블 언더스코어(__)(언더바 두개)는 무슨 의미 인가요?

하급코더 2021. 4. 28. 18:32

오늘의 포스팅은 간단하게 아래 글의 번역본 정도가 될 것 같습니다.

www.dev2qa.com/what-does-double-underscore-__-means-in-django-model-queryset-objects-filter-method/

 

What Does Double Underscore ( __ ) Means In Django Model QuerySet Objects Filter Method

You have learnd how to use Django model manager’s filter or exclude method to get query result data in article Django Simple CRUD Operation Example. To use filter method is so easy as below. Department.objects.filter(dept_desc__contains='dev2qa') <quer< p=""> </quer<>

www.dev2qa.com

 

장고를 하시는 분들은 이미 아주 자연스럽게 쓰는 것 같은데 모르고 있었다는게 너무나 뼈아프네요.

 

예제 자체는 오라클의 연습용 데이블인 emp 테이블을 사용하는것 같습니다.

이전 포스트가 있는 모양인데, 테이블의 내부 구조가 궁금하시면 아래 링크를 참고하시는 것을 추천합니다.

www.dev2qa.com/django-simple-crud-operation-example/

 

-------------------------

 

일단 아래의 한줄 코드를 실행해본다고 생각합시다.

Department.objects.filter(dept_desc__contains='dev2qa')
<QuerySet [<Department: Development,Develop dev2qa.com website use Django>]>

 

필터 함수의 매개변수인 dept_desc__contains='dev2qa' 가 무슨 뜻인지는 직관적으로 이해할 수 있습니다.

쿼리셋의 결과셋에서, dept_desc column이 'dev2qa'를 반드시 포함하고 있어야 한다는 뜻이겠죠.

 

 

 

1. Field Lookups.(필드 조회)

 

장고에서 위에 해당하는 부분을 field lookups 매개변수라고 부릅니다.

매개변수의 형식은 "field명__조회방법=value" 로 정의되어 있습니다.

 

위 예제에서는 dept_desc가 필드명을, contains가 조회방법을 지정하게 됩니다.

 

위 코드가 생성하는 SQL은 대략적으로 아래와 같은 모습을 가지게 됩니다.

SELECT "dept_emp_department"."id", "dept_emp_department"."dept_name", "dept_emp_department"."dept_desc" FROM "dept_emp_department" WHERE "dept_emp_department"."dept_desc" LIKE %dev2qa% ESCAPE '\'

 

2. Built-in Field Lookup Type

 

contains와 같은 다양한 조회 방법을 지정할 수 있다면, 당연히 그 조회 방법을 커스텀하는 방법도 있을 겁니다.

장고는 대체로 그러한 방법으로 만들어져 있으니까요.

 

어쨌거나, 그러한 조회 방법은 이미 구현되어 있는 것이 많습니다.

 

그 중 몇가지가 해당 게시글에 소개되어 있습니다.

 

 

2.1 exact

 

정확히 일치하는 조건일때 결과를 반환해주는 옵션입니다만,

필터의 기본 옵션과 똑같은 내용입니다.

 

dept = Department.objects.filter(dept_name__exact='Develop')

dept = Department.objects.filter(dept_name='Develop')

위 2개 코드의 동작이 다르지 않습니다.

 

 

2.2. iexact

exact옵션과 거의 비슷하지만, 영어의 대소문자를 무시하고 매칭을 시켜주게 됩니다.

None인경우 null과 대응됩니다.

dept = Department.objects.filter(dept_name__iexact='Develop')

dept = Department.objects.filter(dept_name_iexact=None)

 

 

2.3 contains

정확히 포함하는 조건일때 매칭을 시켜줍니다.

dept = Department.objects.filter(dept_name__contains='Develop')

 

 

2.4 icontains

마찬가지로 contains의 조건에서 대소문자를 무시하고 매칭을 시켜주게 됩니다.

dept = Department.objects.filter(dept_name__icontains='Develop')

 

2.5 in

파이썬의 in 연산자에 맞는 동작을 합니다.

list나 tuple인 경우에는 해당 element를 포함하고 있는지 검사하며,

string인 경우 포함 여부를 검사하게 됩니다.

Department.objects.filter(id__in=[1,3,5])

Department.objects.filter(dept_name__in='Dev')

거기에 추가로 쿼리셋을 필터 in 검색조건의 value로 사용할 수 있습니다.

dept_qs = Department.objects.filter(dept_name__exact='Develop')

emp_qs = Employee.objects.filter(dept__in=dept_qs)

 

2.6 gt, gte, lt, lte

 

  1. gt :  > , greater than.
  2. gte : >= , greater than or equal.
  3. lt : < , less than.
  4. lte : <=, less than or equal

간단한 대소비교 조건을 걸 수도 있습니다.

equal인 경우는 당연히 조건을 걸지 않으면 됩니다.

 

 

2.7 startswith, istartsawith

 

해당 문자열로 시작하는지 여부를 검사할 수 있습니다.

i가 붙은 istartswith은 위 다른 조건들과 마찬가지로 대소문자를 무시합니다.

Department.objects.filter(dept_name__startswith='D')

Department.objects.filter(dept_name__istartswith='D')

 

2.8 endswith, iendswith

 

해당 문자열로 끝나는지 여부를 검사할 수 있습니다.

i가 붙은 iendswith은 위 다른 조건들과 마찬가지로 대소문자를 무시합니다.

Department.objects.filter(dept_name__endswith='p')

Department.objects.filter(dept_name__iendswith='e')

 

2.9 range

 

범위를 검사하는 조건입니다.

 

value를 튜플 형태로 입력 받습니다.

 

import datetime
start_date = datetime.date(2018,1,1)
end_date = datetime.date(2020,1,1)
Employee.objects.filter(emp_onboard_date__range=(start_date, end_date))

 

2.10 date

 

정확한 날짜를 검사하는 조건입니다.

value로 datetime.date 객체를 넘겨주어야만 합니다.

 

compare_date = datetime.date(2018,1,1)

Employee.objects.filter(emp_onboard_date__date=compare_date)

 

2.11 year, month, day, week

년도, 월, 날짜, 주차 등을 필터링 할 수 있게 합니다.

 

여기서 특별한 기능이 소개 되는데, 언더스코어 2개 이후 gt 등의 조건을 추가할 수 있다는 점입니다.

 

Employee.objects.filter(emp_onboard_date__year=2019)

Employee.objects.filter(emp_onboard_date__year__gt=2019)

Employee.objects.filter(emp_onboard_date__month=2)

Employee.objects.filter(emp_onboard_date__month__gt=2)

Employee.objects.filter(emp_onboard_date__day=21)

Employee.objects.filter(emp_onboard_date__day__gt=21)

Employee.objects.filter(emp_onboard_date__week=8)

 

당연히 더 많은 field lookups의 검색조건들이 있습니다.

docs.djangoproject.com/en/dev/ref/models/querysets/#field-lookups

 

QuerySet API reference | Django documentation | Django

Django The web framework for perfectionists with deadlines. Overview Download Documentation News Community Code Issues About ♥ Donate

docs.djangoproject.com

 

위 주소로 접속하여 더 많은 조건을 알아볼 수 있습니다.

 

 

 

 

3. 외래키 모델 속성의 참조

 

장고에는 django.contrib.auth라는 강력한 인증 기능이 있습니다.

contrib이라는 이름에서 유추한 바로는 서드파티 패키지인 모양이지만 안쓰는 걸 본적이 없습니다.

 

로그인과 관련된 이 기능에 __를 엮어 외래키 참조를 통해서 유저명으로 검색을 진행할 수 있습니다.

 

from django.contrib.auth.models import User

class Employee(models.Model):
    ......
    user = models.ForeignKey(User, on_delete=models.CASCADE,)
    ......
    
Employee.objects.filter(user__username = 'tom')

 

 

다른 종류의 외래키 참조 속성도 같은 맥락에서 검색이 가능합니다.

 

 

오늘 제가 이 글을 적게 된 계기가 된 taggit 라이브러리의

 

TaggableManager 역시 외래키 필드인 Foreignkey와 같은 보모인 RelatedObject를 상속합니다.

 

그 결과,

Post.objects.filter(tags__name=self.kwargs.get("tag"))

위와 같은 코드가 동작을 하게 되는 것이죠.