WEB

[Django 07] CRUD

mhko411 2021. 3. 11. 21:08
728x90

CRUD는 소프트웨어가 기본적으로 갖는 데이터 처리 기능인 Create(생성), Read(읽기), Update(갱신), Delete(삭제)를 의미한다. Django를 통해 간단한 게시판을 만들어보며 Django의 프로젝트 흐름을 파악하고 CRUD를 이해한다.


프로젝트 생성

①. CRUD라는  프로젝트를 생성한다.

$ django-admin startproject CRUD .

②. 게시판을 만들 posts라는 애플리케이션을 생성한다.

$ python manage.py startapp posts

③. 프로젝트 폴더의 settings.py에서(CRUD/settings.py) 생성된 애플리케이션을 등록한다.


Model 만들기

①. 애플리케이션 폴더의 models.py에서 Post라는 클래스를 정의하여 속성들의 데이터형을 생성한다.

게시판의 경우 간단하게 제목, 내용, 작성시간, 수정시간을 데이터베이스에 저장하도록 한다.

from django.db import models

class Post(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) # 수정시간

    def __str__(self):
        return self.title

②. 설계한 클래스를 바탕으로 데이터베이스에 테이블의 형태로 저장될 수 있도록 설계도를 만들어준다.

$ python manage.py makemigrations

③. 이후 실제로 테이블의 형태로 변환하여 데이터베이스에 저장되도록 migrate를 한다.

$ python manage.py migrate

④. admin 페이지를 위해 관리자 계정을 생성한다. 

$ python manage.py createsuperuser

⑤. 애플리케이션 폴더의 admin.py에 Post 테이블을 등록한다.

from django.contrib import admin
from .models import Post

admin.site.register(Post)

URL 분리와 템플릿 상속

간단한 게시판을 만들 때는 큰 효과를 발휘하지 않겠지만 추후에 더욱더 복잡한 프로젝트를 위해서 애플리케이션 별로 URL을 분리하고 각각의 페이지가 공통된 UI를 갖도록 기본 템플릿을 만들어 상속을 해주도록 한다.

①. 프로젝트 폴더 하위에 templates라는 폴더를 만들어주고 그 안에 기본 베이스가 되는 HTML을 생성한다. (crud/templates/base.html)

②. settings.py에 가서 TEMPLATES라는 리스트 안의 딕셔너리 중 'DIRS'에 템플릿을 상속하여 사용할 수 있도록 설정한다. 설정 후에 애플리케이션의 HTML문서에서 상속하여 사용한다.

'DIRS': [BASE_DIR/'CRUD'/'templates'],

③. urls.py에서 include 모듈을 통해 애플리케이션 별로 URL을 분리한다.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('posts/', include('posts.urls')),
]

④. posts 애플리케이션 폴더에도 urls.py를 생성하여 URL로 요청을 받았을 때 View에서 처리하도록 한다. posts/에 들어오면 views.py에서 index함수가 처리한다.

from djang.urls import path
from . import views

app_name = "posts"
urlpatterns = [
    path('', views.index, name="index")
]

READ

메인 페이지에 작성된 글의 목록을 출력한다. 

①. views.py에서 index 함수를 생성하고 테이블에 저장된 모든 데이터를 불러와 변수로 저장하고 index.html에 넘겨준다.

from django.shortcuts import render
from .models import Post

# READ - 글의 목록을 보여주는 메인 페이지
def index(request):
    posts = Post.objects.order_by('-pk')
    context = {
        'posts':posts,
    }
    return render(request, 'posts/index.html', context)

②. posts/templates/posts/index.html에서 View에서 넘겨받은 변수를 활용하여 글의 목록을 출력한다. for문을 활용하여 모든 글의 정보를 받아 각각의 자리에 위치하도록 하였다.

{% extends 'base.html' %}

{% block content %}
<table class="table">
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">TITLE</th>
      <th scope="col">CONTENT</th>
      <th scope="col">작성 시간</th>
    </tr>
  </thead>
  <tbody>
  {% for post in posts %}
    <tr>
      <th scope="row">{{ post.pk }}</th>
      <td>{{ post.title }}</td>
      <td>{{ post.content }}</td>
      <td>{{ post.updated_at }}</td>
    </tr>
  {% endfor %}  
  </tbody>
