본문 바로가기

Django

20210204_ Django 게시판 리스트 화면, 글쓰기 화면, 글 보기 화면 만들기, template language, form

Django 게시판 만들기


게시판 리스트 화면 만들기

기본 html 화면 구성만들고 연결

  • base.html 가져오기

  • base.html
{% raw %}
<html>
<head>
        <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> -->
    <!-- bootstrap에서 다운받은 stylesheet 를 연결시켜 적용 -->
    <link rel="stylesheet" href="/static/bootstrap.min.css" />
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
</head>
<body>
    <div class="container">
        {% block contents %}
        {% endblock %}
    </div>
</body>
</html>
{% endraw %}

  • board_list.html

  • 테이블 요소의 태그

  • 기본적인 틀만 만들어 둔 상태

{% raw %}
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <table class="table table-light">
            <thead class="thead-light">
                <tr>
                    <th>#</th>
                    <th>제목</th>
                    <th>아이디</th>
                    <th>일시</th>
                </tr>
            </thead>
            <tbody class="text-dark">
                <tr>
                    <th>1</th>
                    <td>테스트 제목</td>
                    <td>테스트 아이디</td>
                    <td>2021-02-04 21:11:00</td>
                </tr>
            </tbody>
        </table>
    </div>
</div>
<!-- 글쓰기 버튼 -->
<div class="row">
    <div class="col-12">
        <buttton class="btn btn-primary">글쓰기</buttton>
    </div>
</div>
{% endblock %}
{% endraw %}


  • 테스트를 위한 간단한 views 생성
from django.shortcuts import render
# Create your views here.


def board_list(request):
    return render(request, 'board_list.html')
  • 테스트를 위한 urls 연결
  • 프로젝트 urls.py
from django.contrib import admin
from django.urls import path, include
from fcuser.views import home

urlpatterns = [
    path('admin/', admin.site.urls),
    path('fcuser/', include('fcuser.urls')),
    path('board/', include('board.urls')),
    path('', home)
]
  • board urls.py
from django.urls import path
from.import views

urlpatterns = [
    path('list/', views.board_list),
]
  • python manage.py runserver를 통해서 확인해 보기


models 만들기

  • models.py

  • Django reference - models.Field options

  • models.Foreignkey(to, on_delete, **options) : to 옵션에 해당하는 모델클래스의 key값을 받아 올수 있음, on_delete 옵션을 통해서 받아오는 모델 클래스 삭제시 해당 모델에서도 삭제할지 어떻게 할지 설정 가능

  • 전에 했던것 처럼 Meta 클래스를 통해서 테이블 이름 지정 가능

from django.db import models

# Create your models here.


class Board(models.Model):
    title = models.CharField(max_length=128, verbose_name='제목')
    contexts = models.TextField(verbose_name='내용')
    writer = models.ForeignKey(
        'sl_user.Sl_user', on_delete=models.CASCADE, verbose_name='작성자')
    # on_delete=models.CASCADE 는 models의 key가 삭제 되면 해당 모델도 지우겠다.
    registered_dttm = models.DateTimeField(
        auto_now_add=True, verbose_name='등록시간')

    def __str__(self):
        return self.title

    class Meta:
        db_table = 'slowbuscamp_board'
        verbose_name = '슬로우버스캠프 게시글'  # 기본적으로 복수형을 지원하기 때문에 s가 자동으로 붙게 되어 있음
        verbose_name_plural = '슬로우버스캠프 게시글'  # 복수형이름을 따로 지정하여 자동으로 붙는 s를 제거 가능
  • 만들고 migrations , migrate 를 해야 반영됨


admin 연결

from django.contrib import admin
from .models import Board
# Register your models here.


class BoardAdmin(admin.ModelAdmin):
    list_display = ('title', 'writer', 'registered_dttm')


admin.site.register(Board, BoardAdmin)  # 연결


view 만들기

from django.shortcuts import render
from .models import Board
# Create your views here.


def board_list(request):
    # all()은 모든 오브젝트를 가져오고 order_by는 정렬인데 id필드를 기준으로  '-'는 역순을 의미 즉, 최신것으로 가져오겠다.
    boards = Board.objects.all().order_by('-id')
    return render(request, 'board_list.html', {'boards': boards})
    # board_list.html의 boards context 를 key로 하여 board_list request에서 지정한 boards 변수를 value로 한다.
    # 즉, models에서 가져온 것을 html에 넣겠다.


