Project Portfolio
검색기능 구현하기
츄98
2023. 4. 15. 12:32
그럼 이제 검색기능을 구현해보자!
검색 기능의 아이디어는, 검색키워드를 받고 그 키워드가 담겨있는 게시글을 보여주거나,
키워드를 가진 게시글이 없다면 혹은 검색어를 입력하지 않은 경우에는 메세지를 띄워주는 것으로 구현을 했다.
구체적으로 코드를 보면서 어떻게 구현했는지 살펴보자.
<post views.py>
def search(request):
if request.method == 'GET':
searched = request.GET.get("searched","")
posts = Posting.objects.filter(content__contains=searched).order_by('-created_at')
if posts.exists() == False:
return render(request, 'posting/searched.html', {'searched': searched, 'error': "찾으시는 검색어를 가진 글은 존재하지 않습니다."})
count = posts.count()
page = int(request.GET.get('page','1'))
paginator = Paginator(posts,5)
post_list = paginator.get_page(page)
return render(request, 'posting/searched.html', {'searched': searched, "post_list": post_list, 'count':count})
else:
return render(request, 'posting/searched.html', {})
<코드 자세히 살펴보기>
- posts = Posting.objects.filter(content__contains=searched).order_by('-created_at')
- 최신순으로 정렬되면서, content__contains=searched의 의미는, 게시글에 검색어가 포함되어있을 경우 가지고오라는 의미이다.
- if posts.exists() == False: 만약 검색결과가 존재하지 않는다면, 에러메세지를 보여주었다.
- count = posts.count() : 검색결과의 개수를 세서 보여주었다.
- 검색결과를 보여주는 페이지 역시 페이징을 써서 한 페이지 당 5개의 게시글을 보여주도록 코드를 짰다.
- 페이징과 관련되어서는 이전 글을 참고해주길 바란다!
<base.html>
<form class="d-flex" method="GET" action="{% url 'search' %}">
{% comment %} {% csrf_token %} {% endcomment %}
<input class="form-control me-sm-2" type="search" placeholder="Search" name="searched" aria-label="Search">
<button class="btn btn-secondary my-2 my-sm-0" type="submit">Search</button>
</form>
<searched html>
{% if searched %}
{% if error %}
<div class="alert alert-danger" role="alert">{{ error }}</div>
{% else %}
<h4> 검색하신 {{ searched }} 정보입니다. 총 {{count}}개의 검색결과가 있습니다.</h4>
<p></p>
{% for ps in post_list %}
<div class="col-md-12 mb-2">
<div class="card">
<div class="card-body">
{% if user == ps.author or user.is_superuser %}
<div style="text-align: right">
<a href="/api/posts/delete/{{ ps.id }}">
<span class="badge rounded-pill bg-danger">삭제</span>
</a>
</div>
{% endif %}
<div style="text-align: right">
<a href="/api/posts/{{ ps.id }}">
<span class="badge rounded-pill bg-success">보기</span>
</a>
</div>
<div class="media">
<div class="media-body">
<h5 class="mt-0">Title: {{ ps.title }}</h5>
<p>{{ ps.content|slice:":15"}}...</p>
<p>Category: {{ ps.category }}</p>
</div>
<div style="text-align: right">
<span style="font-size: small">{{ ps.author_name }}-{{ ps.created_at|timesince }} 전</span>
</div>
</div>
</div>
</div>
</div>
{% endfor %}
<!--페이징 처리 코드는 생략-->
{% endif %}
{% else %}
<h3> 검색어를 입력해주세요. </h3>
{% endif %}
<코드 자세히 살펴보기>
- {% if searched %} 검색어가 있다면
- {% else %} 검색어가 없다면<h3> 검색어를 입력해주세요. </h3>
- 검색어가 있지만 검색결과가 없을 경우 {{error}} 를 보여준다.
- views.py 참고: {'searched': searched, 'error': "찾으시는 검색어를 가진 글은 존재하지 않습니다."})
- 만약 로그인한 사용자와 글쓴이가 같을 경우, 혹은 관리자일 경우, 삭제가 가능하게 했다.
{% if user == ps.author or user.is_superuser %}
<div style="text-align: right">
<a href="/api/posts/delete/{{ ps.id }}">
<span class="badge rounded-pill bg-danger">삭제</span>
</a>
</div>
{% endif %}
- 게시글은, 제목과 글 내용, 카테고리, 저자, 시간이 보이도록 했다.
- 이때 글 내용은 15자까지만 보이도록 slice문을 추가했다.
<h5 class="mt-0">Title: {{ ps.title }}</h5>
<p>{{ ps.content|slice:":15"}}...</p>
<p>Category: {{ ps.category }}</p>
검색기능을 하면서 고민했던 부분은, 검색결과를 유지하면서 어떻게 페이징을 구현할 수 있을까 하는 부분이었다.
원래는 검색기능을 post 메서드로 구현했었는데, 페이징과 함께 구현을 하려고 하다보니 get 메소드로 바꾸게 되었다.
get 메소드로 할 경우 쿼리 파라미터로 구현할 수 있기 때문에, 페이지를 넘기더라도 검색결과가 유지될 수 있다.
<div style="text-align:center">
<ul class="pagination">
<div style="width:35%; margin: 5px;">
{% if post_list.has_previous %}
<a class="abutton" href="?searched={{request.GET.searched}}&page=1">맨 앞으로</a>
<a class="abutton" href="?searched={{request.GET.searched}}&page={{ post_list.previous_page_number }}">이전</a>
{% endif %}
</div>
<div style="width:30%; margin: 5px;">
{% for page in post_list.paginator.page_range %}
{% if page >= post_list.number|add:-2 and page <= post_list.number|add:2 %}
<span class="{% if page == post_list.number %}current{% endif %}">
<a href="?searched={{request.GET.searched}}&page={{ page }}">{{ page }}</a>
</span>
{% elif page >= post_list.number|add:-3 and page <= post_list.number|add:3 %}
..
{% endif %}
{% endfor %}
</div>
<div style="width:35%; margin: 5px;">
{% if post_list.has_next %}
<a class="abutton" href="?searched={{request.GET.searched}}&page={{ post_list.next_page_number }}">다음</a>
<a class="abutton" href="?searched={{request.GET.searched}}&page={{ post_list.paginator.num_pages }}">맨 뒤로</a>
{% endif %}
</div>
</ul>
</div>
<코드 자세히 살펴보기>
- <a class="abutton" href="?searched={{request.GET.searched}}&page=1">
- 검색어를 유지하면서 페이징을 구현할 수 있다.
- 나머지 코드들은 페이징 구현하기 이전 게시글에서 충분히 설명했기 때문에 설명을 생략하도록 하겠다.
<Result>
- 검색결과가 없을 때

- 검색결과가 있을 때

- 검색어가 없을 때

- 로그인한 유저가 쓴 글이 검색결과에 있을 때 (삭제 버튼이 보인다.)
