实操 Todo List应用
一、今日学习内容概述
学习模块 | 重要程度 | 预计学时 | 主要内容 |
---|---|---|---|
项目初始化 | ⭐⭐⭐⭐ | 0.5小时 | 创建项目、配置环境 |
模型设计 | ⭐⭐⭐⭐⭐ | 1小时 | Todo模型设计与实现 |
CRUD视图 | ⭐⭐⭐⭐⭐ | 2小时 | 实现增删改查功能 |
模板开发 | ⭐⭐⭐⭐ | 1.5小时 | 页面布局与交互设计 |
功能测试 | ⭐⭐⭐ | 1小时 | 功能测试与调试 |
二、项目初始化
2.1 创建项目和应用
bash
# 创建项目
django-admin startproject todoproject
# 进入项目目录
cd todoproject
# 创建应用
python manage.py startapp todos
2.2 配置项目
python
# todoproject/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'todos.apps.TodosConfig', # 添加todos应用
]
# 配置时区和语言
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
三、模型设计
3.1 创建Todo模型
python
# todos/models.py
from django.db import models
from django.contrib.auth.models import User
class Todo(models.Model):
PRIORITY_CHOICES = [
('high', '高'),
('medium', '中'),
('low', '低'),
]
title = models.CharField('标题', max_length=200)
description = models.TextField('描述', blank=True)
created_date = models.DateTimeField('创建时间', auto_now_add=True)
due_date = models.DateTimeField('截止时间', null=True, blank=True)
completed = models.BooleanField('是否完成', default=False)
priority = models.CharField('优先级', max_length=6, choices=PRIORITY_CHOICES, default='medium')
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
class Meta:
ordering = ['-created_date']
verbose_name = '待办事项'
verbose_name_plural = verbose_name
def __str__(self):
return self.title
3.2 创建表单类
python
# todos/forms.py
from django import forms
from .models import Todo
class TodoForm(forms.ModelForm):
class Meta:
model = Todo
fields = ['title', 'description', 'due_date', 'priority']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'due_date': forms.DateTimeInput(attrs={
'class': 'form-control',
'type': 'datetime-local'
}),
'priority': forms.Select(attrs={'class': 'form-control'}),
}
四、CRUD视图实现
4.1 视图代码
python
# todos/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import Todo
from .forms import TodoForm
@login_required
def todo_list(request):
todos = Todo.objects.filter(user=request.user)
return render(request, 'todos/todo_list.html', {'todos': todos})
@login_required
def todo_create(request):
if request.method == 'POST':
form = TodoForm(request.POST)
if form.is_valid():
todo = form.save(commit=False)
todo.user = request.user
todo.save()
messages.success(request, '待办事项创建成功!')
return redirect('todo_list')
else:
form = TodoForm()
return render(request, 'todos/todo_form.html', {'form': form, 'title': '创建待办事项'})
@login_required
def todo_update(request, pk):
todo = get_object_or_404(Todo, pk=pk, user=request.user)
if request.method == 'POST':
form = TodoForm(request.POST, instance=todo)
if form.is_valid():
form.save()
messages.success(request, '待办事项更新成功!')
return redirect('todo_list')
else:
form = TodoForm(instance=todo)
return render(request, 'todos/todo_form.html', {'form': form, 'title': '编辑待办事项'})
@login_required
def todo_delete(request, pk):
todo = get_object_or_404(Todo, pk=pk, user=request.user)
if request.method == 'POST':
todo.delete()
messages.success(request, '待办事项删除成功!')
return redirect('todo_list')
return render(request, 'todos/todo_confirm_delete.html', {'todo': todo})
@login_required
def todo_toggle_complete(request, pk):
todo = get_object_or_404(Todo, pk=pk, user=request.user)
todo.completed = not todo.completed
todo.save()
return redirect('todo_list')
4.2 URL配置
python
# todoproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todos.urls')),
]
# todos/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.todo_list, name='todo_list'),
path('create/', views.todo_create, name='todo_create'),
path('update/<int:pk>/', views.todo_update, name='todo_update'),
path('delete/<int:pk>/', views.todo_delete, name='todo_delete'),
path('toggle/<int:pk>/', views.todo_toggle_complete, name='todo_toggle_complete'),
]
五、模板开发
5.1 基础模板
html
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="zh-hans">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Todo List{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'todo_list' %}">Todo List</a>
<div class="collapse navbar-collapse">
<ul class="navbar-nav ms-auto">
{% if user.is_authenticated %}
<li class="nav-item">
<span class="nav-link">{{ user.username }}</span>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">退出</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">登录</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% block content %}
{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
5.2 列表页面
html
<!-- templates/todos/todo_list.html -->
{% extends 'base.html' %}
{% block content %}
<div class="row mb-4">
<div class="col">
<h2>我的待办事项</h2>
</div>
<div class="col text-end">
<a href="{% url 'todo_create' %}" class="btn btn-primary">
添加待办事项
</a>
</div>
</div>
<div class="row">
<div class="col">
{% if todos %}
<div class="list-group">
{% for todo in todos %}
<div class="list-group-item {% if todo.completed %}bg-light{% endif %}">
<div class="d-flex justify-content-between align-items-center">
<div>
<h5 class="mb-1 {% if todo.completed %}text-decoration-line-through{% endif %}">
{{ todo.title }}
</h5>
<p class="mb-1">{{ todo.description }}</p>
<small>
优先级: {{ todo.get_priority_display }} |
创建时间: {{ todo.created_date|date:"Y-m-d H:i" }}
{% if todo.due_date %}
| 截止时间: {{ todo.due_date|date:"Y-m-d H:i" }}
{% endif %}
</small>
</div>
<div>
<form action="{% url 'todo_toggle_complete' todo.pk %}" method="post" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-{% if todo.completed %}warning{% else %}success{% endif %}">
{% if todo.completed %}取消完成{% else %}标记完成{% endif %}
</button>
</form>
<a href="{% url 'todo_update' todo.pk %}" class="btn btn-sm btn-info">编辑</a>
<a href="{% url 'todo_delete' todo.pk %}" class="btn btn-sm btn-danger">删除</a>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-center">暂无待办事项</p>
{% endif %}
</div>
</div>
{% endblock %}
5.3 表单页面
html
<!-- templates/todos/todo_form.html -->
{% extends 'base.html' %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-6 offset-md-3">
<h2>{{ title }}</h2>
<form method="post">
{% csrf_token %}
{% for field in form %}
<div class="mb-3">
{{ field.label_tag }}
{{ field }}
{% if field.errors %}
<div class="alert alert-danger">
{{ field.errors }}
</div>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">保存</button>
<a href="{% url 'todo_list' %}" class="btn btn-secondary">返回</a>
</form>
</div>
</div>
{% endblock %}
六、流程图
七、扩展功能建议
- 添加任务分类功能
- 实现任务搜索和筛选
- 添加任务提醒功能
- 实现任务标签系统
- 添加任务导入导出功能
八、测试用例
python
# todos/tests.py
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.urls import reverse
from .models import Todo
class TodoTests(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(
username='testuser',
password='testpass123'
)
self.client.login(username='testuser', password='testpass123')
def test_todo_list_view(self):
response = self.client.get(reverse('todo_list'))
self.assertEqual(response.status_code, 200)
def test_todo_create(self):
data = {
'title': '测试待办事项',
'description': '这是一个测试',
'priority': 'high'
}
response = self.client.post(reverse('todo_create'), data)
self.assertEqual(response.status_code, 302)
self.assertTrue(Todo.objects.filter(title='测试待办事项').exists())
九、总结
- 学习要点回顾:
- Django模型设计和实现
- CRUD视图的完整实现
- 表单处理和验证
- 模板系统的使用
- 用户认证和权限控制
怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!