본문 바로가기

문돌이 존버/Django 스터디

장고(Django) Queryset을 통한 검색 구현 및 정렬(sorting)

반응형
본 글은 Holix의 "리액트와 함께 장고 시작하기 Complete" 강의를 듣고 작성한 일지입니다.

이번엔 장고의 QuerySet 을 이용하여 간단한 검색 서비스를 구현해보겠습니다. 이를 위해서 이전에 다루지 않았던 views.py 파일에 코드를 추가해야 합니다.

# views.py
from django.shortcuts import render
from .models import Post

# Create your views here.
def post_list(request):
    qs = Post.objects.all()
    q = request.GET.get('q', '') # q가 없으면 빈 문자열 리턴
    if q:
        qs = qs.filter(content__icontains=q)

    # blog1/templates/blog1/post_list.html
    return render(request, 'blog1/post_list.html', {
        'post_list': qs,
        'q': q,
    })
request.GET.get('q'): 우리가 흔히 url에서 볼 수 있는 "?q=" 형식에서의 q를 말합니다. = 뒤에 특정 단어가 있으면 그것을 q라는 변수에 저장하고 없으면 빈 문자열을 저장하겠다는 의미입니다.
render: 직접 만든 html 파일을 웹에 띄울 수 있도록(=렌더링) 지원하는 장고의 기능

아래는 간단하게 표현한 html 파일입니다. 장고에서는 저희가 직접 작성한 html 파일의 위치를 현재 앱 아래 templates 라는 폴더에 있다고 인식합니다. 따라서 templates 아래 폴더를 따로 두었다면(저처럼) 위에서 render() 를 작성할 때 나머지 디렉토리 위치를 적어주시면 됩니다.

<!-- post_list.html -->
<!doctype html>
<html lang="ko">
<head>
    <meta charset="utf-8" />
    <title>Instagram / Post List</title>
    <!-- CSS only -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEAg3QhqLMpG8r+8fhAXLRk2vvoC2f3B09zVXn8CA5QIVfZOJ3BCsw2P0p/We" crossorigin="anonymous">
</head>
<body>

    <form action="" method="get">
        <input type="text" name="q" value="{{ q }}" /> <!-- name="q"로 지정해야 views.py에서 get 방식으로 q값을 가져온 것과 동일하게 동작 -->
        <input type="submit" value="검색" /> <!-- value=""는 기본적으로 표시되는 항목 이름 -->
    </form>

    <table class="table table-bordered table-hover">
        <tbody>
            {% for post in post_list %}
            <tr>
                <td>
                    {% if post.photo %}
                        <img src="{{ post.photo.url }}" style="width: 100px;" />
                    {% else %}
                        No Photo
                    {% endif %}
                </td>
                <td>
                    {{ post.content }}
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</body>
</html>
<link href ... >: Bootstrap에서 제공하는 탬플릿으로 홈페이지를 방문하면 더 자세한 내용이 나와있습니다.

url: http://127.0.0.1:8000/blog1/

아래처럼 "감을" 이라고 검색하면 content 속성 안에 해당 단어가 있는 데이터를 보여주고, form에는 value="{{ q }}" 라고 지정했기 때문에 텍스트 창에 그대로 값이 남아있게 됩니다.

url: http://127.0.0.1:8000/blog1/?q=감을


장고는 부가 기능을 제공하는 extensions가 있습니다. 그중에서도 QuerySet 을 사용하면서 그 내부의 SQL 쿼리문이 어떤지 보여주는 기능이 있습니다.

pip install django-extensions

그리고 settings.py 파일에 가서 INSTALLED_APPS 에 해당 패키지를 추가해야 합니다.

INSTALLED_APPS = [
    # django apps
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # third apps
    'django_extensions',
    # local apps
    'blog1',
]
pip 다운로드할 때와 다르게 '-"가 아닌 "_" 임에 주의하세요.
python manage.py shell_plus --print-sql -ipython

아래처럼 장고의 기본 QuerySet 을 ipython 터미널에 입력하면 이것의 실행을 위한 SQL 쿼리문이 함께 나타납니다.

Post.objects.all()

QuerySet 에 대해서 정렬이나 범위에 조건을 가할 수도 있지만 파이썬 리스트와 비슷한 것 같으면서도 기본적으로 DB를 다루는 것이다 보니 완전히 동일하지 않습니다.

Post.objects.all().order_by('-id') # id 순으로 정렬(큰 순서대로)
Post.objects.all()[:1]

파이썬 리스트에서는 슬라이싱을 할 때 음수 표시를 할 수도 있고(맨 마지막 원소), [start:end:step] 순서로 step을 지정해주면 마지막에 몇 개씩 뛰어넘을 것인지도 정할 수 있습니다. 하지만 QuerySet 에서는 이러한 문법을 지원하지 않습니다!!

아예 models.py 파일에 Class Meta 를 활용하여 자동 정렬하도록 만들 수도 있습니다.

# models.py
from django.db import models
from .utils import uuid_name_upload_to

# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    photo = models.ImageField(blank=True, upload_to=uuid_name_upload_to)
    is_public = models.BooleanField(default=False, verbose_name='공개여부')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.content
    
    # 정렬 조건 추가
    class Meta:
        ordering = ['-id']
728x90
반응형