Do It Django Template Review

์ด์ •์—ฐยท2022๋…„ 10์›” 6์ผ
0

Django

๋ชฉ๋ก ๋ณด๊ธฐ
10/12

๐Ÿ”ง base.html

CODE

<!DOCTYPE html>
{% load static %}
<html lang="ko">
    <head>
        <title>{% block head_title %}Blog{% endblock %}</title>
        <link rel="stylesheet" href="{% static 'blog/bootstrap/bootstrap.min.css' %}" media="screen">
        <script src="https://kit.fontawesome.com/7738fa2555.js" crossorigin="anonymous"></script>
    </head>
    <body>

        {% include 'blog/navbar.html' %}

          <div class="container my-3">
            <div class="row">
                <div class="col-md-8 col-lg-9" id="main-area">
                    {% block main_area %}
                    {% endblock %}
                </div>

                <div class="col-md-4 col-lg-3">
                    <!-- Search widget-->
                    <div class="card mb-4">
                        <div class="card-header">Search</div>
                        <div class="card-body">
                            <div class="input-group">
                                <input class="form-control" type="text" placeholder="Enter search term..." aria-label="Enter search term..." id="search-input" aria-describedby="button-search" />
                                <button class="btn btn-primary" id="button-search" type="button" onclick="searchPost();">Go!</button>
                            </div>
                        </div>
                    </div>
                    <!-- Categories widget-->
                    <div class="card mb-4" id="categories-card">
                        <div class="card-header">Categories</div>
                        <div class="card-body">
                            <div class="row">
                                    <ul>

                                        {% for category in categories %}
                                        <li>
                                            <a href="{{ category.get_absolute_url }}">{{ category }} ({{ category.post_set.count }})</a>
                                        </li>
                                        {% endfor %}
                                        <li>
                                            <a href="/blog/category/no_category/">๋ฏธ๋ถ„๋ฅ˜ ({{ no_category_post_count }})</a>
                                        </li>
                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
          </div>

        <script>
            function searchPost(){
                let searchValue = document.getElementById('search-input').value.trim();
                if (searchValue.length > 1){
                    location.href="/blog/search/" + searchValue + "/";
                }
                else {
                    alert('๊ฒ€์ƒ‰์–ด๊ฐ€ ๋„ˆ๋ฌด ์งง์Šต๋‹ˆ๋‹ค.');
                }
            };

            document.getElementById('search-input').addEventListener('keyup',function(event)
            {
                if(event.key === 'Enter'){
                    searchPost();
                }
            });
        </script>

          <!-- Footer-->
        {% include 'blog/footer.html' %}

          <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/js/bootstrap.min.js" integrity="sha384-w1Q4orYjBQndcko6MimVbzY0tgp4pWB4lZ7lr30WKz0vr/aWKhXdBNmNb5D92v7s" crossorigin="anonymous"></script>
    </body>
</html>

๋ชจ๋“ˆํ™”

  • base.html์€ ๋ชจ๋“ˆํ™”๋ฅผ ์œ„ํ•œ ํ…œํ”Œ๋ฆฟ
  • ๋ชจ๋“ˆํ™”๋Š” ๋ฉ”์ธ ์˜์—ญ์„ ์ œ์™ธํ•˜๊ณ  ๋‚˜๋จธ์ง€ ์˜์—ญ์˜ ๋””์ž์ธ์ด ์ผ๊ด€๋˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ
  • ํ•ด๋‹น ํ…œํ”Œ๋ฆฟ์„ ๋‹ค๋ฅธ ํ…œํ”Œ๋ฆฟ์—์„œ ๊ฐ€์ ธ๋‹ค ์“ฐ๊ธฐ ์œ„ํ•ด ์ œ์ž‘
  • ์ œ์ž‘ ํ•˜๋ ค๋Š” ์›น์˜ ๊ธฐ์ดˆ ๊ณตํ†ต ๋””์ž์ธ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋จ

๋ฒ ์ด์Šค.html

{% block ๋ธ”๋ก๋ช… %}
{% endblock %}

ํ™•์žฅ.html

{% extends 'blog/base.html' %}

{% block ๋ธ”๋ก๋ช… %}
{% endblock %}

