오지's blog

장고(django)를 이용한 게시판 개발(CBV - Class-BasedView) 본문

개발노트/Python

장고(django)를 이용한 게시판 개발(CBV - Class-BasedView)

오지구영ojjy90 2020. 11. 6. 14:06
728x90
반응형

MVC(Model View Controler)에서 웹페이지에 해당하는 View는 Function Based View와 Class Based View가 있는데 두개의 차이는 이름에서도 알수 있는데 함수를 이용한 뷰인지 클래스를 이용한 뷰인지의 차이이다

이 자료에서는 클래스를 이용한 뷰를 이용하는데 장고에서 만들어진 클래스를 상속하여 필요한 부분만 재정의 할것이므로 아주 쉽게 게시판을 만들수 있다. 

 

1. 장고 맛보기 - 초기 프로젝트 실행해보기 

 

step1. 장고 프로젝트 생성

 

Step2. 파이썬 가상환경 생성(참고. 파이참에서는 장고프로젝트 생성시 가상환경 자동적으로 생성됨)

python -m venv venv

 

Step4. 장고 라이브러리 설치

pip install django

 

 

Step5. 장고 초기 프로젝트 실행해보기

python manage.py runserver

브라우저

http://127.0.0.1:8000/

 

 

2. html의 공통점 묶어서 라이브러리 형식으로 만들기

base folder/templates폴더에 base.html생성

아래 코드 삽입

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>게시판</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css"
          integrity="sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2" crossorigin="anonymous">
    <link href="https://fonts.googleapis.com/css2?family=Nanum+Pen+Script&display=swap" rel="stylesheet">
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
            integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj"
            crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
            integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
            crossorigin="anonymous"></script>
    <style>
        div {
            font-family: 'Nanum Pen Script', cursive;
        }
    </style>

</head>
<body>
{% block content %}
    
{% endblock %}
</body>
</html>

 

 

 

2. 본격적으로 장고를 이용한 게시판 개발

Step0. 게시판 개발에 필요한 라이브러리 설치 

게시판에 이미지도 업로드 할수 있도록 이미지업로드를 이용한 pillow 라이브러리 설치 필요

python -m pip install Pillow

 

 

Step1. 게시판 어플 생성

참고. 프로젝트이름을 이미 bbs로 생성햇기때문에 이때 설정파일이 들어있는 폴더이름이 bbs가 bbs로 app를 생성할수가 없었다. 그래서 생성파일들이 있는 폴더를 config로 이름을 바꾸었다.

 

python manage.py startapp article

Step2. settings.py에 생성한 app 등록

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'article',
]

 

Step3. bbs를 위한 모델 생성

model.py

from django.db import models
from django.utils.timezone import now

# Create your models here.

class Article(models.Model):
    title = models.CharField(max_length=256)
    pub_date = models.DateTimeField(default=now())
    update_date = models.DateTimeField(default=now())
    content = models.TextField()
    img = models.ImageField(upload_to="media/", blank=True)

    def __str__(self):
        return self.title

 

 

Step4. 장고에서 제공하는 어드민 페이지를 위한 admin.py에 관련 코드 생성

admin.py

from django.contrib import admin
from .models import Article
# Register your models here.


admin.site.register(Article)

 

 

Step5. html파일들을 위해 bbs폴더내에 templates폴더를 생성한다. 

 

Step6. View에 CRUD를 위한 코드 생성

아래 코드에도 알수 있다시피 대부분 django에서 제공하는 기능을 재정의 한것(재사용)이기 때문에 코드가 굉장히 간결하다.

 

url.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', article_views.index_view ,name="index"),
    path('create/', article_views.ArticleCreateView.as_view() ,name="create"),
    path('detail/<int:pk>', article_views.ArticleDetailView.as_view() ,name="detail"),
    path('update/<int:pk>', article_views.ArticleUpdateView.as_view() ,name="update"),
    path('delete/<int:pk>', article_views.ArticleDeleteView.as_view() ,name="delete"),
    path('list/', article_views.ArticleListView.as_view(), name="list"),
]

views.py

from django.shortcuts import render, redirect
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
from .models import Article
from django.utils.timezone import now
from django.urls import reverse, reverse_lazy
# Create your views here.



