본문 바로가기
코딩 연습/코딩배우기

[python] 장고(django)로 게시판 만들어보기

by good4me 2021. 10. 11.

goodthings4me.tistory.com

 

[python] 장고(django)로 게시판 만들어보기

MariaDB와 파이썬 장고(django) 프레임워크를 이용한 간단한 게시판 만들기

 

◆ 작업할 디렉토리 만들기

- 탐색기에서 장고 게시판 코딩을 할 디렉토리(djangoBoard)를 만들고,
- VS Code를 실행한 후 '폴더열기'로 해당 폴더를 연다.

vs code 폴더열기

 

디렉토리 선택

 

good4me.co.kr

 

◆ 가상환경 설정 및 장고 설치

## 가상환경 설정
C:\Users\xxxxxx\django\djangoBoard>python -m venv venv

## 파이썬 버전 확인
C:\Users\xxxxxx\django\djangoBoard>python -V
Python 3.8.8

## 가상환경 실행
C:\Users\xxxxxx\django\djangoBoard>cd venv/Scripts
(venv) C:\Users\xxxxxx\django\djangoBoard\venv\Scripts>activate
(venv) C:\Users\xxxxxx\django\djangoBoard\venv\Scripts>cd..
(venv) C:\Users\xxxxxx\django\djangoBoard\venv>cd..

## 장고 버전 확인
(venv) C:\Users\xxxxxx\django\djangoBoard>django-admin --version
3.1

## 장고 설치 (버전 3.2)
(venv) C:\Users\xxxxxx\django\djangoBoard>pip install django==3.2
Collecting django==3.2
  Using cached Django-3.2-py3-none-any.whl (7.9 MB)
Collecting pytz
  Using cached pytz-2021.1-py2.py3-none-any.whl (510 kB)
Collecting sqlparse>=0.2.2
  Using cached sqlparse-0.4.2-py3-none-any.whl (42 kB)
Collecting asgiref<4,>=3.3.2
  Using cached asgiref-3.4.1-py3-none-any.whl (25 kB)
Installing collected packages: pytz, sqlparse, asgiref, django
Successfully installed asgiref-3.4.1 django-3.2 pytz-2021.1 sqlparse-0.4.2
WARNING: You are using pip version 20.2.3; however, version 21.2.4 is available.
You should consider upgrading via the 'c:\users\xxxxxx\django\djangoboard\venv\scripts\python.exe -m pip install --upgrade pip' command.

## pip 업그레이드
(venv) C:\Users\xxxxxx\django\djangoBoard>python -m pip install --upgrade pip
Collecting pip
  Using cached pip-21.2.4-py3-none-any.whl (1.6 MB)
Installing collected packages: pip
  Attempting uninstall: pip
    Found existing installation: pip 20.2.3
    Uninstalling pip-20.2.3:
      Successfully uninstalled pip-20.2.3
Successfully installed pip-21.2.4

(venv) C:\Users\xxxxxx\django\djangoBoard>pip list
Package    Version
---------- -------
asgiref    3.4.1
Django     3.2
pip        21.2.4
pytz       2021.1
setuptools 49.2.1
sqlparse   0.4.2

## 장고 프로젝트 생성
(venv) C:\Users\xxxxxx\django\djangoBoard>django-admin startproject config .

 

 

◆ MariaDB 설치

sqlite3 대신 MariaDB(MySQL과 동일한 엔진 사용)를 사용하기 위해 공식사이트에서 Windows 64비트 버전을 다운로드 한 후 설치한다. https://mariadb.org/

 

MariaDB Foundation - MariaDB.org

… Continue reading "MariaDB Foundation"

mariadb.org

- MariaDB Server Version 10.6.4 (52MB)

- MariaDb 설치 시 HeidiSQL DB 관리 툴이 기본적으로 설치되기 때문에 DB 관리가 편리하다.(설치 시 root 비번은 꼭 기억할 것)

 

 

※ 윈도우10에서 MariaDB 설치

 

Next 클릭

 

 

Next 클릭

설치 위치 확인 (Browser 버튼으로 설치 위치 변경 가능)

 

 

Next 클릭

비밀번호를 입력하고, DB character set (Encoding)은 UTF-8 사용으로 체크 Next 클릭

 

 

Next 클릭

 

Service Name 등 그대로 두고 Next, Install 클릭

 

 

 

 

설치 완료 후 윈도우 시작 버튼을 눌러 MariaDB 설치 내용을 확인해본다. 

 

 

