QuerySet API

django

filter( )

filter(**kwargs)

μ§€μ •λœ 쑰회 맀개 λ³€μˆ˜μ™€ μΌμΉ˜ν•˜λŠ” 객체λ₯Ό ν¬ν•¨ν•˜λŠ” μƒˆλ‘œμš΄ QuerySet을 λ°˜ν™˜ν•œλ‹€.

쑰회 맀개 λ³€μˆ˜ (**kwargs)λŠ” ν•„λ“œ 쑰회 ν˜•μ‹μ΄μ–΄μ•Ό ν•œλ‹€. μ—¬λŸ¬ 맀개 λ³€μˆ˜λŠ” κΈ°λ³Έ SQLλ¬Έμ—μ„œ ANDλ₯Ό 톡해 κ²°ν•©λœλ‹€.

더 λ³΅μž‘ν•œ 쿼리 (예λ₯Όλ“€μ–΄ OR문이 μžˆλŠ” 쿼리)λ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•˜λŠ” 경우 Q objectsλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

exclude( )

exclude(**kwargs)

μ§€μ •λœ 쑰회 맀개 λ³€μˆ˜μ™€ μΌμΉ˜ν•˜μ§€ μ•ŠλŠ” 객체λ₯Ό ν¬ν•¨ν•˜λŠ” μƒˆλ‘œμš΄ QuerySet을 λ°˜ν™˜ν•œλ‹€.

쑰회 맀개 λ³€μˆ˜ (**kwargs)λŠ” ν•„λ“œ 쑰회 ν˜•μ‹μ΄μ–΄μ•Ό ν•œλ‹€. μ—¬λŸ¬ 맀개 λ³€μˆ˜λŠ” κΈ°λ³Έ SQLλ¬Έμ—μ„œ ANDλ₯Ό 톡해 κ²°ν•©λ˜λ©° 전체가 NOT()으둜 λ¬Άμ—¬ μžˆλ‹€.

μ•„λž˜ μ˜ˆμ œλŠ” pub_dateκ°€ 2005-01-03이후 이며(AND) 제λͺ©μ΄ β€˜Hello’λͺ¨λ“  ν•­λͺ©μ„ μ œμ™Έν•œλ‹€.

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

SQLλ¬ΈμœΌλ‘œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

SELECT ...
WHERE NOT (pub_date > '2005-01-03' AND headline = 'Hello')

μ•„λž˜ μ˜ˆμ œλŠ” pub_dateκ°€ 2005-01-03이후 이고(OR) 제λͺ©μ΄ β€˜Hello’λͺ¨λ“  ν•­λͺ©μ„ μ œμ™Έν•œλ‹€.

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello')

SQLλ¬ΈμœΌλ‘œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

SELECT ...
WHERE NOT pub_date > '2005-1-3'
AND NOT headline = 'Hello'

λ‘λ²ˆμ§Έ μ˜ˆμ œλŠ” 더 μ œν•œμ μ΄λ©° 더 λ³΅μž‘ν•œ 쿼리 (예λ₯Όλ“€μ–΄ OR문이 μžˆλŠ” 쿼리)λ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•˜λŠ” 경우 Q objectsλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

annotate( )

annotate(*args, **kwargs)

제곡된 쿼리 식 λͺ©λ‘μ„ μ‚¬μš©ν•˜μ—¬ QuerySet의 각 객체에 주석을 μΆ”κ°€ν•œλ‹€. 식은 λ‹¨μˆœ κ°’, λͺ¨λΈμ˜ ν•„λ“œμ— λŒ€ν•œ μ°Έμ‘° (λ˜λŠ” λͺ¨λ“  관계 λͺ¨λΈ), λ˜λŠ” 객체와 κ΄€λ ¨λœ 객체에 λŒ€ν•œ κ³„μ‚°λœ 집계 식 (평균, 합계 λ“±)일 수 μžˆλ‹€.

annotate()의 각 μΈμˆ˜λŠ” λ°˜ν™˜λ˜λŠ” QuerySet의 각 객체에 μΆ”κ°€λ˜λŠ” 주석이닀.

**kwargsλ₯Ό μ‚¬μš©ν•˜μ—¬ μ§€μ •λœ 주석은 ν‚€μ›Œλ“œ μ£Όμ„μ˜ λ³„μΉ­μœΌλ‘œ μ‚¬μš©λœλ‹€. 읡λͺ… μΈμˆ˜μ—λŠ” 집계 ν•¨μˆ˜μ˜ 이름과 μ§‘κ³„λ˜λŠ” λͺ¨λΈ ν•„λ“œμ— 따라 μƒμ„±λœ 별칭이 μžˆλ‹€. 단일 ν•„λ“œλ₯Ό μ°Έμ‘°ν•˜λŠ” μ§‘κ³„μ‹λ§Œ 읡λͺ… μΈμˆ˜κ°€ 될 수 μžˆλ‹€. κ·Έ 밖에 λͺ¨λ“  것은 ν‚€μ›Œλ“œ μΈμˆ˜μ—¬μ•Ό ν•œλ‹€.

예λ₯Όλ“€μ–΄ λΈ”λ‘œκ·Έ λͺ©λ‘μ„ μ‘°μž‘ν•˜λŠ” 경우 각 λΈ”λ‘œκ·Έμ— μž‘μ„±λœ ν•­λͺ© 수λ₯Ό 확인할 수 μžˆλ‹€.

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# 첫번째 λΈ”λ‘œκ·Έ 이름
>>> q[0].name
'Blogasaurus'
# 첫번째 λΈ”λ‘œκ·Έμ˜ ν•­λͺ© 수
>>> q[0].entry__count
42

Blog λͺ¨λΈμ€ 자체적으둜 entry__count속성을 μ •μ˜ν•˜μ§€ μ•Šμ§€λ§Œ **kwargsλ₯Ό μ‚¬μš©ν•˜μ—¬ 집계 ν•¨μˆ˜λ₯Ό μ§€μ •ν•˜λ©΄ μ£Όμ„μ˜ 이름을 μ‚¬μš©μžκ°€ 생성할 수 있으며 μ œμ–΄ ν•  수 μžˆλ‹€.

>>> q = Blog.objects.annotate(number_of_entries=Count('entry'))
# 제곡된 이름 number of entries을 μ‚¬μš©ν•˜λŠ” 첫번째 λΈ”λ‘œκ·Έμ˜ ν•­λͺ© 수
>>> q[0].number_of_entries
42

집계에 λŒ€ν•œ μžμ„Έν•œ λ‚΄μš©μ€ λ‹€μŒμ„ μ°Έκ³ ν•œλ‹€.

order_by( )

order_by(*fields)

기본적으둜 QuerySet에 μ˜ν•΄ λ°˜ν™˜λœ κ²°κ³ΌλŠ” λͺ¨λΈμ˜ Metaν΄λž˜μŠ€μ— μžˆλŠ” orderingμ˜΅μ…˜μ— μ˜ν•΄ μ œκ³΅λ˜λŠ” ordering tuple에 따라 μ •λ ¬λœλ‹€. ν•˜μ§€λ§Œ order_byλ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ QuerySetλ³„λ‘œ 이λ₯Ό μž¬μ •μ˜ ν•  수 μžˆλ‹€.

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

μœ„μ˜ κ²°κ³ΌλŠ” pub_dateλ₯Ό λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ μ •λ ¬ν•œ λ‹€μŒ 제λͺ©μ˜ μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬λœλ‹€. pub_dateμ•žμ˜ -κΈ°ν˜ΈλŠ” λ‚΄λ¦Όμ°¨μˆœμ„ μ˜λ―Έν•œλ‹€. λ¬΄μž‘μœ„λ‘œ μ •λ ¬ν•˜λ €λ©΄ λ‹€μŒκ³Ό 같이 ?λ₯Ό μ‚¬μš©ν•œλ‹€.

Entry.objects.order_by('?')

참고둜 order_by('?')λŠ” μ„±λŠ₯이 맀우 μ•ˆμ’‹μœΌλ‹ˆ μ‚¬μš©μ„ μžμ œν•˜λŠ” 것이 μ’‹λ‹€.

λ‹€λ₯Έ λͺ¨λΈμ˜ ν•„λ“œλ³„λ‘œ μ •λ ¬ν•˜λ €λ©΄ λͺ¨λΈ κ΄€κ³„μ—μ„œ 쿼리할 λ•Œμ™€ λ™μΌν•œ ꡬ문을 μ‚¬μš©ν•œλ‹€. 즉, ν•„λ“œ 이름, double underscore(__), μƒˆ λͺ¨λΈμ˜ ν•„λ“œ 이름등이 κ²°ν•©ν•˜λ €λŠ” λͺ¨λΈ 수만큼 κ³„μ†λœλ‹€. 예λ₯Όλ“€λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

Entry.objects.order_by('blog__name', 'headline')

λ‹€λ₯Έ λͺ¨λΈκ³Ό κ΄€λ ¨λœ ν•„λ“œλ₯Ό κΈ°μ€€μœΌλ‘œ μ •λ ¬ν•˜λ €κ³  ν•˜λ©΄ DjangoλŠ” κ΄€λ ¨ λͺ¨λΈμ˜ κΈ°λ³Έ μˆœμ„œλ₯Ό μ‚¬μš©ν•˜κ±°λ‚˜ Meta클래슀의 ordering이 μ§€μ •λ˜μ§€ μ•Šμ€ 경우 κ΄€λ ¨ λͺ¨λΈμ˜ Primary Keyλ₯Ό κΈ°μ€€μœΌλ‘œ μ •λ ¬ν•œλ‹€. 예λ₯Όλ“€μ–΄, Blogλͺ¨λΈμ—λŠ” κΈ°λ³Έμˆœμ„œκ°€ μ§€μ •λ˜μ–΄ μžˆμ§€ μ•ŠκΈ°λ•Œλ¬Έμ—

Entry.objects.order_by('blog')

이것은 λ‹€μŒκ³Ό κ°™λ‹€.

Entry.objects.order_by('blog__id')

λ§Œμ•½ Blogλͺ¨λΈμ— ordering = ['name']이 μ§€μ •λ˜μ–΄ μžˆλŠ” κ²½μš°λŠ” λ‹€μŒκ³Ό κ°™λ‹€.

