EMDI는 지금도 개발중

Python with Django : 장고 웹 프로그래밍 수정, 삭제 화면 만들기(6) 본문

언어/Python

Python with Django : 장고 웹 프로그래밍 수정, 삭제 화면 만들기(6)

EMDI 2020. 6. 19. 15:26

이번 글에서는 저번에 다루었던 작업에 이어 수정(Update), 삭제(Delete) 작업을 해보도록 하겠습니다.

1. teamDetail.html 생성 - 수정, 삭제를 할 수 있는 상세화면

우선은 수정과, 삭제를 할 수 있는 상세화면을 따로 만들도록 하겠습니다. 저는 teamDetail.html이라 이름을 붙였고, html의 content내용은 등록화면과 거의 동일합니다. 다른 점이라고는 화면이름과 버튼들뿐입니다. 버튼에 대한 설명은 조금있다가 아래에서 설명하도록 하겠습니다.

 

2. views.py와 urls.py 추가

메인화면 만드는 글부터 시작해서 여기까지 오신 분들이라면 이제 대충 어떤 문서로 가야할지 파악하실 수 있으실겁니다. 네 맞습니다. html 이후로는 views.py와 urls.py를 설정해주어야합니다. 

# views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.views.generic import TemplateView, ListView, DetailView

from .models import Team
from .forms import TeamUpload
from django.http import HttpResponse

# Function-Based Views
def index(request):
    return render(request, 'index.html')

def teamInfo(request):
    teams = Team.objects.all()
    context = {'teams': teams}
    return render(request, 'content/teamInfo.html', context)

def teamCreate(request):
    upload = TeamUpload()
    if request.method == 'POST':
        upload = TeamUpload(request.POST)
        if upload.is_valid():
            upload.save()
            return redirect('teamInfo')
        else:
            return redirect('index')
    else:
        upload =  TeamUpload()
        return render(request, 'content/teamAdd.html', { 'form':upload })

# 추가 Update
def teamUpdate(request, team_seq):
    try:
        team_detail = get_object_or_404(Team, pk=team_seq)
    except Team.DoesNotExist:
        return redirect('teamInfo')
    update = TeamUpload(request.POST or None, instance = team_detail)
    if update.is_valid():
        update.save()
        return redirect('teamInfo')
    return render(request, 'content/teamDetail.html', {'form':update })

# 추가 Delete
def teamDelete(request, team_seq):
    try:
        team_detail = get_object_or_404(Team, pk=team_seq)
    except Team.DoesNotExist:
          return redirect('teamInfo')
    team_detail.delete()
    return redirect('teamInfo')

차근차근 설명을 하자면, 우선 수정 함수는 teamUpdate이라고 지었고, 삭제 함수는 teamDelete라고 지었습니다. 각각 함수를 보시면 get_object_or_404라는 함수가 보이실겁니다. 이건 render와 동일하게 django.shortcuts에서 제공하는 함수입니다. 

* get_object_or_404 : 모델로부터 객체를 받았을 때 객체가 있으면 받고, 없으면 404 에러 페이지를 보여주도록 하는 것.

 

# urls.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url 
import fin.views

app_name = 'fin'
urlpatterns = [
    path('admin/', admin.site.urls),
    # 추가로 생성한 urls
    path('', fin.views.index , name='index'),
    path('team/teamInfo/',  fin.views.teamInfo, name='teamInfo'),
    path('team/teamAdd/',  fin.views.teamCreate, name='teamAdd'),
    path('team/teamUpdate/<team_seq>/',  fin.views.teamUpdate, name='teamUpdate'),
    path('team/teamDelete/<team_seq>/',  fin.views.teamDelete, name='teamDelete'),
]

뷰를 생성하였으니 이제 path를 설정하러 가봅시다. urls.py에 가서 저는 teamUpdate과 teamDelete를 지정해주었습니다. 여기서 우리는 teamAdd때와 다른 모습의 path를 보실 수 있는데 바로 /<team_seq>/ 는 무엇인가?

* 등록화면(teamAdd)같은 경우에는 새로운 팀을 생성하는 화면이니 특정 고유키가 필요하지 않습니다. 엄밀히 말하면 특정 고유키가 아직 있지 않은 상태이죠. 하지만 수정, 삭제화면(teamDetail)은 등록화면과 다르게, 이미 만들어져 있는 팀정보(데이터)를 가지고 수정 또는 삭제 작업을 해야합니다. 그렇기에 해당 데이터임을 알 수 있는 고유키가 필요한 것이죠. 

보통은 id(int)를 고유키로 잡고 'team/teamDetail/<int team_id>와 같이 사용하는데 저의 team_seq는 Integer가 아닌 String 형식이기에 저렇게 잡았습니다.

 

3. teamInfo.html에 상세화면으로 이동할 수 있는 링크 연결하기

