본문 바로가기
Python/Django Ⅱ

Todo : Create, Read

by Mr.DonyStark 2024. 5. 15.

□ Todo 리스트를 등록하고 조회할 수 있는 웹을 구현하고자함

추가로 등록된 리스트 중 완료로 처리할 수 있고 완료된 리스트만 따로 조회할 수 있도록 구현하고자함

todo 라는 app을 생성하였고 app의 admin.py에 아래와 같이 추가하였음

from django.contrib import admin
from .models import Todo

admin.site.register(Todo)

 

 코드

  ○ M : Model 생성

from django.db import models

class Todo(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    created = models.DateTimeField(auto_now_add=True)
    complete = models.BooleanField(default=False)
    important = models.BooleanField(default=False)

# __str__ 메서드는 해당 객체가 문자열로 표시될 때 사용. 여기서는 title 필드의 값을 반환하여 객체를 문자열로 표시하도록 지정
    def __str__(self):
        return self.title

 

  ○ V : View

    - todo_post 에서 아래에 있는 app > forms.py를 바탕으로 정보를 POST 방식으로 전달하면 저장되도록 지정

    - todo_detail 에서 등록된 게시글에 대한 pk를 인자로 받아 id로 처리 후 해당 글에 대한 정보를 가져오도록 지정

from django.shortcuts import render, redirect
from .models import Todo
from .forms import TodoForm

#리스트 보기
def todo_list(request):
    todos = Todo.objects.filter(complete = False) #완료되지 않은 것만 전달하기 위해 complete = False, 필터링을 위해 .objects.filter 사용
    return render(request, "todo/todo_list.html", {"todos" : todos})

#생성(등록) : post 방식
def todo_post(request):
    if request.method == "POST":
        form = TodoForm(request.POST) #TodoForm 요청을 POST 방식으로 form 변수에 저장
        if form.is_valid(): #유효성 검사 시작
            todo = form.save(commit=False) # 폼에서 제공된 데이터로 모델 인스턴스 생성 (데이터베이스에 아직 저장되지 않음)
            todo.save() #데이터 저장
            return redirect("todo_list") #리다이렉트 할 함수
    else:
        form = TodoForm()
    return render(request, "todo/todo_post.html", {"form":form})

#상세보기
def todo_detail(request, pk):
    todo = Todo.objects.get(id=pk)#.objects.get 과 model의 pk를 활용하여 정보를 가져오 todo로 넣음
    return render(request, 'todo/todo_detail.html', {'todo': todo})

 

  ○ app > Forms

    - model 값은 app>models.py에서 정의한 클래스명으로 지정하였음

    - fields는 app>models.py에서 클래스 정의시 기재한 필드, 즉, 이를 바탕으로 값을 받을 필드들을 기재함

from django import forms
from .models import Todo

class TodoForm(forms.ModelForm):
    class Meta:
        model = Todo
        fields = (
            "title",
            "description",
            "important"
        )

 

  ○ app > Urls

    - '' = 127.0.0.1/app명 즉, 127.0.0.1/todo를 의미

    - post/ 는 127.0.0.1/todo/post를 의미

    - '<int:pk>/는 127.0.0.1/todo/N을 의미

from django.urls import path
from . import views

urlpatterns = [
    path('', views.todo_list, name="todo_list"), #메인 조회페이지
    path('post/', views.todo_post, name="todo_post"), #등록하기
    path('<int:pk>/', views.todo_detail, name="todo_detail"), #상세보기
]

 

  ○ project > 부모 Urls

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

urlpatterns = [
    path("admin/", admin.site.urls),
    path("todo/", include("todo.urls"))
]

 

  ○ t : Template

    - 조회화면 템플릿

<html>
  <head>
    <title>TODO 목록 앱</title>
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css">
  </head>
  <body>
  <div class="container">
    <h1>TODO 목록 앱</h1>
    <p>
      <!--todo_post urlname을 지정하여 클릭하면 생성페이지로이동-->
      <a href="{% url 'todo_post'%}"><i class="bi-plus"></i>Add Todo</a>
      <a href="{% url 'done_list'%}" class="btn btn-primary" style="float:right">완료한 TODO 목록</a>
    </p>
    <ul class="list-group">
      <!--모델객체의 pk, 제목 가져오기-->
      {% for todo in todos %}
      <li class="list-group-item">
        <a href="{%url 'todo_detail' pk=todo.pk%}">{{ todo.title }}</a>
        {% if todo.important %}
          <span class="badge badge-danger">!</span>
        {% endif %}
        <div style="float:right">
          <a href="{%url "todo_done" pk=todo.pk%}" class="btn btn-danger">완료</a>
          <a href="{%url 'todo_edit' pk=todo.pk%}" class="btn btn-outline-primary">수정하기</a>
        </div>
      </li>
      {% endfor %}
    </ul>
  </body>
  </div>
</html>

 

    - 등록 템플릿

      ※ form.as_p 는 app > forms.py에서 지정한 양식을 바탕으로 입력양식을 뿌려준다는 의미임

      ※ form.as_p를 바탕으로 입력받은 값을 서버로 POST 방식으로 전송되는 데 보안(위조방지)를 위해 CSRF_TOKEN을 <form>태그내에 기재

<html>
  <head>
    <title>TODO 목록 앱</title>
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css">
  </head>
  <body>
  <div class="container">
    <h1>TODO 추가하기</h1>
    <div class="container">
      <div class="row">
        <div class="col-md-12">
          <div class="card">
            <div class="card-body">
              <form method="POST">
                <!--post방식으로 서버에 전달할때 보안을 위해 csrf_token 활용-->
                {% csrf_token %}
                <!--form.as_p 를 사용하면 forms.py에서 작성한 형태로 폼이 생성됨-->
                {{ form.as_p }}
                <button type="submit" class="btn btn-primary">등록</button>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
  </div>
</html>

 

    - 상세보기 템플릿

<html>
  <head>
    <title>TODO 목록 앱</title>
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
    />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.1/font/bootstrap-icons.css">
  </head>
  <body>
  <div class="container">
    <h1>TODO 상세보기</h1>
    <div class="container">
      <div class="row">
        <div class="col-md-12">
          <div class="card">
            <div class="card-body">
              <h5 class="card-title">{{ todo.title }}</h5>
              </h5>
              <p class="card-text">{{ todo.description }}</p>
              <a href="{% url 'todo_list' %}" class="btn btn-primary">목록으로</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
  </div>
</html>

 

조회 페이지(등록전)
등록버튼클릭시 form.as_p를 바탕으로한 등록양식
조회 페이지(등록 후)
조회페이지에서 등록된 글을 클릭 후 상세보기