Entry.objects.order_by('blog__name')

λ˜ν•œ ν‘œν˜„μ‹μ—μ„œ asc() λ˜λŠ” desc()λ₯Ό ν˜ΈμΆœν•˜μ—¬ 쿼리 ν‘œν˜„μ‹λ³„λ‘œ μ •λ ¬ ν•  수 도 μžˆλ‹€.

Entry.objects.order_by(Coalesce('summary', 'headline').desc())
Entry.objects.order_by(Coalesce('summary', 'headline').asc())

asc()와 desc()μ—μ„œλŠ” null값을 μ •λ ¬ν•˜λŠ” 방법을 μ œμ–΄ν•˜λŠ” 인수 nulls_first및 nulls_lastκ°€ μžˆλ‹€.

πŸ’‘M:M λ˜λŠ” μ—­μ°Έμ‘°κ΄€κ³„μ—μ„œμ˜ order_by()

κ²°κ³Όλ₯Ό μ •λ ¬ν•˜κΈ° μœ„ν•΄ 닀쀑 κ°’ ν•„λ“œλ₯Ό μ§€μ •ν•  수 μžˆλ‹€.

# Model
class Event(Model):
  parent = models.ForeignKey(
      'self',
      on_delete=models.CASCADE,
      related_name='children',
  )
  date = models.DateField()
Event.objects.order_by('children__date')

μ—¬κΈ°μ—μ„œ 각 μ΄λ²€νŠΈμ— λŒ€ν•΄ 잠재적으둜 μ—¬λŸ¬ μ£Όλ¬Έ 데이터가 μžˆμ„ 수 μžˆλ‹€. μ—¬λŸ¬ ν•˜μœ„ ν•­λͺ©μ΄ μžˆλŠ” 각 μ΄λ²€νŠΈλŠ” order_by()κ°€ μƒμ„±ν•˜λŠ” μƒˆ QuerySet에 μ—¬λŸ¬λ²ˆ λ°˜ν™˜λœλ‹€. 즉, QuerySetμ—μ„œ order_by()λ₯Ό μ‚¬μš©ν•˜λ©΄ μ²˜μŒμ— μž‘μ—…ν–ˆλ˜ 것 보닀 더 λ§Žμ€ ν•­λͺ©μ„ λ°˜ν™˜ ν•  수 μžˆλ‹€. μ΄λŠ” μ•„λ§ˆλ„ μ˜ˆμƒν•˜μ§€λ„ μ“Έλͺ¨μžˆμ§€λ„ μ•Šμ€ 데이터가 될 것이닀.

λ”°λΌμ„œ 닀쀑 κ°’ ν•„λ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ κ²°κ³Όλ₯Ό μ •λ ¬ ν•  λ•ŒλŠ” μ£Όμ˜ν•΄μ•Ό ν•œλ‹€. μ£Όλ¬Έν•˜λŠ” 각 ν•­λͺ©μ— λŒ€ν•΄ ν•˜λ‚˜μ˜ μ£Όλ¬Έ λ°μ΄ν„°λ§Œ μžˆλ‹€κ³  ν™•μ‹ ν•  수 μžˆλ‹€λ©΄ 이 방법은 λ¬Έμ œλ˜μ§€ μ•ŠλŠ”λ‹€. κ·Έλ ‡μ§€ μ•Šμ€ 경우 κ²°κ³Όκ°€ μ˜ˆμƒ ν•œ 결과인지 확인해야 ν•  ν•„μš”κ°€ μžˆλ‹€.

⚠️ order_by()λŠ” 쿼리λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

order_by()에 μΆ”κ°€λ˜λŠ” 각 ν•„λ“œλŠ” 쿼리λ₯Ό λ°œμƒμ‹œν‚¨λ‹€. μΆ”κ°€ν•˜λŠ” μ™Έλž˜ν‚€ μ—­μ‹œ ν¬ν•¨λœλ‹€.

쿼리에 μˆœμ„œκ°€ μ§€μ •λ˜μ§€ μ•Šμ€ 경우 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ§€μ •λ˜μ§€ μ•Šμ€ μˆœμ„œλ‘œ κ²°κ³Όκ°€ λ¦¬ν„΄λœλ‹€. νŠΉμ • μˆœμ„œλ‘œ μ •λ ¬ν• λ•ŒλŠ” κ²°κ³Όμ—μ„œ 각 객체λ₯Ό κ³ μœ ν•˜κ²Œ μ‹λ³„ν•˜λŠ” ν•„λ“œ μ§‘ν•©μœΌλ‘œ order_by()ν• λ•Œλ§Œ 보μž₯λœλ‹€. 예λ₯Ό λ“€μ–΄ 이름 ν•„λ“œκ°€ κ³ μœ ν•˜μ§€ μ•Šμ€ 경우 이름을 κΈ°μ€€μœΌλ‘œ μ •λ ¬ν•œλ‹€κ³  ν•΄μ„œ 이름이 같은 객체가 항상 같은 μˆœμ„œλ‘œ ν‘œμ‹œλ˜λŠ” 것은 μ•„λ‹ˆλ‹€.

reverse( )

reverse()

reverse()λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ QuerySet의 μš”μ†Œκ°€ λ¦¬ν„΄ν•˜λŠ” μˆœμ„œλ₯Ό λ°˜λŒ€λ‘œ λ§Œλ“€μˆ˜ μžˆλ‹€. reverse()λ₯Ό λ‘λ²ˆ ν˜ΈμΆœν•œλ‹€λ©΄ λ‹€μ‹œ 정상 λ°©ν–₯으둜 λ˜λŒμ•„ μ˜¨λ‹€.

μΏΌλ¦¬μ…‹μ—μ„œ λ§ˆμ§€λ§‰ 5개 ν•­λͺ©μ„ κ²€μƒ‰ν•˜λ €λ©΄ λ‹€μŒκ³Ό 같이 μ‚¬μš© ν•  수 μžˆλ‹€.

my_queryset.reverse()[:5]

이것은 Pythonμ—μ„œ μ‹œν€€μŠ€ 끝에 슬라이슀 ν•˜λŠ”κ²ƒκ³Ό μ™„μ „νžˆ κ°™μ§€ μ•Šλ‹€. μœ„μ˜ μ˜ˆλŠ” λ§ˆμ§€λ§‰ ν•­λͺ©μ„ λ¨Όμ € λ°˜ν™˜ν•œ λ‹€μŒ λ‘λ²ˆμ§Έλ‘œ 5개λ₯Ό λ°˜ν™˜ν•˜λŠ” 절차λ₯Ό μ§„ν–‰ν•œλ‹€. Pythonμ—μ„œλŠ” seq[-5:]와 같이 μ‹œν€€μŠ€μ˜ μˆœμ„œλ₯Ό λ°˜λŒ€λ‘œ λ’€μ§‘μ§€ μ•Šμ•„λ„ λ§ˆμ§€λ§‰ 5개의 ν•­λͺ©μ„ κ°€μ Έμ˜¬ 수 μžˆμ§€λ§Œ Djangoμ—μ„œλŠ” 이런 과정을 SQLμ—μ„œ 효율적으둜 μˆ˜ν–‰ ν•  수 μ—†κΈ°λ•Œλ¬Έμ— λμ—μ„œ μŠ¬λΌμ΄μŠ€μ™€ 같은 μ—‘μ„ΈμŠ€ λͺ¨λ“œλ₯Ό μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€.

λ˜ν•œ reverse()λŠ” 일반적으둜 μ •μ˜ 된 μˆœμ„œκ°€ μžˆλŠ” μΏΌλ¦¬μ…‹μ—μ„œλ§Œ ν˜ΈμΆœλ˜μ–΄μ•Ό ν•œλ‹€. (κΈ°λ³Έ μˆœμ„œλ₯Ό μ •μ˜ν•˜λŠ” λͺ¨λΈμ— λŒ€ν•΄ 쿼리 ν•  λ•Œ λ˜λŠ” order_by()λ₯Ό μ‚¬μš© ν•  λ•Œ), λ§Œμ•½ μ£Όμ–΄μ§„ 쿼리셋에 λŒ€ν•΄ μ΄λŸ¬ν•œ μˆœμ„œκ°€ μ •μ˜λ˜μ–΄ μžˆμ§€ μ•Šμ€ 경우 reverse()λ₯Ό 호좜 해도 νš¨κ³Όκ°€ μ—†λ‹€.

distinct( )

distinct()

# Only PostgreSQL
distinct(*fields)

SQLλ¬Έλ²•μ—μ„œ SELECT DISTINCTλ₯Ό μ‚¬μš©ν•˜λŠ” μƒˆλ‘œμš΄ QuerySet을 λ¦¬ν„΄ν•œλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ 쿼리 κ²°κ³Όμ—μ„œ 쀑볡 행이 μ œκ±°λœλ‹€.

기본적으둜 QuerySet은 쀑볡행을 μ œκ±°ν•˜μ§€ μ•ŠλŠ”λ‹€. μ‹€μ œλ‘œ Bolg.objects.all()κ³Ό 같은 κ°„λ‹¨ν•œ μΏΌλ¦¬λŠ” κ²°κ³Ό 행이 쀑볡 될 κ°€λŠ₯성이 μ—†κΈ° λ•Œλ¬Έμ— μ΄λŠ” 거의 λ¬Έμ œκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€. κ·ΈλŸ¬λ‚˜ 쿼리가 μ—¬λŸ¬ ν…Œμ΄λΈ”μ„ 걸쳐야 ν•œλ‹€λ©΄ μ€‘λ³΅λ˜λŠ” κ²°κ³Όλ₯Ό 얻을 수 μžˆλ‹€ μ΄λŸ΄λ•Œ distinct()λ₯Ό μ‚¬μš©ν•œλ‹€.