{% extends 'base.html' %}
{% block content %}
<div class="container" style="width:100%;">
  <div style="width:1024px;height:400px;margin:0 auto;">
      <div style="margin:0 21 0 22px">
          <h1 style="margin-top:30px;">Project Management Page (TeamInfo)</h1>
          <div class="toptextarea">
              <span>* 팀 목록을 보여주는 홈페이지입니다. </span>
          </div>
          <!-- Table Layout -->
          <div style="margin-top: 20px;">
            <div class="tableline">
                <div class="tablerow">
                    <div class="tablecell_h" style="width:130px;">팀명</div>
                    <div class="tablecell_h" style="width:180px;">팀닉네임</div>
                    <div class="tablecell_h" style="width:180px;">팀멤버수</div>
                    <div class="tablecell_h" style="width:130px;">비고</div>
                </div>
                {% for team in teams %}
                <div class="tablerow">
                    <div class="tablecell" style="width:130px; text-decoration: underline; font-weight: bold;"><a href="{% url 'teamUpdate' team.team_seq %}">{{ team.team_nm }}</a></div>
                    <div class="tablecell" style="width:180px;">{{ team.team_nick }}</div>
                    <div class="tablecell" style="width:180px;">{{ team.team_mems_cnt }}</div>
                    <div class="tablecell" style="width:130px;">{{ team.bigo }}</div>
                </div>
                {% endfor %}
            </div>
          </div>
          <!-- Table Layout -->
          
          <!-- Button START -->
          <div style="margin-top: 20px; float: right;">
            <a href="{% url 'teamAdd' %}"><button type="button" class="btn btn-primary">팀추가하기</button></a>
          </div>
          <!-- Button END -->
          
          
      </div>
  </div>
</div>
{% endblock %}

 

그 다음은 여태까지 만들었던 상세화면을 teamInfo.html에 연결하는 작업입니다. 팀추가하기 버튼과 동일하게 <a>태그를 사용하여 연결하였습니다. 단, 여기서 우리는 팀등록화면으로 이동하는 <a>태그와 팀상세화면으로 이동하는 <a>태그에 차이점이 있다는 사실을 알 수 있습니다.

 

4. 여태까지의 연결구조에 대해 정리하기

일단 여기까지 그냥 따라 오신분들이라면 teamAdd.html때와는 다르게 teamDetail.html은 team_seq라는 것도 있고 뭔가 복잡해보인다라고 생각하실 수도 있습니다. 근데 자세히 살펴보면 그리 어려운 것도 아닙니다. team_seq가 있어야하는 이유는 이미 위에서 설명했기 때문에 패스하고, 천천히 연결고리에 대해 설명하도록 하겠습니다.

# teamInfo.html에서 urls.py의 teamUpdate로 이동할 것인데 team.team_seq 데이터도 같이 가져가겠다.
<a href="{% url 'teamUpdate' team.team_seq %}">{{ team.team_nm }}</a>

# teamInfo.html으로 부터 urls.py로 넘어왔고, teamUpdate이름을 가지고 있으면, views.py의 fin.views.teamUpdate 함수로 이동한다.
# 참고로 teamUpdate 함수가 정상적으로 처리 될 때는 team/teamUpdate/<team_seq>/ 라는 링크로 처리한다.
path('team/teamUpdate/<team_seq>/',  fin.views.teamUpdate, name='teamUpdate'),

# views.py에서 teamUpdate 함수로 도착. 
def teamUpdate(request, team_seq):
    try:
        # 모델 Team을 가지고 pk가 team_seq인 object를 가져오겠다. 만약 없으면 404 출력
        team_detail = get_object_or_404(Team, pk=team_seq)
    except Team.DoesNotExist:
        return redirect('teamInfo')
    update = TeamUpload(request.POST or None, instance = team_detail)
    if update.is_valid():
        update.save()
        return redirect('teamInfo')
    return render(request, 'content/teamDetail.html', {'form':update })

# 수정버튼(submit)을 클릭하는 경우
# update = TeamUpload(request.POST or None, instance = team_detail) 실행
 <form method="POST">
  ...
  <div style="margin-top: 20px; float: right;">
      <button type="submit" class="btn btn-primary">수정</button>
      <a href="{% url 'teamDelete' form.team_seq.value %}"><button type="button" class="btn btn-primary">삭제</button></a>
      <a href="{% url 'teamInfo' %}"><button type="button" class="btn btn-primary">돌아가기</button></a>
  </div>
</form>

# 함수가 성공적인 경우 return redirect('teamInfo') 이동

 

삭제 버튼도 동일합니다. submit만 아닐뿐이지 form.team_seq.value를 가지고 해당 team_seq에 맞는 데이터가 있는 경우 teamDelete 함수에서 처리한다는 의미입니다.

여기까지 진행하셨으면 우리는 장고 웹 사이트의 CRUD과정을 모두 마쳤습니다. 다음부터는 다른 화면 만들기 및 로그인 관련해서 공부해보도록 하겠습니다.

Comments