티스토리 뷰

 

쟝고는 프로젝트별로 서버를 띄움.

빅데이터 분석한 결과를 디비에 저장하고 웹 페이지에 불러오기! 쟝고로

 

1. 쟝고 프로젝트 생성

(1) mySQL 설치 및 디비 생성

#CMD에서

>pip install mysqlclient

(1-2) mysql 스미카 생성

(2) 쟝고 프로젝트 생성

-pyDev Django Project

 

(3) CMD

-현재 작업 폴더(프로젝트)로 이동

#기존에 열어뒀던 거(원래 경로가 D:\work\django\mysite)라면

>cd..

>cd pyweb_board

 

(4) pyweb_board 설정

(4-1) settings.py : 날짜 포맷 변경, 앱(board) 추가, 디비 설정, 언어 설정

...
from pathlib import Path
import os
#날짜 포맷 변경을 위한 모듈 추가
from django.conf.locale.ko import formats as ko_formats

#날짜 포맷 설정
ko_formats.DATE_FORMAT = 'Y-m-d G:i:s'
...
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'board', #추가한 앱 등록
]
...
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'py_web',
        'USER' : 'wsy',
        'PASSWORD' : '1234',
        'HOST': 'localhost',
        'PORT': '3306', #데이터베이스 포트(보통 3306)
    }
}
...
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'ko'  #언어 설정

TIME_ZONE = 'Asia/Seoul'  #타임존 설정
...

 

(4-2) CMD에서 설정 : 기본 테이블 생성, 슈퍼 유저 생성, 어플리케이션 생성

#변경사항 적용
>python manage.py migrate 

>python manage.py createsuperuser
#admin
#admin1234

#프로젝트 내에 'board' 생성
>python manage.py startapp board

 

(4-3)  mysql에서 확인 : 테이블 생성된 것, superuser 생성된 것

 

(5) 게시판 생성 - models.py

(5-1) 게시판 , 댓글 테이블 작성 - models.py

from datetime import datetime
from django.db import models

#게시판 객체에 들어갈 내용 지정
class Board(models.Model):
    idx = models.AutoField(primary_key=True)
    writer = models.CharField(null=False, max_length=50)
    title = models.CharField(null=False, max_length=120)
    hit = models.IntegerField(default=0)
    content = models.TextField(null=False)
    post_date = models.DateTimeField(default=datetime.now, blank=True)
    filename = models.CharField(null=True, blank=True, default="", max_length=500)
    filesize = models.IntegerField(default=0)
    down = models.IntegerField(default=0)
    
    def hit_up(self):
        self.hit += 1
        
    def down_up(self):
        self.down += 1

#댓글 테이블 작성
class Comment(models.Model):
    #댓글 번호
    idx = models.AutoField(primary_key=True)
    
    #게시글 번호
    board_idx = models.IntegerField(null=False)
    writer = models.CharField(null=False, max_length=50)
    content=models.TextField(null=False)
    post_date=models.DateTimeField(default=datetime.now,blank=True)

 

(5-2) 만든 테이블 admin 페이지에 반영 - admin.py

from django.contrib import admin
from board.models import Board

# Register your models here.
# @admin.site.register(Board)
class BoardAdmin(admin.ModelAdmin):
    list_display = ("writer", "title", "content")

admin.site.register(Board, BoardAdmin) #@admin ~ 둘중 하나만 하면 됨.

 

(5-3) 변경사항 디비에 적용 - cmd

#현재 작업 공간
#D:\work\django\pyweb_board

>python manage.py makemigrations #변경사항 디비에 적용

>python manage.py migrate

#서버 실행
>python manage.py runserver localhost:80

-웹 페이지 호출 : http://localhost/admin/   로그인(admin / admin1234) 아래 페이지로 연결

< 2022.07.06 >

>> 결과

(6) list 페이지(리스트 페이지) 만들어서 뿌리기

(6-1) views.py 작성_이거 먼저 만들면 에러 뜨지 않음

from django.shortcuts import render
from board.models import Board

# Create your views here.
def list(request):
    boardCount = Board.objects.count()
    boardList = Board.objects.all().order_by("-idx")
    return render(request, "board/list.html", 
                  {"boardList":boardList, "boardCount":boardCount})

#render(request, 렌더링할 페이지, 렌더링할 페이지를 json 형식으로 보냄)    
#-idx : 내림차순 정렬 / idx : 오름차순 정렬
#경로 : default가 templates. 디폴트 폴더인 templates는 생략 가능