PostgreSQLμ—μ„œλŠ” distinct()에 Positional argumentsλ₯Ό 전달 ν•  수 μžˆλ‹€. μ΄λ•Œ 인자 값은 ν•„λ“œ 값이닀. 이것은 SELECT DISTINCT ONκ³Ό 같은 SQL쿼리둜 λ³€ν™˜λœλ‹€ ν•˜μ§€λ§Œ Raw SQLκ³Ό 차이점이 μ‘΄μž¬ν•˜λŠ”λ° 일반적인 distinct()호좜의 경우 데이터 λ² μ΄μŠ€λŠ” κ΅¬λ³„λ˜λŠ” 행을 κ²°μ •ν•  λ•Œ 각 ν–‰μ˜ 각 ν•„λ“œλ₯Ό λΉ„κ΅ν•œλ‹€. ν•˜μ§€λ§Œ ORMμ—μ„œλŠ” μ§€μ •λœ ν•„λ“œλ₯Ό 인자둜 κ°–λŠ” distinct()호좜의 경우 λ°μ΄ν„°λ² μ΄μŠ€κ°€ μ§€μ •λœ ν•„λ“œ μ΄λ¦„λ§Œ λΉ„κ΅ν•œλ‹€.

πŸ’‘ν•„λ“œλ₯Ό 인수둜 κ°–λŠ” distinct()

ν•„λ“œ 이름을 μ§€μ •ν•  λ•Œ QuerySetμ—λŠ” order_by()κ°€ ν•„μš”ν•˜λ©° order_by()의 ν•„λ“œλŠ” distinct()에 제곡된 ν•„λ“œμ™€ λ™μΌν•˜κ²Œ μ§€μ •λ˜μ–΄μ•Ό ν•œλ‹€.

예λ₯Όλ“€μ–΄ SELECT DISTINCT ON (a)λŠ” aμ—΄μ˜ 각 값에 λŒ€ν•œ 첫 번째 행을 μ œκ³΅ν•œλ‹€. λ§Œμ•½ ordering이 λ˜μ–΄ μžˆμ§€ μ•Šλ‹€λ©΄ μž„μ˜μ˜ 행이 λ¦¬ν„΄λœλ‹€.

μ˜ˆμ‹œ (PostgreSQLμ—μ„œλ§Œ κ°€λŠ₯)

>>> Author.objects.distinct()

>>> Entry.objects.order_by('pub_date').distinct('pub_date')

>>> Entry.objects.order_by('blog').distinct('blog')

>>> Entry.objects.order_by('author', 'pub_date').distinct('author', 'pub_date')

>>> Entry.objects.order_by('blog__name', 'mod_date').distinct('blog__name', 'mod_date')

>>> Entry.objects.order_by('author', 'pub_date').distinct('author')

⚠️ order_by()와 distinct()

order_by()ν˜ΈμΆœμ— μ‚¬μš©λœ λͺ¨λ“  ν•„λ“œλŠ” SQL SELECTμ»¬λŸΌμ— ν¬ν•¨λœλ‹€. 이둜 인해 distinct()와 ν•¨κ»˜ μ‚¬μš©ν•˜λ©΄ 예기치 μ•Šμ€ κ²°κ³Όκ°€ λ°œμƒν•  수 μžˆλ‹€. λ§Œμ•½ μ—°κ³„λœ λͺ¨λΈμ˜ ν•„λ“œλ₯Ό κΈ°μ€€μœΌλ‘œ μ •λ ¬ν•˜λ©΄ ν•΄λ‹Ή ν•„λ“œκ°€ μ„ νƒν•œ 열에 μΆ”κ°€λ˜κ³  κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ μ€‘λ³΅λœ 행이 κ΅¬λ³„λ˜λŠ” 것 처럼 보일 수 μžˆλ‹€. μΆ”κ°€ 열은 λ°˜ν™˜λœ 결과에 ν‘œμ‹œλ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— (μˆœμ„œ 지정을 μ§€μ›ν•˜κΈ° μœ„ν•΄ μ‘΄μž¬ν•˜λŠ” κ²½μš°μ—λ§Œ 리턴값에 ν‘œμ‹œ) λ•Œλ•Œλ‘œ λͺ…ν™•ν•˜μ§€ μ•Šμ€ κ²°κ³Όκ°€ λ°˜ν™˜λ˜λŠ” κ²ƒμ²˜λŸΌ 보인닀.

λ§ˆμ°¬κ°€μ§€λ‘œ values()λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„ νƒν•œ 열을 μ œν•œν•˜λŠ” 경우 order_by()(λ˜λŠ” κΈ°λ³Έ ordering)에 μ‚¬μš©λœ 열이 계속 κ΄€λ ¨λ˜λ©° 결과의 κ³ μœ μ„±μ— 영ν–₯을 미칠수 μžˆλ‹€.

μ—¬κΈ°μ„œ μ€‘μš”ν•œκ²ƒμ€ μ—¬λŸ¬ λͺ¨λΈμ΄ μ—°κ΄€λ˜μ–΄ μžˆλŠ” 쿼리셋에 distinct()λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 μ •λ ¬ 기쀀이 κ΄€λ ¨ λͺ¨λΈμ˜ ν•„λ“œλΌλ©΄ μ£Όμ˜ν•΄μ•Ό ν•œλ‹€λŠ” 것이닀. λ§ˆμ°¬κ°€μ§€λ‘œ, **distinct()및 values()λ₯Ό ν•¨κ»˜ μ‚¬μš©ν•  λ•ŒλŠ” values()의 ν•„λ“œκ°€ μ•„λ‹Œ μ΄μ™Έμ˜ ν•„λ“œλ‘œ μ •λ ¬ ν•  λ•Œ 주의**ν•΄μ•Ό ν•œλ‹€.

λ˜ν•œ order_by()λŠ” μ •μ˜λœ κΈ°λ³Έ κ΄€λ ¨ λͺ¨λΈ μˆœμ„œλ₯Ό μ‚¬μš©ν•œλ‹€. DISTINCT ON식이 ORDER BY절의 μ‹œμž‘ 뢀뢄에 μžˆλŠ” 식과 μΌμΉ˜ν•˜λŠ”μ§€ ν™•μΈν•˜λ €λ©΄ κ΄€κ³„μ˜ _idλ˜λŠ” 참쑰된 ν•„λ“œλ₯Ό κΈ°μ€€μœΌλ‘œ λͺ…μ‹œμ μœΌλ‘œ μ •λ ¬ν•΄μ•Ό ν•  μˆ˜λ„ μžˆλ‹€. 예λ₯Ό λ“€μ–΄ Blogλͺ¨λΈμ΄ μ΄λ¦„μœΌλ‘œ 정렬을 μ •λ ¬ν•œ 경우

Entry.objects.order_by('blog').distinct('blog')

μœ„ μ˜ˆμ‹œλŠ” 쿼리가 blog__name으둜 μ •λ ¬λ˜μ–΄ DISTINCT ONν‘œν˜„μ‹κ³Ό μΌμΉ˜ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μž‘λ™ν•˜μ§€ μ•ŠλŠ”λ‹€. 두 ν‘œν˜„μ‹μ΄ μΌμΉ˜ν•˜λŠ”μ§€ ν™•μΈν•˜λ €λ©΄ 관계 _idν•„λ“œ (이 경우 blog_id)λ˜λŠ” μ°Έμ‘° 된 ν•„λ“œ (blog_pk)λ₯Ό κΈ°μ€€μœΌλ‘œ λͺ…μ‹œμ μœΌλ‘œ μ •λ ¬ν•΄μ•Ό ν•œλ‹€.

values( )

values(*field, **expressions)

iterable둜 μ‚¬μš©λ  λ•Œ λͺ¨λΈ μΈμŠ€ν„΄μŠ€κ°€ μ•„λ‹Œ dictionary을 λ°˜ν™˜ν•˜λŠ” QuerySet을 λ°˜ν™˜ν•œλ‹€.

각 dirctionaryλŠ” λͺ¨λΈ 객체의 속성 이름에 ν•΄λ‹Ήν•˜λŠ” ν‚€κ°€ μžˆλŠ” 객체λ₯Ό λ‚˜νƒ€λ‚Έλ‹€. λ‹€μŒμ˜ μ˜ˆμ œλŠ” values()κ°€ λ°˜ν™˜ν•˜λŠ” dictionary와 일반 λͺ¨λΈ 객체λ₯Ό λΉ„κ΅ν•œλ‹€.

# 이 λͺ©λ‘μ—λŠ” Blog 객체가 ν¬ν•¨λ˜μ–΄ μžˆλ‹€.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>

# 이 λͺ©λ‘μ—λŠ” dictionaryκ°€ ν¬ν•¨λ˜μ–΄ μžˆλ‹€.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

values() λ©”μ„œλ“œλŠ” SELECTκ°€ μ œν•œλ˜μ–΄μ•Ό ν•˜λŠ” ν•„λ“œ 이름을 μ§€μ •ν•˜λŠ” optional positional arguments 인 *fieldsλ₯Ό μ‚¬μš©ν•œλ‹€. ν•„λ“œλ₯Ό μ§€μ •ν•˜λ©΄ 각 dictionaryμ—λŠ” μ§€μ •ν•œ ν•„λ“œμ— λŒ€ν•œ key와 λŒ€μ‘ν•˜λŠ” value만 ν¬ν•¨λœλ‹€. ν•„λ“œλ₯Ό μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ dictionary에 λ°μ΄ν„°λ² μ΄μŠ€ ν…Œμ΄λΈ”μ˜ λͺ¨λ“  ν•„λ“œμ— λŒ€ν•œ key와 valueκ°€ ν¬ν•¨λœλ‹€.

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

values()λ©”μ„œλ“œλŠ” annotate()둜 μ „λ‹¬λ˜λŠ” optional keyword arguments인 **expresstions을 μ‚¬μš©ν•œλ‹€.

>>> from django.db.models.functions import Lower
>>> Blog.objects.values(lower_name=Lower('name'))
<QuerySet [{'lower_name': 'beatles blog'}]>

values()λ©”μ„œλ“œλŠ” μ •λ ¬λœ μ‚¬μš©μž μ§€μ •μ‘°νšŒλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

>>> from django.db.models import CharField
>>> from django.db.models.functions import Lower
>>> CharField.register_lookup(Lower)
>>> Blog.objects.values('name__lower')
<QuerySet [{'name__lower': 'beatles blog'}]>

values()절 λ‚΄μ˜ μ§‘κ³„λŠ” λ™μΌν•œ values()절 λ‚΄μ˜ λ‹€λ₯Έ 인수 보닀 μš°μ„ μˆœμœ„κ°€ λ†’λ‹€. λ‹€λ₯Έ κ°’μœΌλ‘œ κ·Έλ£Ήν™” ν•΄μ•Όν•˜λŠ” 경우 value()절과 λΆ„λ¦¬ν•˜μ—¬ μ§‘κ³„ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

