Development/Django

[Django 프레임워크 제대로 배우기] 3. CRUD 구현

조코링 2023. 1. 29. 17:13

GET과 POST

HTTP Method, 즉 HTTP 프로토콜을 통해서 서버에 값을 전달할 때 사용하는 방식의 종류, 이 외에 다른 방식들도 있음
하나의 URL에 요청을 보낼 때 GET과 POST 방식을 모두 사용할 수 있음, 방식을 기준으로 분기를 나누어 처리하면 됨

GET

  • URL에 파라미터 값으로 데이터를 전달 → URL에 파라미터가 보여짐, 비교적 보안이 취약함
  • 캐시가 남고 브라우저의 히스토리에 파라미터 정보가 저장되어 비교적 속도가 빠름
  • 주로 조회(Read)에 많이 사용, 데이터를 변경하지 않음
  • ?[key1]:[value1]&[key2]:[value2]&...와 같은 형식
  • ex) https://www.google.com/search ?q=%EA%B2%80%EC%83%89&oq=%EA%B2%80%EC%83%89&aqs=chrome..69i57j69i59j69i61l3.581j0j7&sourceid=chrome&ie=UTF-8

POST

  • Request Body에 데이터를 넣어서 전달 → URL에 파라미터가 보여지지 않음, 비교적 안전함(SSL 사용 시 더욱 안전함)
  • 캐시 및 파라미터 정보가 남지 않고 비교적 속도가 느림
  • 주로 입력(Create), 수정(Update), 삭제(Delete)에 많이 사용, 데이터를 변경함
  • URL에 정보를 담을 수 있는 한계를 초과하거나, 바이너리 데이터와 같은 파일 업로드를 처리하는 경우 사용

CRUD 구현

Create

  1. 폼으로 입력받은 데이터를 DB에 저장 후 다시 index 페이지로 이동

    # myapp/views.py
    
    from django.shortcuts import render, redirect
    from .models import Memo
    
    def index(request):
       return render(request, 'index.html')
    
    def createMemo(request):
       # POST 요청으로 받은 데이터 중 text라는 이름으로 넘어온 데이터를 text라는 이름의 변수에 저장
       text = request.POST['text']
    
       # Memo 테이블의 text 필드에 text 변수의 값을 넣어 레코드 생성 후 해당 레코드를 memo라는 이름의 변수에 저장
       memo = Memo(text=text)
    
       # 생성한 레코드를 DB에 저장
       memo.save()
    
       # 지정한 페이지로 이동, 주소를 직접 지정하거나 urls.py에서 지정한 name 값을 적어줌
       return redirect('index')    # = HttpResponseRedirect(reverse('index'))

Read

  1. DB에서 데이터 조회 후 템플릿에 전달

    • 모든 모델 클래스는 objects를 가짐
    • all(): 해당 모델 클래스, 즉 테이블의 모든 레코드를 반환
    # myapp/views.py
    
    ...
    
    def index(request):
       # DB에서 Memo 테이블의 모든 글을 가져옴
       memos = Memo.objects.all()
    
       # 템플릿에 전달하기 위해 데이터를 딕셔너리 타입의 context 변수에 추가, 반드시 딕셔너리 타입이어야 함
       context = {'memos': memos}
    
       # 데이터를 포함하여 템플릿 렌더링
       return render(request, 'index.html', context)
    
    ...
    
  2. 템플릿에서 데이터를 리스트로 표시

    <!-- myapp/templates/index.html -->
    <!-- 이전에 생성한 form 태그 아래에 추가 -->
    
    ...
    
    <div>
     <ul>
       {% for memo in memos %}
       <li>
         #{{ memo.id }} [{{ memo.created_at | date:'Y-m-d' }}] {{ memo.text }}
       </li>
       {% endfor %}
     </ul>
    </div>
    
    ...
    