(6-2) list 템플릿(list.html) 생성

-폴더 생성(board 아래) : templates / board

-템플릿(html 파일) 생성 : list.html

{% load static %} <!-- static 폴더를 로드하겠다는 선언. 경로 설정해서 가져오려면 선언 필요 -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>pyweb_board_list_page</title>
</head>
<body>
<!-- 
게시물 수 출력, 글쓰기 페이지 이동 링크,
-파이프라인(|) : 날짜 형식 지정
-for문으로 데이터 가져와 테이블 형식에 맞춰 출력
-filesize>0 : 파일이 있을 때, 링크 가져오고 이미지까지 가져옴(이미지 저장된 폴더에서)
 -->
<h2>web_board_list</h2>
게시물 수 : {{boardCount}}<br>
	<a href="/write">글쓰기</a>
	<table border="1">
		<tr>
			<th>번호</th>
			<th>이름</th>
			<th>제목</th>
			<th>날짜</th>
			<th>조회수</th>
			<th>첨부파일</th>
			<th>다운로드</th>
		</tr>
		{% for row in boardList %}
		<tr aligin="center">
			<td>{{row.idx}}</td>
			<td>{{row.writer}}</td>
			<td><a href="/detail?idx={{row.idx}}">{{row.title}}</a></td>
			<td>{{row.post_date|date:"Y-m-d"}}</td>
			<td>{{row.hit}}</td>
			<td>
			{% if row.filesize > 0%}
				<a href="/download?idx={{row.idx}}">
					<img src="{% static 'images/img01.gif'%}"> <!--이미지 넣는 척-->
				</a>
				{% endif %}
				{{row.writer}}
			</td>
			<td>{{row.down}}</td>
		</tr>
		{% endfor%}
	</table>
</body>
</html>

-> 이미지 가져오려면, board/static/images 폴더 생성하고 이미지 넣어주기

     img01.gif

KakaoTalk_20220706_094139314.gif
0.00MB

 

 

-> settings.py 에서 경로 설정

#settings.py
...
STATIC_URL = '/board/static/'
...

(6-3) urls.py

...
urlpatterns = [
    path('admin/', admin.site.urls),
    path('list/', views.list),  #list/를 요청하면 view.list를 실행하겠다.
]

 

(6-4) 웹 페이지에서 확인

-새로운 글 등록 후, 웹브라우저 호출 : localhost/list

 

(7) 글쓰기 페이지 만들기

(7-1) write.html

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>글쓰기</h2>
<form id="form1" name="form1" method="post" action="/insert/" 
enctype="multipart/form-data">
  {% csrf_token %} <!-- 보안 -->
  <div>이름 <input name="writer" size="80" placeholder="제목을 입력하세요"></div>
  <div>제목 <input name="title" size="80" placeholder="제목을 입력하세요"></div>
  <div style="width:800px;">내용 
  <textarea name="content"rows="3" cols="80" placeholder="내용을 입력하세요"></textarea>
  </div>
  <div style="width:800px;">첨부파일
    <input type="file" name="file">
  </div>
  <div style="width:700px; text-align: center;">
    <button type="submit" id="btnSave">확인</button>
  </div>
</form>
</body>
</html>

(7-2) views.py에서 wirte 함수 만들기

#글쓰기 페이지
def write(request):
    return render(request, 'board/write.html')

@csrf_exempt #보안 토큰 관련 
def insert(request):
    fname = ''
    fsize=0

#파일 업로드
    if 'file' in request.FILES:
        #write.html에 name='file'이 있는지 확인
        file = request.FILES['file']
        fname = file.name
        fsize = file.size

#파일을 업로드하는 디렉토리 필요 : 맨 위에 생성(↑)
        fp = open("%s%s"%(UPLOAD_DIR, fname), 'wb')
        for chunk in file.chunks():
            fp.write(chunk)
        fp.close()
        
#wb : write binary
#chunk : 파일에서 기록되는 단위 블록
        #POST로 전달된 것 중 wirter/title/content를 변수에 저장
        w = request.POST['writer']
        t = request.POST['title']
        c = request.POST['content']
        
        #객체로 저장해 리턴
        dto = Board(writer=w, title=t, content=c, filename=fname, filesize=fsize)
        dto.save()
        
        #redirect는 render를 import한 것에서 추가만 해주면 됨
        return redirect('/list/') #호출

