每天40分玩转Django:实操 Todo List应用

实操 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 %}

六、流程图

七、扩展功能建议

  1. 添加任务分类功能
  2. 实现任务搜索和筛选
  3. 添加任务提醒功能
  4. 实现任务标签系统
  5. 添加任务导入导出功能

八、测试用例

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())

九、总结

  1. 学习要点回顾:
    • Django模型设计和实现
    • CRUD视图的完整实现
    • 表单处理和验证
    • 模板系统的使用
    • 用户认证和权限控制

怎么样今天的内容还满意吗?再次感谢朋友们的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!

相关推荐
黑客老李10 分钟前
面试经验分享 | 杭州某安全大厂渗透测试岗二面
大数据·服务器·数据库·经验分享·安全·面试·职场和发展
MavenTalk13 分钟前
阿里云免费SSL证书调整为3个月后,自动升级SSL证书方案
python·阿里云·云计算·ansible·ssl
斗-匕18 分钟前
MySQL 索引详解
数据库·mysql
四口鲸鱼爱吃盐19 分钟前
Pytorch | 对比Pytorch中的十种优化器:基于CIFAR10上的ResNet分类器
人工智能·pytorch·python
阿正的梦工坊20 分钟前
PyTorch 的 torch.unbind 函数详解与进阶应用:中英双语
人工智能·pytorch·python
zmd-zk31 分钟前
shuffle——spark
大数据·分布式·python·学习·spark
codists31 分钟前
《Django 5 By Example》阅读笔记:p561-p613
python·django·编程人
Java Fans34 分钟前
构建一个简单的基于 HBase 的搜索引擎
数据库·搜索引擎·hbase
正在努力中的小白♤35 分钟前
CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现
java·后端·spring
DC_BLOG40 分钟前
数据结构树
java·数据结构·算法