Back/Django

3. 장고 부트스트랩으로 화면 꾸미기

2023. 4. 19. 10:13
목차
  1. static 폴더 지정
  2. BootStrap 다운로드
  3. 다운 이후
  4. question_list.html파일 수정
  5. 원래 코드
  6. 수정된 코드
  7. question_detail.html 수정
  8. 원래 코드
  9. 수정된 코드
  10. 실행 결과
  11. 내비게이션 바 추가하기
  12. base.html 코드
  13. 상속받는 코드들
  14. 변경된 question_list.html
  15. 실행 결과
  16. 질문 등록 기능 추가하기
  17. 1. question_list.html에 질문등록 버튼 추가
  18. 2. urls.py 설정
  19. 3. mysite/pybo/forms.py 생성
  20. 4. views.py 설정
  21. 5. question_form.html 파일 생성
  22. 실행 결과
  23. forms.py 수정하여 꾸미기
  24. question_form.html 파일 bootstrap 적용
  25. Answer도 form 형식으로 변경하기
  26. 힌트

static 폴더 지정

  • mysite밑에 static 폴더를 생성한 후
  • config 폴더 밑의 settings.py로 이동해 static_dirs 설정을 추가해 준다.

BootStrap 다운로드

  • 구글창에 bootstrap 다운로드를 검색한 후 아래 All release를 클릭한다.

  • stable 한 최신 버전이 이 글을 작성하는 당시에는 v5.2였음.
  • compiled 된 CSS와 JS를 다운로드한다.

다운 이후

  • 압축을 풀고 bootstrap.min.css와 bootstrap.min.js를 static 폴더에 붙여 넣어 준다.

question_list.html파일 수정

  • bootstrap.min.css 파일 적용을 위해 link를 걸어준다.
  • 그 후 css를 적용하기 위해 HTML을 수정한다.

원래 코드

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset='UTF-8'>
    <link rel="stylesheet" type = "text/css" href = {% static 'bootstrap.min.css' %}>
</head>
<body>
    {% if question_list %}
    <ul>
    {% for question in question_list %}
        <!--<li><a href="/pybo/{{ question.id}}">{{question.subject}}</a></li> 원래 코드-->
        <li><a href = "{% url 'pybo:detail' question.id %}">{{ question.subject }}</a></li><!-- 별칭 수정한 코드-->
    {% endfor %}
    </ul>
    {% else %}
    <p>질문이 없습니다.</p>
    {% endif %}
</body>
</html>

