Django - HTTP & ORM 실습

수현·2023년 9월 30일
0

Django

목록 보기
7/19

HTTP request methods

HTTP : 네트워크 상에서 데이터를 주고 받기 위한 약속

HTTP request methods : 데이터에 어떤 요청을 원하는지를 나타내는 것

GET method

  • 특정 리소스를 조회하는 요청(반드시 조회 시에만 사용)

  • 입력 길이 255자 제한

  • GET으로 데이터 전달 시 Query String 형식으로 보내짐

POST method

  • 특정 리소스에 변경 사항을 만드는 요청

  • 입력 길이 제한 X

  • POST로 데이터 전달 시 HTTP Body에 담겨 보내짐

  • 이는 데이터 베이스에 변경사항을 만드는 요청이므로 CSRF 등의 공격을 막기위해 토큰 사용(최소한의 신원 확인)

  • Security Token(CSRF Token)

    • Cross-Site-Request-Forgery(사이트 간 요청 위조) : 사용자의 의지와 무관하게 공격자가 의도한 행동을 하여 특정 웹 페이지를 보안에 취약하게 하거나 수정, 삭제 등의 작업을 하게 만드는 공격 방법

    • CSRF Token은 CSRF의 대표적인 방어 방법

    • 서버가 사용자 입력 데이터에 임의의 난수값(token)을 부여하여 함께 서버로 전송 시키고 전달된 token이 유효한지 검증

    • form tag 안에 {% csrf_token %} 태그 작성하여 토큰 값 부여


todos app 실습

Django ORM과 View 함수를 결합하여 웹 애플리케이션의 데이터를 저장 & 렌더링

READ

# todos/urls.py

app_name = 'todos'
urlpatterns = [
    # todos app의 메인 페이지
    path('', views.index, name='index'),
    # 단일 todo 조회 시 보여지는 세부정보 페이지
    path('detail/<int:todo_pk>/', views.detail, name='detail'),
]
  • 전체 todo list 조회
# todos/views.py

def index(request):
  # 모든 데이터를 불러와 'todos'변수에 저장하여 return
    todos = Todo.objects.all()
    context = {
        'todos': todos,
    }
    return render(request, 'todos/index.html', context)
<!-- template -->

<h1 class="mx-3 my-5">✨My Todo List✨</h1>
<!-- todo 생성 페이지 -->
<a href="{% url 'todos:new' %}" class="my-3 ms-5">Todo 생성</a>
<!-- 'todos'변수의 데이터를 모두 나열하고
 각각의 세부 페이지로 갈 수 있도록 해당 데이터의 pk인자와 함께 detail url을 걸어줌 -->
{% for todo in todos %}
  <div class="my-3 ms-5">
    <a href="{% url 'todos:detail' todo.pk %}">할 일 {{ todo.pk }}</a>
  </div>
{% endfor %}
  • 단일 todo list 조회
# todos/views.py

def detail(request, todo_pk):
  # 받은 인자와 같은 pk값을 가지는 데이터를 찾아 'todo'변수에 저장 후 return
    todo = Todo.objects.get(pk=todo_pk)
    context = {
        'todo': todo,
    }
    return render(request, 'todos/detail.html', context)
<!-- template -->

<!-- 각각의 값을 출력 -->
<h1>제목 - {{ todo.title }}</h1>
<h3>내용 - {{ todo.content }}</h3>
<h3>우선순위 - {{ todo.priority }}</h3>
<h3>마감기한 - {{ todo.deadline }}</h3>
<h3>완료 여부 - {{ todo.completed }}</h3><br>

<!-- 메인 페이지로 돌아가기 -->
<a href="{% url 'todos:index' %}">뒤로 가기</a>

CREATE

# todos/urls.py

app_name = 'todos'
urlpatterns = [
    path('', views.index, name='index'),
    path('detail/<int:todo_pk>/', views.detail, name='detail'),
    path('new/', views.new, name='new'),
    path('create/', views.create, name='create'),
]
  • 새로운 todo 입력(메인페이지의 Todo 생성 url)
# todos/views.py

def new(request):
    return render(request, 'todos/new.html')
<!-- template -->

<div class="container">
  <h1 class="m-5">Todo list 작성</h1>
  <!-- input 제출 시 create url로 보냄 -->
  <form action="{% url 'todos:create' %}" method='GET'>
    <!-- 각각의 input을 name을 이용해 key 설정 -->
    <div class="mb-3">
      <label for="title" class="form-label">제목</label>
      <input type="text" class="form-control" name="title" id="title">
    </div>
    <div class="mb-3">
      <label for="content" class="form-label">내용</label>
      <textarea class="form-control" name="content" id="content" rows="3"></textarea>
    </div>
    <div class="d-flex justify-content-between">
      <div class="mb-3 col-5">
        <label for="priority" class="form-label">우선순위</label>
        <input type="number" class="form-control" name="priority" id="priority">
      </div>
      <div class="mb-3 col-6">
        <label for="deadline" class="form-label">마감기한</label>
        <input type="date" class="form-control" name="deadline" id="deadline">
      </div>
    </div>
    <!-- 버튼 누르면 제출되어 create url로 이동 -->
    <div class="d-flex justify-content-center mt-5">
      <button type="submit" class="btn btn-outline-primary w-50">list에 추가</button>
    </div>
  </form>
</div>
  • 입력받은 데이터로 todo 생성
# todos/views.py

def create(request):
  # 각각의 값을 변수에 저장
    title = request.GET.get('title')
    content = request.GET.get('content')
    priority = request.GET.get('priority')
    deadline = request.GET.get('deadline')
  # 저장한 값을 Todo 인스턴스 생성 시 argument로 넣어줌
    todo = Todo(title=title, content=content, priority=priority, deadline=deadline)
  # 생성된 인스턴스 저장 = DB에 저장
    todo.save()
    return render(request, 'todos/create.html')
