파이썬 웹프로그래밍 - todo 목록 서비스 만들기 (완)

채연·2024년 5월 15일

study

목록 보기
5/12
post-thumbnail

운영진 스터디 목표

장고를 통해 todo 목록 서비스 프로젝트를 완성하고 이해한다.

  • mytodo/mytodo/settings.py
# Application definition

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "todo",
]
TIME_ZONE = "Asia/Seoul"
  • mytodo/mytodo/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path('', include('todo.urls')),
]
  • mytodo/todo/templates/todo/done_list.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/boot-strap-icons.css">
    </head>
    <body>
        <div class="container">
            <h1>완료 목록</h1>
            <p>
                <a href="{% url 'todo_list' %}" class="btn btn-primary">홈으로</a>
            </p>
            <ul class="list-group">
                {% for done in dones %}
                <li class="list-group-item">
                    <a href="{% url 'todo_detail' pk=done.pk %}">{{done.title}}</a>
                    {% if done.important %}
                        <span class="badge-danger">!</span>
                    {% endif %}
                </li>
                {% endfor %}
            </ul>
        </div>
    </body>
</html>
  • mytodo/todo/templates/todo/todo_detail.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/boot-strap-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>
                                <p class="card-text">{{todo.description}}</p>
                                <a href="{% url 'todo_list' %}" class="btn btn-primary">목록으로 </a>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>
  • mytodo/todo/templates/todo/todo_edit.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/boot-strap-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">
                                    {% csrf_token %} {{form.as_p}}
                                    <button type="submit" class="btn btn-primary">업데이트</button>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>
  • mytodo/todo/templates/todo/todo_list.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <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/boot-strap-icons.css">
</head>
<body>
    <div class="container">
    <h1>Todo 목록 앱</h1>
    <p>
        <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">
        {% 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>
    </div>
</body>
</html>
  • mytodo/todo/templates/todo/todo_post.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/boot-strap-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">
                                    {% csrf_token %} {{form.as_p}}
                                    <button type="submit" class="btn btn-primary">등록</button>
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </body>
</html>
  • mytodo/todo/admin.py
from django.contrib import admin
from .models import Todo # 모델을 관리자 페이지에서 확인해야하므로 모델 등록

admin.site.register(Todo) # 관리자.페이지에.등록(모델)
  • mytodo/todo/apps.py
from django.apps import AppConfig

class TodoConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "todo"
  • mytodo/todo/forms.py
from django import forms
from .models import Todo

class TodoForm(forms.ModelForm):
    class Meta:
        model = Todo
        fields = ('title', 'description', 'important')
  • mytodo/todo/models.py
from django.db import models

class Todo(models.Model):
    objects = None
    title = models.CharField(max_length=100) # 제목 = 문자열 필드 (최대 길이 100)
    description = models.TextField(blank=True) # 설명 = 문자 필드 (빈칸 가능)
    created = models.DateTimeField(auto_now_add=True) # 생성 날짜 = 날짜 필드 (자동 추가)
    complete = models.BooleanField(default=False) # 완료 = 불린 필드 (기본값 = 거짓)
    important = models.BooleanField(default=False) # 중요 = 불린 필드 (기본값 = 거짓)

    def __str__(self):
        return self.title # 제목 리턴
  • mytodo/todo/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.todo_list, name='todo_list'), # 주소에 투두_list 뷰 연결
    path('<int:pk>/', views.todo_detail, name='todo_detail'),
    path('<int:pk>/edit/', views.todo_edit, name='todo_edit'),
    path('done/', views.done_list, name='done_list'),
    path('post/', views.todo_post, name='todo_post'),
    path('done/<int:pk>/', views.todo_done, name='todo_done'),
]
  • mytodo/todo/views.py
from django.shortcuts import render, redirect
from .models import Todo
from .forms import TodoForm

# PPT p.20

def todo_list(request):
    todos = Todo.objects.filter(complete=False) # 완료되지 않은 투두만 전달해야하므로 complete=False 필터링
    return render(request, 'todo/todo_list.html', {'todos' : todos})

# PPT p.25

def todo_detail(request, pk):
    todo = Todo.objects.get(id=pk)
    return render(request, 'todo/todo_detail.html', {'todo' : todo})

# PPT p.35

def todo_edit(request, pk):
    todo = Todo.objects.get(id=pk)
    if request.method == "POST":
        form = TodoForm(request.POST, instance=todo)
        if form.is_valid():
            todo = form.save(commit=False)
            todo.save()
            return redirect('todo_list')
    else:
        form = TodoForm(instance=todo)
    return render(request, 'todo/todo_edit.html', {'form' : form})

# PPT p.40

def done_list(request):
    dones = Todo.objects.filter(complete=True)
    return render(request, 'todo/done_list.html', {'dones':dones})

# PPT p.41
def todo_done(request, pk):
    todo = Todo.objects.get(id=pk)
    todo.complete = True
    todo.save()
    return redirect('todo_list')

def todo_post(request):
    if request.method == "POST":
        form = TodoForm(request.POST)
        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})

실행 결과

Terminal에 python manage.py runserver 명령을 입력하여 출력 결과를 확인한다.



오답노트

모르는 코드 부분을 정확히 이해하고 정리한다.

(1) mytodo/todo/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.todo_list, name='todo_list'), # 주소에 투두_list 뷰 연결
    path('<int:pk>/', views.todo_detail, name='todo_detail'),
    path('<int:pk>/edit/', views.todo_edit, name='todo_edit'),
    path('done/', views.done_list, name='done_list'),
    path('post/', views.todo_post, name='todo_post'),
    path('done/<int:pk>/', views.todo_done, name='todo_done'),
]
  • 장고에서는 URL 패턴을 설정할 때, 각 view에 해당하는 URL을 지정해야 한다. views.py 파일에 정의된 각 함수는 웹 애플리케이션에서 특정 URL을 처리하는 데 사용되기 때문에 각 view에 대한 URL을 꼭 정의해야 한다.

(2) mytodo/todo/templates/todo/todo_list.html

<a href="{% url 'todo_detail' pk=todo.pk %}">{{todo.title}}</a>
  • todo의 제목을 누르면 views.py에 상세페이지(todo_detail.html)를 호출하고 이동하게 하는 코드이기 때문에 todo_list.html에 입력한다.

(3) mytodo/todo/views.py

  • 이처럼 Unresolved reference 'TodoForm'; 이라는 경고문이 떴을 때에는 import를 하였는지 확인하고 views.py에 from .forms import TodoForm을 불러와야 한다.

0개의 댓글