Django model(객체)을 DB table로 변환시켜 조작하게 해줌(SQL없이도 DB를 조작할 수있음)
DBMS에 상관없이 사용 가능하고 객체지향의 관점에서 DB를 설계할 수 있음
단, 기계가 작성하는 것이기때문에 반드시 효율적인 SQL로 변환되는 것은 아님
django ORM 기본 사용법
간단한 모델 클래스 2개가 있다. 2개의 클래스로 기본적인 사용법을 예로 들면,
Select
위는 ORM을 사용한 select명령들이다. get이나 filter에서 사용된 조건은 where절과 동일하다고 생각하면 쉽다.
위 4개의 명령 중 get은 Post의 오브젝트를 바로 반환하지만, filter와 all의 경우 쿼리셋을 반환하는 차이가 있다.
django에서는 쿼리가 평가(실행)되는 시점이 실제 쿼리셋이 사용될 때까지 지연된다. 그 때문에
filter와 all은 실제로는 DB 커넥션이 이루어지지 않고 쿼리셋만을 반환한다.
order_by의 경우 특정 칼럼을 기준으로 정렬할 때 사용한다. -의 경우 내림차순, 기본은 오름차순이다.
만약, Model class에 Meta클래스를 선언하고 ordering을 줄 경우 해당 Model class의 쿼리셋의 기본 정렬순서를 정해줄 수 있다.
추가로 다양한 조건으로 쿼리를 실행할 수 있다.
Insert
Update
Delete
django ORM 효율적 사용
앞서 말했듯이 django에서 ORM을 통해 얻은 쿼리의 실제 실행 시점은 유저가 쿼리를 실행한 시점이 아닌 경우가 많다.
if나 for문, 혹은 실제로 데이터를 가지고 와야할 때 실행되는데 일반적으로는 성능 이슈에서 큰 부분을 차지하는 DB connection이
무분별하게 생성되는 것을 막아준다. 하지만 대부분의 쿼리가 지연되기때문에 몇가지 문제가 발생할 수 있는데
그에 대한 해결 방법을 설명하고자 한다.
쿼리셋의 캐시
쿼리셋은 한번 평가(실행)되면 캐시되는데 쿼리셋을 캐시로 생성할지 말지를 경우에 따라 고려해주어야한다.
같은 쿼리가 여러번 실행되어햐 할 경우에는 있는 그대로 쿼리를 실행하면, django ORM의 캐시 혜택을 받을 수 있지만,
쿼리가 한번만 실행되는 경우나, 캐시되어야할 쿼리셋이 너무 큰 경우에는 캐시가 생설될 경우 오히려 비효율적이다.
특히, 쿼리셋에 값이 있는지 여부나 쿼리셋의 길이만을 알고 싶은 경우 exists()와 count()를 활용해야한다.
조금 더 효율적인 쿼리
ORM을 사용하다보면 흔히 생기는 성능 이슈가 N+1 문제인데, django에서는 모든 경우 적용되는 것은 아니지만
select_related와 prefetch_related를 활용하여 해결할 수 있다.
N+1 문제
모든 Post의 모든 Comment를 불러낼 때 일어날 수 있는 문제이다.
위의 경우 각 post의 comment를 가져오기위해 쿼리가 N+1번 발생하게되고, 불필요한 DB 커넥션이 생성되어 성능에 문제가
생길 수 있다.(모든 쿼리를 저런식으로 작성한다면..)
이때 1:N의 관계일 때 사용할 수 있는 것이 select_related, N:M일 때는 prefetch_related이다.
두 메소드는 ForeignKey와 ManyToManyField로 선언된 경우만 사용 가능, 즉 Post에서 select_related(‘comment’)만 가능하다.
values와 values_list
values와 values_list는 Model class에서 특정 필드만을 필요로할 때 사용할 수 있으며, 클래스 전체를 호출하는 것 보다 효율적이다.
고급 쿼리
django 쿼리셋을 조금 더 효과적으로 쓸 수 있게해주는 Annotation과 Aggregation에 대해 소개하고자 한다.
두 메소드는 django.db.models에 있는 F,Sum, Count나
djaogn.db.models.functions에 있는 Length 등과 자주 쓰인다.
Annotation은 쿼리에 임시 쿼리를 추가하는 메소드이다.
Annotation이 칼럼을 늘린다면, Aggregation은 반대로 Sum등을 활용하여 여러행을 하나로 합쳐서 하나의 행으로 만들어준다.