</table>
{% endblock content %}

③. 여기까지 만들어진 메인 페이지는 다음과 같다.


CREATE

네비게이션 바에서 "새 글 작성"을 누르면 글을 작성하는 페이지로 이동하도록 한다.

①. urls.py에 create라는 경로를 추가한다.

from django.urls import path
from . import views

app_name = "posts"
urlpatterns = [
    path('', views.index, name="index"),
    path('create/', views.create, name="create"),
]

②. View에서 "새 글 작성"을 눌렀을 때 새 글을 작성하는 페이지로 이동하고 새 글을 작성했을 때는 데이터베이스에 저장하는 로직을 구성한다. "새 글 작성"은 <a>태그로 되어 있는데 이는 GET 요청을 한다.

def create(request):
    if request.method == 'POST':
        pass
    else:
        return render(request, 'articles/newpost.html')

③. 새 글을 작성하는 페이지인 newpost.html로 이동하여 form형태 안에서 글을 작성하도록 한다. 작성된 글은 POST로 다시 create에 보내지게 된다.

{% extends 'base.html' %}

{% block content %}
<form actions="{% url 'posts:create' %}" method="POST">
  {% csrf_token %}
  <h2>새 글 작성</h2>
  <div class="mb-3">
    <label for="exampleInputEmail1" class="form-label">TITLE</label>
    <input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="title">
  </div>
  <div class="form-floating mb-3">
  <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea" name="content"></textarea>
  <label for="floatingTextarea">CONTENT</label>
</div>
  <button type="submit" class="btn btn-primary">완료</button>
</form>
{% endblock content %}

④. 이렇게 만들어진 새 글 작성 페이지는 다음과 같다.

⑤. 이제 views.py로 돌아가 create의 POST 동작을 구현한다. 작성된 글은 POST로 전송이되고 객체의 create() 메서드를 통해 데이터베이스에 저장하도록 한다. 완료가 되었다면 redirect()로 메인 페이지를 보여주도록 한다. redirect는 import해서 사용한다.

def create(request):
    if request.method == 'POST':
        Post.objects.create(title=request.POST.get('title'), content=request.POST.get('content'))
        return redirect('posts:index')
    else:
        return render(request, 'posts/newpost.html')

이렇게 작성된 글은 메인 페이지의 글 목록에 추가된다.


상세 페이지 보기

글의 내용 클릭시 글의 세부 내용을 보여주는 화면으로 이동하는 것을 구현한다.

①. detail이라는 경로를 urls.py에 추가하며 URL에 해당 글의 pk값을 전달받도록 한다.

from django.urls import path
from . import views

app_name = "posts"
urlpatterns = [
    path('', views.index, name="index"),
    path('create/', views.create, name="create"),
    path('detail/<int:pk>', views.detail, name="detail"),
]

②. View에서 해당 pk값에 해당하는 글을 보여주도록 구현하여 detail.html에 전달한다.

def detail(request, pk):
    post = Post.objects.get(pk=pk)
    context = {
        'post':post,
    }
    return render(request, 'posts/detail.html', context)

③. detail.html에 상세 내용을 보여주는 템플릿을 작성한다.

{% extends 'base.html' %}

{% block content %}
  <h2>TITLE: {{ post.title }}</h2>
  <hr>
  <p>{{ post.content }}</p>
  <p class="text-sm">작성시간: {{post.created_at}}</p>
  <p clas="text-sm">수정시간: {{post.updated_at}}</p>
  <div class="d-flex">
  <button type="button" class="btn btn-primary">수정</button>
  <button type="button" class="btn btn-danger">삭제</button>
  </div>
  <a href="{% url 'posts:index'%}">BACK</a>
{% endblock content %}

 

④. 상세 내용 페이지는 다음과 같다.


UPDATE

상세 내용 페이지에서 수정을 누르면 글을 수정하는 페이지로 넘어가 글을 수정해보자.

