운영진 스터디 목표
장고를 통해 todo 목록 서비스 프로젝트를 완성하고 이해한다.
- mytodo/mytodo/settings.py
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"
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>
from django.contrib import admin
from .models import Todo
admin.site.register(Todo)
from django.apps import AppConfig
class TodoConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "todo"
from django import forms
from .models import Todo
class TodoForm(forms.ModelForm):
class Meta:
model = Todo
fields = ('title', 'description', 'important')
from django.db import models
class Todo(models.Model):
objects = None
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)
def __str__(self):
return self.title
from django.urls import path
from . import views
urlpatterns = [
path('', views.todo_list, name='todo_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'),
]
from django.shortcuts import render, redirect
from .models import Todo
from .forms import TodoForm
def todo_list(request):
todos = Todo.objects.filter(complete=False)
return render(request, 'todo/todo_list.html', {'todos' : todos})
def todo_detail(request, pk):
todo = Todo.objects.get(id=pk)
return render(request, 'todo/todo_detail.html', {'todo' : todo})
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})
def done_list(request):
dones = Todo.objects.filter(complete=True)
return render(request, 'todo/done_list.html', {'dones':dones})
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'),
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을 불러와야 한다.