๋ฒ ์ด์Šค.html์˜ ๋ธ”๋ก๋ช…์— ํ™•์žฅ.html์˜ ๋ธ”๋ก๋ช…์ด ์‚ฝ์ž…๋œ๋‹ค.


๐Ÿ“„ post_list.html

CODE

{% extends 'blog/base.html' %}

{% block main_area %}
    {% if user.is_authenticated%}
        {% if user.is_superuser or user.is_staff %}
            <a class="btn btn-info btn-sm float-right" href="/blog/create_post/" role="button"><i class="fas fa-pen"></i>&nbsp;&nbsp;New Post</a>
        {% endif %}
    {% endif %}

    <h1>
        Blog
        {% if search_info %}<small class="text-muted">{{ search_info }}</small>{% endif %}
        {% if category %}<span class="badge badge-secondary">{{ category }}</span>{% endif %}
        {% if tag %}<span class="badge badge-light"><i class="fas fa-tags"></i>{{ tag }} ({{ tag.post_set.count }})</span>{% endif %}
    </h1>

    {% if post_list.exists %}

        {% for p in post_list %}
            <!-- Blog Post -->
            <div class="card mb-4" id="post-{{ p.pk }}">
                {% if p.head_image %}
                    <img class="card-img-top" src="{{ p.head_image.url }}" alt="{{ p }} head image">
                {% else %}
                    <img class="card-img-top" src="https://picsum.photos/seed/{{ p.id }}/800/200" alt="random_image">
                {% endif %}
                <div class="card-body">
                    {% if p.category %}
                        <span class="badge badge-secondary float-right">{{ p.category }}</span>
                    {% else %}
                        <span class="badge badge-secondary float-right">๋ฏธ๋ถ„๋ฅ˜</span>
                    {% endif %}

                    <h2 class="card-title">{{ p.title}}</h2>
                    {% if p.hook_text %}
                        <h5 class="text-muted">{{ p.hook_text }}</h5>
                    {% endif %}
                    <p class="card-text">{{ p.get_content_markdown | truncatewords_html:45 | safe }}</p>

                    {% if p.tags.exists %}
                        <i class="fas fa-tags"></i>
                        {% for tag in p.tags.iterator %}
                            <a href="{{ tag.get_absolute_url }}"><span class="badge badge-pill badge-light">{{ tag }}</span></a>
                        {% endfor %}
                        <br/>
                        <br/>
                    {% endif %}

                    <a href="{{ p.get_absolute_url }}" class="btn btn-primary">Read More &rarr;</a>
                </div>
                <div class="card-footer text-muted">
                    Posted on {{ p.created_at}} by
                    <a href="#">{{ p.author | upper }}</a>
                </div>
            </div>
        {% endfor %}

    {% else %}
        <h3>์•„์ง ๊ฒŒ์‹œ๋ฌผ์ด ์—†์Šต๋‹ˆ๋‹ค.</h3>
    {% endif %}

    {% if is_paginated %}
        <!-- Pagination -->
        <ul class="pagination justify-content-center mb-4">
            {% if page_obj.has_next %}
                <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.next_page_number }}">&larr; Older</a>
                </li>
            {% else %}
                <li class="page-item disabled">
                    <a class="page-link" href="#">&larr; Older</a>
                </li>
            {% endif %}

            {% if page_obj.has_previous %}
                <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Newer &rarr;</a>
                </li>
            {% else %}
                <li class="page-item disabled">
                    <a class="page-link" href="#">Newer &rarr;</a>
                </li>
            {% endif %}
        </ul>
    {% endif %}
{% endblock %}
  • ์งˆ๋ฌธ: user๋Š” ์žฅ๊ณ  ๋‚ด์žฅ ํ•จ์ˆ˜์ธ์ง€?
  • base.html ํ™•์žฅ

ํ—ค๋“œ

  • ๋กœ๊ทธ์ธ์‹œ ํฌ์ŠคํŠธ ์ƒ์„ฑ ๋ฒ„ํŠผ์ด ๋ณด์ด๋„๋ก ๋””์ž์ธ
  • ๊ฒ€์ƒ‰์‹œ ๊ฒ€์ƒ‰์–ด์™€ ๊ฐœ์ˆ˜๋ฅผ ์ถœ๋ ฅ
  • ์นดํ…Œ๊ณ ๋ฆฌ์™€ ํƒœ๊ทธ๊ฐ€ ์žˆ์„์‹œ ์ด๋ฅผ ๋ณด์—ฌ์คŒ