(HeidiSQL 설치 확인 및 클릭)

 

 

※ HeidiSQL 실행

 

HeidiSQL

- 세션 이름은 원하는 이름으로 입력(입력 안해도 되지만..)
- 암호 넣고 '열기' 클릭

 

초기화면

 

 

- 장고 프로젝트에서 사용할 database 생성 (세션이름 마우스 우클릭 후 새로생성 > 데이터베이스)

 

 

- 장고 settings.py에서 접속할 데이터베이스 이름임(borameboard)

 

 

데이터베이스 생성 확인

 

◆ 장고와 MariaDB 연동을 위해 라이브러리(mysqlclient)를 설치한다.

(venv) C:\Users\xxxxxx\django\djangoBoard>pip install mysqlclient
Collecting mysqlclient
  Downloading mysqlclient-2.0.3-cp38-cp38-win_amd64.whl (179 kB)
     |████████████████████████████████| 179 kB 6.4 MB/s
Installing collected packages: mysqlclient
Successfully installed mysqlclient-2.0.3

- 이 패키지 설치를 안하면 database 연결이 안됨

 

 

◆ 장고 sqlite3 대신 사용할 db 접속 정보 설정하기 - settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

#위 코드를 아래처럼 수정한다.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'borameboard',
        'USER': 'root',
        'PASSWORD': '**********',
        'HOST': 'localhost',
        'PORT': '3306'
    }
}

 

 

 

● django 연동 기본 DB 초기화 (migrate 명령) 및 테스트 서버 구동(runserver)
- HeidiSQL에서 borameboard database가 필히 생성되어있어야 함

(venv) C:\Users\xxxxxx\django\djangoBoard>python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK

(venv) C:\Users\xxxxxx\django\djangoBoard>python manage.py run server

- admin, auth, board, contenttypes, sessions 관련 모델(db)이 생성됨

 

 

※ 브라우저에서 127.0.0.1:8000 또는 localhost:8000 으로 접속

장고 테스트 서버 초기 화면

 

 

 

◆ 사용자(관리자) 계정 설정

(venv) C:\Users\xxxxxx\django\djangoBoard>python manage.py createsuperuser
사용자 이름 (leave blank to use 'xxxxxx'): admin
이메일 주소: 
Password: 
Password (again):
Superuser created successfully.

 

- 관리자 계정으로 접속

admin 페이지

 

 

게시판 앱(borameboard) 생성 및 앱 등록

(venv) C:\Users\xxxxxx\django\djangoBoard>python manage.py startapp borameboard

 

- borameboard 생성 후 settings.py에 앱 등록

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'borameboard',
]

 

 

◆ URL Conf 설정

# config>urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('board/', include('borameboard.urls')),
]

- 127.0.0.1:8000/board 접속 시 borameboard 앱의 urls.py로 보내도록 경로 지정

 

 

- borameboard 앱에 urls.py를 만들고,  
User Interface인 html 파일을 만들 templates 폴더 / borameboard  폴더를 만든다.

# borameboard>urls.py
from django.urls import path
from . import views

app_name = 'borame_board'

urlpatterns = [
    path('', views.index, name='index'),
]

 

 

 

◆ views.py 작성

from django.shortcuts import render

def index(request):
    context = {'board_list': '테스트입니다.'}
    return render(request, 'borameboard/index.html', context)

 

- templates / borameboard 밑에 index.html 템플릿 파일을 생성하고, views.py의 index() 반환값이 출력되는지 확인한다.

# index.html

<html>
<body>
    {{board_list}}
</body>
</html>

 

 

- 127.0.01:8000/board 로 사이트 접속 

index.html 접속 확인

 

 

◆ 모델 (DB) 생성 - modes.py

- 장고의 ORM 기능인 models를 통해 게시판 글을 관리하는 DB를 생성한다.

 

- 장고에서 모든 database table은 모델(Model)로 관리할 수 있다.

 

- 장고에서는 primary key를 관리하는 id 속성이 자동으로 지정되어 관리된다.
# id = models.AutoField(primary_key=True)

 

- 다른 컬럼(필드)에 primary key를 부여하고 싶으면 해당 필드에 primary_key=True 옵션을 준다.
# models.CharField는 문자열 필드, 
# models.TextField는 문장을 입력할 수 있는 필드(textarea)
# DateTimeField는 날짜 필드를 의미한다. 