Update

  1. urls.py에 수정 경로 추가

    • Dynamic URL: 요청 시 URL에 전달되는 파라미터 값을 변수로 사용하여 페이지를 렌더링, <[타입]:[변수명]> 형태로 작성
    # myapp/urls.py 
    # 이전에 생성한 urlpatterns에 경로 추가
    
    ...
    
    urlpatterns = [ 
       ...,
       path('update/<str:id>/', views.updateMemo, name='updateMemo'),
    ]
  2. views.py에 updateMemo 함수 추가

    • Dynamic URL로 전달받은 변수가 request와 함께 함수의 입력값으로 전달됨
    • get(): 해당 모델 클래스, 즉 테이블에서 필드 값이 지정한 값과 같은 레코드 하나만 반환 → 존재하지 않거나 둘 이상인 경우 에러 발생
    # myapp/views.py
    
    ...
    
    def updateMemo(request, id):
         # URL로 전달받은 id와 같은 id를 가진 Memo 테이블의 레코드를 찾아 memo 변수에 저장
       memo = Memo.objects.get(id=id)
    
       # GET 요청이라면, 즉 수정 페이지를 표시하라는 요청이라면
       # context에 레코드를 담아 템플릿에 전달
       if request.method == 'GET':
           context = {'memo': memo}
    
           return render(request, 'update.html', context)
    
       # POST 요청이라면, 즉 수정한 내용이 포함된 요청이라면
       # 전달 받은 text로 레코드의 text 필드 값을 대체하여 DB에 저장 후 path의 name이 index인 페이지로 이동
       elif request.method == 'POST':
           memo.text = request.POST['text']
           memo.save()
    
           return redirect('index')
    
    ...
    
  3. 템플릿에 수정 페이지로 이동하는 링크 생성

    <!-- myapp/templates/index.html -->
    <!-- 이전에 생성한 리스트 항목에 링크 추가 -->
    
    ...
    
    <div>
     <ul>
       {% for memo in memos %}
       <li>
         #{{ memo.id }} [{{ memo.created_at | date:'Y-m-d' }}] {{ memo.text }}
         <a href="./update/{{ memo.id }}/">[ 수정 ]</a>
       </li>
       {% endfor %}
     </ul>
    </div>
    
    ...
    
  4. 수정 페이지로 사용할 템플릿 생성

    <!-- myapp/templates/update.html -->
    
    <!DOCTYPE html>
    <html lang="en">
     <head>
       <meta charset="UTF-8" />
       <meta http-equiv="X-UA-Compatible" content="IE=edge" />
       <meta name="viewport" content="width=device-width, initial-scale=1.0" />
       <title>Update Memo</title>
       <style>
         form > label {
           display: inline-block;
           width: 120px;
         }
    
         form > input {
           padding: 4px;
           margin-bottom: 4px;
         }
       </style>
     </head>
     <body>
       <h1>Memo Form</h1>
    
       <form action="#" method="POST" id="updateMemoForm">
         {% csrf_token %}
         <label for="text">메모 수정</label>
         <input
           type="text"
           id="text"
           name="text"
           autocomplete="off"
           placeholder="내용을 입력하세요."
           value="{{ memo.text }}"
         />
       </form>
    
       <button type="submit" form="updateMemoForm">완료</button>
     </body>
    </html>

Delete

  1. urls.py에 삭제 경로 추가

    # myapp/urls.py 
    # 이전에 생성한 urlpatterns에 경로 추가
    
    ...
    
    urlpatterns = [ 
       ...,
       path('delete/<str:id>/', views.deleteMemo, name='deleteMemo'),
    ]
  2. views.py에 deleteMemo 함수 추가

    • delete(): 해당 레코드를 DB에서 삭제
    # myapp/views.py
    
    ...
    
    def deleteMemo(request, id):
       # URL로 전달받은 id와 같은 id를 가진 Memo 테이블의 레코드를 찾아 memo 변수에 저장
       memo = Memo.objects.get(id=id)
    
       # 해당 레코드를 DB에서 삭제
       memo.delete()
    
       # path의 name이 index인 페이지로 이동
       return redirect('index')
    
    ...
    
  3. 템플릿에 삭제 요청을 보내는 링크 생성

    <!-- myapp/templates/index.html -->
    <!-- 이전에 생성한 리스트 항목에 링크 추가 -->
    
    ...
    
    <div>
     <ul>
       {% for memo in memos %}
       <li>
         #{{ memo.id }} [{{ memo.created_at | date:'Y-m-d' }}] {{ memo.text }}
         <a href="./update/{{ memo.id }}/">[ 수정 ]</a> |
         <a href="./delete/{{ memo.id }}/">[ 삭제 ]</a>
       </li>
       {% endfor %}
     </ul>
    </div>
    
    ...
    

참고: 하나의 URL에서 GET과 POST를 모두 처리하는 방법

  • view 함수에서 분기 처리
def test(request):
    if request.method == 'GET':
        # GET 방식으로 요청한 경우 수행할 작업
    elif request.method == 'POST:
        # POST 방식으로 요청한 경우 수행할 작업

※ 강의 링크: https://inf.run/DmuA