수정된 코드

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
</head>
<body>
<div class="container my-3">
    <table class="table">
        <thead>
        <tr class="table-dark">
            <th>번호</th>
            <th>제목</th>
            <th>작성일시</th>
        </tr>
        </thead>
        <tbody>
        {% if question_list %}
        {% for question in question_list %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>
                <a href="{% url 'pybo:detail' question.id %}">{{ question.subject }}</a>
            </td>
            <td>{{ question.create_date }}</td>
        </tr>
        {% endfor %}
        {% else %}
        <tr>
            <td colspan="3">질문이 없습니다.</td>
        </tr>
        {% endif %}
        </tbody>
    </table>
</div>
</body>
</html>

실행 결과

question_detail.html 수정

원래 코드

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>{{ question.subject }}</h1>
    <div>{{ question.content }}</div>

    <hr>
    <h5>{{ question.answer_set.count }}개의 답변이 있습니다.</h5>
    <div>
        <ul>
            {% for answer in question.answer_set.all  %}
                <li>{{ answer.content }}</li>
            {% endfor %}
        </ul>
    </div>
    <hr>
    <form action="{% url 'pybo:answer_create' question.id %}" method="post">
        {% csrf_token %} <!-- CSRF 공격을 막기 위해 명시적으로 넣어줘야 실행됨.-->
        <textarea name="content" id="content" cols="30" rows="10"></textarea>
        <input type="submit" value = "답변등록">
    </form>
</body>
</html>

수정된 코드

{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
<div class="container my-3">
    <!-- 질문 -->
    <h2 class="border-bottom py-2">{{ question.subject }}</h2>
    <div class="card my-3">
        <div class="card-body">
            <div class="card-text" style="white-space: pre-line;">{{ question.content }}</div>
            <div class="d-flex justify-content-end">
                <div class="badge bg-light text-dark p-2">
                    {{ question.create_date }}
                </div>
            </div>
        </div>
    </div>
    <!-- 답변 -->
    <h5 class="border-bottom my-3 py-2">{{question.answer_set.count}}개의 답변이 있습니다.</h5>
    {% for answer in question.answer_set.all %}
    <div class="card my-3">
        <div class="card-body">
            <div class="card-text" style="white-space: pre-line;">{{ answer.content }}</div>
            <div class="d-flex justify-content-end">
                <div class="badge bg-light text-dark p-2">
                    {{ answer.create_date }}
                </div>
            </div>
        </div>
    </div>
    {% endfor %}
    <!-- 답변 등록 -->
    <form action="{% url 'pybo:answer_create' question.id %}" method="post" class="my-3">
        {% csrf_token %}
        <div class="mb-3">
            <label for="content" class="form-label">답변내용</label>
            <textarea name="content" id="content" class="form-control" rows="10"></textarea>
        </div>
        <input type="submit" value="답변등록" class="btn btn-primary">
    </form>
</div>

실행 결과

내비게이션 바 추가하기

  • 내비게이션은 상속 개념으로 앞선 html 파일들에 적용한다.

base.html 코드

  • templates 밑에 생성
{% load static %}
<!doctype html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" type="text/css" href="{% static 'bootstrap.min.css' %}">
    <title>Hello, pybo!</title>
</head>
<body>
<!-- 네비게이션바 -->
<nav class="navbar navbar-expand-lg navbar-light bg-light border-bottom">
    <div class="container-fluid">
        <a class="navbar-brand" href="{% url 'pybo:index' %}">메인페이지</a>
        <button class="navbar-toggler" type="button"
                data-bs-toggle="collapse"
                data-bs-target="#navbarSupportedContent"
                aria-controls="navbarSupportedContent"
                aria-expanded="false"
                aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
                <li class="nav-item">
                    <a class="nav-link" href="#">로그인</a>
                </li>
            </ul>
        </div>
    </div>
</nav>
<!-- 기본 템플릿 안에 삽입될 내용 Start -->
{% block content %}
{% endblock %}
<!-- 기본 템플릿 안에 삽입될 내용 End -->
</body>
</html>
  • 내비게이션을 상속받는 코드들은 block content부터 endblock 사이에 들어가게 된다.
  • 그래서 html, body 태그를 작성하지 않아도 정상 작동됨을 볼 수 있다.

상속받는 코드들

  • extends를 통해 base.html을 상속받고
  • block content와 endblock 사이에 코드를 작성하면 된다.
{% extends 'base.html' %}
{% block content %}

{% endblock %}

변경된 question_list.html

{% extends 'base.html' %}
{% block content %}
<div class="container my-3">
    <table class="table">
        <thead>
            <tr class="table-dark text-center">
                <th>번호</th>
                <th>제목</th>
                <th>작성일시</th>
            </tr>
        </thead>
        <tbody>
        {% if question_list %}
        {% for question in question_list %}
        <tr class="text-center">
            <td>{{ forloop.counter }}</td>
            <td>
                <a href="{% url 'pybo:detail' question.id %}">{{ question.subject }}</a>
            </td>
            <td>{{ question.create_date }}</td>
        </tr>
        {% endfor %}
        {% else %}
        <tr>
            <td colspan="3">질문이 없습니다.</td>
        </tr>
        {% endif %}
        </tbody>
    </table>
</div>
{% endblock %}

실행 결과

  • question_detail.html 파일도 동일한 형태로 수정해 줍니다.

질문 등록 기능 추가하기

1. question_list.html에 질문등록 버튼 추가

  • a태그를 사용하여 버튼을 만들어준다.
<a href="{% url "pybo:question_create" %}" class = "btn btn-primary">질문 등록</a>

2. urls.py 설정

  • a태그를 누르면 이동할 pybo:question_create URL 매핑이 필요하다.
  • 아래 코드 추가
#질문 등록에 대한 URL 매핑
    path("question/create/", views.question_create, name = "question_create"),

3. mysite/pybo/forms.py 생성

  • 폼 형태를 처음 만드는 것이기 때문에 파이썬 코드 생성
  • QuestionForm 클래스는 질문을 등록하기 위해 사용하는 장고의 폼이다.
  • ModelForm 클래스를 상속받아서 만듦.
from django import forms
from .models import Question

# forms 라이브러리의 ModelForm 상속받아서 사용
class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question # models.py에서 정의한 클래스(모델)
        fields = ["subject", "content"] # models.py에서 정의한 필드들(날짜는 입력받는게 아니므로 제외(create_date)

4. views.py 설정

  • POST로 request 요청이 들어오면 DB에 저장하고 Get으로 request요청이 들어오면 단순하게 질문등록 페이지를 요청함.
def question_create(request):
    if request.method =="POST": #post로 들어오면 입력한 값들을 전달받아DB에 저장
        form = QuestionForm(request.POST)
        if form.is_valid():
            question = form.save(commit = False)
            question.create_date = timezone.now()
            question.save()
            return redirect("pybo:index") # main 화면으로 보냄
    else: #Get 요청일 때는 단순하게 질문등록 페이지 요청
        form = QuestionForm()
    context = {"form" : form}
    return render(request, "pybo/question_form.html",context)

5. question_form.html 파일 생성

  • action을 안 쓰는 이유 : 장고의 기능 중 하나로 action을 사용하지 않으면 기본 페이지 URL이 default로 설정된다.
  • form.as_p는 form의 형태를 보고 as_p는 p태그로 만들어 달라는 장고의 명령어임
  • 다만 css 적용에 좋지 않기 때문에 자주 사용되지는 않는다.
{% extends 'base.html' %}
{% block content %}
    <div class = "container">
        <h5 class="my-3 border-bottom pb-2">질문등록</h5>
        <form method = "post"> 
            {% csrf_token %}
            {{ form.as_p }} <!-- form 형태대로 html 코드를 만들어주는 함수-->
            <button type = "submit" class = "btn btn-primary">질문 저장</button>
        </form>
    </div>
{% endblock %}

실행 결과

forms.py 수정하여 꾸미기

from django import forms
from .models import Question

# forms 라이브러리의 ModelForm 상속받아서 사용
class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question # models.py에서 정의한 클래스(모델)
        fields = ["subject", "content"] # models.py에서 정의한 필드들(날짜는 입력받는게 아니므로 제외(create_date)
        widgets = {
            'subject': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10})
        }
        labels = {
            'subject': '제목명',
            'content': '질문의 내용'
        }

