Django 구현 기초-Todo List 웹서비스
- 1. Todo List 웹서비스 시작하기
- 2. Todo 전체 조회 기능 만들기
- 3. Todo 상세 조회 기능 만들기
- 4. Todo 생성 기능 만들기
- 5. Todo 수정 기능 만들기
- 6. Todo 완료 기능 만들기
1. Todo List 웹서비스 시작하기
- 코드출처: 백엔드를 위한 Django REST Framework with 파이썬(영진닷컴)
1.1 프로젝트 기능 정리
- Todo List 전체 조회하기
- Todo List 상세 조회하기
- Todo 생성하기
- 데이터 입력을 위한 Form 필요
- Todo 수정하기
- 데이터 수정을 위한 Form 필요
- Todo 생성용 Form 활용 가능
- Todo 완료 처리하기
1.2 프로젝트 생성
- 가상환경 설정
python -m venv django_todolist
cd todolist
source ./bin/activate
# Windows의 경우: ./Scripts/activate
- Django 설치 및 확인
pip install django
python -m django --version
python --version
- Project 시작
django-admin startproject todoweb .
- 앱 추가
python manage.py startapp todolist
- 웹서버 실행
python manage.py runserver
- 웹 브라우저에서 웹서버 시작 확인
- https://127.0.0.1:8000
1.3 프로젝트 설정
- 웹서버 설정 수정
#//file: "./todoweb/settings.py"
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todolist', # 추가할 것
]
TIME_ZONE = 'Asia/Seoul' # 시간대를 한국으로 바꿀 것
- 웹서버 설정 URL 확인
#//file: "./todoweb/urls.py"
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
]
- Migration 수행
python manage.py migrate
- 계정 만들기
python manage.py createsuperuser
1.4 Todo 모델 생성
- Model 만들기
#//file: "./todolist/models.py"
from django.db import models
# Create your models here.
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)
def __str__(self):
return self.title
- Model 적용시키기
python manage.py makemigrations
python manage.py migrate
- Model을 Admin 페이지에 적용시키기
#//file: "./todolist/admin.py"
from django.contrib import admin
from todolist.models import Todo
# Register your models here.
admin.site.register(Todo)
2. Todo 전체 조회 기능 만들기
2.1 Todo 전체 조회 기능 컨셉
- 첫 페이지에서 Todo List를 보여줌
- 완료되지 않은 항목만 보여줌
2.2 Bootstrap으로 좀 더 멋진 템플릿 만들기
- Bootstrap
- 웹 개발을 쉽게 할 수 있도록 도와주는 HTML, CSS, JavaScript 프레임워크
- Twitter의 개발자들이 처음 만들었으며, 현재는 오픈 소스로 제공 중
- 특징
- 반응형 디자인: 다양한 기기(모바일, 태블릿, 데스크탑)에서 잘 작동하도록 설계됨
- 미리 정의된 스타일: 버튼, 폼, 네비게이션 바 등 다양한 UI 요소에 대한 스타일 제공
- 확장성: 필요에 따라 커스터마이징이 가능하며, 다양한 플러그인과 함께 사용할 수 있음
- CDN 지원: 별도의 설치 없이 CDN 링크를 통해 쉽게 사용할 수 있음
2.3 Todo 전체 조회 템플릿 만들기
<!--//file: "./todolist/templates/todo_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/bootstrap-icons.css">
</head>
<body>
<div class="container">
<h1>TODO 목록 앱</h1>
<p>
<a href=""><i class="bi-plus"></i>Add Todo</a>
<a href="" class="btn btn-primary" style="float:right">완료한 TODO 목록</a>
</p>
<ul class="list-group">
<li class="list-group-item">
<a href="">{ { todo.title }}</a>
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-outline-primary">수정하기</a>
</div>
</li>
<li class="list-group-item">
<a href="">{ { todo.title }}</a>
<span class="badge badge-danger">!</span>
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-outline-primary">수정하기</a>
</div>
</li>
<li class="list-group-item">
<a href="">{ { todo.title }}</a>
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-outline-primary">수정하기</a>
</div>
</li>
</ul>
</body>
</div>
</html>
2.4 Todo 전체 조회 뷰 만들기
#//file: "./todolist/views.py"
from django.shortcuts import render, redirect
from todolist.models import Todo
def todo_list(request):
todos = Todo.objects.filter(complete=False)
return render(request, 'todolist/todo_list.html', {'todos': todos})
2.5 Todo 전체 조회 템플릿 수정하기
<!--//file: "./todolist/templates/todo_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/bootstrap-icons.css">
</head>
<body>
<div class="container">
<h1>TODO 목록 앱</h1>
<p>
<a href=""><i class="bi-plus"></i>Add Todo</a>
<a href="" 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="">{ { todo.title }}</a>
{ % if todo.important %}
<span class="badge badge-danger">!</span>
{ % endif %}
<div style="float:right">
<a href="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-outline-primary">수정하기</a>
</div>
</li>
{ % endfor %}
</ul>
</body>
</div>
</html>
2.6 Todo 전체 조회 URL 연결하기
#//file: "./todolist/urls.py"
from django.urls import path
from . import views
urlpatterns = [
path('', views.todo_list, name='todo_list'),
]
#//file: "./todoweb/urls.py"
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('todolist/', include('todolist.urls')),
]
3. Todo 상세 조회 기능 만들기
3.1 Todo 상세 조회 기능 컨셉
- Todo List에서 Todo를 선택했을 때 조회
- Todo의 제목과 설명을 보여줌
3.2 Todo 상세 조회 템플릿 만들기
<!--//file: "./todolist/templates/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/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>
3.3 Todo 상세 조회 뷰 만들기
#//file: "./todolist/views.py"
from django.shortcuts import render, redirect
from .models import Todo
def todo_list(request):
todos = Todo.objects.filter(complete=False)
return render(request, 'todolist/todo_list.html', {'todos': todos})
def todo_detail(request, pk):
todo = Todo.objects.get(id=pk)
return render(request, 'todolist/todo_detail.html', {'todo': todo})
3.4 Todo 전체 조회 템플릿 수정하기
- 상세 조회 페이지로 들어오기 위한 링크 걸기
<!--//file: "./todolist/templates/todo_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/bootstrap-icons.css">
</head>
<body>
<div class="container">
<h1>TODO 목록 앱</h1>
<p>
<a href=""><i class="bi-plus"></i>Add Todo</a>
<a href="" 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="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-outline-primary">수정하기</a>
</div>
</li>
{ % endfor %}
</ul>
</body>
</div>
</html>
3.5 Todo 상세 조회 URL 연결하기
#//file: "./todolist/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'),
]
4. Todo 생성 기능 만들기
4.1 Todo 생성 기능 컨셉
- 제목, 설명, 중요도 입력
- Form 필요 → ./todolist/forms.py
4.2 Todo 생성 템플릿 만들기
4.2.1 Todo 내용 입력을 위한 Form 만들기
#//file: "./todolist/forms.py"
from django import forms
from .models import Todo
class TodoForm(forms.ModelForm):
class Meta:
model = Todo
fields = ('title', 'description', 'important')
4.2.2 템플릿 만들기
<!--//file: "./todolist/templates/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/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">
{ % csrf_token %} { { form.as_p }}
<button type="submit" class="btn btn-primary">등록</button>
</form>
</div>
</div>
</div>
</div>
</div>
</body>
</div>
</html>
4.3 Todo 생성 뷰 만들기
#//file: "./todolist/views.py
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, 'todolist/todo_list.html', {'todos': todos})
def todo_detail(request, pk):
todo = Todo.objects.get(id=pk)
return render(request, 'todolist/todo_detail.html', {'todo': todo})
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, 'todolist/todo_post.html', {'form': form})
4.4 Todo 전체 조회 템플릿 수정하기
- Todo 생성을 위한 링크 걸기
<!--//file: "./todolist/templates/todo_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/bootstrap-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="" 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="" class="btn btn-danger">완료</a>
<a href="" class="btn btn-outline-primary">수정하기</a>
</div>
</li>
{ % endfor %}
</ul>
</body>
</div>
</html>
4.5 Todo 생성 URL 연결하기
#//file: "./todolist/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('post/', views.todo_post, name='todo_post'),
]
5. Todo 수정 기능 만들기
5.1 Todo 수정 기능 컨셉
- Todo 생성 기능과 거의 동일
- 선택된 Todo의 내용이 Form에 미리 입력되어 있음
5.2 Todo 수정 뷰 만들기
#//file: "./todolist/views.py"
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, 'todolist/todo_list.html', {'todos': todos})
def todo_detail(request, pk):
todo = Todo.objects.get(id=pk)
return render(request, 'todolist/todo_detail.html', {'todo': todo})
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, 'todolist/todo_post.html', {'form': form})
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, 'todolist/todo_post.html', {'form': form})
5.4 Todo 전체 조회 템플릿 수정하기
- Todo 수정을 위한 링크 걸기
<!--//file: "./todolist/templates/todo_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/bootstrap-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="" 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="" 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>
5.4 Todo 수정 URL 연결하기
#//file: "./todolist/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('post/', views.todo_post, name='todo_post'),
path('<int:pk>/edit/', views.todo_edit, name='todo_edit'),
]
6. Todo 완료 기능 만들기
6.1 Todo 완료 기능 컨셉
- 완료 버튼을 눌렀을 때 Todo의 complete 값을 True로 설정
- 완료 Todo 조회 기능: 완료된 Todo List만 보여줌
6.2 Todo 완료 템플릿 만들기
<!--//file: "./todolist/templates/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/bootstrap-icons.css">
</head>
<body>
<div class="container">
<h1>DONE 목록</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 badge-danger">!</span>
{ % endif %}
</li>
{ % endfor %}
</ul>
</body>
</div>
</html>
6.3 Todo 전체 조회 템플릿 수정하기
- Todo 완료를 위한 링크 걸기
<!--//file: "./todolist/templates/todo_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/bootstrap-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>
</body>
</div>
</html>
6.4 Todo 완료 뷰 만들기
#//file: "./todolist/views.py"
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, 'todolist/todo_list.html', {'todos': todos})
def todo_detail(request, pk):
todo = Todo.objects.get(id=pk)
return render(request, 'todolist/todo_detail.html', {'todo': todo})
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, 'todolist/todo_post.html', {'form': form})
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, 'todolist/todo_post.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')
6.5 Todo 완료 URL 연결하기
#//file: "./todolist/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('post/', views.todo_post, name='todo_post'),
path('<int:pk>/edit/', views.todo_edit, name='todo_edit'),
path('done/', views.done_list, name='done_list'),
path('done/<int:pk>/', views.todo_done, name='todo_done'),
]