ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 게시판 예제
    Python Django 2022. 10. 23. 15:17

     

    settings.py

    INSTALLED_APPS = [
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        "myboard",
    ]
    
    DATABASES = { #리모트(원격) 디비를 걸어놓았다 
        'default': {
            'ENGINE': 'django.db.backends.mysql', 
            'NAME': 'boarddb',                #<--- DB명 : db는 미리 작성되어 있어야 함.       
            'USER': 'root',                #<--- 계정명 
            'PASSWORD': '비밀번호 입력',        #<--- 계정 암호           
            'HOST': '127.0.0.1',          #<--- DB가 설치된 컴의 ip          
            'PORT': '3306',               #<--- DBMS의 port 번호     
        }
    }
    
    LANGUAGE_CODE = "ko-kr"
    
    TIME_ZONE = "Asia/Seoul"

     

    models.py

    from django.db import models
    
    # Create your models here.
    class BoardTab(models.Model):
        name = models.CharField(max_length = 20)
        passwd = models.CharField(max_length = 20)
        mail = models.CharField(max_length = 30)
        title = models.CharField(max_length = 100)
        cont = models.TextField()
        bip = models.GenericIPAddressField() # ip를 담기 위해 사용
        bdate = models.DateTimeField()
        readcnt = models.IntegerField()
        gnum = models.IntegerField() # 그룹 넘버(원글 번호와 같음)
        onum = models.IntegerField() # 원글의 댓글이면 1, 댓글의 댓글이면 2...,
        nested = models.IntegerField() # 원글의 댓글이면 1, 댓글의 댓글이면 2..., 댓글의 댓글의 댓글이면 3...,

     

     

    부모 urls.py

    from django.contrib import admin
    from django.urls import path
    from myboard.views import views1
    from django.urls.conf import include
     
    urlpatterns = [
        path("admin/", admin.site.urls),
        
        path("", views1.mainFunc),
        path("board/", include('myboard.urls'))
    ]

     

    자식 urls.py

    from django.urls import path
    from myboard.views import views1
    
    urlpatterns = [
        path("list", views1.listFunc),
        
        path("insert", views1.insertFunc),
        path("inserOk", views1.inserOkFunc),
        
        path("search", views1.searchFunc), # 검색
        
        path("content", views1.contentFunc), # 자세히보기
        
        path("update", views1.updateFunc),
        path("updateok", views1.updateOkFunc),
        
        path("delete", views1.deleteOkFunc),
        path("deleteok", views1.deleteOkFunc),
    ]

     

    board.html 페이지 작성

     

    boardmain.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    {{msg | safe}}
    메뉴1 분석1 <a href="/board/list">게시판</a>
    </body>
    </html>

    {{msg | safe}} 를 사용해서 html 형식으로 넣어주었다.

     

    board.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" />
    </head>
    <body style="margin:20px;">
    <div style="width:95%; text-align: center;">
    	<h3>** 게시판 목록 **</h3>
    </div>
    <a href="/board/insert">글쓰기</a>
    
    <table style="width: 95%;" class="table">
    	<tr>
    		<th>번호</th>
    		<th>제 목</th>
    		<th>작성자</th>
    		<th>작성일</th>
    		<th>조회수</th>
    	</tr>
    	{% if datas %}
    	{% for d in datas %}
    	<tr>
    		<td>{{d.id}}</td>
    		<td>{{d.title}}</td>
    		<td>{{d.name}}</td>
    		<td>{{d.bdate.year}}.{{d.bdate.month}}.{{d.bdate.day}}</td>
    		<td>{{d.readcnt}}</td>
    	</tr>
    	{% endfor %}
    	
    	<!-- 페이징 처리 -->
    	<tr>
    		<td colspan="5" style="text-align: center;">
    		{% if datas.paginator.num_pages > 1 %}
    			<div>
    				{% if datas.has_previous %} <!-- 이전 페이지가 있다면 -->
    					<a href="/board/list?page={{datas.previous_page_number}}">&laquo;이전</a>
    				{% endif %}
    				{% if datas.has_next %} <!-- 이전 페이지가 있다면 -->
    					<a href="/board/list?page={{datas.next_page_number}}">다음&raquo;</a>
    				{% endif %}
    				&nbsp;&nbsp;
    				(페이지:{{datas.number}} / {{datas.paginator.num_pages}})
    			</div>
    		{% endif %}
    		</td>
    	</tr>
    	
    	{% else %}
    	<tr>
    		<td colspan="5">자료가 없습니다.</td>
    	</tr>
    	{% endif %}
    	
    	<!-- 검색 기능 -->
    	<tr>
    		<td colspan="5" style="text-align: center;">
    			<form action="/board/search" method="post">{% csrf_token %}
    				검색 :
    				<select name="s_type">
    					<option value="title" selected>글제목</option>
    					<option value="name" selected>작성자</option>
    				</select>
    				<input type="text" name="s_value"/>
    				<input type="submit" value="확인" />
    			</form>
    		</td>
    	</tr>
    </table>
    </body>
    </html>

    datas의 값들을 반복문 돌려 DB를 분류시킨 뒤, {{}} 태그로 값을 넣어준다.

    페이징 처리를 한다. 검색 기능도 넣어준다.

     

    views.py

    from django.shortcuts import render
    from myboard.models import BoardTab
    from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
    
    # Create your views here.
    def mainFunc(request):
        aa = "<div><h2>게시판 메인</h2></div>"
        return render(request, 'boardmain.html', {'msg': aa})
    
    def listFunc(request):
        data_all = BoardTab.objects.all().order_by('-id') # 댓글 코드 X
        
        paginator = Paginator(data_all, 5)
        page = request.GET.get('page')
        try:
            datas = paginator.page(page)
        except PageNotAnInteger:
            datas = paginator.page(1)
        except EmptyPage:
            datas = paginator.page(paginator.num_pages)
    
        return render(request, 'board.html', {'datas':datas})

    페이징 처리를 위해 paginator를 import 하여 사용하였다.

    BoardTab DB의 내용을 data_all에 담아 그것을 다시 paginator에 넣어 그 값을 datas에 담아 board.html로 보내준다.

     

     

     

    게시글 insert

    views.py

    def insertFunc(request):
        return render(request, 'insert.html')
    
    
    def insertOkFunc(request):
        if request.method == 'POST':
            try:
                gbun = 1 # Group number 구하기
                datas = BoardTab.objects.all()
                if datas.count() != 0:
                    gbun = BoardTab.objects.latest('id').id + 1
                
                BoardTab( # table 순서대로 들어가야 된다.
                    name = request.POST.get('name'),
                    passwd = request.POST.get('passwd'),
                    mail = request.POST.get('mail'),
                    title = request.POST.get('title'),
                    cont = request.POST.get('cont'),
                    bip = request.META['REMOTE_ADDR'], # 클라이언트 컴퓨터 ip를 찾아주는 코드
                    bdate = datetime.now(), # datetime import 해서 사용
                    readcnt = 0,
                    gnum = gbun,
                    onum = 0,
                    nested = 0
                    ).save()
            except Exception as e:
                print('insert err : ', e)
                return render(request, 'error.html')
    
        return redirect('/board/list') # 추가 후 목록 보기

    datas에 아무것도 존재하지 않는다면 error.html로 가도록 만든다.

    만약 들어있다면 post방식으로 가져온 값들을 넣어주고 그 값들을 save한다.

    새로 생성되는 글은 gbun == id값이므로 + 1을 해줘서 만약 id = 8번이면 새로 생성되는 글은 9번으로 생성되게 한다.

    redirec 방식으로 /board/list로 보내서 board.html를 출력하게 한다.

     

     

    error.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    </head>
    <body>
    <p>작업 중 에러 발생!!!</p>
    <a href="/board/list?page=1">게시판 글 목록</a>
    </body>
    </html>

     

    insert.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" />
    <script>
    window.onload = function(){
    	document.querySelector("#btnOk").onclick = chkFunc;
    }
    
    function chkFunc(){
    	// alert('a');
    	if(frm.name.value === ""){
    		alert('작성자를 입력하시오');
    		frm.name.focus()
    		return;
    	}
    	// 나머지는 생략
    	
    	frm.submit();
    }
    
    </script>
    </head>
    <body style="margin:20px;">
    <p>** 새글 입력 **</p>
    <form action="/board/insertok" name="frm" method="post">{% csrf_token %}
    	<table style="width: 95%;" class="table">
    		<tr>
    			<td>작성자:</td>
    			<td><input type="text" name="name"/></td>
    		</tr>
    		<tr>
    			<td>비밀번호:</td>
    			<td><input type="text" name="passwd"/></td>
    		</tr>
    		<tr>
    			<td>이메일:</td>
    			<td><input type="email" name="mail"/></td>
    		</tr>
    		<tr>
    			<td>글제목:</td>
    			<td><input type="text" style="width: 99%" name="title"/></td>
    		</tr>
    		<tr>
    			<td>글내용:</td>
    			<td><textarea rows="5" style="width: 99%" name="cont"></textarea></td>
    		</tr>
    		<tr>
    			<td colspan="2" style="text-align: center;">
    				<input type="button" value="등록" id="btnOk" class="btn btn-primary"/>
    			</td>
    		</tr>
    	</table>
    </form>
    </body>
    </html>

    POST 타입으로 값들을 views.py의 insertok 함수로 보낸다.

     

     

    추가 되면 밑에 lsit가 최신화 된다. redirec 요청

     

     

    search (검색기능)

    views.py

    def searchFunc(request):
        if request.method == 'POST':
            s_type = request.POST.get('s_type')
            s_value = request.POST.get('s_value')
            # print(s_type, s_value)
            # SQL의 like 연산 --> ORM에서는 __contains=값
            if s_type == 'title':
                datas_search = BoardTab.objects.filter(title__contains=s_value).order_by('-id')
            elif s_type == 'name':
                datas_search = BoardTab.objects.filter(name__contains=s_value).order_by('-id')
                
            paginator=Paginator(datas_search, 5)
            page=request.GET.get('page')
            
            try:
                datas=paginator.page(page)
            except PageNotAnInteger:
                datas=paginator.page(1)
            except EmptyPage:
                datas=paginator.page(paginator.num_pages)
            
            return render(request, 'board.html', {'datas':datas})

    POST값으로 받은 s_value값과, s_type 값을 받은 뒤에 filter를 건 다음 datas_search라는 변수명에 넣은 뒤에 페이징 처리를 하고 그 데이터를 board.html로 보낸다.

     

    board.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" />
    </head>
    <body style="margin:20px;">
    <div style="width:95%; text-align: center;">
    	<h3>** 게시판 목록 **</h3>
    </div>
    <a href="/board/insert">글쓰기</a>
    
    <table style="width: 95%;" class="table">
    	<tr>
    		<th>번호</th>
    		<th>제 목</th>
    		<th>작성자</th>
    		<th>작성일</th>
    		<th>조회수</th>
    	</tr>
    	{% if datas %}
    	{% for d in datas %}
    	<tr>
    		<td>{{d.id}}</td>
    		<td>
    			<a href="/board/content?id={{d.id}}&page={{datas.number}}">{{d.title}}</a>
    		</td>
    		<td>{{d.name}}</td>
    		<td>{{d.bdate.year}}.{{d.bdate.month}}.{{d.bdate.day}}</td>
    		<td>{{d.readcnt}}</td>
    	</tr>
    	{% endfor %}
    	
    	<!-- 페이징 처리 -->
    	<tr>
    		<td colspan="5" style="text-align: center;">
    		{% if datas.paginator.num_pages > 1 %}
    			<div>
    				{% if datas.has_previous %} <!-- 이전 페이지가 있다면 -->
    					<a href="/board/list?page={{datas.previous_page_number}}">&laquo;이전</a>
    				{% endif %}
    				{% if datas.has_next %} <!-- 이전 페이지가 있다면 -->
    					<a href="/board/list?page={{datas.next_page_number}}">다음&raquo;</a>
    				{% endif %}
    				&nbsp;&nbsp;
    				(페이지:{{datas.number}} / {{datas.paginator.num_pages}})
    			</div>
    		{% endif %}
    		</td>
    	</tr>
    	
    	{% else %}
    	<tr>
    		<td colspan="5">자료가 없습니다.</td>
    	</tr>
    	{% endif %}
    	
    	<!-- 검색 기능 -->
    	<tr>
    		<td colspan="5" style="text-align: center;">
    			<form action="/board/search" method="post">{% csrf_token %}
    				검색 :
    				<select name="s_type">
    					<option value="title" selected>글제목</option>
    					<option value="name" selected>작성자</option>
    				</select>
    				<input type="text" name="s_value"/>
    				<input type="submit" value="확인" />
    			</form>
    		</td>
    	</tr>
    </table>
    </body>
    </html>

    select 문을 만들어서 글제목, 작성자 형태로 출력시킨다. 글제목은 title, 작성자는 name으로 만든다.

    그것들을 select문의 name으로 s_type으로 만든 뒤, 입력창을 s_value로 지은다음 그것을 /board/search로 보낸다.

     

     

    조회수 기능(content 게시판 자세히보기)\

    views.py

    def contentFunc(request):
        page = request.GET.get('page')
        data = BoardTab.objects.get(id=request.GET.get('id'))
        data.readcnt = data.readcnt + 1 # 조회수 증가
        data.save() # 조회수 update
        
        return render(request, 'content.html', {'data_one':data, 'page':page})

    해당 글이 있는 page 번호를 가져온 뒤, 그 아이디 값의 데이터를 가져온다. readcnt가 조회수이기 때문에 해당 글에 들어가게 되면 조회수가 올라가도록 +1을 한다. 그 값을 다시 dict 타입으로 content.html로 가지고 간다.

     

    content.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" />
    </head>
    <body style="margin:20px;">
    <h2>* {{data_one.id}}번 게시글 내용 *</h2>
    <table class="table">
    	<tr style="width:95%;">
    		<td style="text-align: right; width: 95%">
    			<a href="/board/update?id={{data_one.id}}">수정</a>
    			<a href="/board/delete?id={{data_one.id}}">삭제</a>
    			<a href="/board/list?page={{page}}">목록</a>
    		</td>
    	</tr>
    </table>
    
    <table class="table">
    	<tr style="width: 95%; background-color: lightgray">
    		<td>
    			작성자 : <a href="mailto:{{data_one.mail}}">{{data_one.name</a>
    			(ip : {{data_one.bip}})
    		</td>
    		<td>작성일 : {{data_one.bdate}}</td>
    		<td>조회수 : {{data_one.readcnt}}</td>
    	</tr>
    	<tr>
    		<td colspan="3">제목 : {{data_one.title}}</td>
    	</tr>
    	<tr>
    		<td colspan="3">
    			<textarea rows="5" style="width:99%;" readonly>{{data_one.cont}}</textarea>
    		</td>
    	</tr>
    </table>
    </body>
    </html>

    board.html에서 content.html로 갈 수 있게 a태그를 사용했다. content.html로 오면 id값으로 데이터를 불러와 그것을 넣어준다. 목록으로 돌아갈 수도 있기때문에 해당 글이 있는 페이지로 갈 수 있도록 page 데이터를 넣어주었다.

     

    글 수정하기

    views.py

    def updateFunc(request):
        try:
            data = BoardTab.objects.get(id=request.GET.get('id'))
            
        except Exception as e:
            return render(request, 'error.html')
            
        return render(request, 'update.html', {'data_one':data})
    
    
    def updateOkFunc(request):
        try:
            upRec = BoardTab.objects.get(id=request.POST.get('id'))
        
            # 비밀번호 비교 후 수정 여부 결정
            if upRec.passwd == request.POST.get('up_passwd'):
                upRec.name = request.POST.get('name'),
                upRec.mail = request.POST.get('mail'),
                upRec.title = request.POST.get('title'),
                upRec.cont = request.POST.get('cont'),
                upRec.save()
            else:
                return render(request, 'update.html', {'data_one':upRec, 'msg':'비밀번호 불일치'})
        
        except Exception as e:
            return render(request, 'error.html')
    
        return redirect('/board/list')  # 수정 후 목록 보기

    get 방식으로 받은 id값으로 해당 칼럼의 데이터를 data라는 변수명에 넣고 만약 id 값이 안 넘어오면 error.html 페이지로 이동시키고 들어온다면 그 데이터를 가지고 update.html로 이동시킨다.

    update.html에서 post타입으로 보낸 값들을 DB에 넣어줌으로서 수정한다.

    그리고 /board/list로 redirect 시킨다.

     

     

    update.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" />
    <script>
    window.onload = function(){
    	document.querySelector("#btnUpdate").onclick = chkFunc;
    }
    
    function chkFunc(){
    	// alert('a');
    	if(frm.name.value === ""){
    		alert('작성자를 입력하시오');
    		frm.name.focus()
    		return;
    	}
    	// 나머지는 생략
    	
    	frm.submit();
    }
    </script>
    </head>
    <body>
    <h2>* 게시글 수정 *</h2>
    <form action="/board/updateok" method="post" name="frm">{% csrf_token %}
    	<input type="hidden" name="id" value="{{data_one.id}}"/>
    	<table style="width: 95%;" class="table">
    		<tr>
    			<td>작성자:</td>
    			<td><input type="text" name="name" value="{{data_one.name}}"/></td>
    		</tr>
    		<tr>
    			<td>비밀번호:</td> <!-- 비밀번호를 수정 X, 비밀번호를 비교해서 맞은 경우 수정이 가능하도록 할 것이다. -->
    			<td><input type="text" name="up_passwd"/>
    				<span style="color:red">{{msg}}</span>
    			</td>
    		</tr>
    		<tr>
    			<td>이메일:</td>
    			<td><input type="email" name="mail" value="{{data_one.mail}}"/></td>
    		</tr>
    		<tr>
    			<td>글제목:</td>
    			<td><input type="text" style="width: 99%" name="title" value="{{data_one.title}}"/></td>
    		</tr>
    		<tr>
    			<td>글내용:</td>
    			<td><textarea rows="5" style="width: 99%" name="cont">{{data_one.cont}}</textarea></td>
    		</tr>
    		<tr>
    			<td colspan="2" style="text-align: center;">
    				<input type="button" value="수정" id="btnUpdate" class="btn btn-primary"/>
    				<input type="button" value="이전" class="btn btn-warning" onclick="history.back()"/>
    			</td>
    		</tr>
    	</table>
    </form>
    </body>
    </html>

    id값을 가져오기 위해 input 타입을 hidden으로 주고 post방식으로 다른 값들과 같이 넘겼다.

    비밀번호는 수정을 하려는 것이 아닌, 비밀번호를 비교해서 맞은 경우 수정이 가능하도록 할 것이다.

     

     

    글 삭제하기

    views.py

    def deleteFunc(request):
        try:
            del_data = BoardTab.objects.get(id=request.GET.get('id'))
            
        except Exception as e:
            return render(request, 'error.html')
            
        return render(request, 'delete.html', {'data_one':del_data})
    
    
    def deleteOkFunc(request):
        del_data=BoardTab.objects.get(id=request.POST.get('id'))
        
        if del_data.passwd == request.POST.get('del_passwd'):
            del_data.delete();
            return redirect('/board/list') #삭제 후 보드의 리스트로 보내주기(목록보기)
        else:
            return render(request, 'error.html')

    id 값을 가져와서 그것을 다시 deleteOkFunc 로 보내서 id값의 컬럼을 삭제한다.

     

     

    delete.html

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" />
    <script>
    window.onload = function(){
    	document.querySelector("#btnDel").onclick = delFunc;
    	document.querySelector("#btnCancel").onclick = cancelFunc;
    }
    
    function delFunc(){
    	// alert("a");
    	if (document.querySelector("#del_passwd").value === ""){
    		document.querySelector("#del_passwd").placeholder = "비밀번호 입력";
    	}else{
    		let result = confirm("정말 삭제할까요?");
    		if(result){
    			document.querySelector("form").submit()
    		}
    	}
    }
    
    function cancelFunc(){
    	// alert("b");
    	history.back();
    }
    </script>
    </head>
    <body style="margin:20px;">
    <h2>* 게시글 삭제 *</h2>
    <form action="/board/deleteok" method="post"> {% csrf_token %}
    <input type="hidden" name="id" value="{{data_one.id}}"/>
    <table style="width:95%" class="table">
    	<tr>
    		<th colspan="2">삭제하려면 비밀번호를 입력하세요</th>
    	</tr>
    	<tr>
    		<td>비밀번호 : </td>
    		<td><input type="text" name="del_passwd" id="del_passwd"/></td>
    	</tr>
    	<tr>
    		<td colspan="2">
    			<input type="button" value="삭제" id="btnDel"/>&nbsp;
    			<input type="button" value="취소" id="btnCancel"/>
    		</td>
    	</tr>
    </table>
    </form>
    </body>
    </html>

     

     

    댓글

Designed by Tistory.