question_form.html 파일 bootstrap 적용

  • as_p로 작성한 코드는 지운다.
{% extends "base.html" %}
{% block content %}
    <div class="container">
        <h5 class="my-3 border-bottom pb-2">질문등록</h5>
        <form method="post">
{#        Django에서 form 태그내에서 action 속성을 사용하지 않으면, 기본 페이지 URL이 Default로 설정 #}
{#        <form method="post" action="{% url "pybo:question_create" %}">로 사용해도 되고, 안해도 무관 #}
            {% csrf_token %}
            <!-- 오류표시 Start -->
            {% if form.errors %}
            <div class="alert alert-danger" role="alert">
                {% for field in form %}
                {% if field.errors %}
                <div>
                    <strong>{{ field.label }}</strong>
                    {{ field.errors }}
                </div>
                {% endif %}
                {% endfor %}
            </div>
            {% endif %}
            <!-- 오류표시 End -->
            <div class="mb-3">
                <label for="subject" class="form-label">제목</label>
                <input type="text" class="form-control" name="subject" id="subject"
                       value="{{ form.subject.value|default_if_none:'' }}">
            </div>
            <div class="mb-3">
                <label for="content" class="form-label">내용</label>
                <textarea class="form-control" name="content"
                          id="content" rows="10">{{ form.content.value|default_if_none:'' }}</textarea>
            </div>
            <button type="submit" class="btn btn-primary">질문저장</button>
        </form>
    </div>
{% endblock %}
  • HTML 수정 코드는 에러 시에 오류표시를 함을 볼 수 있다.

Answer도 form 형식으로 변경하기

  • question의 form 형식을 만드는 것과 동일
  • 한번 스스로 만들어 보시길 바랍니다.

힌트

  1. forms.py에서 AnswerForm 클래스가 필요합니다.
  2. views.py에서 answer_create함수를 변경해 주어야 합니다.
  3. question_detail.html 코드를 수정하여 에러 출력부를 붙여 넣어야 합니다.

'Back > Django' 카테고리의 다른 글

4. 게시판 페이징 기능 추가하기  (0) 2023.04.19
3.5 Django 흐름 정리  (0) 2023.04.19
2. 장고의 기본 요소 Admin, 렌더링, 동적URL매핑,DB연동  (0) 2023.04.13
1. 장고의 기본 요소 Url, Database 연동, ORM  (0) 2023.04.13
0. 장고 개발 준비  (0) 2023.04.12
  1. static 폴더 지정
  2. BootStrap 다운로드
  3. 다운 이후
  4. question_list.html파일 수정
  5. 원래 코드
  6. 수정된 코드
  7. question_detail.html 수정
  8. 원래 코드
  9. 수정된 코드
  10. 실행 결과
  11. 내비게이션 바 추가하기
  12. base.html 코드
  13. 상속받는 코드들
  14. 변경된 question_list.html
  15. 실행 결과
  16. 질문 등록 기능 추가하기
  17. 1. question_list.html에 질문등록 버튼 추가
  18. 2. urls.py 설정
  19. 3. mysite/pybo/forms.py 생성
  20. 4. views.py 설정
  21. 5. question_form.html 파일 생성
  22. 실행 결과
  23. forms.py 수정하여 꾸미기
  24. question_form.html 파일 bootstrap 적용
  25. Answer도 form 형식으로 변경하기
  26. 힌트
'Back/Django' 카테고리의 다른 글
  • 4. 게시판 페이징 기능 추가하기
  • 3.5 Django 흐름 정리
  • 2. 장고의 기본 요소 Admin, 렌더링, 동적URL매핑,DB연동
  • 1. 장고의 기본 요소 Url, Database 연동, ORM
잘잔디
잘잔디
4학년이 되고 취업 준비를 위해 2023-01-01부터 공부한 내용을 정리한 블로그입니다.
잘잔디
MBCS 공부일지
잘잔디
전체
오늘
어제
  • 분류 전체보기 (217)
    • 파이썬 (28)
      • 파이썬 이론 (8)
      • NumPy (3)
      • Pandas (6)
      • 파이썬 시각화 (8)
      • 응용 (2)
    • Java (3)
    • Back (38)
      • DataBase이론 (12)
      • MySQL (2)
      • JSP (8)
      • JSTL (2)
      • Spring (0)
      • Django (8)
      • MongoDB (6)
      • FastAPI (0)
    • Front (8)
      • HTML (3)
      • CSS (2)
      • JS (1)
    • 회고록 (10)
    • 알고리즘 문제 풀이 (95)
      • 알고리즘 이론 공부 (14)
      • 프로그래머스 (69)
      • 백준 (12)
    • 머신러닝 (0)
    • 딥러닝 (0)
    • Git (3)
    • R 프로그래밍 (3)
    • 빅데이터 관리 (16)
      • 리눅스 (4)
      • Hadoop (12)
    • AWS (2)
    • 일상 (10)
      • 책 리뷰 (5)
      • TOEIC (2)
      • 자잘하게 공부한 것들 (2)
    • 사이버보안 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • playdata
  • web
  • 골드
  • JavaScript
  • git
  • CSS
  • 즐거웠다
  • Java
  • HTML
  • Encore
  • 백준
  • JS
  • 객체지향
  • 이중우선순위 큐
  • 독산역
  • db
  • Database
  • backend
  • OOP

최근 댓글

최근 글

hELLO · Designed By 정상우.
잘잔디
3. 장고 부트스트랩으로 화면 꾸미기
상단으로

티스토리툴바

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.