(7-3) urls.py

urlpatterns = [
    #관리자 관련
    path('admin/', admin.site.urls),
    #게시판 관련
    path('list/', views.list),
    path('write/', views.write),
    path('insert/', views.insert),
]

(7-4) CMD 적용 및 웹페이지 호출

>python manage.py runserver localhost:80

-localhost/list 호출해서

 글쓰기 버튼 눌러서 테스트 : 첨부파일까지 모두 입력 -> 결과물(↓)

 

(8) 상세보기 페이지(detail page)

(8-1) detail.html

더보기
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script>
function home(){
	location.href="/list/";
}
function update(){
	document.form1.action="/update/";
	document.form1.submit();
}
function del(){
	if(confirm("삭제하시겠습니까?")){
		document.form1.action="/delete/";
		document.form1.submit();
	}
}
</script>
</head>

<body>
<h2>게시물 편집</h2>
<form method="post" name="form1" enctype="multipart/form-data">
<table border="1" width="700px">
	<tr><td>조회수</td><td>{{dto.hit}}</td></tr>
	<tr><td>이름</td>	<td><input name="writer" value="{{dto.writer}}"></tr>
	<tr><td>제목</td>	<td><input name="title" value="{{dto.title}}"></tr>
	<tr><td>날짜</td>	<td>{{dto.post_date}}</td></tr>
	<tr><td>내용</td>	<td><textarea rows="5" cols="60" 
			name="content">{{dto.content}}</textarea></td></tr>
	<tr><td>첨부파일</td>
		<td>
			<!-- filesize >0일때만 표현하라(=파일이 있을 때) -->
			{% if dto.filesize > 0 %}
			  <a href="/download?idx={{dto.idx}}">
			  {{dto.filename}} ({{filesize}}KB) </a>
			{% endif %}
			<input type="file" name="file">
		</td>
	</tr>
	<tr><td colspan="2" align="center">
			<!-- 현재 게시물의 값을 hidden으로 보냄 -->
			<input type="hidden" name="idx" value="{{dto.idx}}">
			<input type="button" value="목록" onclick="home()">
			<input type="button" value="수정" onclick="update()">
			<input type="button" value="삭제" onclick="del()">
		</td>
	</tr>
</table>
</form>

<!-- 댓글 작성폼 -->
<form method="post" action="/reply_insert/">
{% csrf_token %}
  <input name="writer" placeholder="이름"><br>
  <textarea rows="5" cols="80" name="content"
placeholder="댓글 내용을 입력하세요"></textarea><br>
  <input type="hidden" name="idx" value="{{dto.idx}}">
  <button>댓글쓰기</button>
</form>
<!-- 댓글 목록 -->
<h4>댓글 목록</h4>
<table border="1" width="700px">
	{% for row in commentList %}
	<tr><td>
			<b>{{row.writer}}</b> ( {{row.post_date}} )<br>
			{{row.content}}
	</td></tr>
	{% endfor %} 
</table>
</body>
</html>

(8-2) views.py

from board.models import Board, Comment #Comment만 추가

#상세보기 페이지
def detail(request):
    id = request.GET['idx']
    dto = Board.objects.get(idx=id) #get 데이터 하나 가져오기
    dto.hit_up() #조회수 증가
    dto.save()
    
    #상세보기엔 '댓글'  #해당 게시물의 댓글 가져옴
    commentList = Comment.objects.filter(board_idx=id).order_by('-idx') #최근 댓글 맨위
    
    filesize = "%.2f"%(dto.filesize/1024) #KB 변환
    return render(request, "board/detail.html", {'dto':dto, 'filesize':filesize, 'commentList':commentList})

(8-3) urls.py

urlpatterns = [
    #관리자 관련
    path('admin/', admin.site.urls),
    #게시판 관련
    path('list/', views.list),
    path('write/', views.write),
    path('insert/', views.insert),
    path('detail/', views.detail),
]

(8-4) cmd 적용 및 웹페이지서 확인 : localhost/list

 

(9) 다운로드 처리

(9-1) views.py

#import
# from django.utils.http import urlquote
from django.http.response import HttpResponse

...