from django.db import models

class Board(models.Model):
    title = models.CharField(max_length=200, verbose_name='글 제목', help_text='* 제목은 최대 100자 이내')
    author = models.CharField(max_length=100, verbose_name='글쓴이')
    content = models.TextField(verbose_name='글 내용')
    published_date = models.DateTimeField(auto_now=True, verbose_name='등록(수정)일')

    def __str__(self):
        return self.title

- __str__() 함수는 객체가 생성되면 객체를 문자열로 반환해주는 함수인데, title(제목)을 반환하도록 지정함

(venv) C:\Users\xxxxxx\django\djangoBoard>python manage.py makemigrations
Migrations for 'borameboard':
  borameboard\migrations\0001_initial.py
    - Create model Board
(venv) C:\Users\xxxxxx\django\djangoBoard>python manage.py migrate       
Operations to perform:
  Apply all migrations: admin, auth, borameboard, contenttypes, sessions
Running migrations:
  Applying borameboard.0001_initial... OK

- makemigrations 명령으로 생성된 모델을 마이그레이션 객체로 변환, migrate 명령으로 데이터베이스에 table 생성

 

 

HeidiSQL로 확인

 

◆ admin 페이지에 등록

from django.contrib import admin
from .models import Board

@admin.register(Board)
class BoardAdmin(admin.ModelAdmin):
    list_display = ['id', 'title', 'author', 'published_date']
    list_display_links = ['id', 'title']
    list_per_page = 10

- list_display : Admin 목록에 보여질 필드 목록
- list_display_links : 목록 내에서 링크로 지정할 필드 목록
- list_per_page : 페이지 별로 보여질 개수 (디폴트 100)

 

 

◆ models 마이그레이트 후 어드민 계정 접속

등록한 Board Model 확인

 

- Boards 클릭

 

 

 - BOARD 추가 버튼 클릭하여 테스트 데이터 입력 

 

 

 

 

◆ Django Template 상속

- 템플릿 파일의 중복 내용을 상속을 통해 중복되지 않도록 할 수 있다. 
- 전체 레이아웃(부모 템플릿) 내에  {% block 블럭 영역 명칭 %} {% endblock %}으로 정의 후 각 템플릿(자식 템플릿)에서 다음과 같이 재정의하여 사용

{% extends '부모 템플릿(경로)' %}


{% block 블럭 영역 명칭 %}

{% endblock %}

- djangoBoard 내 하위 폴더 layout을 만들고 장고 전체에서 사용할 base.html 템플릿 파일을 생성한다. 

# base.html 템플릿 파일 경로 설정 : settings.py에 디렉토리 경로 지정

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [ ],
에서 

'DIRS': [ ], 부분을
'DIRS': [os.path.join(BASE_DIR, 'layout')], 처럼 수정한다.

# import os 필요함

- base.html 파일을 전체 레이아웃으로 사용하기 위해 bootstrap을 적용한다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
    <title>Borame Board</title>
</head>
<body>
    <div class="container-fluid">
        <nav class="navbar navbar-expand-lg navbar-light bg-light">
            <div class="container-fluid">
                <a class="navbar-brand" href="#">LOGO</a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse"
                    data-bs-target="#navbarNavDropdown" aria-controls="navbarNavDropdown" aria-expanded="false"
                    aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="collapse navbar-collapse" id="navbarNavDropdown">
                    <ul class="navbar-nav">
                        <li class="nav-item">
                            <a class="nav-link active" aria-current="page" href="#">Home</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="#">Menu1</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="#">Menu2</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="#">borameBoard</a>
                        </li>                        
                    </ul>
                </div>
            </div>
        </nav>
    </div>
    <div class="container">
        {% block content %}

        {% endblock %}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

- Navbar 종류 중 하나를 선택하여 복사한 후 <div class="container-fluid"> 밑에 붙여넣기