html 데이터 표현형식 변환하기 (view를 통해서 가져온 models를 표시하기 위해서)

  • board_list.html
  • The Django template language
  • views에서 변수를 끌어와서 for, if등 자유롭게 사용 가능
  • 여기에서는 views에서 render로 설정한 boards를 끌어와서 for문에서 board 변수로 하나씩 꺼내서 반복
  • 그래서 {{}}를 통해서 포맷팅시 .을 통해서 field를 지정할 수도 있음
{% raw %}
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <table class="table table-light">
            <thead class="thead-light">
                <tr>
                    <th>#</th>
                    <th>제목</th>
                    <th>아이디</th>
                    <th>일시</th>
                </tr>
            </thead>
            <tbody class="text-dark">
                {% for board in boards %}
                <tr>
                    <th>{{ board.id }}</th>
                    <td>{{ board.title }}</td>
                    <td>{{ board.writer }}</td>
                    <td>{{ board.registered_dttm }}</td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>
<!-- 글쓰기 버튼 -->
<div class="row">
    <div class="col-12">
        <buttton class="btn btn-primary">글쓰기</buttton>
    </div>
</div>
{% endblock %}
{% endraw %}

게시판 글쓰기 화면 만들기

form class 만들기 (forms.py)

  • 게시판에 들어갈 부분은 제목, 내용만 있으면 됨
  • widget 옵션의 경우 html input 태그를 생성하는데 옵션값을 무엇을 주느냐에 따라 input되는 type이 바뀐다. (widget의 속성은 form field의 속성에 따라 맞춤, 그리고 form field는 model field의 속성에 따라 맞춤)
  • forms.Textarea로 설정하여 내용을 쓸수 있는 기능을 실현(일반 text 형식과 다르게 긴 내용으로 여러줄 입력 가능 )
from django import forms


# 입력 받을 값은 제목과 내용 두개
class BoardForm(forms.Form):
    title = forms.CharField(error_messages={
        'required': '제목을 입력해 주세요.'
    }, max_length=128, label="제목")
    contents = forms.CharField(error_messages={
        'required': '내용을 입력해 주세요.'
    }, widget=forms.Textarea, label="내용")

view 만들기 (views.py)

  • 만들어 놓은 form 형식을 view를 통해서 html과 조합해서 렌더링 시킬수 있게 먼저 작업함 return render(request, 'board_write.html', {'form': form})
  • 글을 입력하고 POST 요청시 글이 잘써 졌는지 체크하는 로직을 작성
  • 입력 값이 제대로됬으면 세션을 받아 데이터베이스에서 작성자 이름을 찾아 가져오는 로직 작성
  • 해당 값을 model에 접근하여 database에 저장하는 로직 작성(저장할 값은 로그인된 사람의 작성자, 제목, 내용)
from django.shortcuts import render, redirect
from sl_user.models import Sl_user
from .models import Board
from .forms import BoardForm

def board_write(request):
    if request.method == "POST": # POST 요청 조건
        form = BoardForm(request.POST) 
        # form 변수에 BoardForm 폼 클래스에 POST 값을 적용한 폼 객체를 할당 
        if form.is_valid(): # form 변수 타당성 검증
            user_id = request.session.get('user') 
            # user에 해당하는 세션 값(user의 그냥 id값) 가져와서 user_id에 할당 
            sl_user = Sl_user.objects.get(pk=user_id)
            # Sl_user 모델 객체에서 pk가 user_id인 것을 가져와 sl_user에 할당 
            board = Board() # Board 모델을 board 변수에 할당하여 생성
            board.title = form.cleaned_data['title']
            # form에서 검증된 데이터의 title 필드의 값을 board모델 title 필드에 할당
            board.contents = form.cleaned_data['contents']
            # form에서 검증된 데이터의 contents 필드의 값을 board모델 contents 필드에 할당

            board.writer = sl_user # sl_user 모델 자체는 name속성이라서 name을 board모델 writer필드에 할당
            board.save() # board모델 SQL INSERT

            return redirect('/board/list/')
            # 저장후 list 페이지로 이동

    else:
        form = BoardForm() # POST 요청 하지 않으면 form에 BoardForm 폼 생성할당

    return render(request, 'board_write.html', {'form': form})
    # board_write.html 파일에 form 을 form 변수로 렌더링