>>> from django.db.models import Count
>>> Blog.objects.values('entry__authors', entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 20}, {'entry__authors': 1, 'entries': 13}]>
>>> Blog.objects.values('entry__authors').annotate(entries=Count('entry'))
<QuerySet [{'entry__authors': 1, 'entries': 33}]>

μ‚¬μš© κ°€λŠ₯ν•œ 일뢀 ν•„λ“œμ˜ κ°’λ§Œ ν•„μš”ν•˜κ³  λͺ¨λΈ μΈμŠ€ν„΄μŠ€ 객체의 κΈ°λŠ₯이 ν•„μš”ν•˜μ§€ μ•Šλ‹€λŠ” 것을 μ•Œκ³ μžˆμ„λ•Œ μœ μš©ν•˜λ‹€. μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” ν•„λ“œλ§Œ μ„ νƒν•˜λŠ” 것이 νš¨μœ¨μ μ΄λ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ values()호좜 후에 filter(), order_by()등을 호좜 ν•  수 μžˆλ‹€. μ΄λŠ” 이 두 호좜이 λ™μΌν•˜λ‹€λŠ” 것을 μ˜λ―Έν•œλ‹€.

Blog.objects.values().order_by('id')
Blog.objects.order_by('id').values()

OneToOneField, ForeignKey및 ManyToManyField속성을 톡해 μ—­μ°Έμ‘°κ°€ μžˆλŠ” κ΄€λ ¨ λͺ¨λΈμ˜ ν•„λ“œλ₯Ό μ°Έμ‘° ν•  μˆ˜λ„ μžˆλ‹€.

>>> Blog.objects.values('name', 'entry__headline')
<QuerySet [{'name': 'My blog', 'entry__headline': 'An entry'},
     {'name': 'My blog', 'entry__headline': 'Another entry'}, ...]>

⚠️ ManyToManyFieldμ—μ„œ values()μ‚¬μš©μ‹œ 주의 사항

ManyToManyField속성 및 μ—­μ°Έμ‘° κ΄€κ³„μ—λŠ” μ—¬λŸ¬ 행이 μžˆμ„ 수 μžˆμœΌλ―€λ‘œ 이λ₯Ό ν¬ν•¨ν•˜μ—¬ κ²°κ³Ό μ§‘ν•©μ˜ 리턴값이 배둜 λŠ˜μ–΄λ‚˜λŠ” κ²°κ³Όλ₯Ό 얻을 수 μžˆλ‹€. μ΄λŠ” values()쿼리에 μ΄λŸ¬ν•œ ν•„λ“œλ₯Ό μ—¬λŸ¬ 개 ν¬ν•¨ν•˜λŠ” 경우 특히 자주 λ°œμƒν•˜λ©°, 이 경우 κ°€λŠ₯ν•œ λͺ¨λ“  쑰합이 λ°˜ν™˜λœλ‹€.

