Lina's Toolbox

Django MTV 사용하기 RUD (Read, Update, Delete) 본문

스파르타 내일 배움 캠프 AI 웹개발 과정/Django framework

Django MTV 사용하기 RUD (Read, Update, Delete)

Woolina 2024. 8. 30. 15:22

 

 

보통 글작성하면 이화면이 나오지는 않죠?

  • 글을 작성한 뒤 글 목록 페이지를 보여주도록 수정해볼까요?

→ 아 ! 그러면 /create/ 경로에서 create.html 을 랜더링하는게 아니라 articles.html 을 랜더링하면 되지!

그 결과

 

…. ?

  1. 글목록 어디갔지?
  2. url은 왜 /articles/create/ 지?

html만 다른 템플릿으로 랜더링 했을뿐, 실제로 url이 이동해서 GET 요청을 보낸 것이 아닙니다!

 

 단순히 랜더링 해주는게 아니라, 리다이렉트(내가 지정한 url로 돌려보내는 것)를 해줘야한다.

 

 

redirect 사용하기

  • redirect는 지정한 URL로 되돌리는 것을 말합니다.
  • 우리가 웹 사이트를 이용하면서 많이 봐왔던 동작 방식입니다.

articles/views.py

from django.shortcuts import render, redirect

...

def create(request):
  title = request.POST.get("title")
  content = request.POST.get("content")
  article = Article(title=title, content=content)
  article.save()
  return redirect("articles")

redirect()함수 import 필요

view.py 에서 def create()를 다 돈 후,

view.py의 articles로 가서

articles의 코드를 실행하는것!!

 

💡 PRG 패턴
이러한 패턴을 PRG(Post-Redirect-Get)패턴이라고 합니다.
POST요청을 서버에서 처리하고 서버에서는 다른 주소로 Redirect하도록 응답하고 브라우저는 GET방식으로 서버를 호출하여 사용자의 요청이 반영된 것을 보여줍니다.
게시글 작성후 제출 버튼을 눌렀을 때 화면이 이동되지 않는다면 제출 버튼을 여러번 누를 수가 있겠죠?
그러면 중복된 게시글이 작성될 수 있습니다.
PRG패턴을 사용하면 반복적인 POST호출을 막을 수 있고 사용자의 입장에서도 처리가 끝나고 처음 단계로 돌아간다는 느낌을 주게 됩니다.

상세페이지 조회하기

지금까지 우리가 설계한 URL구조는 이렇습니다.

  • /articles/ → 전체 글 목록 페이지
  • /articles/new/ → 글 작성 페이지
  • /articles/create/ → 실제 글 추가 로직

→ 뭔가 허전하지 않으신가요? 게시글을 생성했다면?? 게시글을 클릭해서 읽어볼 수도 있어야겠죠? ☝️

→ 맞습니다! 글 상세페이지를 위한 URL을 추가해봅시다!

 

 

구현하기

(detail) articles/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path("", views.articles, name="articles"),
    path("<int:pk>/", views.article_detail, name="article_detail"),
    path("new/", views.new, name="new"),
    path("create/", views.create, name="create"),
    ...
]

 

(detail) articles/views.py

def article_detail(request, pk):
  article = Article.objects.get(pk=pk)
  context = {
      "article": article,
  }
  return render(request, "article_detail.html", context)

 

articles/templates/article_detail.html

{% extends "base.html" %}

{% block content %}

    <h2>글 상세 페이지</h2>
    <p>제목: {{ article.title }}</p>
    <p>내용: {{ article.content }}</p>
    <p>작성일시: {{ article.created_at }}</p>
    <p>수정일시: {{ article.updated_at }}</p>

    <a href="{% url 'articles' %}">목록 보기</a>

{% endblock content %}

 

 

추가 작업

  • 자 이렇게 해서 상세페이지도 불러올 수 있게 되었습니다.
  • 저희는 리다이렉트도 배웠고 게시글 상세페이지URL도 설계했으니 게시글 작성후 상세페이지로 리다이렉트시키는 걸 구현해봅시다.

articles/views.py

def create(request):
	title = request.POST.get("title")
	content = request.POST.get("content")
	article = Article(title=title, content=content)
	article.save()
  return redirect("article_detail", article.id)

 

 

편의성 업데이트

  • 프로젝트 편의성 업데이트를 해볼까요?
  • 프로젝트에서 불편한 점을 찾아봅시다.
    1. 새로운 글을 작성하려면 브라우저 주소창에 /new/경로를 입력해서 작성해야된다.
    2. 게시글 작성페이지에서 게시글 목록 페이지로 이동할 수 있는 링크가 있으면 좋겠다.
    3. 본문이 바로 보인다.(지금은 본문이 짧지만 본문이 500줄, 1000줄 정도 되면 다른 게시물을 보기가 힘들 것 같네요 😮‍💨)

articles/articles.html

{% extends "base.html" %}

{% block content %}
    <h1>Articles</h1>
    <a href="{% url 'new' %}">새 글 작성</a>

    <ul>
        {% for article in articles %}
            <li>
                <a href="{% url 'article_detail' article.id %}">
                    <div>[{{ article.id }}] {{ article.title }}</div>
                </a>
                <br>
            </li>
        {% endfor %}
    </ul>

{% endblock content %}

 

articles/new.html

<a href="{% url 'articles' %}">목록으로</a>

 