class ArticleCreateView(CreateView):
    model = Article
    fields = ['title', 'content', 'img']
    template_name = 'create.html'

    def get_success_url(self):
        return reverse('detail', kwargs={'pk':self.object.pk})



class ArticleDetailView(DetailView):
    model = Article
    template_name = 'detail.html'

    def get_context_date(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['update_date'] = now()
        return context

class ArticleUpdateView(UpdateView):
    model = Article
    fields = ['title', 'content', 'img']
    template_name = 'update.html'

    def get_success_url(self):
        return reverse('detail', kwargs={'pk':self.object.pk})

class ArticleDeleteView(DeleteView):
    model = Article
    template_name = 'delete.html'
    success_url = reverse_lazy('index')

    def get_success_url(self):
        return reverse('list')


class ArticleListView(ListView):
    model = Article
    paginate_by = 10
    template_name = 'list.html'

    def get_context_data(self, *, object_list=None, **kwargs):
        context = super().get_context_data()
        return context

def index_view(request):
    return render(request, 'index.html')

 

각각 html 생성

create.html

{% extends 'base.html' %}
{% block content %}
    <form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
        <input type="submit" value="저장">
    </form>
{% endblock %}

 

detail.html

{% extends 'base.html' %}
{% block content %}

    title: {{ object.title }}<br>
    published_date: {{ object.pub_date }}<br>
    updated_date: {{ object.update_date }}<br>
    content: {{ object.content }}<br>
    image:    <img src="{{ object.img }}" alt=""><br>


    <a href="{% url 'update' object.id %}">공지 수정</a><br>
    <a href="{% url 'delete' object.id %}">접수 취소</a></a>
    <a href="{% url 'list' %}">저장</a><br>

{% endblock %}

 

update.html

{% extends 'base.html' %}
{% block content %}
    <form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}
        <input type="submit" value="저장">
    </form>
{% endblock %}

 

delete.html

{% extends 'base.html' %}
{% block content %}
    <form action="" method="post">
    {% csrf_token %}
        <div>{{ object.title }}</div>
        <div>{{ object.pub_date }}</div>
        <div>{{ object.update_date }}</div>
        <div>{{ object.content }}</div>
        <input type="submit" value="접수 취소">
    </form>
{% endblock %}

 

list.html

{% extends 'base.html' %}
{% block content %}
<h1>Notice List</h1>
    {% for notice in object_list %}
        <li>{{ notice.pub_date}}{{ notice.title }} <a href="{% url 'detail' notice.pk %}">내용보기</a></li>
        {% empty %}
        <li>empty notice</li>
    {% endfor %}
{% endblock %}

 

Step7. 웹서버 실행을 위한 DB내 스키마 생성

python manage.py makemigrations

 

python manage.py migrate

 

 

Step8. 웹서버 실행을 위해  index페이지 생성

article/templates폴더내에 index.html파일 생성

{% extends 'base.html' %}
{% block content %}

    <a href="{% url 'create' %}">새글쓰기</a>

{% endblock %}

view생성

웹서버를 처음 실행했을대의 응답을 index.html로 주겠다는 의미

views.py

def index_view(request):
    return render(request, 'index.html')

 

url등록

url.py

index로 연결하기 위한 설정 추가

    path('', article_views.index_view ,name="index"),

 

 

 

3. 웹서버 실행 및 CRUD

Step1.웹서버 실행

python manage.py runserver

 

 

 

 

 

 

 

이때 발견은 워닝

 

 

class Notice(models.Model):
    title = models.CharField(max_length=256)
    pub_date = models.DateTimeField(default=now())
    update_date = models.DateTimeField(default=now())
    content = models.TextField()
    img = models.ImageField(upload_to="media/", blank=True)

 

 

여기서 now()이 부분을 now로 수정하여 해결

class Notice(models.Model):
    title = models.CharField(max_length=256)
    pub_date = models.DateTimeField(default=now)
    update_date = models.DateTimeField(default=now)
    content = models.TextField()
    img = models.ImageField(upload_to="media/", blank=True)

 

 

 

 

 

전체 소스코드는 github 저장소를 참고해주세요.

github.com/ojjy/bbs

Comments