Django QuerySet 에 정렬과 필터를 다이나믹하게(정해진 갯수없이) 거는 방법
Order, Filter 프론트에서 요청하는대로 알아서 처리하게 할수 있을까요? 네 가능합니다.
다음 과 같은 Post 모델이 있다.
class Post(models.Model):
title = models.CharField() #제목
content = models.TextFiled() #내용
author = models.CharField() #저자
timestamp = models.DateTimeField(default=timezone.now) #만들어진 시간
like_count = models.IntegerField() #좋아요 눌린 횟수
view_count = models.IntegerField() #뷰 횟수
hate_count = models.IntegerField() #싫어요 횟수
Dynamic sorting
1개의 Order를 거는 방법은
posts = Post.objects.all().order_by('view_count') #좋아요 오름차순 정렬
posts = Post.objects.all().order_by('-view_count') #좋아요 내림차순 정렬
N개의 order를 거는 방법은
posts = Post.objects.all().order_by('view_count','like_count') #조회수, 좋아요 오름차순 정렬,
Order 는 Chainning 이 가능하다. 위의 방법을 Chainnig으로 표현하면, 아래처럼도 작성이 가능하다.
posts = Post.objects.all().order_by('view_count') #조회수 오름차순 정렬
posts = posts.order_by('like_count')#조회수 오름차순 정렬 한것중 좋아요 오름차순 정렬
Order 갯수가 정해져 있지 않고, 1개 또는 그 이상의 Order를 QuerySet에 거는 방법은 크게 두가지 이다.
- Order 를 List로 만들고 그 리스트를 통해 Order 입력
- Order 값의 연속 Chainning
1번 방법의 정석은 아래와 같다.
# request URL : url/order1=view_count,order2=like_count,order3=-hate_count....order[n]='value' 최대 n개의 Order 정렬 요청을 받는다.
order_list = [request.GET.get(f'order{i}', '-timestamp') for i in range(n)] #받은 요청들을 리스트에 담는다. '-timestamp'를 넣지 않을 경우, 값이 없거나, 잘못되었을 경우 에러가 난다. 에러방지용.
print(order_list)
[
'view_count', 'like_count', '-hate_count'
]
ordered_post1 = Post.objects.order_by(*order_list) #order_list에 담긴 값으로 Order를 해준다!
ordered_post2 = Post.objects.order_by('view_count', 'like_count', '-hate_count') # ordered_post1과 ordered_post2의 정렬된 내용은 같다.
좀더 편리하게 처리하자면 이렇게도 해볼수 있을 것 같다.
# request URL : url/order=like_count,view_count,-hate_count 기한 없이 Order 정렬 요청을 받는다.쿼리스트링이 조금더 간결해졌다.
order_list = request.GET.get('order', '-timestamp').split(',')
#받은 요청들을 리스트에 담는다. '-timestamp'를 넣지 않을 경우, 값이 없거나, 잘못되었을 경우 에러가 난다.
print(order_list)
[
'view_count', 'like_count', '-hate_count'
]
ordered_post1 = Post.objects.order_by(*order_list) #order_list에 담긴 값으로 Order를 해준다!
ordered_post2 = Post.objects.order_by('view_count', 'like_count', '-hate_count') # ordered_post1과 ordered_post2의 정렬된 내용은 같다.
2번 방법은 다음과 같다.
#다중 Order body로 받을시,
data = {
"order_by":
[
{"order":"", "field":"timestamp"},
{"order": "", "field":"view_count"},
{"order":"asc","field":"like_count"},
]
}
posts = Post.objcects.all()
if 'order_by' in data: #정렬값이 request에 있다면,
for order in data['order_by']: #그 값들을 찾아서
field = order['field'] #연속 channing으로 Order를 건다.
asc = '-' if not order['asc'] else ""
posts = posts.order_by(f"{asc}{field}")
필터의 동적 적용!
위와 비슷하지만 살짝 다른 방법으로 filter도 동적으로 걸 수 있다. 필터는 List 가 아닌 Dict를 활용한다.
# request URL : url/title=wecode최고,content__contain=developer,author=바름
#Dict로 받기 때문에 사전에 정해진 필터값들만 받을 수 있다.
filter_dic = {}
data = request.GET
try:
if data.get('title',False):
filter_dic['title'] = data.get('title')
if data.get('content__contain',False):
filter_dic['content__contain'] = data.get('content__contain')
if data.get('author',False):
filter_dic['author'] = data.get('author')
except ValueError, SyntaxError:
return HttpResponse(status=400)
#각각의 필터마다 받는 값이 정해져 있기 때문에 이에 대해서는 미리 validation 처리를 해놓아야 한다. e.g id에 str인 경우.
print(filter_dic)
{
'title':'wecode'최고,'content__contain':'developer','author':'바름'
}
filtereded_post1 = Post.objects.order_by(**filter_dic) #filter_dic에 담긴 값으로 filter를 해준다!
filtered_post2 = Post.objects.order_by(title='wecode최고',content__contain='developer',author='바름'
) # ordered_post1과 ordered_post2의 filter된 내용은 같다.
위의 방법은 손이 가는 부분이 많다. post를 통해 request body로 받으면 더 쉬워진다.
# request URL : url/
data = {
filter_data:
{
'title':'wecode'최고,'content__contain':'developer','author':'바름'
}
}
if data['filter_data']:
try:
posts = Post.objects.filter[**filter_data]
except ValueError, SyntaxError... :
return HttpResponse(status=400)
print(filter_dic)
{
'title':'wecode'최고,'content__contain'=developer',''author'='바름'
}
filtereded_post1 = Post.objects.order_by(**filter_dic) #filter_dic에 담긴 값으로 filter를 해준다!
filtered_post2 = Post.objects.order_by(title='wecode최고',content__contain='developer',author='바름'
) # ordered_post1과 ordered_post2의 filter된 내용은 같다.
filter 도 Chainning은 가능하나 Sorting처럼 다이나믹하게 활용하기 어렵다. 키워드가 정해져 있기 때문에 filter 키워드를 동적으로 처리하지 못한다.
posts.objects.filter(search=value)
#Search는 Post에 없는 Field다. Post가 가지고 있는 filed가 아니면 정의부터 error를 터뜨린다.