💡 편의성 업데이트

1. 글 작성 링크 만들기 ✅
2. 게시글 작성 페이지에 게시글 목록 페이지로 이동할 수 있는 링크 만들기 ✅
3. 글 제목만 보여주고 글제목을 클릭하면 상세페이지로 이동하기 ✅ 

글 삭제하기

  • 게시글 삭제도 구현해봅시다.
  • 구현
    1. 글 삭제 로직을 진행하는 url 만들기
    2. 글 삭제하는 view 만들기
      1. 삭제하고자 하는 글 가져오기
      2. 글 삭제
      3. 삭제한다음 이동할곳으로 redirect
    3. 글 삭제 버튼 만들어주기

(delete) articles/urls.py

path("<int:pk>/delete/", views.delete, name="delete"),

 

(delete) articles/views.py

def delete(request, pk):
  article = Article.objects.get(pk=pk)
  article.delete()
  return redirect("articles")

 

 

삭제 버튼 만들기

글삭제 버튼은 어떻게 만들면 좋을까? 또 a태그?

NO! a태그는 누르면 해당 url로 GET 요청을 보내는 것!

  • 삭제라는 것도 결국에는 데이터베이스를 조작하는 요청 !
  • POST 방식으로 요청해야함
  • 따라서→ form 사용

 

 

(delete) articles/detail.html

{% extends "base.html" %}

{% block content %}

    <h2>글 상세 페이지</h2>
    <p>제목: {{ article.title }}</p>
    <p>내용: {{ article.content }}</p>
    <p>작성일시: {{ article.created_at }}</p>
    <p>수정일시: {{ article.updated_at }}</p>

    <a href="{% url 'articles' %}"><button>목록 보기</button></a>

    <hr>
    <form action="{% url 'delete' article.pk %}" method="POST">
        {% csrf_token %}
        <input type="submit" value="글삭제">
    </form>

{% endblock content %}

 

 

💡 엇 그런데 말입니다?

127.0.0.1:8000/articles/3/delete/ 를 직접 입력해서 들어가면 어떻게 될까요?

→ 삭.제.된.다

→ 왜?

→ 로직에 이상이 없으니까 !

  • 위 URL로 GET 요청이 들어오면
  • view에서 처리하니까
  • 삭제된다
  • 이렇게 url로 임의로 글 삭제가 가능해져버리면 안되겠죠? 수정해봅시다!

요청방식이 POST인 경우만 삭제하도록 수정

 

  • [코드스니펫] (POST인 경우에만 삭제)articles/views.py
def delete(request, pk):
  article = Article.objects.get(pk=pk)
  if request.method == "POST":
      article.delete()
      return redirect("articles")
  return redirect("article_detail", article.pk)

 post 요청일 경우에만 삭제처리 하고 아닐경우(get 요청 등)엔 그냥 상세페이지를 보여주게 수정


글 수정하기

  • 글을 수정하는 로직을 생각해 봅시다.
    1. 글 수정하는 url 만들기
      1. 수정할 글을 보여주는 url
      2. 글 수정을 처리할 url
    2. 글 수정하는 로직을 수행하는 view 만들기
      1. 수정할 글을 보여주는 view
      2. 글 수정을 처리하는 view
    3. 글 수정을 수행하는 template 만들기
    4. 글 수정 버튼 만들기

💡 전체 흐름

  1. 글 수정하는 url 만들기
    1. 수정할 글을 보여주는 url (articles/<pk>/edit/)
    2. 글 수정을 처리할 url(articles/<pk>/update/)
  2. 글 수정하는 로직을 수행하는 view 만들기
    1. 수정할 글을 보여주는 view (edit view)
    2. 글 수정을 처리하는 view (update view)
  3. 글 수정을 수행하는 template 만들기 (edit.html)
  4. 글 수정 버튼 만들기 → detail 페이지에 만들어주기

(edit) articles/urls.py

path("<int:pk>/edit", views.edit, name="edit"),

 

(edit) articles/views.py

...
def edit(request, pk):
	article = Article.objects.get(pk=pk)
	context = {
	    "article": article,
	}
	return render(request, "edit.html", context)
...

 

articles/templates/edit.html

{% extends "base.html" %}


{% block content %}
<h1>Update Article</h1>

<form action="{% url 'update' article.pk %}" method="POST">
	  {% csrf_token %}
    <label for="title">제목</label>
    <input type="text" name="title" id="title" value={{ article.title }}><br><br>

    <label for="content">내용</label>
    <textarea name="content" id="content" cols="30" rows="10">{{ article.content }}</textarea><br><br>

    <button type="submit">저장</button>
</form>


{% endblock content %}

 

(update) articles/urls.py

path("<int:pk>/update/", views.update, name="update"),

 

(update) articles/views.py

...
def update(request, pk):
  article = Article.objects.get(pk=pk)
  article.title = request.POST.get("title")
  article.content = request.POST.get("content")
  article.save()
  return redirect("article_detail", article.pk)
...

 

(update) detail.html

<a href="{% url 'edit' article.pk %}"><button>글수정</button></a>

 


 

💡 수정하기 버튼은 a 태그일까 form일까?

수정하는 페이지로 넘어가는 화면은 데이터베이스의 조작이 없다.

데이터가있는 화면만 보여주는 것이다

→ form 태그가 아닌 a태그 사용하는 GET요청이다!