[Django] QuerySet select_related()와 prefetch_related()

황인용·2020년 8월 30일
1

Django

목록 보기
11/13
post-custom-banner

select_related()

  • django의 objects가 데이터베이스에 접근 시 foreign key가 설정되어 있는 테이블들까지 조회하는 메소드
  • SQL의 join
  • OneTonOneField 관계 그리고 정방향으로 참조하는 형태로 조회한다
  • 매개변수로는 참조키를 지정한다

models.py

class City(models.Model):
	name = models.Charfield(max_length=100)
    
 	class Meta:
    	db_table = 'CITY'


class Person(models.Model):
    name = models.Charfield(max_length=100)
    city = models.ForeignKey(City, on_delete=models.CASCADE)
  	
    class Meta:
    	db_table = 'PERSON'


class PersonInfo(models.Model):
    address = models.Charfield(max_length=300)
    zipcode = models.Charfield(max_length=6)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    
    class Meta:
    	db_table = 'PERSONINFO'

CITY table

idname(이름)
1Seoul
2Suwon
3Daejeon
4Jeonju
5Yeosu
6Busan
7Jeju
......

PERSON table

idname(이름)city_id
1Kim1
2Lee1
3Park2
4Jeong3
5Hong6
6Hwang7
.........

PERSONINFO table

idaddress(이름)zipcodeperson_id
1서울특별시 은평구1111111
2서울특별시 송파구2222222
3경기도 수원시3333333
4대전광역시4444444
5부산광역시5555555
6제주도6666666
............
>>> person = Person.objects.get(id=1)
>>> person_city = person.city
>>> person_city
<City: City objects (1)>
>>> person_city.name
Seoul
  • select_related()가 없으면 Person에서 objects를 통해 queryset을 구하고, 그 queryset에서 참조키인 city를 참조해야만 person의 city값을 확인할 수 있다
>>> person_city = Person.objects.select_related('city').filter(id=1)
<Person: Person object (1)>
>>> person_city.city.name
Seoul
  • select_related()사용 시 참조키인 city를 매개변수로 지정하고 queryset를 반환한다
  • 반환된 queryset에서 city의 값을 확인할 수 있다
>>> person_city = Person.objects.select_related('city').filter(id=1)
<Person: Person object (1)>
>>> str(person_city.query)
'SELECT `PERSON`.`id`, `PERSON`.`name`, `PERSON`.`city_id`, `CITY`.`id`, `CITY`.`name` FROM `PERSON` INNER JOIN `CITY` ON (`PERSON`.`city_id` = `CITY`.`id`) WHERE `PERSON`.`id` = 1'
  • select_related()를 사용 시 해당 참조되는 컬럼(참조키) 기준으로 INNER JOIN하는 것을 확인할 수 있다

prefetch_related()

  • select_related()와 같이 foreign key가 설정되어 있는 테이블에서 사용하는 메소드
  • OneTonOneField, ManyToManyField, ManyToOneField 관계 그리고 역방향으로 참조하는 형태로 조회한다
  • models.py에서 ManyToManyField 또는 relate_name 매개변수로 지정된 키들을 참조해서 사용한다
  • query를 확인해보면 역 참조되는 테이블과 참조하고 있는 테이블 각각 쿼리가 2번 실행된다.
  • 테이블의 row수의 따라 참조 구성에 따라 query lazy 현상이 일어날 수 도 있음.
  • 따라서 왠만하면 select_related() 사용 권장.

models.py

class Person(models.Model):
    name = models.Charfield(max_length=100)
    city = models.ForeignKey('City', on_delete=models.CASCADE)
    language = models.ManyToManyField('Language', through='PersonLanguage')
    
 	class Meta:
    	db_table = 'PERSON'


class Language(models.Model):
    name = models.Charfield(max_length=100)
  	
    class Meta:
    	db_table = 'LANGUAGE'


class Person_Language(models.Model):
    person = models.ForeignKey('Person', on_delete=models.CASCADE)
    language = models.ForeignKey('Language', on_delete=models.CASCADE)
    
    class Meta:
    	db_table = 'PERSON_LANGUAGE'

PERSON table

idname(이름)city_id
1Kim1
2Lee1
3Park2
4Jeong3
5Hong6
6Hwang7
.........

LANGUAGE table

idname(이름)
1Korean
2English
3Japanese
4Chinese
......

PERSON_LANGUAGE table

idperson_id(이름)language_id
111
212
321
431
544
652
.........
>>> person = Person.objects.get(id=1).values()
>>> person
<QuerSet [{'id': 1, 'name': 'Kim', 'city_id': 1}]>

>>> person = Person.objects.get(id=1)
>>> person_language = Person_Language.objects.filter(person_id=person.id)
<QuerSet [<Person_Language: Person_Language objects (1)>, <Person_Language: Person_Language objects (2)>]>
>>> person_language.language.values()
<QuerSet [{'id': 1, 'name': 'Korean'}, {'id': 2, 'name': 'English'}]>
>>> person_language = Person.objects.prefetch_related('language').filter(id=1)
>>> person_language
<QuerySet [<Person: Person object (1)>]>
>>> person_laguage[0]language.values()
<QuerySet [{'id': 1, 'name': 'Korean'}, {'id': 2, 'name': 'English'}]>
  • prefetch_related() 매개변수로 역참조 되는 Person의 참조키 language를 지정
  • related_name 변수로 지정시
>>> person_language = Person.objects.prefetch_related('language').filter(id=1)
>>> str(person_language.query)
'SELECT `PERSON`.`id`, `PERSON`.`name`, `PERSON`.`city_id` FROM `PERSON` WHERE `PERSON`.`id` = 1'

>>> str(person_language[0].language.all().query)
'SELECT `LANGUAGE`.`id`, `LANGUAGE`.`name` FROM `LANGUAGE` INNER JOIN `PERSON_LANGUAGE` ON (`LANGUAGE`.`id` = `PERSON_LANGUAGE`.`LANGUAGE_id`) WHERE `PERSON_LANGUAGE`.`PERSON_id` = 1'
  • prefetch_related()로 query를 확인 시, 제일먼저 PERSON 테이블 기준으로 쿼리를 실행한다
  • 추가로 참조되는 키 기준으로 query를 확인 시, 참조테이블인 LANGUAGE 테이블 기준으로 쿼리를 실행한다.
  • 즉, 2번의 쿼리가 실행되는 것을 확인 할 수 있다.
profile
dev_pang의 pang.log
post-custom-banner

0개의 댓글