#파일 다운로드
def download(request):
    id = request.GET['idx'] #list.html 파일다운로드 할 때, 게시판 idx를 가져오고 있음.
    dto = Board.objects.get(idx = id)
    path = UPLOAD_DIR+dto.filename
    filename = os.path.basename(path)
    # filename = urlquote(filename) #파일의 한글 이름 처리를 위해
    
    with open(path, 'rb') as file:
        response = HttpResponse(file.read(), content_type='application/actet=stream')
        response['Content-Disposition'] = "attachment;filename*=UTF-8''{0}".format(filename) #파일 다운 받는 과정 코드
    #response 객체로 담아서 다운로드 받음
    dto.down_up() #다운로드받았기 때문에 다운로드 횟수 증가
    dto.save()
    return response #파일 다운로드 완료

(9-2) urls.py

urlpatterns = [
    #관리자 관련
    path('admin/', admin.site.urls),
    #게시판 관련
    path('list/', views.list),
    path('write/', views.write),
    path('insert/', views.insert),
    path('detail/', views.detail),
    path('download/', views.download), #추가
]

(9-3) 웹 페이지 오픈

-cmd에서 서버 호출

>d:
>cd D:\work\django\pyweb_board
>python manage.py runserver localhost:80

-> 첨부된 이미지 파일이 제대로 다운 받아지는 걸 확인. pdf 파일(첨부파일명 test)은 제대로 다운로드 되지 않았음...

 

(10) 댓글 관련 _ detail.html

(10-1) detail.html에서 댓글 작성 영역

<!-- 댓글 작성폼 -->
<form method="post" action="/reply_insert/"> <!--슬래시 잘 붙여야 함-->
{% csrf_token %} <!-- post의 데이터를 전송과정에서 탈취당해 도용당하는 것 방지 -->
  <input name="writer" placeholder="이름"><br>
  <textarea rows="5" cols="80" name="content"
placeholder="댓글 내용을 입력하세요"></textarea><br>
  <input type="hidden" name="idx" value="{{dto.idx}}">
  <button>댓글쓰기</button>
</form>
<!-- 댓글 목록 -->
<h4>댓글 목록</h4>
<table border="1" width="700px">
	{% for row in commentList %}
	<tr>
		<td><b>{{row.writer}}</b> ( {{row.post_date}} )<br>{{row.content}}</td>
		<!-- 댓글 작성자, 날짜, 내용 -->
	</tr>
	{% endfor %} 
</table>

(11-1) views.py

from django.http.response import HttpResponse, HttpResponseRedirect

...

#댓글_detail.html
@csrf_exempt #보안 토큰
def reply_insert(request):
    id = request.POST['idx'] #게시물 번호
    #댓글 객체 생성
    dto = Comment(board_idx = id, writer = request.POST["writer"], content=request.POST["content"])
    #insert query 실행
    dto.save()
    #detail?idx = 글번호 페이지 이동  #댓글이 달리는 게시글로 이동하는 것
    return HttpResponseRedirect("/detail?idx="+id)

(11-2) urls.py

urlpatterns = [
    #관리자 관련
    path('admin/', admin.site.urls),
    #게시판 관련
...
    path('reply_insert', views.reply_insert),
]

 

(11-3) 웹페이지에서 댓글 입력 및 목록 조회

 

 

(12) 게시글 수정/삭제

(12-1) detail.html

<script>
//게시글 홈
function home(){
	location.href="/list/";
}
//게시글 수정
function update(){
	document.form1.action="/update/";
	document.form1.submit();
}
//게시글 삭제
function del(){
	if(confirm("삭제하시겠습니까?")){
		document.form1.action="/delete/";
		document.form1.submit();
	}
}
</script>
...
<body>
...
       <td> <!--파일은 수정 안 하는 경우 고려해야 함-->
			<!-- filesize >0일때만 표현하라(=파일이 있을 때) -->
			{% if dto.filesize > 0 %}
			  <a href="/download?idx={{dto.idx}}">
			  {{dto.filename}} ({{filesize}}KB) </a>
			{% endif %}
			<input type="file" name="file">
		</td>
...     
			<input type="button" value="목록" onclick="home()">
			<input type="button" value="수정" onclick="update()">
			<input type="button" value="삭제" onclick="del()">
...
</body>

(12-2) views.py

...

