본문 바로가기

django

Django ORM

ORM이란?

 

ORM(Object-relational Mapping)은 데이터 베이스와 객체지향 프로그래밍 언어 간의 호환되지 않는 데이터를 변환하는 프로그래밍 기법이다.

원래 데이터베이스 서버의 어떤 데이터를 조회, 추가, 수정, 삭제 등을 할 때는 sql이라는 언어를 사용해야 한다. 그런데 sql을 사용하지 않고 각 언어 그대로를 활용해서 sql 코드를 만들어내는 라이브러리가 있다. 그걸 ORM 이라고 한다.

장고에서는 Django Model 이 장고의 ORM이다.

 

Model Manager

  • 데이터 베이스 질의 인터페이스를 제공한다.
  • 기본 Manager로써 모델클래스.object 를 제공한다.
  • Model Manager를 통해서 해당 모델 클래스의 DB 데이터를 추가, 조회, 수정, 삭제가 가능하다.

 

QuerySet

  • SQL을 생성해주는 인터페이스
  • queryset을 통하여 별도로 SQL을 작성할 필요 없이 DB로부터 데이터를 가져오고 추가, 수정, 삭제가 가능하다.
  • Model Manager를 통해서 해당 Model에 대한 QuerySet을 획득한다.
# 30세 이상 멤버 정보 가져오기 in sql
SELECT *
FROM member 
WHERE age >= 30;

# 30세 이상 멤버 정보 가져오기 in django orm
Member.objects.filter(age__gte=30)

 

  • Chaning 을 지원한다.
# 30세 이상이면서 신장이 190 이하인 멤버 정보 가져오기 in sql
SELECT *
FROM member
WHERE (age >= 30 AND height <= 190);

# 30세 이상이면서 신장이 190 이하인 멤버 정보 가져오기 in django orm
Member.objects.filter(age__gte=30).filter(height__lte=190)

 

ORM을 사용하면 관계형 데이터베이스의 제약을 최대한 받지 않으면서, 객체를 클래스로 표헌하는 것과 같이 관계형 데이터베이스를 객체처럼 쉽게 사용할 수 있도록 해준다.

 

하지만 이 ORM의 가장 무서운 점은 실제 안에서 어떤 쿼리문을 발생시켜서 저렇게 쉽게 쓸수 있도록 해주는지 알 수 없다는 점이다.

 

ORM 쿼리가 어떻게 요청이 되는지 고려하지 않고 코드를 작성하게 되면 "좋지 않다" 혹은 "나중에 데이터가 쌓이면 성능적으로 치명적인 이슈가 된다"라는 말을 자주 보았지만 크게 와닿지가 않았다. 그래서

관계형 데이터베이스의 장점은 Foreign Key를 묶을 수 있고 해당 Foreign Key로 다른 테이블에도 접근할 수 있다는 것이다. 하지만 이렇게 Foregin Key로 묶인 데이터를 ORM에서 가지고 올 때 주의해야 할 부분이 있다.

 

# 30세 이상이면서 신장이 190 이하인 멤버 정보 가져오기 in django orm
Member.objects.filter(age__gte=30).filter(height__lte=190)

위의 코드에서 해당하는 유저가 1만 명이면 1만번의 쿼리를 반환하게 된다. 이 때 select_related 를 사용하면 쿼리를 줄일 수 있다.

 

 

select_related 란?

  • 1:1 관계에서 사용할 수 있고, 1:N의 관계에서 N이 사용할 수 있다. 즉 정방향 참조(Member 모델에서 Pet 모델의 정보를 찾으려고 할 때)에서의 JOIN에 유리하게 사용된다.

모델을 만들어서 확인해보자

 

class Member(models.Model):
    name = models.CharField(max_length=10)
    email = models.CharField(max_length=32)
		
    def __str__(self):
        return self.name

class Pet(models.Model):
    name = models.CharField(max_length=32)
		age = models.IntegerField()
		owner = models.OneToOneField(Member, 
						on_delete=models.CASCADE,
						related_name='member_pet') #related_name='%(class)s_room'

위의 Member 모델과 Pet 모델의 관계는 OneToOne이다.

위를 보면 3명의 Member들은 1개의 Pet들과 OneToOne 관계로 연결되어 있다.

이 때, 모든 Pet들의 쿼리셋을 가져온다면 일반적으로 Pet.objects.all() 로 가져올 수 있다.

 

이 때 쿼리는 총 몇 개가 실행될까??

 

총 4번의 쿼리를 가져온다.

  • Pet 모델의 데이터를 가져오는 쿼리 1개
  • Pet 모델에서 가져온 3개의 데이터에 OneToOne으로 연결된 Member 모델을 가져오는 쿼리 3개

 

그럼 Pet.object.all().select_related('member')를 사용하여 Member 모델들까지 모두 가져와보자.

이 때 몇 개의 쿼리가 실행될까??

 

 

select_related는 INNER JOIN으로 데이터를 가져오기 때문에 쿼리를 1개만 사용한다.

만약, Pet에 100개의 Member와 OneToOne 관계를 가지고 있는 100명의 유저가 있다면

Pet.objects.all() 를 사용해서 101번의 쿼리를 가지고 오는 것보다 Pet.object.all().select_related('member') 로 단 1개의 쿼리를 가지고 오는 것이 더 효율적이다.

 

prefetch_related란?

prefetch_related는 반대로 1:N의 관계에서 1이 사용할 수 있고, M:N의 관계에서 사용할 수 있다. 즉, 역방향 참조( Pet 모델에서 Member 모델의 정보를 찾으려고 할 때)에 유리하게 사용된다.

select_related는 INNER JOIN으로 쿼리셋을 가져오고, prefetch_related 는 Member, Pet에 대해 각각의 쿼리로 쿼리셋을 가져온다.

 

 

정리

  • select_related 는 INNER JOIN 으로 쿼리셋을 가져온다.
  • prefetch_related 는 모델별로 쿼리를 실행해 쿼리셋을 가져온다.
  • 이 모든건 qeryset들이 캐싱되기 때문에 가능

'django' 카테고리의 다른 글

Django Admin 커스텀  (0) 2022.11.22
Django와 Axios를 이용한 좋아요 구현하기  (0) 2022.01.15
Django CBV 기반 CRUD 만들기  (0) 2022.01.09
Django 프로젝트 구조  (0) 2021.12.07
Django Deploy  (0) 2021.12.07