- 부트스트랩 사이트(https://getbootstrap.com/docs/5.1/getting-started/introduction/)에서 css와 js 링크를 복사하여 붙여 넣고 사용함

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>

 

 

◆ views.py 내용 수정

from django.shortcuts import render
from .models import Board

def index(request):
    board_list = Board.objects.all().order_by('-id')
    context = {'board_list': board_list}
    return render(request, 'borameboard/index.html', context)

- Board 객체 매니저인 objects를 사용하여 해당 db table의 데이터 추출 QuerySet을 변수 board_list에 저장한다.
# 데이터 전체(all())를 id 역순으로 정렬(order_by('-id'))하는 QuerySet
- 추출한 쿼리셋 데이터를 index.html로 렌더링하여 브라우저로 보여준다.

 

 

◆ index.html 내용 수정

 {% extends 'base.html' %}
{% block content %}

<div class="container">
    <div class="row pt-4 my-3">
        <div class="col-md-12">
            <h3>게시판</h3>
        </div>
    </div>
    <div class="row my-3">
        <table class="table table-hover table-bordered">
            <thead>
                <th>No</th>
                <th>제목</th>
                <th>작성자</th>
                <th>내용</th>
                <th>발행일</th>
                <th>비고</th>
            </thead>
            <tbody>
                {% for post in board_list %}
                <tr>
                    <td>{{ forloop.revcounter }}</td>
                    <td><a href="#">{{ post.title }}</a></td>
                    <td>{{ post.author }}</td>
                    <td>{{ post.content|truncatewords:5 }}</td>
                    <td>{{ post.published_date|date:"Y-m-d" }}</td>
                    <td></td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>

{% endblock %}

 

 

 

◆ 127.0.0.1:8000 으로 접속 시 index.html로 바로 접속하도록 path 설정

- borameboard의 urls.py에서 path를 받아 views.py에서 redirect() 함수로 처리해도 되지만,
- config / urls.py 에서 아래처럼 path('', lambda r: redirect('borame_board:index')), 추가하면 바로 redirect 처리할 수도 있다.
* from django.shortcuts import redirect 추가하고, lambda 함수와 redirect 함수 이용

from django.contrib import admin
from django.urls import path, include
from django.shortcuts import redirect

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', lambda r: redirect('borame_board:index')),
    path('board/', include('borameboard.urls')),
]

 

 

◆ 글 등록을 위해 urls.py 수정

- path('regist/', views.regist, name='regist'), 추가

 

 

◆ index.html 템플릿 파일 하단에 '글 등록' 버튼 추가

<div style="padding: 2px 20px; float:right;">
    <a href="{% url 'borame_board:regist' %}" class="btn btn-primary">글 등록</a>
</div>

 

 

◆ 글 등록은 장고 폼(forms.ModelForm)을 사용해본다.

- borameboard 앱에 forms.py 파일을 생성한다.

from django import forms
from django.forms import widgets
from .models import Board

class RegistForm(forms.ModelForm):
    class Meta:
        model = Board
        fields = ['title', 'author', 'content']  # '__all__'
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'author': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
        }

- Model 클래스와 유사하게 Form 클래스 정의 (HTML Form 요소를 파이썬 클래스화)
- 장고 models 에 대한 form을 생성하기 위해 모델명을 import하고, 보여 줄 컬럼(필드)를 지정한다.('__all__'인 경우 전체 지정됨)
- 템플릿에서 보여질 html의 요소(Element)를 지정하기 위해 widgets을 import 하고 설정한다.

 

 

 

◆ 등록 폼 템플릿 추가 

- borameboard / templates / borameboard / regist_form.html

{% extends 'base.html' %}
{% block content %}

<div class="container">
    <div class="row pt-4 my-3">
        <div class="col-md-12">
            <h3>글 등록</h3>
        </div>
    </div>
    <div class="row my-3"></div>
        <form action="" method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <div style="padding: 5px 50px; float:right;">
                <input class="btn btn-primary" type="submit" value="글 등록">
                <a href="#" class="btn btn-primary">글 목록</a>
            </div>
        </form>
    </div>
</div>

{% endblock %}

- csrf_token : (Cross Site Request Forgeries) 사이트 간 요청 위조 공격 막기 위함 

 

 

글 등록 폼

 

 

◆ 입력된 글 등록(처리)를 위해 views.py 에 regist() 추가

from borameboard.models import Board
from django.shortcuts import render,redirect
from .models import Board
from .forms import RegistForm

def index(request):
    board_list = Board.objects.all().order_by('-id')
    context = {'board_list': board_list}
    return render(request, 'borameboard/index.html', context)


def regist(request):
    if request.method == 'POST':
        form = RegistForm(request.POST)
        if form.is_valid():
            post = form.save()
            return redirect('borame_board:index')
    else:
        form = RegistForm()
    context = {'form': form,}
    return render(request, 'borameboard/regist_form.html', context)
​

 

 

 

◆ 글 내용 보기

