Codes/Python

How does Django execute SQL Query? (Korean version)

2022. 12. 16. 19:41

careems! at Dubai

How does Django execute SQL Query?

 

 Django에서는 내부적으로 ORM 기능을 구현해두어 데이터베이스 연동 번거로움을 최소화해두었다. 또한, Django에서 지원해주는 ORM 기능 덕분에 Django에서는 보통 SQL Injection 취약점이 발생하지 않는다고 봐도 무방하다. Django에서 발생하는 SQL Injection 취약점을 분석하기 위해서는 Django가 내부적으로 어떻게 SQL 쿼리를 실행하는지 이해할 필요가 있다.

 

1. QuerySet 내부 매직메소드에 의한 private 메소드 실행

 

 Django는 내부적으로 Lazy Loading 기능을 구현해두었다. Model.objects.all(), Model.objects.filter() 같이 QuerySet 내부의 메소드를 실행시킨다고 해서 쿼리가 바로 시작되지 않는다. 이는 쿼리의 결과를 최대한 효율적으로 이용하기 위함이며, QuerySet 클래스 내부에 정의된 특정 매직 메소드(__getstate__, __len__, __iter__, __bool__)에서 QuerySet 클래스 _fetch_all() 메소드를 수행함으로써 실제 SQL Query 실행 과정이 트리거 된다.

 

django.db.models.query.QuerySet

 

 위 코드는 django.db.models.query.QuerySet 에 정의된 __len__ 매직메소드다. self._fetch_all() 메소드를 실행시키고 self._result_cache 를 반환해주고 있다.

 

2. QuerySet._fetch_all() 메소드 실행

 

_fetch_all() defined in django.db.models.QuerySet

 

 QuerySet._fetch_all() 메소드에서는 내부적으로 사용되는 self._result_cache 값을 검사한다. 보통 처음 쿼리가 실행될 때 self._result_cache는 None 이다. 캐시에 로드된 데이터가 없다면 self._iterable_class 에 self를 인자로 전달해 나온 쿼리 결과가 list() 함수를 거쳐 self._result_cache에 저장된다. 이때 넘겨지는 self 는 QuerySet 클래스다.

 

여기서 self._iterable_class 는 Model.objects.values() 메소드로 쿼리를 실행하는 경우를 제외하고는 django.db.models.query.ModelIterable 클래스로 설정된다. values() 메소드를 실행하는 경우에는 ValuesIterable 클래스가 사용된다.

 

3. ModelIterable 클래스에서 SQL Compiler 로드

 

django.db.models.query.ModelIterable

 

ModelIterable 클래스 내부에 정의된 __iter__ 메소드가 2번 과정에서의 list() 함수에 의해 호출되며 execute_sql() 메소드가 실행된다. db에는 Django 프로젝트 디렉터리에 있는 settings.py 의 DATABASES 값에 지정된 키 값이 들어간다. 데이터베이스 설정을 바꾸지 않았다면 "default" 가 들어간다.

 

settings.py

 

get_compiler() method defined at django.db.models.query.Query

 

위 ModelIterable에서 queryset.query.get_compiler(using=db)가 실행되면 내부적으로 django.db.models.sql.query.Query 에 정의된 get_compiler() 메소드를 실행한다. 최종적으로 이 메소드는 자신이 settings.py에 설정한 데이터베이스에 해당하는 SQL Compiler를 반환한다.

 

4. ModelIterable 클래스에서 execute_sql() 메소드 실행

 

 앞선 과정에서 사용하는 DBMS에 맞게 SQLCompiler 클래스가 로드되었다. 각 SQLCompiler에 정의되어 있는 execute_sql() 메소드가 실행되며 SQL 쿼리가 보내진다.

 

execute_sql() method defined at django.db.models.sql.compiler.SQLCompiler

 

self.as_sql() 로 sql과 params 값을 로드하고, 이것을 cursor.execute() 함수를 실행하며 최종적으로 SQL Query가 실행된다.