QuerySet Filtering
QuerySet은 Django에서 데이터베이스 쿼리 결과를 나타내는 객체로, 데이터를 효율적으로 조회하고 관리할 수 있게 해줍니다. 다양한 메서드를 통해 특정 조건을 만족하는 데이터를 필터링하거나, 정렬 및 집계하는 등의 작업을 할 수 있습니다.
# models.py 샘플 모델
from django.db import models
# 작성자 모델
class Author(models.Model):
username = models.CharField(max_length=100)
email = models.EmailField()
def __str__(self):
return self.username
# 카테고리 모델
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
# 태그 모델
class Tag(models.Model):
name = models.CharField(max_length=100)
# 게시글 모델 (ForeignKey 관계)
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
tags = models.ManyToManyField(Tag)
author = models.ForeignKey(Author, on_delete=models.CASCADE)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Filtering
필터링은 조건에 맞는 여러 객체를 쿼리셋 형태로 반환하며, 조건에 맞는 객체가 없으면 빈 쿼리셋을 반환합니다. 여러 조건이 필요하다면 조건을 결합하여 쿼리할 수 있으며, 조건들은 모두 AND
연산으로 결합됩니다.
# shell
from myapp.models import Post
from datetime import datetime, timedelta
# 모든 게시글 조회
posts = Post.objects.all()
# 특정 조건으로 데이터 필터링
filtered_posts = Post.objects.filter(title__icontains='Python')
# 제목이 'Python'을 포함하고, 생성 날짜가 7일 이내인 게시글
start_date = datetime.now() - timedelta(days=7)
filtered_posts = Post.objects.filter(title__icontains='Python', created_at__gte=start_date)
연산자 | 연산자 (대소문자 구분하지 않음) | 조건 |
---|---|---|
exact | iexact | 정확히 일치하는 값 |
startswith | istartswith | 앞부분 일치 |
endswith | iendswith | 뒷부분 일치 |
contains | icontains | 부분 일치 |
gt | igt | 값이 큰 |
lt | ilt | 값이 작은 |
gte | igte | 값이 크거나 같은 |
lte | ilte | 값이 작거나 같은 |
exclude
exclude
메서드는 filter
와는 반대로 조건에 맞지 않는 데이터를 반환합니다.
# 게시글 제목에 'Python'이 포함되지 않은 데이터 조회
filtered_posts = Post.objects.exclude(title__icontains='Python')
order_by
order_by
메서드는 특정 필드를 기준으로 데이터를 정렬합니다.
-필드명
: 내림차순 정렬.필드명
: 오름차순 정렬.
# 게시글을 생성 날짜 기준으로 내림차순 정렬
posts = Post.objects.order_by('-created_at')
# 좋아요 수 기준으로 오름차순 정렬
posts = Post.objects.order_by('likes')
# 생성 날짜 기준으로 내림차순, 조회수 기준으로 오름차순 정렬
posts = Post.objects.order_by('-created_at', 'views')
distinct
distinct
메서드는 쿼리셋에서 중복된 데이터를 제거합니다.
# 작성자를 기준으로 중복 제거된 게시글 조회
authors = Post.objects.values('author').distinct()
Case and When
Case
와 When
객체를 사용하면 조건에 따라 다른 값을 처리할 수 있습니다.
from django.db.models import Case, When, Value
# 조회수가 100 이상인 게시글은 '인기', 그렇지 않은 게시글은 '일반'으로 라벨링
posts = Post.objects.annotate(
popularity=Case(
When(views__gte=100, then=Value('Popular')),
default=Value('Normal'),
output_field=models.CharField()
)
)
# 조회수가 100 이상인 게시글을 우선 정렬
posts = Post.objects.annotate(
is_popular=Case(
When(views__gte=100, then=Value(1)),
default=Value(0),
output_field=IntegerField()
)
).order_by('-is_popular', '-views')
Subquery
서브쿼리를 작성할 때 사용됩니다. SQL의 서브쿼리와 유사한 쿼리를 작성할 수 있습니다.
from django.db.models import Subquery, OuterRef
# 가장 최근 댓글의 내용을 게시글에 추가
recent_comments = Comment.objects.filter(post=OuterRef('pk')).order_by('-created_at').values('content')[:1]
posts = Post.objects.annotate(latest_comment=Subquery(recent_comments))
Relationship-Based Filtering
외래키(ForeignKey)와 다대다 관계(ManyToMany) 필드 관계를 통해 연결된 모델을 기준으로 데이터를 필터링할 수 있습니다. 이를 통해 관계된 모델의 데이터를 기반으로 쿼리를 수행할 수 있습니다.
ForeignKey
외래키 관계에서 관련된 모델의 필드를 통해 데이터를 필터링할 수 있습니다.
__(더블 언더스코어)
를 사용하여 관련 모델의 필드에 접근합니다.
from django.db.models import Count
# 특정 작성자가 작성한 게시글 조회
posts = Post.objects.filter(author__username='john_doe')
# 게시글의 카테고리가 'Python'인 게시글 조회
posts = Post.objects.filter(category__name='Python')
# 댓글 수가 3개 이상인 게시글 조회
posts = Post.objects.annotate(comment_count=Count('comments')).filter(comment_count__gte=3)
ManyToMany
다대다 관계에서 특정 조건을 만족하는 데이터를 필터링합니다.
__(더블 언더스코어)
를 사용하여 관련 모델의 필드에 접근합니다.
# 특정 태그를 포함한 게시글 조회
posts = Post.objects.filter(tags__name='Django')