λͺ‡κ°€μ§€ 유의 사항

  • λ§Œμ•½ μ™Έλž˜ν‚€μΈ fooλΌλŠ” ν•„λ“œκ°€ μžˆλŠ” 경우 기본적으둜 values()ν˜ΈμΆœμ€ μ‹€μ œ 값을 μ €μž₯ν•˜λŠ” μˆ¨κ²¨μ§„ λͺ¨λΈ μ†μ„±μ˜ 이름이기 λ•Œλ¬Έμ— foo_idλΌλŠ” dictionary keyλ₯Ό λ°˜ν™˜ν•˜κ²Œ λœλ‹€. (foo 속성은 μ—°κ΄€ λͺ¨λΈμ„ λ‚˜νƒ€λ‚Έλ‹€) values()λ₯Ό ν˜ΈμΆœν•˜κ³  ν•„λ“œ 이름을 전달할 λ•Œ foo λ˜λŠ” foo_idλ₯Ό 전달할 수 있으며 λ™μΌν•œ κ²°κ³Όλ₯Ό 얻을 수 μžˆλ‹€. (dictionarykeyλŠ” μ „λ‹¬ν•œ ν•„λ“œ 이름과 μΌμΉ˜ν•œλ‹€.)

    >>> Entry.objects.values()
    <QuerySet [{'blog_id': 1, 'headline': 'First Entry', ...}, ...]>
    
    >>> Entry.objects.values('blog')
    <QuerySet [{'blog': 1}, ...]>
    
    >>> Entry.objects.values('blog_id')
    <QuerySet [{'blog_id': 1}, ...]>
  • distinct()와 ν•¨κ»˜ values()λ₯Ό μ‚¬μš©ν•  λ•Œ μ‚¬μš©ν•˜λŠ” μˆœμ„œκ°€ 결과에 영ν–₯을 쀄 수 μžˆλ‹€.
  • extra()호좜 후에 values()μ ˆμ„ μ‚¬μš©ν•˜λŠ” 경우 extra()μ—μ„œ μ„ νƒμΈμžλ‘œ μ •μ˜λœ λͺ¨λ“  ν•„λ“œκ°€ values()ν˜ΈμΆœμ— λͺ…μ‹œμ μœΌλ‘œ ν¬ν•¨λ˜μ–΄μ•Ό ν•œλ‹€. values()호좜 이후에 μˆ˜ν–‰λœ extra()ν˜ΈμΆœμ€ μΆ”κ°€λ‘œ μ„ νƒλœ ν•„λ“œκ°€ λ¬΄μ‹œλœλ‹€.
  • values()뒀에 only()및 defer()λ₯Ό ν˜ΈμΆœν•˜λŠ” 것은 μ˜λ―Έκ°€ μ—†μœΌλ―€λ‘œ TypeErrorκ°€ λ°œμƒν•œλ‹€.
  • λ³€ν™˜κ³Ό 집계λ₯Ό κ²°ν•©ν•˜λ €λ©΄ λͺ…μ‹œμ μœΌλ‘œ λ˜λŠ” values()에 λŒ€ν•œ ν‚€μ›Œλ“œ 인수둜 두 개의 annotate()ν˜ΈμΆœμ„ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€. μœ„μ™€ 같이 ν•΄λ‹Ή ν•„λ“œ μœ ν˜•μ— λ³€ν™˜μ΄ λ“±λ‘λœ 경우 첫번째 annotate(0λ₯Ό μƒλž΅ ν•  수 μžˆμœΌλ―€λ‘œ λ‹€μŒ μ˜ˆμ œλŠ” λ™μΌν•œ κ²°κ³Όλ₯Ό κ°–λŠ”λ‹€.

    >>> from django.db.models import CharField, Count
    >>> from django.db.models.functions import Lower
    >>> CharField.register_lookup(Lower)
    >>> Blog.objects.values('entry__authors__name__lower').annotate(entries=Count('entry'))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    >>> Blog.objects.values(
    ...     entry__authors__name__lower=Lower('entry__authors__name')
    ... ).annotate(entries=Count('entry'))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>
    >>> Blog.objects.annotate(
    ...     entry__authors__name__lower=Lower('entry__authors__name')
    ... ).values('entry__authors__name__lower').annotate(entries=Count('entry'))
    <QuerySet [{'entry__authors__name__lower': 'test author', 'entries': 33}]>

values_list( )

values_list(*fields, flat=False, named=False)

λ°˜ν™˜ 값이 values()와 λ‹€λ₯΄κ²Œ νŠœν”Œμ„ λ°˜ν™˜ν•œλ‹€λŠ” 점을 μ œμ™Έν•˜λ©΄ values()와 μœ μ‚¬ν•˜λ‹€. 각 νŠœν”Œμ—λŠ” values_list()ν˜ΈμΆœμ— μ „λ‹¬λœ 각 ν•„λ“œ λ˜λŠ” ν‘œν˜„μ‹μ˜ 값이 ν¬ν•¨λœλ‹€. λ”°λΌμ„œ 첫번째 ν•­λͺ©μ€ 첫번째 ν•„λ“œμ΄λ‹€. 예λ₯Όλ“€λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

>>> Entry.objects.values_list('id', 'headline')
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list('id', Lower('headline'))
<QuerySet [(1, 'first entry'), ...]>

단일 ν•„λ“œλ§Œ μ „λ‹¬ν•˜λŠ” 경우 flat맀개 λ³€μˆ˜λ„ 전달할 수 μžˆλ‹€. Trueλ©΄ λ¦¬ν„΄λœ κ²°κ³Όκ°€ 단일 νŠœν”Œμ΄ μ•„λ‹ˆλΌ 단일 κ°’μž„μ„ μ˜λ―Έν•œλ‹€.

>>> Entry.objects.values_list('id').order_by('id')
<QuerySet[(1,), (2,), (3,), ...]>

>>> Entry.objects.values_list('id', flat=True).order_by('id')
<QuerySet [1, 2, 3, ...]>

단, ν•„λ“œκ°€ λ‘κ°œ 이상인 경우 flat은 μ—λŸ¬λ₯Ό λ°œμƒ μ‹œν‚¨λ‹€.

named = Trueλ₯Ό μ „λ‹¬ν•˜μ—¬ namedtuple()을 결과둜 얻을 수 μžˆλ‹€.

>>> Entry.objects.values_list('id', 'headline', named=True)
<QuerySet [Row(id=1, headline='First entry'), ...]>

namedtuple()을 μ΄μš©ν•˜λ©΄ κ²°κ³Όλ₯Ό 더 읽기 μ‰½κ²Œ λ§Œλ“€ 수 μžˆμ§€λ§Œ namedtuple()ν˜•μœΌλ‘œ λ³€ν™˜λ˜λ©΄μ„œ μ•½κ°„μ˜ μ„±λŠ₯ μ €ν•˜κ°€ λ°œμƒλœλ‹€.

values_list()에 값을 μ „λ‹¬ν•˜μ§€ μ•ŠμœΌλ©΄ μ„ μ–Έλœ μˆœμ„œλŒ€λ‘œ λͺ¨λΈμ˜ λͺ¨λ“  ν•„λ“œκ°€ λ°˜ν™˜λœλ‹€.

일반적으둜 values_list()λ₯Ό μ‚¬μš©ν•˜λŠ”κ²ƒμ€ νŠΉμ • λͺ¨λΈ μΈμŠ€ν„΄μŠ€μ˜ νŠΉμ • ν•„λ“œμ˜ 값을 κ°€μ Έμ˜¬ λ•Œ μ‚¬μš©ν•œλ‹€. 이λ₯Ό μœ„ν•΄ values_list()λ‹€μŒ get()을 ν˜ΈμΆœν•œλ‹€.

>>> Entry.objects.values_list('headline', flat=True).get(pk=1)
'First entry'

values()및 values_list()λŠ” λͺ¨λ‘ νŠΉμ • μ‚¬μš© 사둀에 λŒ€ν•œ μ΅œμ ν™”λ₯Ό μœ„ν•œ 것이닀. λͺ¨λΈ μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“œλŠ” μ˜€λ²„ν—€λ“œ 없이 데이터 ν•˜μœ„ 집합을 κ²€μƒ‰ν•œλ‹€. λ”°λΌμ„œ M:M 및 기타 닀쀑 관계 (예 : 1:Mκ΄€κ³„μ—μ„œ μ—­μ°Έμ‘°)λ₯Ό λ‹€λ£° λ•Œ β€œν•˜λ‚˜μ˜ ν–‰, ν•˜λ‚˜μ˜ 객체”가 μ μš©λ˜μ§€ μ•Šκ³  λͺ¨λ‘ 뢄리 λœλ‹€.

λ‹€μŒμ€ ManyToManyFieldλ₯Ό 쿼리 ν•  λ•Œμ˜ λ™μž‘μ΄λ‹€.

>>> Author.objects.values_list('name', 'entry__headline')

<QuerySet [('Noam Chomsky', 'Impressions of Gaza'),
 ('George Orwell', 'Why Socialists Do Not Believe in Fun'),
 ('George Orwell', 'In Defence of English Cooking'),
 ('Don Quixote', None)]>

λ‹€μˆ˜μ˜ 책을 μž‘μ„±ν•œ μž‘μ„±μžλŠ” μ—¬λŸ¬λ²ˆ ν‘œμ‹œλ˜κ³  ν•­λͺ©μ΄ μ—†λŠ” μž‘μ„±μžλŠ” entry__headline이 None으둜 ν‘œμ‹œλ˜λŠ” 것을 확인 ν•  수 μžˆλ‹€.

λ§ˆμ°¬κ°€μ§€λ‘œ μ—­μ°Έμ‘°λ₯Ό 쿼리할 λ•Œ μž‘μ„±μžκ°€ μ—†λŠ” ν•­λͺ©μ— λŒ€ν•΄ None으둜 ν‘œμ‹œλœλ‹€.

>>> Entry.objects.values_list('authors')

<QuerySet [('Noam Chomsky',), ('George Orwell',), (None,)]>

date( )

date(field, kind, order='ASC')

QuerySet의 λ‚΄μš© λ‚΄μ—μ„œ νŠΉμ • μ’…λ₯˜μ˜ μ‚¬μš© κ°€λŠ₯ν•œ λͺ¨λ“  λ‚ μ§œλ₯Ό λ‚˜νƒ€λ‚΄λŠ” datetime.date 객체 λͺ©λ‘μœΌλ‘œ ν‰κ°€λ˜λŠ” QuerySet을 λ¦¬ν„΄ν•œλ‹€.

ν•„λ“œλŠ” λͺ¨λΈμ˜ DateFieldμ–΄μ•Ό ν•œλ‹€. kindλŠ” year, month, week, dayμ–΄μ•Ό ν•œλ‹€. κ²°κ³Ό λͺ©λ‘μ˜ 각 datetime.date κ°μ²΄λŠ” μ£Όμ–΄μ§„ μœ ν˜•μœΌλ‘œ 데이터λ₯Ό μž˜λΌλ‚Έλ‹€.

  • yearλŠ” ν•„λ“œμ— λŒ€ν•œ λͺ¨λ“  고유 연도 값이 λ‹΄κΈ΄ listλ₯Ό λ°˜ν™˜ν•œλ‹€.
  • monthλŠ” ν•„λ“œμ— λŒ€ν•œ κ³ μœ ν•œ 연도/μ›” 값이 λ‹΄κΈ΄ listλ₯Ό λ°˜ν™˜ν•œλ‹€.
  • weekλŠ” ν•„λ“œμ— λŒ€ν•œ λͺ¨λ“  κ³ μœ ν•œ 연도/μ£Ό 값이 λ‹΄κΈ΄ listλ₯Ό λ°˜ν™˜ν•˜λ©° λͺ¨λ“  λ‚ μ§œλŠ” μ›”μš”μΌμ΄λ‹€.
  • dayλŠ” ν•„λ“œμ— λŒ€ν•œ λͺ¨λ“  κ³ μœ ν•œ μ—°/μ›”/일 값이 λ‹΄κΈ΄ listλ₯Ό λ°˜ν™˜ν•œλ‹€.

기본적으둜 μ •λ ¬λ˜λŠ” μˆœμ„œλŠ” ASC(μ˜€λ¦„μ°¨μˆœ)이며 orderλ₯Ό μ§€μ •ν•  λ•ŒλŠ” ASCλ˜λŠ” DESCμ—¬μ•Ό ν•œλ‹€.

>>> Entry.objects.dates('pub_date', 'year')
[datetime.date(2005, 1, 1)]
>>> Entry.objects.dates('pub_date', 'month')
[datetime.date(2005, 2, 1), datetime.date(2005, 3, 1)]
>>> Entry.objects.dates('pub_date', 'week')
[datetime.date(2005, 2, 14), datetime.date(2005, 3, 14)]
>>> Entry.objects.dates('pub_date', 'day')
[datetime.date(2005, 2, 20), datetime.date(2005, 3, 20)]
>>> Entry.objects.dates('pub_date', 'day', order='DESC')
[datetime.date(2005, 3, 20), datetime.date(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date', 'day')
[datetime.date(2005, 3, 20)]

datetimes( )

datetimes(field_name, kind, order='ASC', tzinfo=None, is_dst=None)

QuerySet의 λ‚΄μš© λ‚΄μ—μ„œ νŠΉμ • μ’…λ₯˜μ˜ μ‚¬μš© κ°€λŠ₯ν•œ λͺ¨λ“  λ‚ μ§œλ₯Ό λ‚˜νƒ€λ‚΄λŠ” datetime.datetime 객체 λͺ©λ‘μœΌλ‘œ ν‰κ°€λ˜λŠ” QuerySet을 λ¦¬ν„΄ν•œλ‹€.

field_name은 λͺ¨λΈμ˜ DateTimeFieldμ—¬μ•Ό ν•œλ‹€. kindλŠ” year, month, week, day, hour, minute, secondμ–΄μ•Ό ν•œλ‹€. κ²°κ³Ό λͺ©λ‘μ˜ 각 datetime.datetime κ°μ²΄λŠ” μ£Όμ–΄μ§„ μœ ν˜•μœΌλ‘œ 데이터λ₯Ό μž˜λΌλ‚Έλ‹€.

기본적으둜 μ •λ ¬λ˜λŠ” μˆœμ„œλŠ” ASC(μ˜€λ¦„μ°¨μˆœ)이며 orderλ₯Ό μ§€μ •ν•  λ•ŒλŠ” ASCλ˜λŠ” DESCμ—¬μ•Ό ν•œλ‹€.

tzinfoλŠ” 자λ₯΄κΈ° 전에 datetime이 λ³€ν™˜λ˜λŠ” μ‹œκ°„λŒ€λ₯Ό μ •μ˜ν•œλ‹€. μ‹€μ œλ‘œ μ£Όμ–΄μ§„ datetime은 μ‚¬μš©μ€‘μΈ μ‹œκ°„λŒ€μ— 따라 λ‹€λ₯Έ ν‘œν˜„μ„ κ°–λŠ”λ‹€. 이 맀개 λ³€μˆ˜λŠ” datetime.tzinfo객체여야 ν•œλ‹€. None이면 Django의 ν˜„μž¬ μ‹œκ°„λŒ€λ₯Ό μ‚¬μš©ν•œλ‹€. USE_TZκ°€ Falseλ©΄ μ•„λ¬΄λŸ° λ³€ν™”κ°€ μ—†λ‹€.

is_dstλŠ” pytzκ°€ μ„œλ¨Ένƒ€μž„μ„ μ‚¬μš©ν•˜λŠ”μ§€μ— λŒ€ν•œ μ—¬λΆ€λ₯Ό λ‚˜νƒ€λ‚Έλ‹€. 기본적으둜 (is_dst = None) pyztλŠ” μ΄λŸ¬ν•œ λ‚ μ§œ μ‹œκ°„μ— λŒ€ν•΄ μ˜ˆμ™Έλ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

πŸ’‘λ°μ΄ν„°λ² μ΄μŠ€μ˜ μ‹œκ°„λŒ€λ₯Ό μ‚¬μš©ν•˜λŠ” datetime()

datetime()은 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 직접 μ‹œκ°„λŒ€ λ³€ν™˜μ„ μˆ˜ν–‰ν•œλ‹€. λ”°λΌμ„œ λ°μ΄ν„°λ² μ΄μŠ€λŠ” tzinfo.tzname (None)의 값을 해석 ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€. λ”°λΌμ„œ λ°μ΄ν„°λ² μ΄μŠ€λŠ” λ‹€μŒκ³Ό 같은 μš”κ΅¬μ‚¬ν•­μ΄ μΆ©μ‘±λ˜μ–΄μ•Ό ν•œλ‹€.

  • SQLite : μš”κ΅¬ 사항이 μ—†λ‹€. λ³€ν™˜μ€ Pythonμ—μ„œ pytzλ₯Ό μ‚¬μš©ν•˜μ—¬ μˆ˜ν–‰λ˜λ©° 이 νŒ¨ν‚€μ§€λŠ” Djangoλ₯Ό μ„€μΉ˜ν• λ•Œ 이미 μ„€μΉ˜λ˜μ–΄ μžˆλŠ” νŒ¨ν‚€μ§€μ΄λ‹€.
  • PostgreSQL : μš”κ΅¬μ‚¬ν•­ μ—†μŒ (μ°Έμ‘°)
  • Oracle : μš”κ΅¬μ‚¬ν•­ μ—†μŒ (μ°Έμ‘°)
  • MySQL : mysql_tzinfo_to_sql을 μ‚¬μš©ν•˜μ—¬ μ‹œκ°„λŒ€ ν…Œμ΄λΈ”μ„ κ°€μ Έμ˜¨λ‹€. (μ°Έκ³ )

none( )

none()

none()을 ν˜ΈμΆœν•˜λ©΄ 객체λ₯Ό λ°˜ν™˜ν•˜μ§€ μ•ŠλŠ” 쿼리셋이 μƒμ„±λ˜κ³  결과에 μ—‘μ„ΈμŠ€ ν•  λ•Œ 쿼리가 μ‹€ν–‰λ˜μ§€ μ•ŠλŠ”λ‹€. qs.none()쿼리셋은 EmptyQuerySet의 μΈμŠ€ν„΄μŠ€κ°€ λœλ‹€.

>>> Entry.objects.none()
<QuerySet []>
>>> from django.db.models.query import EmptyQuerySet
>>> isinstance(Entry.objects.none(), EmptyQuerySet)
True

all( )

all()

ν˜„μž¬ QuerySet (λ˜λŠ” QuerySet ν•˜μœ„ 클래슀)의 볡사본을 λ°˜ν™˜ν•œλ‹€. μ΄λŠ” λͺ¨λΈ κ΄€λ¦¬μž λ˜λŠ” QuerySet에 μ „λ‹¬ν•˜κ³  결과에 λŒ€ν•œ μΆ”κ°€ 필터링을 μˆ˜ν–‰ν•˜λ €λŠ” μƒν™©μ—μ„œ μœ μš©ν•  수 μžˆλ‹€. 두 객체 λͺ¨λ‘ all()을 호좜 ν•˜λ©΄ μž‘μ—…ν•  QuerySet을 ν™•μ‹€ν•˜κ²Œ κ°–κ²Œλœλ‹€.

QuerySet이 평가 될 λ•Œ 일반적으둜 κ²°κ³Όλ₯Ό μΊμ‹œν•œλ‹€. QuerySet이 평가 된 이후 λ°μ΄ν„°λ² μ΄μŠ€μ˜ 데이터가 λ³€κ²½λœ 경우 이전에 ν‰κ°€λœ QuerySetμ—μ„œ all()을 ν˜ΈμΆœν•˜μ—¬ λ™μΌν•œ 쿼리에 λŒ€ν•œ μ—…λ°μ΄νŠΈ 된 κ²°κ³Όλ₯Ό 얻을 수 μžˆλ‹€.

union( )

union(*other_qs, all=False)

SQL의 UNION을 μ‚¬μš©ν•˜μ—¬ λ‘˜ μ΄μƒμ˜ QuerySet κ²°κ³Όλ₯Ό κ²°ν•©ν•œλ‹€.

>>> qs1.union(qs2, qs3)

UNION μ—°μ‚°μžλŠ” 기본적으둜 κ³ μœ ν•œ κ°’λ§Œ μ„ νƒν•œλ‹€. (distinct()κ°€ 기본적으둜 μˆ˜ν–‰λ¨.) 이 λ•Œ 쀑볡 값을 ν—ˆμš©ν•˜λ €λ©΄ all = True인수λ₯Ό μ‚¬μš©ν•œλ‹€.

union(), intersection(), difference()λŠ” μΈμˆ˜κ°€ λ‹€λ₯Έ λͺ¨λΈμ˜ QuerySet인 κ²½μš°μ—λ„ 첫번째 QuerySetμœ ν˜•μ˜ λͺ¨λΈ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€. SELECT λͺ©λ‘μ΄ λͺ¨λ“  QuerySetμ—μ„œ λ™μΌν•˜λ©΄ λ‹€λ₯Έ λͺ¨λΈμ„ μ „λ‹¬ν•˜λŠ” 것이 μž‘λ™ν•œλ‹€. (μ΅œμ†Œν•œ μœ ν˜•, μœ ν˜•μ΄ λ™μΌν•œ μˆœμ„œλ©΄ 이름이 μ€‘μš”ν•˜μ§€ μ•Šλ‹€.) μ΄λŸ¬ν•œ 경우 QuerySet에 적용된 QuerySetλ©”μ„œλ“œμ˜ 첫번째 QuerySet의 μ—΄ 이름을 μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

>>> qs1 = Author.objects.values_list('name')
>>> qs2 = Entry.objects.values_list('headline')
>>> qs1.union(qs2).order_by('name')

λ˜ν•œ κ²°κ³Ό QuerySetμ—λŠ” LIMIT, OFFSET, COUNT(*), ORDER BY및 μ—΄ μ§€μ • (μŠ¬λΌμ΄μ‹±, count(), order_by(), value(), values_list())만 ν—ˆμš©λœλ‹€. λ°μ΄ν„°λ² μ΄μŠ€λŠ” κ²°ν•© 된 μΏΌλ¦¬μ—μ„œ ν—ˆμš©λ˜λŠ” μž‘μ—…μ— μ œν•œμ„ λ‘”λ‹€. 예λ₯Ό λ“€μ–΄ λŒ€λΆ€λΆ„μ˜ λ°μ΄ν„°λ² μ΄μŠ€λŠ” κ²°ν•© 된 μΏΌλ¦¬μ—μ„œ LIMITλ˜λŠ” OFFSET을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€.

intersection( )

intersection(*other_qs)

SQL의 INTERSECTλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‘˜ μ΄μƒμ˜ QuerySet의 ꡐ집합을 λ°˜ν™˜ν•œλ‹€.

>>> qs1.intersection(qs2, qs3)

intersection()의 μ œν•œμ‚¬ν•­μ€ union()κ³Ό λ™μΌν•˜λ‹€.

difference( )

difference(*other_qs)

SQL의 EXCEPTμ—°μ‚°μžλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‘˜ μ΄μƒμ˜ QuerySet의 차집합을 λ°˜ν™˜ν•œλ‹€.

>>> qs1.difference(qs2, qs3)

difference()의 μ œν•œμ‚¬ν•­μ€ union()κ³Ό λ™μΌν•˜λ‹€.

extra( )

extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

λ•Œλ•Œλ‘œ Django 쿼리 ꡬ문 λ§ŒμœΌλ‘œλŠ” λ³΅μž‘ν•œ WHEREμ ˆμ„ μ‰½κ²Œ ν‘œν˜„ν•  수 μ—†λ‹€. μ΄λŸ¬ν•œ 상황을 μœ„ν•΄ DjangoλŠ” extra()QuerySetν•œμ • 자λ₯Ό μ œκ³΅ν•œλ‹€. 이것은 QuerySet에 μ˜ν•΄ μƒμ„±λœ SQL에 νŠΉμ • μ ˆμ„ μ‚½μž…ν•˜κΈ° μœ„ν•œ 방법이닀.

πŸ’‘μ΄ 방법은 μ΅œν›„μ˜ μˆ˜λ‹¨μœΌλ‘œ μ‚¬μš©ν•  것

extra()λŠ” ν–₯ν›„ Djangoμ—μ„œ 지원을 μ€‘λ‹¨ν•˜λ €λŠ” 였래된 API이닀. λ‹€λ₯Έ 쿼리셋 λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 쿼리λ₯Ό ν‘œν˜„ν•  수 μ—†λŠ” κ²½μš°μ—λ§Œ μ‚¬μš©ν•  것을 ꢌμž₯ν•œλ‹€. DjangoλŠ” 더 이상 extra()에 λŒ€ν•œ 버그λ₯Ό κ°œμ„ ν•˜κ±°λ‚˜ μˆ˜μ •ν•˜μ§€ μ•ŠλŠ”λ‹€.

>>> qs.extra(
...     select={'val': "select col from sometable where othercol = %s"},
...     select_params=(someparam,),
... )

이것은 RawSQL을 μ‚¬μš©ν•˜λŠ” λ‹€μŒκ³Ό λ™μΌν•˜λ‹€.

>>> qs.annotate(val=RawSQL("select col from sometable where othercol = %s", (someparam,)))

RawSQLμ‚¬μš©μ‹œ μ£Όμš” 이점은 ν•„μš”ν•œ 경우 output_fieldλ₯Ό μ„€μ •ν•  수 μžˆλ‹€λŠ” 것이닀. 주된 단점은 raw SQLμ—μ„œ μΏΌλ¦¬μ…‹μ˜ 일뢀 ν…Œμ΄λΈ” 별칭을 μ°Έμ‘°ν•˜λ©΄ Djangoκ°€ ν•΄λ‹Ή 별칭을 λ³€κ²½ν•  수 μžˆλ‹€λŠ” 것이닀. (예: 쿼리셋이 또 λ‹€λ₯Έ μΏΌλ¦¬μ—μ„œ μ„œλΈŒμΏΌλ¦¬λ‘œ μ‚¬μš©λ˜λŠ” 경우)

⚠️ SQL injection에 μ·¨μ•½ν•œ extra()

extra()λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” SQL injection곡격에 맀우 μ·¨μ•½ν•˜λ―€λ‘œ μ‚¬μš©μžκ°€ 맀개 λ³€μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μ œμ–΄ν•  수 μžˆλŠ” λͺ¨λ“  맀개 λ³€μˆ˜λ₯Ό μ΄μŠ€μΌ€μ΄ν”„ν•΄μ•Ό ν•œλ‹€.

λ˜ν•œ SQLλ¬Έμžμ—΄μ—μ„œ 자리 ν‘œμ‹œμžλ₯Ό μΈμš©ν•˜μ§€ μ•Šμ•„μ•Ό ν•œλ‹€. 이 μ˜ˆμ œλŠ” %sμ£Όμœ„μ˜ ''μ‚¬μš©μœΌλ‘œ 이해 SQL injection에 μ·¨μ•½ν•˜λ‹€.

SELECT col FROM sometable WHERE othercol = '%s'  # unsafe!

Django의 SQL injection 보호 μž‘λ™ 방식에 λŒ€ν•΄μ„  μ—¬κΈ°λ₯Ό μ°Έκ³  ν•˜λ„λ‘ ν•˜μž.

defer( )

defer(*fields)

일뢀 λ³΅μž‘ν•œ 데이터 λͺ¨λΈλ§μ—μ„œλŠ” λͺ¨λΈμ— λ§Žμ€ ν•„λ“œκ°€ ν¬ν•¨λ˜λ©°, 그쀑 μΌλΆ€μ—λŠ” λ§Žμ€ 데이터(ex. TextField)κ°€ 포함될 수 μžˆκ±°λ‚˜ Python 객체둜 λ³€ν™˜ν•˜κΈ° μœ„ν•΄ 무거운 μ²˜λ¦¬κ°€ ν•„μš”ν•  수 μžˆλ‹€. 데이터λ₯Ό 처음 κ°€μ Έμ˜¬ λ•Œ νŠΉμ • ν•„λ“œκ°€ ν•„μš”ν•œμ§€ μ—¬λΆ€λ₯Ό μ•Œ 수 μ—†λŠ” μƒν™©μ—μ„œ μΏΌλ¦¬μ…‹μ˜ κ²°κ³Όλ₯Ό μ‚¬μš©ν•˜λŠ” 경우 Django에 λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ κ²€μƒ‰ν•˜μ§€ μ•Šλ„λ‘ μ§€μ‹œ ν•  수 μžˆλ‹€.

μ΄λŠ” λ‘œλ“œ λ˜μ§€ μ•Šλ„λ‘ ν•„λ“œμ΄λ¦„μ„ defer()에 μ „λ‹¬ν•˜μ—¬ μˆ˜ν–‰λœλ‹€.

# body와 headlineν•„λ“œλ₯Ό λͺ¨λ‘ κ²€μƒ‰μ—μ„œ μ œμ™Έ μ‹œν‚¨λ‹€.
Entry.objects.defer("body").filter(rating=5).defer("headline")

μ§€μ—°λœ 집합에 ν•„λ“œκ°€ μΆ”κ°€λ˜λŠ” μˆœμ„œλŠ” μ€‘μš”ν•˜μ§€ μ•Šλ‹€. 이미 κ²€μƒ‰ν•˜μ§€ μ•Šλ„λ‘ 제거된 ν•„λ“œ μ΄λ¦„μœΌλ‘œ defer()λ₯Ό ν˜ΈμΆœν•˜λŠ” 것은 ν•΄κ°€ λ˜μ§€ μ•ŠλŠ”λ‹€. (ν•„λ“œλŠ” 여전이 κ²€μƒ‰μ—μ„œ μ œμ™Έλ˜μ–΄ μžˆλ‹€.)

double underscore (__)λ₯Ό μ‚¬μš©ν•˜μ—¬ κ΄€λ ¨ ν•„λ“œλ₯Ό κ΅¬λΆ„ν•˜μ—¬ κ΄€λ ¨λœ λͺ¨λΈμ˜ ν•„λ“œλ₯Ό κ²€μƒ‰μ—μ„œ μ œμ™Έμ‹œν‚¬ 수 μžˆλ‹€.(select_relatedλ₯Ό 톡해 쿼리셋을 κ°€μ Έμ˜¬ 경우)

Blog.objects.select_related().defer("entry__headline", "entry__body")

κ²€μƒ‰μ—μ„œ μ œμ™Έλœ ν•„λ“œ 집합을 ν•΄μ œν•˜λ €λ©΄ defer()의 λ§€κ°œλ³€μˆ˜λ‘œ None을 μ „λ‹¬ν•œλ‹€.

# λͺ¨λ“  ν•„λ“œλ₯Ό κ°€μ Έμ˜¨λ‹€.
my_queryset.defer(None)

λͺ¨λΈμ˜ 일뢀 ν•„λ“œλŠ” κ²€μƒ‰μ—μ„œ μ œμ™Έλ˜μ§€ μ•ŠλŠ”λ‹€. 특히 Primary KeyλŠ” κ²€μƒ‰μ—μ„œ μ œμ™Έ μ‹œν‚¬ 수 μ—†λ‹€. select_related()λ₯Ό μ‚¬μš©ν•˜μ—¬ κ΄€λ ¨ λͺ¨λΈμ„ κ²€μƒ‰ν•˜λŠ” 경우 κΈ°λ³Έ λͺ¨λΈμ—μ„œ κ΄€λ ¨λͺ¨λΈλ‘œ μ—°κ²°λ˜λŠ” ν•„λ“œλ₯Ό κ²€μƒ‰μ—μ„œ μ œμ™Έμ‹œν‚€λ©΄ μ•ˆλœλ‹€. λ§Œμ•½ κ²€μƒ‰μ—μ„œ μ œμ™Έλœλ‹€λ©΄ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

κ²€μƒ‰μ—μ„œ μ œμ™Έλœ ν•„λ“œκ°€ μžˆλŠ” μΈμŠ€ν„΄μŠ€μ— λŒ€ν•΄ save()λ₯Ό ν˜ΈμΆœν•˜λ©΄ κ²€μƒ‰λœ ν•„λ“œλ§Œ μ €μž₯λœλ‹€.

only( )

only(*fields)

only()λ©”μ„œλ“œλŠ” defer()와 λ°˜λŒ€λ˜λŠ” κ°œλ…μ΄λ‹€. λͺ¨λΈμ„ 검색할 λ•Œ λ°˜λ“œμ‹œ ν¬ν•¨λ˜μ–΄μ•Ό ν•˜λŠ” ν•„λ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ ν˜ΈμΆœν•œλ‹€. 거의 λͺ¨λ“  ν•„λ“œλ₯Ό κ²€μƒ‰μ—μ„œ μ œμ™Έν•΄μ•Ό ν•˜λŠ” λͺ¨λΈμ΄ μžˆλŠ” 경우 only()λ₯Ό μ‚¬μš©ν•˜λ©΄ μ½”λ“œκ°€ 더 κ°„κ²°ν•΄ 질 수 μžˆλ‹€.

이름, λ‚˜μ΄, 생애전기가 μžˆλŠ” λͺ¨λΈμ΄ μžˆλ‹€κ³  κ°€μ •ν•˜κ³  λ‹€μŒ 두 개의 쿼리셋은 κ²€μƒ‰λ˜λŠ” ν•„λ“œμ˜ μ œν•œμ— μžˆμ–΄μ„œ λ™μΌν•˜λ‹€.

Person.objects.defer("age", "biography")
Person.objects.only("name")

only()λ₯Ό 호좜 ν•  λ•Œλ§ˆλ‹€ μ¦‰μ‹œ λ‘œλ“œν•  ν•„λ“œ μ„ΈνŠΈλ₯Ό λŒ€μ²΄ ν•œλ‹€. λ©”μ„œλ“œ 이름은 mnemonic이닀. ν•΄λ‹Ή ν•„λ“œ 만 μ¦‰μ‹œ λ‘œλ“œλ˜λ©° λ‚˜λ¨Έμ§€λŠ” κ²€μƒ‰μ—μ„œ μ œμ™Έλœλ‹€. λ”°λΌμ„œ only()λ₯Ό μ—°μ†μ μœΌλ‘œ ν˜ΈμΆœν•˜λ©΄ λ§ˆμ§€λ§‰ ν•„λ“œλ§Œ κ³ λ €λœλ‹€.

# μ΅œμ’… κ²°κ³ΌλŠ” `headline`을 μ œμ™Έν•œ λͺ¨λ“  것이 κ²€μƒ‰μ—μ„œ μ œμ™Έ λœλ‹€.
Entry.objects.only("headline", "body").defer("body")

# μ΅œμ’… κ²°κ³ΌλŠ” `headline`κ³Ό `body`λ₯Ό μ¦‰μ‹œ λ‘œλ“œν•œλ‹€. (only()λŠ” κΈ°μ‘΄ ν•„λ“œ μ„ΈνŠΈλ₯Ό λŒ€μ²΄ν•œλ‹€.)
Entry.objects.defer("body").only("headline", "body")

defer()의 λͺ¨λ“  주의 사항은 only()에도 μ μš©λœλ‹€. λ‹€λ₯Έ μ˜΅μ…˜μ„ λ‹€ μ‚¬μš©ν•œ ν›„μ—λ§Œ μ‘°μ‹¬μŠ€λŸ½κ²Œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.only()λ₯Ό μ‚¬μš©ν•˜κ³  select_related()λ₯Ό μ‚¬μš©ν•˜μ—¬ μš”μ²­ 된 ν•„λ“œλ₯Ό μƒλž΅ν•˜λŠ” 것도 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

κ²€μƒ‰μ—μ„œ μ œμ™Έλœ ν•„λ“œκ°€ μžˆλŠ” μΈμŠ€ν„΄μŠ€μ— λŒ€ν•΄ save()λ₯Ό ν˜ΈμΆœν•˜λ©΄ κ²€μƒ‰λœ ν•„λ“œλ§Œ μ €μž₯λœλ‹€.

using( )

using(alias)

이 λ©”μ†Œλ“œλŠ” λ‘˜ μ΄μƒμ˜ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 경우 쿼리셋이 평가 될 λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ œμ–΄ν•˜κΈ° μœ„ν•œ 것이닀. 이 λ©”μ†Œλ“œμ˜ λ§€κ°œλ³€μˆ˜λŠ” DATABASES에 μ •μ˜λœ λ°μ΄ν„°λ² μ΄μŠ€ 별λͺ…이닀.

# 'default'라고 μ§€μ •λœ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‚¬μš©ν•œλ‹€.
>>> Entry.objects.all()

# 'backup'라고 μ§€μ •λœ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‚¬μš©ν•œλ‹€.
>>> Entry.objects.using('backup')

select_for_update( )

select_for_update(nowait=False, skip_locked=False, of=())

νŠΈλžœμž­μ…˜μ΄ 끝날 λ•ŒκΉŒμ§€ 행에 락을 κ±Έκ³  μ§€μ›λ˜λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ SELECT ... FOR UPDATE SQL문을 μƒμ„±ν•˜λŠ” 쿼리셋을 λ°˜ν™˜ν•œλ‹€.

from django.db import transaction

entries = Entry.objects.select_for_update().filter(author=request.user)
with transaction.atomic():
    for entry in entries:
        ...

쿼리셋을 평가 될 λ•Œ (이 경우 ν•­λͺ©μ˜ ν•­λͺ©μ— λŒ€ν•΄) μΌμΉ˜ν•˜λŠ” λͺ¨λ“  ν•­λͺ©μ€ νŠΈλžœμž­μ…˜ 블둝이 끝날 λ•ŒκΉŒμ§€ 락이 κ±Έλ¦°λ‹€. 즉, λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ΄ ν•΄λ‹Ή ν•­λͺ©μ— λŒ€ν•œ 락을 λ³€κ²½ν•˜κ±°λ‚˜ νšλ“ν•˜μ§€ λͺ»ν•˜λ„둝 ν•œλ‹€.

일반적으둜 λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ΄ μ„ νƒν•œ ν–‰ 쀑에 ν•˜λ‚˜μ— λŒ€ν•΄ 이미 락이 κ±Έλ¦° 경우 락이 ν•΄μ œλ  λ•Œ κΉŒμ§€ 쿼리가 μ°¨λ‹¨λœλ‹€. 이것이 μ›ν•˜λŠ” λ™μž‘μ΄ μ•„λ‹Œ 경우 select_for_update(nowait = True)λ₯Ό ν˜ΈμΆœν•œλ‹€. μ΄λ ‡κ²Œν•˜λ©΄ 호좜이 μ°¨λ‹¨λ˜μ§€ μ•ŠλŠ”λ‹€ μΆ©λŒν•˜λŠ” 락이 이미 λ‹€λ₯Έ νŠΈλžœμž­μ…˜μ— μ˜ν•΄ νšλ“ 된 경우 쿼리셋이 평가 될 λ•Œ λ°μ΄ν„°λ² μ΄μŠ€ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€. λŒ€μ‹  select_for_update(skip_locked = True)λ₯Ό μ‚¬μš©ν•˜μ—¬ 락이 κ±Έλ¦° 행을 λ¬΄μ‹œν•  μˆ˜λ„ μžˆλ‹€. nowaitκ³Ό skip_lockedλŠ” μƒν˜Έ 배타적이며 두 μ˜΅μ…˜μ„ λͺ¨λ‘ ν™œμ„±ν™” ν•œ μƒνƒœμ—μ„œ select_for_update()λ₯Ό ν˜ΈμΆœν•˜λ©΄ ValueErrorκ°€ λ°œμƒν•œλ‹€.

기본적으둜 select_for_update()λŠ” μΏΌλ¦¬μ—μ„œ μ„ νƒν•œ λͺ¨λ“  행에 락을 건닀. 예λ₯Ό λ“€μ–΄ select_related()에 μ§€μ •λœ κ΄€λ ¨ 객체의 행은 쿼리 μ…‹ λͺ¨λΈμ˜ ν–‰κ³Ό ν•¨κ»˜ μž κΈ΄λ‹€. μ›ν•˜μ§€ μ•ŠλŠ” 경우 select_related()와 λ™μΌν•œ ν•„λ“œ ꡬ문을 μ‚¬μš©ν•˜μ—¬ select_for_update(of = (...))λ₯Ό 톡해 락을 κ±Έλ €λŠ” κ΄€λ ¨ 객체λ₯Ό μ§€μ •ν•œλ‹€. 검색어 μ„ΈνŠΈμ˜ λͺ¨λΈμ„ μ°Έμ‘°ν•˜λ €λ©΄ selfλ₯Ό μ‚¬μš©ν•œλ‹€.

πŸ’‘select_for_update(of=(...))μ—μ„œ μƒμœ„ λͺ¨λΈ 락 κ±ΈκΈ°

닀쀑 ν…Œμ΄λΈ” 상속을 μ‚¬μš©ν•  λ•Œ μƒμœ„ λͺ¨λΈμ— 락을 κ±Έλ €λ©΄ ofμΈμˆ˜μ— μƒμœ„ 링크 ν•„λ“œ (기본적으둜 <parent_model_name>_ptr)λ₯Ό μ§€μ •ν•΄μ•Ό ν•œλ‹€. 예λ₯Ό λ“€λ©΄

Restaurant.objects.select_for_update(of=('self', 'place_ptr'))

nullableκ΄€κ³„μ—λŠ” select_for_update()λ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€.

>>> Person.objects.select_related('hometown').select_for_update()

Traceback (most recent call last):
...
django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join

μ΄λŸ¬ν•œ μ œν•œμ„ νšŒν”Όν•˜κΈ° μœ„ν•΄ null이 ν•„μš” μ—†λŠ” 경우 null객체λ₯Ό μ œμ™Έ μ‹œν‚¬ 수 μžˆλ‹€.

>>> Person.objects.select_related('hometown').select_for_update().exclude(hometown=None)
<QuerySet [<Person: ...)>, ...]>

ν˜„μž¬ PostgreSQL, Oracle, MySQLλ°μ΄ν„°λ² μ΄μŠ€λŠ” select_for_update()λ₯Ό μ§€μ›ν•œλ‹€. κ·ΈλŸ¬λ‚˜ MariaDB 10.3+λŠ” nowait만 μ§€μ›λ˜λ©° MySQL 8.0.1+λŠ” nowait및 skip_lockedλ₯Ό μ§€μ›ν•œλ‹€. MySQLκ³Ό MariaDBλŠ” of인수λ₯Ό μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€.

MySQLκ³Ό 같이 μ΄λŸ¬ν•œ μ˜΅μ…˜μ„ μ§€μ›ν•˜μ§€ μ•ŠλŠ” λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ nowait=True, skip_locked = Trueλ˜λŠ” select_for_update(of=(...))λ₯Ό μ „λ‹¬ν•˜λ©΄ NotSupportedErrorκ°€ λ°œμƒν•œλ‹€.

SELECT ... FOR UPDATEλ₯Ό μ§€μ›ν•˜λŠ” λ°μ΄ν„°λ² μ΄μŠ€μ˜ μžλ™ 컀밋 λͺ¨λ“œμ—μ„œ select_for_update()λ₯Ό μ‚¬μš©ν•˜μ—¬ μΏΌλ¦¬μ„ΈνŠΈλ₯Ό ν‰κ°€ν•˜λŠ” 것은 이 경우 행에 락이 걸리지 μ•ŠκΈ° λ•Œλ¬Έμ— TransactionManagementErrorκ°€ λ°œμƒν•œλ‹€. λ§Œμ•½ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•Šκ²Œ ν—ˆμš©ν•œλ‹€λ©΄ νŠΈλžœμž­μ…˜μ™ΈλΆ€μ˜ νŠΈλžœμž­μ…˜μ—μ„œ μ‹€ν–‰ 될 κ²ƒμœΌλ‘œ μ˜ˆμƒλ˜λŠ” μ½”λ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ 데이터 손상이 μ‰½κ²Œ λ°œμƒν•  수 μžˆλ‹€.

SELECT ... FOR UPDATEλ₯Ό μ§€μ›ν•˜μ§€ μ•ŠλŠ” λ°μ΄ν„°λ² μ΄μŠ€ (ex. SQLite)μ—μ„œ select_for_update()λ₯Ό μ‚¬μš©ν•˜λ©΄ νš¨κ³Όκ°€ μ—†λ‹€. SELECT ... FOR UPDATEλŠ” 쿼리에 μΆ”κ°€λ˜μ§€ μ•ŠμœΌλ©° μžλ™ 컀밋 λͺ¨λ“œμ—μ„œ select_for_update()λ₯Ό μ‚¬μš©ν•˜λ©΄ 였λ₯˜κ°€ λ°œμƒν•˜μ§€ μ•ŠλŠ”λ‹€.

⚠️ TestCaseμ—μ„œ select_for_update()μ‚¬μš©μ‹œ 주의 사항

select_for_update()λŠ” 일반적으둜 μžλ™ 컀밋 λͺ¨λ“œμ—μ„œ μ‹€νŒ¨ν•˜μ§€λ§Œ TestCaseλŠ” νŠΈλžœμž­μ…˜μ˜ 각 ν…ŒμŠ€νŠΈλ₯Ό μžλ™μœΌλ‘œ λž˜ν•‘ν•˜λ―€λ‘œ atomic()블둝 μ™ΈλΆ€μ—μ„œλ„ TestCaseμ—μ„œ select_for_update()λ₯Ό ν˜ΈμΆœν•˜λ©΄ TransactionManagementErrorλ₯Ό λ°œμƒμ‹œν‚€μ§€ μ•Šκ³  (μ•„λ§ˆλ„ 예기치 μ•Šκ²Œ) ν†΅κ³Όλœλ‹€.select_for_update()λ₯Ό μ œλŒ€λ‘œ ν…ŒμŠ€νŠΈ ν•˜λ €λ©΄ TransactionTestCaseλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•œλ‹€.

πŸ’‘μ§€μ›λ˜μ§€ μ•ŠλŠ” ν‘œν˜„ : Window function

select_for_update()PostgreSQL의 Window function expression을 μ§€μ›ν•˜μ§€ μ•ŠλŠ”λ‹€.

raw( )

raw(raw_query, params=None, translations=None)

raw SQL 쿼리λ₯Ό λ°›μ•„μ„œ μ‹€ν–‰ν•˜κ³  django.db.models.query.RawQuerySetμΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€. 이 RawQuerySetμΈμŠ€ν„΄μŠ€λŠ” 객체 μΈμŠ€ν„΄μŠ€λ₯Ό μ œκ³΅ν•˜κΈ° μœ„ν•΄ 일반 QuerySet처럼 반볡 될 수 μžˆλ‹€.

μžμ„Έν•œ λ‚΄μš©μ€ μ—¬κΈ°λ₯Ό μ°Έμ‘°ν•œλ‹€.

⚠️ raw()μ‚¬μš©μ‹œ 주의 사항

raw()λŠ” 항상 μƒˆ 쿼리λ₯Ό νŠΈλ¦¬κ±°ν•˜κ³  이전 필터링을 κ³ λ €ν•˜μ§€ μ•ŠλŠ”λ‹€. λ”°λΌμ„œ 일반적으둜 Manager λ˜λŠ” μƒˆλ‘œμš΄ 쿼리셋 μΈμŠ€ν„΄μŠ€μ—μ„œ ν˜ΈμΆœν•΄μ•Ό ν•œλ‹€.


Yongineer
Written by@Yongineer
Backend Developer

GitHubInstagram