[웹 프로그래밍 기초] 웹페이지 댓글 기능 구현하기

2020. 6. 17. 14:54노트/Django : 웹

 

  • VS code TERMINAL에 project 폴더로 이동해서 가상환경 활성화 

python -m venv venv

source venv/Scripts/activate

 

  • 가상환경 내에 django 설치 

pip install django==2.2.13

 

 

  • data가 있을 경우 마이그레이션 

 

python manage.py makemigrations 

python manage.py migrate

 

  • formclass > articles> models.py 
from django.db import models

# Create your models here.
class Article(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)


# 1:N Relation 
class Comment(models.Model):
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    article = models.ForeignKey(Article, on_delete = models.CASCADE)
    # Article : Comment = 1: N 
    #(부모): (자식)


    # on_delete 옵션
    # 1. CASCADE - 부모가 삭제되면, 자식도 삭제됨 
    # 2. PROTECT - 자식이 있으면 , 부모 삭제 불가 
    # 3. SET_NULL - 부모가 삭제되면, 자식의 FK에 null 할당 
    # 4. SET_DEFAULT - ...., 자식의 FK에 default 값 할당 
    # 5. DO_NOTHING - 아무것도 하지 않음 

 

 # 1. Create 

    # article = Article.objects.get(pk=1)

    # comment = Comment()

    # comment.content = '첫 댓글입니다!'

    # comment.article = article 

    # ( comment.article_id = 1 ) 이렇게도 가능 

    # comment.save()

 

# 2. Read 

# 2-1. 부모로 부터 자식들 가져오기 

    # article = Article.objects.get(pk=1)

    # comments = article.comment_set.all()

 

# 2-2. 자식 테이블에서 조건으로 가져오기

    # article = Article.objects.get(pk=1) 

    # comments = Comments.objects.filter(article)

    # ( .filter(article_id = 1) )  이렇게도 가능 

 

# 3. 자식이 부모를 부르기 ( N에서 1 불러오기 )

    # comment = Comment.objects.get(pk=1)

    # article = comment.article 

    # article.title #=> '안녕'

    # article.comntent #=> '반가워' 

 

 

  • formclass>articles>admin.py 
from django.contrib import admin
from .models import Article, Comment

class ArticleAdmin(admin.ModelAdmin):
    list_display = ('pk', 'title', 'created_at', 'updated_at',)

class CommentAdmin(admin.ModelAdmin):
    list_display = ('pk', 'content',)

admin.site.register(Article, ArticleAdmin )
admin.site.register(Comment, CommentAdmin )

 

  • formclass>articles>forms.py
from django import forms
from .models import Article, Comment


class ArticleForm(forms.ModelForm):
    class Meta:
        model = Article
        fields = ('title', 'content',)

class CommentForm(forms.ModelForm):
    class Meta: 
        model = Comment
        fields = ('content', )


# class ArticleForm(forms.Form):
#     title = forms.CharField(
#                     max_length=10,
#                     label='제목',
#                     widget=forms.TextInput(
#                         attrs={
#                             'class': 'title',
#                             'placeholder': '제목을 입력하세요.',
#                         }
#                     )
#                 )
#     content = forms.CharField(
#                     label='내용',
#                     widget=forms.Textarea(
#                         attrs={
#                             'class': 'content',
#                             'rows': 5,
#                             'cols': 20,
#                         }
#                     )
#                 )

 

  • formclass> articles>templates>articles>detail.html 
{% extends 'base.html' %}

{% block body %}

<h1>Article Detail</h1>

<p>PK: {{ article.pk }}</p>
<p>제목: {{ article.title }}</p>
<p>내용: {{ article.content }}</p>
<p>생성 시각: {{ article.created_at }}</p>
<p>수정 시각: {{ article.updated_at }}</p>

<a href="{% url 'articles:index' %}">Index</a>
<a href="{% url 'articles:edit' article.pk %}">Edit</a>

<form action="{% url 'articles:delete' article.pk %}" method="POST">
    {% csrf_token %}
    <input type="submit" value="Delete">
</form>