- borameboard / urls.py 에 path('detail/<int:pk>/', views.detail, name='detail'), 추가
- 글 목록(리스트)에서 글 제목을 클릭하면 글의 내용을 볼 수 있도록 하기 위해 

index.html의 <td><a href="#">{{ post.title }}</a></td> 부분을 
<td><a href="{% url 'borame_board:detail' post.id %}">{{ post.title }}</a></td> 로 수정한다.

 

 

◆ views.py 에 detail() 추가

def detail(request, pk):
    board_list = get_object_or_404(Board, id=pk)
    context = {'board_list': board_list}
    return render(request, 'borameboard/detail.html', context)

- views 내에서 특정 조건에 맞는 모델 인스턴스를 가져오려고 할 때는 항상 get_object_or_404() 를 쓴다. 
* 글 삭제 또는 글이 없어서 발생하는 오류 코드(status code)가 500이 아닌 404로 되도록 하기 위함
*  get_object_or_404 추가 (from django.shortcuts import render, redirect, get_object_or_404)


- 글 세부 내용 보기 템플릿 - borameboard / templates / borameboard / detail.html

{% extends 'base.html' %}
{% block content %}

<div class="container">
    <div class="row pt-4 my-3">
        <div class="col-md-12">
            <h3>글 세부 내용</h3>
        </div>
    </div>
    <div class="row my-3">
        <table class="table table-bordered">
            <tr>
                <td>[글 제목] : {{ board_list.title }}</td>
            </tr>
            <tr>
                <td>
                    <p>[글 내용]</p>
                    {{ board_list.content|linebreaks }}
                    <p><small style="color:silver;text-align:right;">[{{ board_list.published_date|date:"Y-m-d H:i:s" }}]</small></p>
                </td>
            </tr>
        </table>
    </div>
    <div style="padding: 2px 20px; float:right;">
        <a href="{% url 'borame_board:index' %}" class="btn btn-primary">글 목록</a>
    </div>
</div>

{% endblock %}

 

 

 

◆ 글 수정 기능

- 글 수정을 위해 borameboard / urls.py 에 
path('edit/<int:pk>/', views.edit, name='edit'), 추가

- 글 목록에서 '비고' 내의 '수정' 링크(버튼)를 클릭하면, 수정페이지로 전환되고 수정할 글의 id를 urls에 전달되도록 위해 index.html의 비고 부분에 <a href="{% url 'borame_board:edit' post.id %}" class="btn btn-outline-info btn-sm">수정</a> 를 추가한다.

 

views.py 에 edit() 추가

def edit(request, pk):
    post = get_object_or_404(Board, id=pk)
    if request.method == 'POST':
        form = RegistForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save()
            return redirect('borame_board:index')
    else:
        form = RegistForm(instance=post)
    context = {'form': form,}
    return render(request, 'borameboard/edit_form.html', context)

- id=pk인 모델 인스턴스를 get_object_or_404()로 가져와서 post 변수에 저장하고, 이 인스턴스 변수를 기준으로 폼을 생성한다. ( instance=post)

 

- 수정 폼은 등록 폼을 그대로 활용하도록 작성한다.

{% extends 'base.html' %}
{% block content %}

<div class="container">
    <div class="row pt-4 my-3">
        <div class="col-md-12">
            <h3>글 수정</h3>
        </div>
    </div>
    <div class="row my-3"></div>
        <form action="" method="post">
            {% csrf_token %}
            {{ form.as_p }}
            <div style="padding: 5px 50px; float:right;">
                <input class="btn btn-primary" type="submit" value="글 수정">
                <a href="{% url 'borame_board:index' %}" class="btn btn-primary">글 목록</a>
            </div>
        </form>
    </div>
</div>

{% endblock %}
​

 

 

 

◆ 글 삭제 기능

- 글 삭제 기능도 글 목록의 '비고' 내에 '삭제' 링크(버튼)를 만들어서 처리한다.

* index.html의 비고 부분에 <a href="{% url 'borame_board:delete' post.id %}" onclick="if(!confirm('정말 삭제하시겠습니까?')){return false;}" class="btn btn-outline-danger btn-sm">삭제</a>추가

- urls.py 에 path('delete/<int:pk>/', views.delete, name='delete'), 추가

 

 

views.py 에 delete() 추가

def delete(request, pk):
    post = get_object_or_404(Board, id=pk)
    post.delete()
    return redirect('borame_board:index')

 

 