html 만들기 (board_write.html)

  • view에서 렌더링 요청한 form을 받아서 html 만들기
  • label 태그의 경우 for와 요소의 id를 일치시켜 결합하게 함 Django Working with forms
    • 그래서 요소에서의 id, label의 for에 {{ 필드변수.id_for_label }} 를 넣어 일치 시켜 주는데 html 결과는 id_필드명으로 나타난다.
    • {{ 필드변수.label }}의 경우에는 그냥 필드의 labe 이름을 불러옴
  • {{ field.field }} 의 경우 field라는 필드 변수의 필드 속성에 접근할수 있다.
  • {{ field.field.widget.input_type }} 을 요소의 type에 포맷팅 하여 넣을수 있다.
  • {% ifequal a b %} ... {% endifequal %}{% if a == b %} ... {% endif %} 을 쓴것과 같다.
{% raw %}
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12">
        <form method="POST", action=".">
            {% csrf_token %}
            {% for field in form %}
            <!-- form이라는 폼객체를 받아서 field라는 변수로 필드 객체를 하나씩 반복 -->
            <div class="form-group">
                <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                {{ field.field.widget.name}}
                {% ifequal field.name 'contents' %}
                <textarea class="form-control" name="{{ field.name }}" placeholder="{{ field.label }}"></textarea>
                {% else %}
                <input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}">
                {% endifequal %}
            </div>
            {% if field.errors %}
            <span style="color: red">{{ field.errors }}</span>
            {% endif %}
            {% endfor %}
            <button type="submit" class="btn btn-primary">글쓰기</button>
        </form>
    </div>
</div>
{% endblock %}
{% endraw %}



  • url 연결하기(board 앱 urls.py)
from django.urls import path
from.import views

urlpatterns = [
    path('list/', views.board_list),
    path('write/', views.board_write),
]

게시판 글 상세보기 화면 만들기

view 만들기

  • 상세 페이지의 경우에는 각 글마다 다르게 열려야 함으로 pk값을 더 받아야 한다.
  • pk 값의 경우 model에서 self를 title로 해 놓았기 때문에 title을 기준으로 pk가 생기는 것이다.
from .models import Board
from django.shortcuts import render, redirect

def board_detail(request, pk): # pk 값을 받는다는 것 선언
    board = Board.objects.get(pk=pk) # board 변수(인스턴스)에 모델의pk 값이 받아오는 pk값과 같은 경우의 Board 모델 객체를 할당
    return render(request, 'board_detail.html', {'board': board})
    # board 를 대체하여 렌더링 표시 할수 있게

html 만들기

  • 표시될 정보는 제목과 내용 뿐이고 읽기 전용
  • post를 보내지 않으므로 보안 토큰이 필요 없고, 폼도 필요없다.
    • 폼이 필요 없으므로 폼과 연결시킬 필요 없이 여러 내용은 default 값으로 해 놓아도 된다.
  • 그냥 모델의 정보를 html과 연결시켜 보여주기만 하면 되고 세션도 필요 없다.
  • input요소에서 value 옵션에 해당 pk board모델의 title 필드 값을 포맷팅 하게 한다. readonly옵션을 통해서 읽기 전용으로 한다.
  • textarea 요소의 경우에는 contents 필드 값을 포맷팅
{% raw %}
{% extends "base.html" %}

{% block contents %}
<div class="row mt-5">
    <div class="col-12">
            <div class="form-group">
                <label for="title">제목</label>
                <input type="text" class="form-control" id="title" value="{{ board.title }}" readonly>
                <label for="contents">내용</label>
                <textarea class="form-control" readonly>{{ board.contents }}</textarea>
            </div>
            <button class="btn btn-primary">목록</button>
    </div>
</div>
{% endblock %}
{% endraw %}

url 연결하기

  • view pk 설정(하위 주소에 <int:pk>/를 넣어서 pk를 숫자 형태로 넣게 조건 설정 가능)
from django.urls import path
from.import views

urlpatterns = [
    path('list/', views.board_list),
    path('detail/<int:pk>/', views.board_detail),
    path('write/', views.board_write),
]




  • 참고로 github markdown 문서 사용시 liquid tag {{% %}}는 코드블럭에서 표현되지 않는다. 방법으로는 liquid rawtag로 감싸는 것이라고 한다. {{% raw %}}, {{% endraw %}}로 감싸주면 된다고 하는데 테스트 중이다. 그래서 tistory의 경우 귀찮아서 그냥 코드블럭안에는 rawtag있는것이다.