훈훈훈
파이썬(Django) :: Select_related 와 Prefetch_related 본문
이번에는 Selected_related와 Prefetch_related에 대해 정리 해보려고 한다.
Django ORM으로 DB에 쿼리 시 get, filter, all 이외에 Selected_related와 Prefetch_related를 적절히 사용하면 아주 강력한 무기가 될 수 있다.
이제 Selected_related와 Prefetch_related가 무엇인지 알아보도록 하자.
Selected related
# Selected_related 란 ?
Selected_related는 SQL Query 문의 JOIN 을 사용하여 foreign-key(one to one, many to one)를 사용하여 정참조할 때 사용하며 QuerySet을 가져올 때, 미리 related objects까지 불러오는 함수이다.
비록 쿼리는 복잡해지지만 불러온 data들은 데이터베이스 서버가 종료되기 전 까지 Cache에 남게되어 매 쿼리마다 DB에 접근하지 않아도 된다.
# Why Selected_related ??
Selected_related를 사용함으로서 데이터베이스에 접근하는 빈도를 줄여 자원 낭비를 차단할 수 있음
# Example
하기 파일(models.py)에 City, Person, ID에 대한 테이블을 만듬
class City(models.Model):
'''
+----+---------+
| id | name |
+----+---------+
| 1 | Seoul |
| 2 | Tokyo |
| 3 | NewYork |
+----+---------+
'''
name = models.CharField(max_length=50)
class Meta:
db_table = 'cities'
class Person(models.Model):
'''
+----+-------+--------------+
| id | name | city_id |
+----+-------+--------------+
| 1 | Kim | 1 |
| 2 | Lee | 1 |
| 3 | Naoki | 2 |
| 4 | 2Pac | 3 |
+----+-------+--------------+
'''
name = models.CharField(max_length=50)
city = models.ForeignKey('City', on_delete=models.CASCADE)
class Meta:
db_table = 'persons'
class Identification(models.Model):
'''
+----+----------------+----------------+
| id | identification | person_id |
+----+----------------+----------------+
| 1 | 940101-1111111 | 1 |
| 2 | 970101-1111111 | 2 |
| 3 | 800101-1111111 | 3 |
| 4 | 200101-1111111 | 4 |
+----+----------------+----------------+
'''
identification = models.CharField(max_length=50)
person = models.ForeignKey('Person', on_delete=models.CASCADE)
class Meta:
db_table = 'identifications'
- Selected_related 사용 x
>>> Person.objects.get(id=1).city
<City: City object (1)>
>>> a = Person.objects.get(id=1)
>>> b = a.city
>>> b
<City: City object (1)>
--> Selected_related 함수를 사용하지 않을 시 위 명령어와 같이 매번 Person 클래스에서 City 클래스를 참조할떄마다 두번 씩 쿼리를 수행한다는 것을 알 수 있음
- Selected_related 사용 0
>>> Person.objects.select_related('city').get(id=1)
<Person: Person object (1)>
--> 위 명령어를 보면 Selected_related 를 사용하지 않은 명령어보다 복잡해보이지만 한번 데이터베이스에 접근하여 Data를 가져오기 때문에 리소스를 훨씬 더 경제적으로 사용할 수 있다.
--> 물론 filter( )와 같은 명령어도 캐싱 기능이 있지만, Foreign_Key에 해당하는 참조 대상까지는 저장하지 않기 때문에 상황에 맞게 잘 사용하면 리소스를 효율적으로 사용할 수 있다.
추가적으로 아래와 같이 Person 클래스에서 city 칼럼이 참조하는 클래스를 바라본 후 ".key.[참조하고 있는 클래스의 칼럼]" 으로 DB로 쿼리를 할 수 있다.
>>> Person.objects.select_related('city').get(id=1).city.name
'Seoul'
Prefetch related
# Prefetch_related 란?
prefetch_related는 selected_related와 같이 data를 Cache에 저장하며, 모든 relationships에서 사용이 가능하다.
# Selected_related vs Prefetch_related
Selected_related는 하나의 Query로 related Objects들을 불러오지만, Prefetch_related는 main query가 실행이 된 후 별도의 query가 실행이 된다. 따라서 1번 이상 쿼리가 진행되기 때문에 가급적 selected_related를 사용하는 것이 리소스 소모를 줄일 수 있다,
# Example
하기 파일(models.py)에 Person, Language, PersonLanguage 에 대한 테이블을 만듬
class Person(models.Model):
'''
+----+-------+--------------+
| id | name | city_name_id |
+----+-------+--------------+
| 1 | Kim | 1 |
| 2 | Lee | 1 |
| 3 | Naoki | 2 |
| 4 | 2Pac | 3 |
+----+-------+--------------+
'''
name = models.CharField(max_length=50)
city_name = models.ForeignKey('City', on_delete=models.CASCADE)
language = models.ManyToManyField('Language', through='PersonLanguage')
class Meta:
db_table = 'persons'
class Language(models.Model):
'''
+----+----------+
| id | name |
+----+----------+
| 1 | Korean |
| 2 | japanese |
| 3 | english |
+----+----------+
'''
name = models.CharField(max_length=50)
class Meta:
db_table = 'languages'
class PersonLanguage(models.Model):
'''
id | language_id | person_id |
+----+-------------+-----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 3 |
| 4 | 3 | 4 |
| 5 | 2 | 4 |
| 6 | 1 | 4 |
+----+-------------+-----------+
'''
person = models.ForeignKey(Person, on_delete=models.CASCADE)
language = models.ForeignKey(Language, on_delete=models.CASCADE)
class Meta:
db_table = 'person_languages'
- Prefetch_related 사용 x
>>> Person.objects.get(id=4).language.values()
<QuerySet [{'id': 1, 'name': 'Korean'}, {'id': 2, 'name': 'japanese'}, {'id': 3, 'name': 'english'}]>
>>> a = Person.objects.get(id=4)
>>> b = a.language.values()
>>> b
<QuerySet [{'id': 1, 'name': 'Korean'}, {'id': 2, 'name': 'japanese'}, {'id': 3, 'name': 'english'}]>
--> Prefetch_related 함수를 사용하지 않을 시 위 명령어와 같이 매번 Person 클래스에서 language 클래스 many to many로 참조할떄마다 두번 씩 쿼리를 수행한다는 것을 알 수 있음
- Prefetch_related 사용 0
>>> Person.objects.prefetch_related('language').get(id=4).language.values()
<QuerySet [{'id': 1, 'name': 'Korean'}, {'id': 2, 'name': 'japanese'}, {'id': 3, 'name': 'english'}]>
--> Selected_related 함수와 마찬가지로 쿼리문은 좀 더 길어지지만 many to many 관계를 대상으로 참조할때 Data를 참조 대상까지 Cache에 저장하기 때문에 해당 함수를 사용하길 권장한다.
** 추가내용
- Prefetch_related로 역참조 시 lookup에 '_set' 을 사용
'파이썬 > Django' 카테고리의 다른 글
파이썬(Django) :: Django-mptt 정리 (0) | 2020.04.18 |
---|---|
파이썬(Django) :: Django-Crontab 정리 (0) | 2020.04.18 |
파이썬(Django) :: 회원가입 시 입력 값 검증 함수 (0) | 2020.03.21 |
파이썬(Django) :: 회원가입 시 이메일 인증 API (1) | 2020.03.21 |
파이썬(Django) :: 데이터베이스에 csv파일 삽입하기 (0) | 2020.03.07 |