#게시글 업데이터
@csrf_exempt
def update(request):
    id = request.POST['idx']
    dto_src = Board.objects.get(idx=id)
    #기존 파일일 경우
    fname = dto_src.filename
    fsize = dto_src.filesize
    #파일이 교체되는 경우
    if 'file' in request.FILES:
        #write.html에 name='file'이 있는지 확인
        file = request.FILES['file']
        fname = file.name
        fp = open("%s%s"%(UPLOAD_DIR, fname), 'wb')
        for chunk in file.chunks():
            fp.write(chunk)
        fp.close()
        fsize = os.path.getsize(UPLOAD_DIR+fname)
    dto_new = Board(idx = id, 
        writer = request.POST['writer'],
        title = request.POST['title'],
        content = request.POST['content'],
        filename = fname, filesize = fsize)
    dto_new.save()
    return redirect("/list/")

#게시글 삭제
@csrf_exempt
def delete(request):
    id = request.POST['idx']
    Board.objects.get(idx=id).delete()
    return redirect("/list/")
...

(12-3) urls.py

urlpatterns = [
    #관리자 관련
    path('admin/', admin.site.urls),
    #게시판 관련
    path('list/', views.list),
    path('write/', views.write),
    path('insert/', views.insert),
    path('detail/', views.detail),
    path('download/', views.download),
    path('reply_insert/', views.reply_insert),
    #수정/삭제
    path('update/', views.update),
    path('delete/', views.delete),
]

(12-4) 웹페이지 확인

 

(13) 검색 및 페이징

(13-1) list.html

-기존의 list.html은 list1.html로 변경 하고 제공된 파일 board 패키지 안에 붙여넣기