ํฌ์ŠคํŠธ ๋‚ด์šฉ

  • ๊ฒŒ์‹œ๋ฌผ์ด ์žˆ์„๊ฒฝ์šฐ ์ธ๋„ค์ผ, ์นดํ…Œ๊ณ ๋ฆฌ, ํƒœ๊ทธ, READ MORE ๋ฒ„ํŠผ
  • READ MORE ๋ˆ„๋ฅด๋ฉด post_detail.html๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ
  • ๊ฒŒ์‹œ๋ฌผ์ด ์—†์œผ๋ฉด "์•„์ง ๊ฒŒ์‹œ๋ฌผ์ด ์—†์Šต๋‹ˆ๋‹ค." ์ถœ๋ ฅ

๐Ÿ…ฐ post_detail.html

CODE

{% extends 'blog/base.html' %}
{% load crispy_forms_tags %}

{% block head_title %}
    {{ post.title }} - Blog
{% endblock %}


{% block main_area %}
    <div id="post-area">
        {% if post.category %}
            <span class="badge badge-secondary float-right">{{ post.category }}</span>
        {% else %}
            <span class="badge badge-secondary float-right">๋ฏธ๋ถ„๋ฅ˜</span>
        {% endif %}

        <!-- Title -->
        <h1 class="mt-4">{{ post.title }}</h1>
        <h5 class="text-muted">{{ post.hook_text }}</h5>
        <!-- Author -->
        <p class="lead">
            by
            <a href="#">{{ post.author | upper }}</a>
        </p>

        <hr>

        {% if user.is_authenticated and user == post.author %}
            <a class="btn btn-info btn-sm float-right" href="/blog/update_post/{{ post.pk }}/" role="button"><i class="fas fa-pen"></i>  Edit Post</a>
        {% endif %}

        <!-- Date/Time -->
        <p>Posted on {{ post.created_at }}</p>

        <hr>

        <!-- Preview Image -->
        {% if post.head_image %}
            <img class="img-fluid rounded" src="{{ post.head_image.url }}" alt="{{ post.title }} head_image">
        {% else %}
            <img class="img-fluid rounded" src="https://picsum.photos/seed/{{ post.id }}/800/200" alt="random_image">
        {% endif %}


        <hr>

        <!-- Post Content -->
        <p>{{ post.get_content_markdown | safe }}</p>

        {% if post.tags.exists %}
            <i class="fas fa-tags"></i>
            {% for tag in post.tags.all %}
                <a href="{{ tag.get_absolute_url }}" class="badge badge-light">{{ tag }}</a>
            {% endfor %}
            <br/>
            <br/>
        {% endif %}


        {% if post.file_upload %}
            <a href="{{ post.file_upload.url }}" class="btn btn-outline-dark" role="button" download>
                Download:

                {% if post.get_file_ext == 'csv' %}
                    <i class="fas fa-file-csv"></i>
                {% elif post.get_file_ext == 'xlsx' or post.get_file_ext == 'xls' %}
                    <i class="fas fa-file-excel"></i>
                {% elif post.get_file_ext == 'docx' or post.get_file_ext == 'doc' %}
                    <i class="fas fa-file-word"></i>
                {% else %}
                    <i class="far fa-file"></i>
                {% endif %}

                {{ post.get_file_name }}
            </a>
        {% endif %}

        <hr>
    </div>

    <div id="comment-area">
        <!-- Comments Form -->
        <div class="card my-4">
            <h5 class="card-header">Leave a Comment:</h5>
            <div class="card-body">
                {% if user.is_authenticated %}
                    <form id="comment-form" method="POST" action="{{ post.get_absolute_url }}new_comment/">
                        {% csrf_token %}
                        <div class="form-group">
                            {{ comment_form | crispy }}
                        </div>
                        <button type="submit" class="btn btn-primary">Submit</button>
                    </form>
                {% else %}
                    <a role="button" class="btn btn-outline-dark btn-block btn-sm" href="#" data-toggle="modal" data-target="#loginModal">Log in and leave a comment</a>
                {% endif %}
            </div>
        </div>

        {% if post.comment_set.exists %}
            {% for comment in post.comment_set.iterator %}
                <!-- Single Comment -->
                <div class="media mb-4" id="comment-{{ comment.pk }}">
                    <img class="d-flex mr-3 rounded-circle" src="{{ comment.get_avatar_url }}" alt="{{ comment.author }}" width="60px">
                    <div class="media-body">
                        {% if user.is_authenticated and comment.author == user %}
                            <div class="float-right">
                                <a role="button"
                                   class="btn btn-sm btn-info"
                                   id="comment-{{ comment.pk }}-update-btn"
                                   href="/blog/update_comment/{{ comment.pk }}/">
                                    edit
                                </a>
                                <a role="button"
                                   href="#"
                                   id="comment-{{ comment.pk }}-delete-modal-btn"
                                   class="btn btn-sm btn-danger"
                                   data-toggle="modal" data-target="#deleteCommentModal-{{ comment.pk }}">
                                    delete
                                </a>
                            </div>

                            <!-- Modal -->
                            <div class="modal fade" id="deleteCommentModal-{{ comment.pk }}" tabindex="-1" role="dialog" aria-labelledby="deleteCommentModalLabel" aria-hidden="true">
                                <div class="modal-dialog" role="document">
                                    <div class="modal-content">
                                        <div class="modal-header">
                                            <h5 class="modal-title" id="deleteModalLabel">Are You Sure?</h5>
                                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                                <span aria-hidden="true">&times;</span>
                                            </button>
                                        </div>
                                        <div class="modal-body">
                                            <del>{{ comment | linebreaks }}</del>
                                        </div>
                                        <div class="modal-footer">
                                            <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
                                            <a role="button" class="btn btn-danger" href="/blog/delete_comment/{{ comment.pk }}/">Delete</a>
                                        </div>
                                    </div>
                                </div>
                            </div>

                        {% endif %}
                        <h5 class="mt-0">
                            {{ comment.author.username }}
                            &nbsp;&nbsp;<small class="text-muted">{{ comment.created_at }}</small>
                        </h5>
                        <p>{{ comment.content | linebreaks }}</p>
                        {% if comment.created_at != comment.modified_at %}
                            <p class="text-muted float-right"><small>Updated: {{ comment.modified_at }}</small></p>
                        {% endif %}
                    </div>
                </div>
            {% endfor %}
        {% endif %}
    </div>
{% endblock %}

