본문 바로가기

PYTHON-BACK

#파이썬 34일차_이커머스 클론코딩5_(1)

728x90

Pystagram Project (4)

1. 글 상세 페이지

  • Post에 대한 상세 페이지 구현
  • Template의 중복된 내용을 제거하는 리팩토링 실행
1.1 글 상세 페이지

1.1.1 기본구조 구현

 

  • View: posts/views.py → post_detail
  • URL: /posts/int:post_id/
  • Template: templates/posts/post_detail.html
  • posts/views.py
def post_detail(request, post_id):
    post = Post.objects.get(id=post_id)
    context = { "post": post }
    return render(request, "posts/post_detail.html", context)
  • posts/urls.py
from posts.views import ..., post_detail

app_name = "posts"
urlpatterns = [
    ...
    path("<int:post_id>/", post_detail, name="post_detail"),
]
  • templates/posts/post_detail.html
{% extends 'base.html' %}

{% block content %}
<div id="post_detail">
    <h1>Post Detail</h1>
</div>
{% endblock %}

1.1.2 Template 내용 구현

  • templates/posts/feeds.html
{% extends 'base.html' %}
{% block content %}
<nav>...</nav>
<div id="feeds" class="post-container">
    {% for post in posts %}
        <article id="post-{{ post.id }}" class="post">
        ...
        </article>
    {% endfor %}
</div>
...
{% endblock %}
  • templates/posts/post_detail.html
    • 피드페이지에서는 for 반복문 안의 < article> 요소가 각각 하나의 Post를 나타냄
    • Post 상세화면에서는 Post Queryset 대신 단일 Post 객체가 전달되며 나머지 모습은 피드페이지와 동일함
{% extends 'base.html' %}

{% block content %}
<div id="feeds" class="post-container">
    <article id="post-{{ post.id }}" class="post">
    ...
    </article>
</div>
{% endblock %}

1.1.3 PostForm 전달

  • posts/views.py
def post_detail(request, post_id):
    post = Post.objects.get(id=post_id)
    comment_form = CommentForm()
    context = {
        "post": post,
        "comment_form": comment_form,
    }
    return render(request, "posts/post_detail.html", context)

1.1.4 {% include %} 태그로 Template 재사용

  • templates/posts/post.html
    • < article> 태그를 post.html로 재사용
<article id="post-{{ post.id }}" class="post">
...
</article>
  • templates/posts/feed.html
{% extends 'base.html' %}
{% block content %}
<nav>...</nav>
<div id="feeds" class="post-container">
    {% for post in posts %}
        {% include 'posts/post.html' %}
    {% endfor %}
</div>
...
{% endblock %}
  • templates/posts/post_detail.html
{% extends 'base.html' %}
{% block content %}
<nav>...</nav>
<div id="feeds" class="post-container">
    {% include 'posts/post.html' %}
</div>
...
{% endblock %}
  • templates/nav.html
    • < nav> 태그의 내용을 별도의 nav.html로 이동
<nav>
    <h1>
        <a href="{% url 'posts:feeds' %}">Pystagram</a>
    </h1>
    <a href="{% url 'posts:post_add' %}">Add post</a>
    <a href="{% url 'users:logout' %}">Logout</a>
</nav>
  • templates/posts/feeds.html, templates/posts/post_detail.html 공통
{% extends 'base.html' %}
{% block content %}
{% include 'nav.html' %}
<div id="feeds" class="post-container">
    {% include 'posts/post.html' %}
</div>
...
{% endblock %}

1.1.5 해시태그 검색결과에 링크 추가

  • templates/posts/tags.html
<div class="post-grid">
    <a href="{% url 'posts:post_detail' post_id=post.id %}">
        <img src="{{ post.postimage_set.first.photo.url }}" alt="">
    </a>
</div>

1.2 글 작성 후 이동할 위치

1.2.1 Post 상세 화면에서 댓글 작성 시 상세화면으로 이동

  • posts/views.py
    • 기존의 댓글 작성 후 redirect
def comment_add(request):
    ...
    if form.is_valid():
        ...
        url_next = reverse("posts:feeds") + f"#post-{comment.post.id}"
        return HttpResponseRedirect(url_next)
  • templates/posts/post_detail.html
    • 댓글은 피드 페이지와 글 상세 페이지 양쪽에서 작성할 수 있음
    • 댓글 작성 완료 후 사용자를 이동시킬 페이지를 각각의 경우에 따라 다르게 지정할 필요가 있음
...
<div id="feeds" class="post-container">
    {% url 'posts:post_detail' post.id as action_redirect_to %}
    {% include 'posts/post.html' with action_redirect_url=action_redirect_to %}
</div>
...
  • templates/posts/post.html
...
<div class="post-comment-create">
    <form method="POST" action="{% url 'posts:comment_add' %}?next={{ action_redirect_url }}">
        {% csrf_token %}
        <input type="hidden" name="post" value="{{ post.id }}">
        {{ comment_form.content }}
        <button type="submit">게시</button>
    </form>
</div>
...
  • posts/views.py
def comment_add(request):
    ...
    if form.is_valid():
        ...
        comment.save()

        # URL로 "next"값을 전달받았다면 댓글 작성 완료 후 전달받은 값으로 이동한다
        if request.GET.get("next"):
            url_next = request.GET.get("next")

        # "next"값을 전달받지 않았다면 피드페이지의 글 위치로 이동한다
        else:
            url_next = reverse("posts:feeds") + f"#post-{comment.post.id}"

        return HttpResponseRedirect(url_next)
  • templates/posts/feeds.html
...
<div id="feeds" class="post-container">
    {% for post in posts %}
        {% with post.id|stringformat:"s" as post_id %}
            {% url 'posts:feeds' as action_redirect_to %}
            {% include 'posts/post.html' with action_redirect_url=action_redirect_to|add:'#post-'|add:post_id %}
        {% endwith %}
    {% endfor %}
</div>
...

1.2.2 Custom Template Filter

  • posts/templatetags/custom_tags.py
from django import template

register = template.Library()

@register.filter
def concat(value, arg):
    return f"{value}{arg}"
  • templates/posts/feeds.html
{% extends 'base.html' %}
{% load custom_tags %}

{% block content %}
{% include 'nav.html' %}
<div id="feeds" class="post-container">
    {% for post in posts %}
        {% url 'posts:feeds' as action_redirect_to %}
        {% include 'posts/post.html' with action_redirect_url=action_redirect_to|concat:'#post-'|concat:post_id %}
    {% endfor %}
</div>
...
{% endblock %}
  • templates/posts/post_detail.htlm
  • 해쉬태그 타고 들어갔을때 이미지 안나오는거 해결
{% extends 'base.html' %}

{% block content %}
{% include 'nav.html' %}
<div id="feeds" class="post-container">
    {% url 'posts:post_detail' post.id as action_redirect_to %}
    {% include 'posts/post.html' with action_redirect_url=action_redirect_to %}
</div>
<script>
    const elms = document.getElementsByClassName('splide')
    for (let i = 0; i < elms.length; i++){
        new Splide(elms[i]).mount();
    }
</script>
{% endblock %}

 

728x90