{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>게시판</h2>
게시물수 : {{ boardCount }}<br>
	<a href="/write">글쓰기</a>
<form method="post">
	{% csrf_token %}
<!-- 검색기능 -->
	<select name="search_option">
{% if search_option == "writer" %}
	<option vaule="">선택하세요</option>
	<option value="writer" selected>이름</option>
	<option value="title">제목</option>
	<option value="content">내용</option>
	<option value="all">이름+제목+내용</option>
{% elif search_option == "title" %} <!-- 검색 내용이 유지되도록 설정해주는 것 -->
	<option vaule="">선택하세요</option>
	<option value="writer">이름</option>
	<option value="title" selected>제목</option>
	<option value="content">내용</option>
	<option value="all">이름+제목+내용</option>
{% elif search_option == "content" %}<!-- 검색 내용이 유지되도록 설정해주는 것 -->
	<option vaule="">선택하세요</option>
	<option value="writer">이름</option>
	<option value="title">제목</option>
	<option value="content" selected>내용</option>
	<option value="all">이름+제목+내용</option>
{% elif search_option == "all" %}<!-- 검색 내용이 유지되도록 설정해주는 것 -->
	<option vaule="">선택하세요</option>
	<option value="writer">이름</option>
	<option value="title">제목</option>
	<option value="content">내용</option>
	<option value="all" selected>이름+제목+내용</option>
{% else %}<!-- 아무 것도 선택되지 않았을 때, -->
	<option vaule="" selected>선택하세요</option>
	<option value="writer">이름</option>
	<option value="title">제목</option>
	<option value="content">내용</option>
	<option value="all">이름+제목+내용</option>
{% endif %}
	</select>
	<input type="text" name="search" value="{{search}}"> <!-- 검색하기 위한 입력 박스에 이전에 입력했던 내용이 유지되도록 설정 -->
	<input type="submit" value="검색">
</form>
	<table border="1">
		<tr>
			<th>번호</th>
			<th>이름</th>
			<th>제목</th>
			<th>날짜</th>
			<th>조회수</th>
			<th>첨부파일</th>
			<th>다운로드</th>
		</tr>
		{% for row in boardList %}
		<tr align="center">
			<td>{{row.idx}}</td>
			<td>{{row.writer}}</td>
			<td><a href="/detail?idx={{row.idx}}">{{row.title}}</a></td>
			<td>{{row.post_date|date:"Y-m-d"}}</td>
			<td>{{row.hit}}</td>
			<td>
				{% if row.filesize > 0 %}
				<a href="/download/?idx={{row.idx}}">
				<img src="{% static "images/img01.gif" %}">
				</a>
				{% endif %}
			</td>
			<td>{{row.down}}</td>
		</tr>
		{% endfor %}
<!-- 페이징 처리 : 열 7개 합친 곳에 설정  -->		
		<tr>
		<td colspan="7" align="center">
{% if start_page >= block_size %}
<a href="/list/?start={{prev_list}}">[이전]</a>
{% endif %}
{% autoescape off %}<!-- escape 문자 끄겠다. -->
{% for link in links %}<!-- 링크는 위쪽에서 만들기 때문에 뿌리기만 하면 됨. -->
	{{link}}
{% endfor %}
{% endautoescape %} <!-- escape 문자 켜겠다. -->
{% if end_page < total_page %}
<a href="/list/?start={{next_list}}">[다음]</a>
{% endif %}
		</td>
		</tr>
	</table>
</body>
</html>

(13-2) views.py

-기존 def list는 주석 처리

#글 리스트 페이지_2
def list(request):
##검색 처리  #검색 종류 선택, 검색어 입력 했을 때와 하지 않았을 때 처리를 위한 try
    try:
        search_option = request.POST['search_option']
    except:
        search_option = ''
    
    try:
        search = request.POST['search']
    except:
        search=''  
    
    #검색 결과에 따른 레코드 개수 계산
    #필드명_contains = 값 : where 필드명 like '%값%'
    #count() : select count(*)
    if search_option == 'all':
        boardCount = Board.objects.filter(Q(writer__contains=search)
                                          |Q(title__contains=search)
                                          |Q(content__contains=search)).count()
    elif search_option == 'writer':
        boardCount = Board.objects.filter(Q(writer__contains=search)).count()
    elif search_option == 'title':
        boardCount = Board.objects.filter(Q(title__contains=search)).count()
    elif search_option == 'content':
        boardCount = Board.objects.filter(Q(content__contains=search)).count()
    else:
        boardCount = Board.objects.count()
##페이지 처리
    try:
        start = int(request.GET['start'])
    except:
        start=0
    page_size = 5
    block_size = 5
    
    end = start + page_size
    
    total_page = math.ceil(boardCount / page_size)
    current_page = math.ceil((start+1)/page_size)
    start_page = math.floor((current_page-1)/block_size)*block_size+1
    end_page = start_page + block_size -1
    
    #마지막 페이지가 토탈 페이지보다 클 경우 고려하여 보정
    if end_page > total_page:
        end_page = total_page
        
    print('total page: ', total_page)
    print('current page: ', current_page)
    print('start page: ', start_page)
    print('end page: ', end_page)
    
    #프리뷰 리스트
    if start_page >= block_size:
        prev_list = (start_page -2)*page_size
    else:
        prev_list=0
    #넥스트 리스트
    if end_page < total_page:
        nest_list = end_page * page_size
    else:
        next_list = 0
    
    if search_option == 'all':
        boardList = Board.objects.filter(Q(writer__contains=search)
                                          |Q(title__contains=search)
                                          |Q(content__contains=search)).order_by("-idx")[start:end]
    elif search_option == 'writer':
        boardList = Board.objects.filter(Q(writer__contains=search)).order_by("-idx")[start:end]
    elif search_option == 'title':
        boardList = Board.objects.filter(Q(title__contains=search)).order_by("-idx")[start:end]
    elif search_option == 'content':
        boardList = Board.objects.filter(Q(content__contains=search)).order_by("-idx")[start:end]
    else:
        boardList = Board.objects.all().order_by("-idx")[start:end]
    
    #link 태그를 미리 만들어
    links = []
    for i in range(start_page, end_page +1):
        page_start = (i-1)*page_size
        links.append("<a href='/list/?start="+str(page_start)+"'>"+str(i)+"</a>")
    
    return render(request,'board/list.html',
                  {"boardList": boardList, 
                    "boardCount": boardCount,
                    "search_option": search_option, 
                    "search": search,
                    "range":range(start_page-1,end_page),
                    "start_page":start_page,
                    "end_page":end_page,
                    "total_page":total_page,
                    "prev_list":prev_list, 
                    "next_list":next_list,
                    "links":links})

(13-3) 웹페이지에서 확인

>> 페이징 확인 / 검색 확인 ↓


! 모든 코드 취합 !

(0) 프로젝트 폴더 및 패키지 구조

(1) settings.py

...
from pathlib import Path
import os
#날짜 포맷 변경을 위한 모듈 추가
from django.conf.locale.ko import formats as ko_formats

#날짜 포맷 설정
ko_formats.DATE_FORMAT = 'Y-m-d G:i:s'
...
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'board',
]
...
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'py_web',
        'USER' : 'wsy',
        'PASSWORD' : '1234',
        'HOST': 'localhost',
        'PORT': '3306', #데이터베이스 포트(보통 3306)
    }
}
...
# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/
LANGUAGE_CODE = 'ko'
TIME_ZONE = 'Asia/Seoul'
...
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
STATIC_URL = '/board/static/'
...