게시판 목록(리스트)

 

◆ base.html 상단 메뉴의 Home과 borameboard에 게시판 리스트 페이지를 링크한다.

<a class="nav-link active" aria-current="page" href="#">Home</a>
<a class="nav-link" href="#">borameBoard</a>

아래 처럼 수정

<a class="nav-link active" aria-current="page" href="{% url 'borame_board:index' %}">Home</a>
<a class="nav-link" href="{% url 'borame_board:index' %}">borameBoard</a>

 

글 상세 내용 보기

 

 

글 수정 페이지

 

 

 

글 삭제 버튼 클릭

 

 

◆ 페이징 처리

- 사전 작업 : 장고 쉘(shell)을 이용한 데이터 입력

(venv) C:\Users\xxxxxxdjango\djangoBoard>python manage.py shell
Python 3.8.8 (default, Apr 13 2021, 15:08:03) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
>>> from django.utils import timezone
>>> from borameboard.models import Board
>>> query = Board(title='페이징 처리', author='2000', content='페이징 처리 데이터 입력', published_date=timezone.now())
>>> query.save()
>>> Board.objects.all()
<QuerySet [<Board: 김장용품>, <Board: 반려견 애정템>, <Board: 가성비 좋은 노트북>, <Board: 크리스마스 선물>, <Board: 맥북 
프로 16인치>, <Board: 갤럭시 워치4>, <Board: 페이징 처리>]>
>>> for i in range(60):
...     query = Board(title='페이징 처리-'+str(i), author='2000', content='페이징 처리 데이터 입력_'+str(i), published_date=timezone.now())
...     query.save()
...
>>> Board.objects.all()
<QuerySet [<Board: 김장용품>, <Board: 반려견 애정템>, <Board: 가성비 좋은 노트북>, <Board: 크리스마스 선물>, <Board: 맥북 
프로 16인치>, <Board: 갤럭시 워치4>, <Board: 페이징 처리>, <Board: 페이징 처리-0>, <Board: 페이징 처리-1>, <Board: 페이징  
처리-2>, <Board: 페이징 처리-3>, <Board: 페이징 처리-4>, <Board: 페이징 처리-5>, <Board: 페이징 처리-6>, <Board: 페이징 처 
리-7>, <Board: 페이징 처리-8>, <Board: 페이징 처리-9>, <Board: 페이징 처리-10>, <Board: 페이징 처리-11>, <Board: 페이징 처 
리-12>, '...(remaining elements truncated)...']>
>>>

# 장고에서는 파이썬 쉘이 아닌 장고 쉘을 이용한다. (python manage.py shell 로 쉘에 진입 후 작업 진행)

 

 

데이터 입력 후

 

 

※ 장고의 ORM 관련 메서드 간략하게 알아보기

all() 
해당 table의 데이타 전체 가져오기
데이터 전체를 QuerySet 타입으로 반환
dict 타입 사용 방법으로 개별 row를 출력할 수 있다.
rs = Board.objects.all()
print(rs[0]['title'])

 

get()
row 1개만 가져오기
QuerySet이 아닌 단일 행(모델 타입) 반환
반환 값(row 수)이 1개 이상일 경우 에러 발생함
rs = Board.objects.get(id=1)
print(rs.title)


filter()
특정 조건에 맞는 row들을 가져오기
조건에 맞는 여러 행(row) 반환(QuerySet 타입)
rs = Board.objects.filter(title='갤럭시') 
또는 Board.objects.all().filter(title='갤럭시') 와 동일, all() 생략 가능
print(rs[0]['title'])

 

order_by(key)
인자(키)에 따라 데이터 정렬하며, 정렬 키는 나열 가능
기본 정렬은 오름차순이고, 키 앞에 -가 붙으면 내림차순
Board.objects.order_by('id')

 


value()
특정 컬럼만을 대상으로 값을 반환할 때 사용
QuerySet 타입 반환
Board.objects.value('title')

 

exclude()
특정 조건을 제외한 나머지 row들을 가져오기
Board.objects.exclude(title='갤럭시')

 

distinct()
중복된 값은 하나로만 표시하기(SELECT DISTINCT 와 같은 효과)
Board.objects.distinct('title')

 

first()
데이타들 중 맨 처음에 있는 row만 가져오기
Board.objects.order_by('title').first()

 

last()
데이타들 중 맨 마지막에 있는 row만 가져오기 
Board.objects.filter(title='갤럭시').order_by('-id').first()

 