①. update라는 경로를 추가해주고 pk값을 전달받아 해당 글을 수정할 수 있도록 한다.

from django.urls import path
from . import views

app_name = "posts"
urlpatterns = [
    path('', views.index, name="index"),
    path('create/', views.create, name="create"),
    path('detail/<int:pk>', views.detail, name="detail"),
    path('update/<int:pk>', views.update, name="update"),
]

②. View에서 글을 수정하는 페이지로 이동하는 GET 요청을 받았을 때 update.html로 이동하도록 한다.

def update(request, pk):
    if request.method == 'POST':
        pass
    else:
        post = Post.objects.get(pk=pk)
        contenxt = {
            'post':post,
        }
        return render(requst, 'posts/update.html', context

③. update.html에서는 newpost.html과 비슷하게 구성하여 수정하는 페이지를 구성한다. 이때 title의 value를 전달받은 변수의 title값으로 하고 textarea안에는 text.content를 추가하도록 한다.

{% extends 'base.html' %}

{% block content %}
<form method="POST">
  {% csrf_token %}
  <h2>수정하기</h2>
  <div class="mb-3">
    <label for="exampleInputEmail1" class="form-label">TITLE</label>
    <input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="title" value="{{post.title}}">
  </div>
  <div class="form-floating mb-3">
  <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea" name="content">{{post.content}}</textarea>
  <label for="floatingTextarea">CONTENT</label>
</div>
  <button type="submit" class="btn btn-primary">수정</button>
</form>
{% endblock content %}

④. 이렇게 수정하는 페이지로 이동하면 다음과 같이 출력된다.

⑤. 수정하기 버튼을 눌렀을 때 views.py의 update함수에 전달되어 POST일 때 처리를 하도록 한다. 수정이 완료되었을 때 다시 메인 페이지로 돌아가도록 한다.

def update(request, pk):
    if request.method == 'POST':
        post = Post.objects.get(pk=pk)
        post.title = request.POST.get('title')
        post.content = request.POST.get('content')
        post.save()
        return redirect('posts:index')
    else:
        post = Post.objects.get(pk=pk)
        context = {
            'post':post,
        }
        return render(request, 'posts/update.html', context)

⑥. 실제 수정 후에 메인 페이지에 적용된 결과는 다음과 같다. 위에서 작성한 내용과 달라진 것을 확인할 수 있다.

 


DELETE

이제 상세 내용 페이지에 들어가서 글을 삭제 해보자

①. 삭제 버튼을 눌렀을 때 삭제를 하는 URL에 접근할 수 있도록 urls.py에 delete를 추가해준다. 이때에도 pk값을 넘겨주어 해당 글을 삭제하도록 한다.

from django.urls import path
from . import views

app_name = "posts"
urlpatterns = [
    path('', views.index, name="index"),
    path('create/', views.create, name="create"),
    path('detail/<int:pk>', views.detail, name="detail"),
    path('update/<int:pk>', views.update, name="update"),
    path('delete/<int:pk>', views.delete, name="delete"),
]

 

②. View에서는 삭제한다는 GET 요청을 받아 처리를 한다. 수정과 삭제 버튼은 <a> 태그로 감쌌으며 <a> 태그는 GET 요청을 한다.

def delete(request, pk):
    post = Post.objects.get(pk=pk)
    post.delete()
    return redirect('posts:index')

③. 글을 삭제 후에는 메인 페이지로 이동하며 삭제된 것을 확인할 수 있다.


이렇게 간단한 게시판을 만들어보면서 CRUD가 무엇인지 이해할 수 있었다. 또한 계속해서 게시판을 만들 때 버튼을 눌렀을 때 이동하지 않거나 오류가 많이 발생했는데 정리를 하면서 헷갈렸던 부분들을 확실히 알 수 있었다.

'WEB' 카테고리의 다른 글

[Django 09] Authentication  (0) 2021.03.23
[Django 08] Forms  (0) 2021.03.16
[Django 06] Model  (0) 2021.03.10
[Django 05] form 사용하기  (0) 2021.03.09
[Django 04] URL 분리하기  (0) 2021.03.09