(2) views.py

import math
import os

from Demos.mmapfile_demo import page_size
from django.db.models.query_utils import Q
from django.http.response import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect
from django.views.decorators.csrf import csrf_exempt
from board.models import Board, Comment

# from django.utils.http import urlquote
#파일 저장될 경로
UPLOAD_DIR = 'D:/work/django/upload/'

# Create your views here.

#게시글 업데이터
@csrf_exempt
def update(request):
    id = request.POST['idx']
    dto_src = Board.objects.get(idx=id)
    #기존 파일일 경우
    fname = dto_src.filename
    fsize = dto_src.filesize
    #파일이 교체되는 경우
    if 'file' in request.FILES:
        #write.html에 name='file'이 있는지 확인
        file = request.FILES['file']
        fname = file.name
        fp = open("%s%s"%(UPLOAD_DIR, fname), 'wb')
        for chunk in file.chunks():
            fp.write(chunk)
        fp.close()
        fsize = os.path.getsize(UPLOAD_DIR+fname)
    dto_new = Board(idx = id, 
        writer = request.POST['writer'],
        title = request.POST['title'],
        content = request.POST['content'],
        filename = fname, filesize = fsize)
    dto_new.save()
    return redirect("/list/")

#게시글 삭제
@csrf_exempt
def delete(request):
    id = request.POST['idx']
    Board.objects.get(idx=id).delete()
    return redirect("/list/")
    
#파일 다운로드
def download(request):
    id = request.GET['idx'] #list.html 파일다운로드 할 때, 게시판 idx를 가져오고 있음.
    dto = Board.objects.get(idx = id)
    path = UPLOAD_DIR+dto.filename
    filename = os.path.basename(path)
    # filename = urlquote(filename) #파일의 한글 이름 처리를 위해
    
    with open(path, 'rb') as file:
        response = HttpResponse(file.read(), content_type='application/actet=stream')
        response['Content-Disposition'] = "attachment;filename*=UTF-8''{0}".format(filename) #파일 다운 받는 과정 코드
    #response 객체로 담아서 다운로드 받음
    dto.down_up() #다운로드받았기 때문에 다운로드 횟수 증가
    dto.save()
    return response #파일 다운로드 완료

#댓글_detail.html
@csrf_exempt #보안 토큰
def reply_insert(request):
    id = request.POST['idx'] #게시물 번호
    #댓글 객체 생성
    dto = Comment(board_idx = id, writer = request.POST["writer"], content=request.POST["content"])
    #insert query 실행
    dto.save()
    #detail?idx = 글번호 페이지 이동  #댓글이 달리는 게시글로 이동하는 것
    return HttpResponseRedirect("/detail?idx="+id) 

#글 리스트 페이지_2
def list(request):
##검색 처리  #검색 종류 선택, 검색어 입력 했을 때와 하지 않았을 때 처리를 위한 try
    try:
        search_option = request.POST['search_option']
    except:
        search_option = ''
    
    try:
        search = request.POST['search']
    except:
        search=''  
    
    #검색 결과에 따른 레코드 개수 계산
    #필드명_contains = 값 : where 필드명 like '%값%'
    #count() : select count(*)
    if search_option == 'all':
        boardCount = Board.objects.filter(Q(writer__contains=search)
                                          |Q(title__contains=search)
                                          |Q(content__contains=search)).count()
    elif search_option == 'writer':
        boardCount = Board.objects.filter(Q(writer__contains=search)).count()
    elif search_option == 'title':
        boardCount = Board.objects.filter(Q(title__contains=search)).count()
    elif search_option == 'content':
        boardCount = Board.objects.filter(Q(content__contains=search)).count()
    else:
        boardCount = Board.objects.count()