<!-- 댓글 목록 -->
<h3>댓글 목록 ({{ article.comment_set.all|length }})</h3>
<!-- +length, .count 갯수 구하기-->
<ul>
    {% for comment in article.comment_set.all %}
    <li>
        {{ comment.content }} - {{ comment.created_at }}
        <a href="{% url 'articles:comments_edit' article.pk comment.pk %}">Edit</a>
        <form action="{% url 'articles:comments_delete' article.pk comment.pk %}" method = "POST">
            {% csrf_token %}
            <input type="submit" value="Delete!">
        </form>
    </li>
    {% empty %}
    <li>
        작성된 댓글이 없습니다 :(
    </li>
    {% endfor %}
</ul>

<!-- 댓글 입력창 -->
<h3>댓글 입력</h3>
<form action="{% url 'articles:comments_new' article.pk%}" method="POST">
    {% csrf_token %}
    {{ comment_form }}
    <input type ="submit">
</form>

{% endblock %}

 

  • formclass>articles>views.py 
from django.shortcuts import render, redirect
from .models import Article, Comment
from .forms import ArticleForm, CommentForm

# Create your views here.
def index(request):
    articles = Article.objects.all()
    context = {
        'articles': articles,
    }
    return render(request, 'articles/index.html', context)

def new(request):
    if request.method == 'POST':
        # Database에 저장
        # 1. 요청에 실려온 data 꺼내오기
        # title = request.POST.get('title')
        # content = request.POST.get('content')
        form = ArticleForm(request.POST)
        
        # 2-1. data 유효성 검사
        if form.is_valid():
            # (ModelForm) 2-2. Database에 저장
            article = form.save()
            # # 2-2. 검증된 data 꺼내오기
            # title = form.cleaned_data.get('title')
            # content = form.cleaned_data.get('content')
            # # 2-3. Database에 저장
            # article = Article(title=title, content=content)
            # article.save()
            # 3. 저장된 data를 확인할 수 있는 곳으로 안내
            return redirect('articles:detail', article.pk)

    else: # GET
        # 작성 양식 보여주기
        form = ArticleForm()
    context = {
        'form': form,
    }
    return render(request, 'articles/new.html', context)


def detail(request, pk):
    # Database에서 data 가져오기
    article = Article.objects.get(pk=pk)


    # 댓글 작성 양식 가져오기 
    comment_form = CommentForm()

    context = {
        'article': article,
        'comment_form': comment_form,
    }
    return render(request, 'articles/detail.html', context)


def delete(request, pk): # POST
    article = Article.objects.get(pk=pk)
    if request.method == 'POST':
        article.delete()
    return redirect('articles:index')

def edit(request, pk):
    # 1. Database에서 data 가져오기
    article = Article.objects.get(pk=pk)

    if request.method == 'POST':
        # data 수정!
        
        # (ModelForm) 2-1. form에 data 집어넣기 + instance와 연결
        form = ArticleForm(request.POST, instance=article)
        # # 2-1. form에 data 집어넣기(검증 목적)
        # form = ArticleForm(request.POST)
        # 2-2. form에서 data 유효성 검사
        if form.is_valid():
            # (ModelForm) 2-3. Database에 저장
            article = form.save()
            # # 2-3. 검증된 data를 반영하기(저장)
            # article.title = form.cleaned_data.get('title')
            # article.content = form.cleaned_data.get('content')
            # article.save()
            # 3. 저장된 내용을 확인할 수 있는 페이지로 안내
            return redirect('articles:detail', article.pk)
    else:  
        # 수정 양식 보여주기!
        # (ModelForm) 2. Form에 data 채워 넣기
        form = ArticleForm(instance=article)
        # # 2. Form에 data 채워 넣기
        # form = ArticleForm(initial=article.__dict__)
    context = {
        'form': form,
    }
    return render(request, 'articles/edit.html', context)

def comments_new(request, article_pk): #POST 
    # 1. 요청이 POST 인지 점검
    if request.method  == 'POST': 
        # 2. form에 data를 집어넣기 (목적 == 유효성 검사)
        form = CommentForm(request.POST)
        # request.POST #=>
        # 3. form에서 유효성 검사를 시행
        if form.is_valid():
            # 4. 통과하면 database에 저장 
            comment = form.save(commit=False)
            # 4-1. article 정보 주입
            comment.article_id = article_pk 
            comment.save()
    # 5. 생성된 댓글을 확인할 수 있는 곳으로 안내 
    return redirect('articles:detail', article_pk)

def comments_delete(request, article_pk, pk): # POST 
    # 0. 요청이 POST인지 점검 
    if request.method == 'POST':
        # 1. pk를 가지고 삭제하려는 data를 꺼내오기 
        comment = Comment.objects.get(pk=pk)
        # 2. 삭제
        comment.delete()
    # 3. 삭제되었는지 확인 가능한 곳으로 안내 
    
    return redirect('articles:detail', article_pk)

def comments_edit(request,article_pk, pk): # GET , POST 
    # Database에서 수정하려 하는 data 가져오기 
    comment = Comment.objects.get(pk=pk)
    # 0. 요청의 종류가 POST인지 GET인지 점검 
    if request.method == 'POST':
        # 실제로 수정 ! 
        # 1. form에 '넘어온 data' & '수정하려는 data' 집어넣기 
        form = CommentForm(request.POST, instance=comment)
        # 2. 유효성 검사 
        if form.is_valid():
            # 3. 검사를 통과했다면, save 
            comment = form.save()
            # 4. 변경된 결과 확인하는 곳으로 안내 
            return redirect('articles:detail', article_pk)
    else : 
        # 수정 양식 보여주기! 
        # 1. form class 초기화 (생성)
        form = CommentForm(instance=comment)

    context ={
        'form' : form,
    }
    return render(request,'articles/comments_edit.html', context)

 

  • formclass>articles>templates>articles>comments_edit.html 
{% extends 'base.html' %}

{% block body %}

<h1> Comments Edit </h1>
<form action="" method ="POST">
    {% csrf_token %}
    {{ form }}
    <input type="submit">

</form>

{% endblock %}

 

  • formclass> articles>urls.py 
from django.urls import path
from . import views

app_name = 'articles'

urlpatterns = [
    path('', views.index, name='index'),
    path('new/', views.new, name='new'),
    path('<int:pk>/', views.detail, name='detail'),
    path('<int:pk>/delete/', views.delete, name='delete'),
    path('<int:pk>/edit/', views.edit, name='edit'),
    # /articles/1/comments/new/
    path('<int:article_pk>/comments/new/', views.comments_new, name='comments_new'),
    path('<int:article_pk>/comments/<int:pk>/delete/', views.comments_delete, name = 'comments_delete'),
    path('<int:article_pk>/comments/<int:pk>/edit', views.comments_edit, name ='comments_edit'),