count()
데이타의 갯수(row 수) count
Board.objects.count()

 

Data INSERT 하기
테이블에 해당하는 모델(model)로부터 객체 생성 후, 그 객체의 save() 메서드를 호출한다.
q = Board(title='페이징 처리', author='2000', content='페이징 처리 데이터 입력', published_date=timezone.now())
q.save()

 

Data UPDATE 하기
업데이트 할 row 객체를 생성하고 변경할 컬럼(필드)를 수정한 후, save() 메서드를 호출한다.
q = Board.objects.get(id=1)
q.title = '갤럭시 워치 4 싸게 구매하기'
q.save()

 

Date DELETE 하기
데이타를 삭제할 row 객체를 생성하고 delete() 메서드를 호출한다.
q = Board.objects.get(id=2)
q.delete()

 


 

- 페이징 처리를 위해 views.py 에서 index() 함수 수정하기
from django.core.paginator import Paginator 추가

def index(request):
    page = request.GET.get('page', '1')  # 페이지 파라미터 얻기, 없으면 1
    board_list = Board.objects.order_by('-id')

    # 페이징 처리
    paginator = Paginator(board_list, 10)  # 페이지당 10개씩 보여주기
    page_obj = paginator.get_page(page)  # page에 해당하는 페이징 객체 생성

    context = {'board_list': page_obj}   # 페이징 객체(page_obj) 전달
    return render(request, 'borameboard/index.html', context)


# 페이징 객체(page_obj) 속성
count : 총 객체 수 
paginator.count : 전체 게시물 개수
paginator.per_page : 페이지당 보여줄 게시물 개수
paginator.page_range : 페이지 범위
number : 현재 페이지 번호
get_page : 페이지 가져오기
page_range : 페이지 리스트(1~)
num_pages: 총 페이지 수
page(n) : n 번째 페이지
previous_page_number : 이전 페이지 번호
next_page_number : 다음 페이지 번호
has_previous : 이전 페이지 유무(boolean)
has_next : 다음 페이지 유무(boolean)
start_index : 현재 페이지 시작 인덱스(1부터 시작)
end_index : 현재 페이지의 끝 인덱스(1부터 시작)

 

 

- index.html 수정

{% extends 'base.html' %}
{% block content %}

<div class="container">
    <div class="row pt-4 my-3">
        <div class="col-md-12">
            <h3>게시판</h3>
        </div>
    </div>
    <div class="row my-3">
        <table class="table table-hover table-bordered">
            <thead>
                <th>No</th>
                <th>제목</th>
                <th>작성자</th>
                <th>내용</th>
                <th>발행일</th>
                <th>비고</th>
            </thead>
            <tbody>
                {% for post in board_list %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td><a href="{% url 'borame_board:detail' post.id %}">{{ post.title }}</a></td>
                    <td width="100">{{ post.author }}</td>
                    <td>{{ post.content|truncatewords:5 }}</td>
                    <td>{{ post.published_date|date:"Y-m-d" }}</td>
                    <td width="120">
                        <a href="{% url 'borame_board:edit' post.id %}" class="btn btn-outline-info btn-sm">수정</a> 
                        <a href="{% url 'borame_board:delete' post.id %}" onclick="if(!confirm('정말 삭제하시겠습니까?')){return false;}" 
                        class="btn btn-outline-danger btn-sm">삭제</a>
                    </td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
    <div class="row">
        <div class="col-md-12 ">
            <a href="{% url 'borame_board:regist' %}" class="btn btn-outline-secondary">글 등록</a>
        </div>
    </div>

    <!-- 페이징 처리 --->
    <div class="row my-3">
        <div class="col-md-12 text-center">
            {% if board_list.has_previous %}
                <a href="?page=1">처음</a>
                <a href="?page={{ board_list.previous_page_number }}">이전</a>
            {% endif %}
                <!-- 페이지리스트 -->
                <span style="color:red;font-weight:bold;">{{ board_list.number }}</span> 
                <span> / </span>
                <span style="font-weight:bold;">{{ board_list.paginator.num_pages }}</span>
            {% if board_list.has_next %}
                <a href="?page={{ board_list.next_page_number }}">다음</a>
                <a href="?page={{ board_list.paginator.num_pages }}">끝</a>
            {% endif %}            

        </div>
    </div>
</div>

{% endblock %}

 

 

페이징 처리 후 index 페이지

 

댓글