##페이지 처리
    try:
        start = int(request.GET['start'])
    except:
        start=0
    page_size = 5
    block_size = 5
    
    end = start + page_size
    
    total_page = math.ceil(boardCount / page_size)
    current_page = math.ceil((start+1)/page_size)
    start_page = math.floor((current_page-1)/block_size)*block_size+1
    end_page = start_page + block_size -1
    
    #마지막 페이지가 토탈 페이지보다 클 경우 고려하여 보정
    if end_page > total_page:
        end_page = total_page
        
    print('total page: ', total_page)
    print('current page: ', current_page)
    print('start page: ', start_page)
    print('end page: ', end_page)
    
    #프리뷰 리스트
    if start_page >= block_size:
        prev_list = (start_page -2)*page_size
    else:
        prev_list=0
    #넥스트 리스트
    if end_page < total_page:
        nest_list = end_page * page_size
    else:
        next_list = 0
    
    if search_option == 'all':
        boardList = Board.objects.filter(Q(writer__contains=search)
                                          |Q(title__contains=search)
                                          |Q(content__contains=search)).order_by("-idx")[start:end]
    elif search_option == 'writer':
        boardList = Board.objects.filter(Q(writer__contains=search)).order_by("-idx")[start:end]
    elif search_option == 'title':
        boardList = Board.objects.filter(Q(title__contains=search)).order_by("-idx")[start:end]
    elif search_option == 'content':
        boardList = Board.objects.filter(Q(content__contains=search)).order_by("-idx")[start:end]
    else:
        boardList = Board.objects.all().order_by("-idx")[start:end]
    
    #link 태그를 미리 만들어
    links = []
    for i in range(start_page, end_page +1):
        page_start = (i-1)*page_size
        links.append("<a href='/list/?start="+str(page_start)+"'>"+str(i)+"</a>")
    
    return render(request,'board/list.html',
                  {"boardList": boardList, 
                    "boardCount": boardCount,
                    "search_option": search_option, 
                    "search": search,
                    "range":range(start_page-1,end_page),
                    "start_page":start_page,
                    "end_page":end_page,
                    "total_page":total_page,
                    "prev_list":prev_list, 
                    "next_list":next_list,
                    "links":links})
                                          
#글 리스트 페이지_1
# def list(request):
#     boardCount = Board.objects.count()
#     boardList = Board.objects.all().order_by("-idx")
#     return render(request, "board/list.html", 
#                   {"boardList":boardList, "boardCount":boardCount})

#render(request, 렌더링할 페이지, 렌더링할 페이지의 데이터를 json 형식으로 보냄)    
#-idx : 내림차순 정렬 / idx : 오름차순 정렬
#경로 : default가 templates. 디폴트 폴더인 templates는 생략 가능

#글쓰기 페이지
def write(request):
    return render(request, 'board/write.html')

@csrf_exempt #보안 토큰 관련 
def insert(request):
    fname = ''
    fsize=0

#파일 업로드
    if 'file' in request.FILES:
        #write.html에 name='file'이 있는지 확인
        file = request.FILES['file']
        fname = file.name
        fsize = file.size

#파일을 업로드하는 디렉토리 필요 : 맨 위에 생성(↑)
        fp = open("%s%s"%(UPLOAD_DIR, fname), 'wb')
        for chunk in file.chunks():
            fp.write(chunk)
        fp.close()
#wb : write binary
#chunk : 파일에서 기록되는 단위 블록
        #POST로 전달된 것 중 wirter/title/content를 변수에 저장
        w = request.POST['writer']
        t = request.POST['title']
        c = request.POST['content']
        
        #객체로 저장해 리턴
        dto = Board(writer=w, title=t, content=c, filename=fname, filesize=fsize)
        dto.save()
        #redirect는 render를 import한 것에서 추가만 해주면 됨
        return redirect('/list/') #호출

#상세보기 페이지
def detail(request):
    id = request.GET['idx']
    dto = Board.objects.get(idx=id) #get 데이터 하나 가져오기
    dto.hit_up() #조회수 증가
    dto.save()
    
    #상세보기엔 '댓글'  #해당 게시물의 댓글 가져옴
    commentList = Comment.objects.filter(board_idx=id).order_by('-idx') #최근 댓글 맨위
    
    filesize = "%.2f"%(dto.filesize/1024) #KB 변환
    return render(request, "board/detail.html", {'dto':dto, 'filesize':filesize, 'commentList':commentList})

(3) urls.py

...
from django.contrib import admin
from django.urls import path
from board import views


urlpatterns = [
    #관리자 관련
    path('admin/', admin.site.urls),
    #게시판 관련
    path('list/', views.list),
    path('write/', views.write),
    path('insert/', views.insert),
    path('detail/', views.detail),
    path('download/', views.download),
    path('reply_insert/', views.reply_insert),
    path('update/', views.update),
    path('delete/', views.delete),
]

'수업 > └빅데이터' 카테고리의 다른 글

[Django]빅데이터 분석한 것 쟝고(Django)로  (0) 2022.07.08
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함