Comment Form

  • POST ๋ฐฉ์‹์œผ๋กœ ์ „์†กํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”๋””์— ๋‚ด์šฉ๋“ค์„ ์ ์–ด์„œ ๋ณด๋ƒ„

csrf_token

  • CSRF = Cross Site Request Forgery
  • ์ง์—ญํ•˜๋ฉด ์ข€ ์ด์ƒํ•จ ์‚ฌ์ดํŠธ๋ฅผ ํšก๋‹จํ•ด์„œ ์œ„์กฐ๋ฅผ ์š”์ฒญํ•œ๋‹ค??? ...
  • ์˜๋ฏธ๋ฅผ ๋‹ค์‹œ ๊ณฑ์”น์–ด๋ณด๋ฉด ์ข€ ๋ง์ด ๋˜๋Š” ๊ฒƒ ๊ฐ™๊ธฐ๋„ ...
  • ์‰ฝ๊ฒŒ ๋งํ•ด ์‚ฌ์šฉ์ž์˜ ์˜๋„์™€ ์ƒ๊ด€ ์—†์ด ํ–‰ํ•ด์ง€๋Š” ๊ณต๊ฒฉ์ด๋‹ค.
  • ๋” ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด ๊ทธ๋ƒฅ ํ•ดํ‚น
  • POST ํ†ต์‹ ์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ฐ˜๋“œ์‹œ csrf ๋ฐฉ์–ด ์ฝ”๋“œ๋ฅผ ์ ์–ด์ค˜์•ผ ํ•จ

0๊ฐœ์˜ ๋Œ“๊ธ€