<!-- template -->

<h1>할 일을 등록하였습니다!</h1>
<!-- 등록 확인 후 메인 페이지로 돌아가기 -->
<a href="{% url 'todos:index' %}">돌아가기</a>

DELETE

  • 데이터 삭제
# todos/urls.py

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:pk>/', views.detail, name='detail'),
    path('new/', views.new, name='new'),
    path('create/', views.create, name='create'),
    path('<int:pk>/delete/', views.delete, name='delete'),
]
# todos/views.py
def delete(request, pk):
    todo = Todo.objects.get(pk=pk)
    todo.delete()
    return redirect('todos:index')
  • redirect() : 인자에 작성된 주소로 다시 요청을 보냄

UPDATE

# todos/urls.py

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:pk>/', views.detail, name='detail'),
    path('new/', views.new, name='new'),
    path('create/', views.create, name='create'),
    path('<int:pk>/delete/', views.delete, name='delete'),
    path('<int:pk>/edit/', views.edit, name='edit'),
    path('<int:pk>/update/', views.update, name='update'),
]
  • 수정할 내용 입력
# todos/views.py

def edit(request, pk):
  # 수정할 데이터 조회하여 변수에 저장
    todo = Todo.objects.get(pk=pk)
    context = {
        'todo': todo,
    }
    return render(request, 'todos/edit.html', context)
<!-- template -->

<div class="container">
  <h1 class="m-5">Todo list 수정</h1>
  <!-- form 제출 시 todo.pk의 값과 함께 데이터를 update url로 보냄 -->
  <!-- 변경사항을 만드므로 POST method 사용 -->
  <form action="{% url 'todos:update' todo.pk %}" method='POST'>
    <!-- POST method 사용 시 토큰태그 작성 -->
    {% csrf_token %}
    <div class="mb-3">
      <label for="title" class="form-label">제목</label>
      <!-- value에 값을 넣어 기존 상태 보여줌 -->
      <input type="text" class="form-control" name="title" id="title" value={{ todo.title }}>
    </div>
    <div class="mb-3">
      <label for="content" class="form-label">내용</label>
      <textarea class="form-control" name="content" id="content" rows="3">{{ todo.content }}</textarea>
    </div>
    <div class="d-flex justify-content-between">
      <div class="mb-3 col-5">
        <label for="priority" class="form-label">우선순위</label>
        <input type="number" class="form-control" min="1" name="priority" id="priority" value={{ todo.priority }}>
      </div>
      <div class="mb-3 col-6">
        <label for="deadline" class="form-label">마감기한</label>
        <!-- date type은 value에 {{ todo.deadline|date:"Y-m-d" }} dateFormat 넣어줘야 함 -->
        <input type="date" class="form-control" name="deadline" id="deadline" value={{ todo.deadline|date:"Y-m-d" }}>
      </div>
    </div>
    <div class="d-flex justify-content-center mt-5">
      <button type="submit" class="btn btn-outline-primary w-50">저장하기</button>
    </div>
  </form>
</div>
  • 수정한 내용 저장
# todos/views.py

def update(request, pk):
  # 수정한 데이터 조회하여 저장
    todo = Todo.objects.get(pk=pk)
  # 수정된 데이터를 가져와 각각의 필드에 저장
    todo.title = request.POST.get('title')
    todo.content = request.POST.get('content')
    todo.priority = request.POST.get('priority')
    todo.deadline = request.POST.get('deadline')
  # 변경사항 저장
    todo.save()
  # 종료 시 해당 데이터의 detail 페이지로 감
    return redirect('todos:detail', pk)

Todo 목록의 완료 여부 구현

<!-- index.html template -->
<div class="container mt-5 mx-auto">
  <h1 class="mb-5">✨My Todo List✨</h1>
  <ul>
    {% for todo in todos %}
      <li class="mb-3">
        <a href="{% url 'todos:detail' todo.pk %}">{{ todo.title }}</a>
        <!-- 현재 상태 출력 -->
        / 완료여부 - {{ todo.completed }}
        <!-- if문으로 완료 여부에 따라 다른 text 보이게 함 -->
        {% if todo.completed %}
          <a href="{% url 'todos:complete' todo.pk %}">완료 취소</a>
        {% else %}
          <a href="{% url 'todos:complete' todo.pk %}">완료 하기</a>
        {% endif %}
      </li>
    {% endfor %}
  </ul>
  <a class="btn btn-outline-primary m-3" href="{% url 'todos:new' %}" role="button">Todo 추가하기</a>
</div>
# todos/urls.py

urlpatterns = [
    path('', views.index, name='index'),
    path('<int:pk>/', views.detail, name='detail'),
    path('new/', views.new, name='new'),
    path('create/', views.create, name='create'),
    path('<int:pk>/delete/', views.delete, name='delete'),
    path('<int:pk>/edit/', views.edit, name='edit'),
    path('<int:pk>/update/', views.update, name='update'),
    # 해당 pk값을 받아 complete 함수 실행
    path('<int:pk>/complete/', views.complete, name='complete'),
]
# todos/views.py

def complete(request, pk):
    todo = Todo.objects.get(pk=pk)
    # 완료이면 False, 미완료이면 True로 바꾼 후 저장
    if todo.completed:
        todo.completed = False
    else:
        todo.completed = True
    todo.save()
    return redirect('todos:index')
profile
실패와 성장을 기록합니다 🎞️

0개의 댓글