https://itcreator.tistory.com/129
이어서 하기
이전버전은 USER는 DB를 만들지 않아도 따로 생성되었음 (물론 커스터마이징 가능하지만 그건 나중에)
이번은 이전설정과 거의 비슷하게 한번 더 앱을 만들어 보고 게시판/댓글 DB 까지 다뤄보기
1. 기초설계
1. ctrl + shift + ~ 터미널 창 열어주기
2. board 앱 폴더 생성 및 등록
# root 기준 터미널 명령어
python manage.py startapp <app 이름>
mkdir -p <app 이름>/templates/<app 이름> # 템플릿 생성
cd <app 이름>/templates/<app 이름> # 최하단 까지 이동
touch detail.html form.html index.html
#detail 게시판글 자세히보기
#form 게시판 작성 폼
#index 게시글 리스트
3. urls.py 및 forms.py 생성
# 다시 최상단 으로 돌아와서( 보통 cd .. 3번 하면됨)
Touch <app이름>/urls.py # (..../<app이름> 으로 들어오는 통로 열어주기)
Touch <app이름>/forms.py
# Form 이 친구는 왜만드는거임?
# 1. 유효성 체크 (validation check)
# 2. HTML 안에 인풋태그 만들기 귀찮
# 3. 저장할 때 request.POST에서 일일이 꺼내기 귀찮아서
# 이런 작업을 Form 하게 만들려고 미리 폴더 생성
2. board > urls.py 세팅해주기 (앱이름을 board 로 할 경우)
from django.urls import path
from . import views
app_name = 'board'
urlpatterns = [
# articles/create/
path('create/', views.create_article, name='create_article'), # url에 articles/create/으로 접근이 온다면 => views 에 있는 create_article 함수 실행 / 그리고 이 과정을 create_article 이 라고 칭하겠다.
# articles/
path('', views.article_index, name='article_index'),
# articles/1/
path('<int:article_pk>/', views.article_detail, name='article_detail'),
# articles/1/update/
path('<int:article_pk>/update/', views.update_article, name='update_article'),
# articles/1/delete/
path('<int:article_pk>/delete/', views.delete_article, name='delete_article'),
3. Model 단 (board > models.py)
from django.db import models
from django.conf import settings
class Article(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) # settings.AUTH_USER_MODEL 파이썬이 이렇게 써달래
title = models.CharField(max_length=100) # on_delete=models.CASCADE 이게 지워지면 안에 다른것도 다 지워달라
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Comment(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
content = models.CharField(max_length=200)
# foreign key 쓰면 필드명에 _id 붙이지 않기.
# migrate 하면 알아서 뒤에 _id가 붙음
article = models.ForeignKey(Article, on_delete=models.CASCADE)
Created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
3-1 . Form 단 (board > forms.py)
from django import forms
from .models import Article, Comment
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
#fields= '__all__'
exclude = ('user',) # user는 Article의 FK라 article테이블의 user의 키가
# User테이블의 pk값이 없는걸 넣을려고 하면 DB에서 막음 (무결성)
class CommentForm(forms.ModelForm):
content = forms.CharField(min_length=2, max_length=200,
widget=forms.TextInput(attrs={'autofocus': True})
)
class Meta:
model = Comment
# fields = ('content',)
exclude = ('article', 'user')
model. form 끝냈으면 마이그레이트 시작
python manage.py makemigrations board
python manage.py migrate board
4. Control 단 (board > views.py)
1. 쉬운 버전용 필요한 클래스 import 하기
# 화면구성, 화면이동, 객체를 받거나 없으면 404에러를 터트려줌 (이거 없으면 서버 문제가 아닌데도 500 서버단 에러를 터트림)
from django.shortcuts import render, redirect, get_object_or_404
# get, post등 url 접근 방식에 따라 분류해서 허용하겠다
from django.views.decorators.http import (require_http_methods, require_POST, require_safe, )
# 로그인 상태에만 해당 함수 실행가능
from django.contrib.auth.decorators import login_required
# DB 불러오기
from .models import Article, Comment
# 앞서 만듬 form 불러오기
from .forms import ArticleForm, CommentForm
2. create_article 작성
@login_required # 로그인이 되어야 이 작업가능
@require_http_methods(['GET', 'POST']) # GET, POST로 들어오는 경우
def create_article(request): #request 요청 받은 정보 전부 인수로 전달한뒤 함수 실행
if request.method == 'POST': # POST라면(게시글 등록버튼 누를때)
form = ArticleForm(request.POST) # POST로 쏘는 내용물 채워서 form에 저장
if form.is_valid(): # 값이 유효하다면
article = form.save(commit=False) # 잠깐 저장은 하지말아봐 # article부터 만들어야하는데 save를 하면 오류터짐
article.user = request.user # 페이징 요청자의 정보를 글작성자의 정보로 넣어줌
article.save() # 게시글 저장(내장함수)
return redirect('board:article_detail', article.pk) # 글번호와 함께 detail() 함수실행
else: # GET으로 왔다면(페이지를 요청할때)
form = ArticleForm() # form.py에서 만든걸 form으로
context = {'form': form } # context에 담아서
return render(request, 'board/form.html', context) # form.html로 넘겨줌
3. read
@require_safe # get, head 요청만 허용하겠다.
def article_index(request):
articles = Article.objects.all() # DB의 article의 모든 값을 articles에 넣어줌
context = {'articles': articles, } # 그걸 context에 담아서
return render(request, 'board/index.html', context) # index.html로 넘겨줌
@require_safe
def article_detail(request, article_pk): # 몇번째 게시글인지 알 수 있게 article_pk 받아야함
article = get_object_or_404(Article, pk=article_pk) # db에서 객체를 받던가 404를 띄워라! why? DB에 없는걸 요청한다면 니 잘못
form = CommentForm() # 댓글보여줄려고-> detail.html에 board/_comment_form.html에 form 쓸려고
context = { # 게시글 정보와 댓글을 context에 담아서
'article': article,
'form': form
}
return render(request, 'board/detail.html', context) # detail.html로 보냄
4. update
@login_required # 로그인 필요
@require_http_methods(['GET', 'POST']) # GET, POST로 오는 경우
def update_article(request, article_pk):
article = get_object_or_404(Article, pk=article_pk) # 받은 번호의 게시글 정보를 DB에서 찾아 article에 넣어줌
if request.user != article.user: # 작성자 아니면
return redirect('board:article_detail', article.pk) # 게시글 번호와함께 detail.html로 보내버림
if request.method == 'POST': # POST인 경우(게시글 수정 버튼을 누를때)
form = ArticleForm(request.POST, instance=article) # instance=student 가 없으면 수정이 아니라 새 값으로 저장되어버림 + 이전정보 넣어주기
if form.is_valid(): # form이 유효하다면
# 기존에 저장된 user_id 갱신할 필요가 없기때문에 commit=False 필요 X
article = form.save() # 저장해서 article에 넣어주기
return redirect('board:article_detail', article.pk) # 게시글 번호 주면서 detail() 함수실행
else: # get방식이라면 (이 페이지를 요청할때)
form = ArticleForm(instance=article) # # instance=student => form에다가 이전 정보 넣기
context = {'form': form} # context에 담아서
return render(request, 'board/form.html', context) # form.html로 보냄
5. Delete
@login_required # 로그인 필요
@require_POST # POST인 경우(url로 들어오는거 막겠다/ 제출버튼으로 토큰 던지면서 와라)
def delete_article(request, article_pk): # 요청받은 정보와 게시글 번호를 주면서
article = get_object_or_404(Article, pk=article_pk) # 번호에 맞는 Article DB에서 객체를 꺼내서 article에 주던가 404로
if request.user == article.user: # 요청자와 글쓴이가 같다면
article.delete() # 게시글 삭제(내장함수)
return redirect('board:article_index') # index로 보내주기
else: # 요청자와 글쓴이가 같지않다면
return redirect('board:article_detail', article.pk) # 게시글 번호와함께 detail.html로 보냄
6. 댓글 (간단하게 CRD만)
@require_POST
def create_comment(request, article_pk): # 요청 정보와 게시글 번호를 받음
form = CommentForm(request.POST) # 댓글 제출 버튼시 값들을 form에 넣어줌
article = get_object_or_404(Article, pk=article_pk) # 받은 번호의 게시글 정보를 DB에서 찾아 article에 넣어줌
if form.is_valid(): # form이 유효하다면
comment = form.save(commit=False) # 완전 저장시 NOT NULL 에러 뜨니까, 직전에 멈춰 주세요.
comment.article = article # 여기선 알아서 값 넣어줌
comment.user = article # 여기선 알아서 값 넣어줌
comment.save() # 값 저장(내장함수)
return redirect('board:article_detail', article.pk) # 게시글 번호 받아서 detail() 함수 실행
# comment.article.pk 게시글 번호임
@require_safe # get, head 요청만 허용하겠다.
def article_index(request): # 요청한값 받음
articles = Article.objects.all() Article DB 값 받아서 articles에 넣어줌
context = {'articles': articles, } # 그 값을 context 넣고
return render(request, 'board/index.html', context) # index.html로 보내줌
@require_safe
def article_detail(request, article_pk): # 요청정보와 게시글 번호 받음
article = get_object_or_404(Article, pk=article_pk) # 받은 번호의 게시글 정보를 DB에서 찾아 article에 넣어줌
form = CommentForm() # 댓글보여줄려고-> detail.html에 board/_comment_form.html에 form 쓸려고
context = {
'article': article,
'form': form
}
return render(request, 'board/detail.html', context)
@login_required
@require_POST
def delete_comment(request, article_pk, comment_pk):
comment = get_object_or_404(Comment, pk=comment_pk)
article = get_object_or_404(Article, pk=article_pk) # article.pk 를 쓰기위해서 만듬
if request.user == comment.user:
comment.delete()
return redirect('board:article_detail', article.pk)
# article_pk 안쓰는 이유는 사용자를 믿을수없어서.
# 사용자가 보낸거 _pk 구분한다음 내부적으로 확인해서 믿을하면 .pk 써도 무방
총정리
@login_required
@require_http_methods(['GET', 'POST'])
def create_article(request):
if request.method == 'POST':
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.user = request.user
article.save()
return redirect('board:article_detail', article.pk)
else:
form = ArticleForm()
context = {'form': form }
return render(request, 'board/form.html', context)
@login_required
@require_http_methods(['GET', 'POST'])
def update_article(request, article_pk):
article = get_object_or_404(Article, pk=article_pk)
if request.user != article.user:
return redirect('board:article_detail', article.pk)
if request.method == 'POST':
form = ArticleForm(request.POST, instance=article)
if form.is_valid():
article = form.save()
return redirect('board:article_detail', article.pk)
else:
form = ArticleForm(instance=article)
context = {'form': form}
return render(request, 'board/form.html', context)
@login_required
@require_POST
def delete_article(request, article_pk):
article = get_object_or_404(Article, pk=article_pk)
if request.user == article.user:
article.delete()
return redirect('board:article_index')
else:
return redirect('board:article_detail', article.pk)
@require_POST
def create_comment(request, article_pk):
form = CommentForm(request.POST)
article = get_object_or_404(Article, pk=article_pk)
if form.is_valid():
comment = form.save(commit=False)
comment.article = article
comment.user = article
comment.save()
return redirect('board:article_detail', article.pk)
@require_safe
def article_index(request):
articles = Article.objects.all()
context = {'articles': articles, }
return render(request, 'board/index.html', context)
@require_safe
def article_detail(request, article_pk):
article = get_object_or_404(Article, pk=article_pk)
form = CommentForm()
context = {
'article': article,
'form': form
}
return render(request, 'board/detail.html', context)
@login_required
@require_POST
def delete_comment(request, article_pk, comment_pk):
comment = get_object_or_404(Comment, pk=comment_pk)
article = get_object_or_404(Article, pk=article_pk)
if request.user == comment.user:
comment.delete()
return redirect('board:article_detail', article.pk)