Django에는 QuerySet이라는 객체가 있고 filter및 order_by등의 대표적인 메소드 외에, distinct 라는 메소드가 있습니다.
이 메소드를 사용하면 QuerySet 결과에서 중복 레코드(행, 오브젝트)를 제거할 수 있습니다.
distinct()검증을 위해 모델을 만듭니다.
Person 이라는 모델을 만듭니다.
이것은 사람을 표현하는 모델이며 이름과 나이와 같은 필드가 있습니다.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length = 128)
age = models.IntegerField()
name나 age필드에는 unique을 붙이지 않습니다.
따라서 name와 age값은 중복 될 수 있습니다.
이러한 필드의 중복된 데이터를 distinct()필터링해 보자는 느낌입니다.
↑의 Person모델을 사용해 오브젝트를 ↓와 같이 초기화해 둡니다.
Person.objects.create(name='김일', age=20)
Person.objects.create(name='김일', age=30)
Person.objects.create(name='이이', age=16)
Person.objects.create(name='이이', age=32)
Person.objects.create(name='삼셋', age=30)
name에서 김일과 이이가 중복되어 있습니다.
또한 age역시 30이라는 값에서 중복 있습니다.
distinct()를 단독으로 사용
그럼 distinct()사용해봅시다.
distinct()는 아래와 같이 단체로 사용할 수 있습니다.
Person.objects.distinct()
↑ 코드의 SQL 쿼리를 살펴 보겠습니다.
print(Person.objects.distinct().query)
SELECT DISTINCT "myapp_person"."id", "myapp_person"."name", "myapp_person"."age" FROM "myapp_person"
↑을 보면 DISTINCT SQL문이 생성되고 있는 것을 알 수 있습니다.
기본적으로 ↑와 같이 모델의 모든 필드를 조건으로 지정하여 쿼리를 생성합니다.
따라서 Person.objects.distinct()결과는 ↓와 같은 결과입니다.
<QuerySet [
<Person:Person object(1)>,
<Person:Person object(2)>,
<Person:Person object(3)>,
<Person:Person object(4)>,
<Person:Person object(5))>
]>
모든 객체가 검색되었습니다.
즉 모델의 모든 필드를 DISTINCT의 조건으로 했을 경우, 중복되는 레코드는 없다는 것입니다.
distinct()와 values_list()를 결합
distinct()에서 특정 필드를 조건으로 지정하고 싶은 경우 values_list()와 조합합니다.
distinct()를 호출한 후 ↓처럼 values_list()호출합니다.
Person.objects.distinct().values_list('name')
↑의 경우 values_list()에는 Person필드 name을 지정합니다.
이렇게하면 name필드를 조건으로 DISTINCT만듭니다.
SQL 문을 살펴 보겠습니다.
print (Person.objects.distinct().values_list('name').query)
SELECT DISTINCT "myapp_person"."name" FROM "myapp_person"
↑와 같이 SQL문이 생성되고 있습니다. DISTINCT의 필드는 name단독으로 지정됩니다.
따라서 Person.objects.distinct().values_list('name')결과는 ↓와 같습니다.
<QuerySet [('김일',), ('이이',), ('삼셋',)]>
name필드는 김일과 이이가 중복으로 되어 있었지만, 결과에서 중복이 제거되어있는 것을 알 수 있습니다.
values_list()에는 여러 인수(필드 이름)를 지정할 수 있습니다.
age의 필드도 지정해 봅시다.
Person.objects.distinct().values_list('name', 'age')
SQL문은 ↓와 같이 됩니다.
SELECT DISTINCT "myapp_person"."name", "myapp_person"."age" FROM "myapp_person"
name그리고 age이 DISTINCT조건에 지정되었습니다.
Person.objects.distinct().values_list('name', 'age')의 결과는 ↓와 같습니다.
<QuerySet [('김일', 20), ('김일', 30), ('이이', 16), ('이이', 32), ('삼사', 30)]>
name그리고 age을 조건으로 한 경우 중복 된 개체는 존재하지 않기 때문에 ↑처럼 모든 개체를 검색합니다.
distinct() 및 order_by()
distinct()와 order_by()조합할 때는 주의가 필요합니다.
order_by()에 지정한 필드가 DISTINCT에 추가됩니다.
예를 들어 ↓ 코드를 살펴 보겠습니다.
Person.objects.order_by('id').distinct().values_list('name')
order_by()에서 id키를 정렬하고 distinct().value_list()를 사용하고 있습니다.
distinct()와 value_list()조합하면 ↑의 경우 name에만 DISTINCT조건으로 지정됩니다.
하지만 ↑처럼 order_by()지정한 경우는 다릅니다.
↑ 코드의 SQL 문장을 살펴 보겠습니다.
SELECT DISTINCT "myapp_person"."name", "myapp_person"."id" FROM "myapp_person" ORDER BY "myapp_person"."id" ASC
↑ 와 같은 DISTINCT조건으로 id설정되어 있습니다.
이것에 대한 Person.objects.order_by('id').distinct().values_list('name')결과는 ↓와 같습니다.
<QuerySet [('김일',), ('김일',), ('이이',), ('이이',), ('삼사',)]>
name의 중복을 제거 조건을 제외하고, order_by()로 id를 지정하고 있으므로↑와 같이 id와 name를 조건에 중복이 제거됩니다.
결과적으로 모든 오브젝트가 검색됩니다.
결론
distinct()를 사용하면 중복 객체를 결과에서 제거할 수 있습니다.
간단 요약
A는 그냥 A
B = A에 여러개 붙을 수 있음
distinct는 A모델을 필터링 할 때 사용하는데 B를 기준으로 필터링할때 B가 n개 이상이고 각각의 필드가 다른 경우에 A가 B의 개수만큼 나오기 때문에 이를 막고자 사용하며 사용하게 되면 B로 인해 중복되어 여러개 나오던 A가 1개만 나오게 됨.
A에 B가 2개 이상이 아닌 경우에는 사용하지 않도록 해야함.
'python > Django' 카테고리의 다른 글
Django의 Meta 클래스 사용법 [ordering, db_table, verbose_name, etc] (0) | 2021.12.03 |
---|---|
Django의 channel로 만드는 간단한 채팅 앱 (0) | 2021.12.03 |
Django values, valuse_list (0) | 2021.12.03 |
Django transaction atomic (0) | 2021.10.07 |
[오픈갤러리] Django 서버 정리 작업 ⑨ - 기프트카드 개선작업 (1) | 2021.10.01 |
댓글