티스토리 뷰
쟝고는 프로젝트별로 서버를 띄움.
빅데이터 분석한 결과를 디비에 저장하고 웹 페이지에 불러오기! 쟝고로
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
-> 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
- ScriptTag
- html input type
- A%B
- typeof
- border-spacing
- CascadingStyleSheet
- 입력양식
- selcetor
- html atrribute
- 외부구성요소
- html pre
- initialized
- JavaScript
- 스크립태그
- 변수
- empty-cell
- html
- BAEKJOON
- improt
- scanner
- input type 종류
- 미디어 태그
- Java
- html base tag
- html layout
- text formatting
- html a tag
- 기본선택자
- caption-side
- css
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |