首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

django33全栈班2025年023 计划看板项目实战

前言

目前我们已经有了一些静态的页面,接下来我们就进行数据的交互。

首先我们要学习模板的抽离,因为每个没有有很多相同的代码,我们可以把公共的部分抽离出来,比如导航。

抽离基础模板

新增base.html作为基础模板。

{% load static %}

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>学生教学计划系统</title>

  <link href="{% static 'django33/django33.css' %}" rel="stylesheet">

  <link href="{% static 'django33/django33.js' %}" rel="stylesheet">

  <a href="#" class="logo">Python私教</a>

  <div class="nav-links">

      <a href="#">首页</a>

      <a href="#">课程</a>

      <a href="#">关于我们</a>

      <a href="#">联系我们</a>

  </div>

  <div class="search-box">

      <input type="text" placeholder="搜索课程..."/>

      <button></button>

  </div>

{% block content %}

{% endblock %}

修改student.html页面。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  <div class="container-blue mt1">

      <h1 class="title-white-center">学生列表</h1>

      <div class="flex-center">

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">张三</h3>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-blue">未开始</button>

                  <button class="btn-green">进行中</button>

                  <button class="btn-red">已完成</button>

              </div>

          </div>

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">李四</h3>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-blue">未开始</button>

                  <button class="btn-green">进行中</button>

                  <button class="btn-red">已完成</button>

              </div>

          </div>

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">王五</h3>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-blue">未开始</button>

                  <button class="btn-green">进行中</button>

                  <button class="btn-red">已完成</button>

              </div>

          </div>

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">赵六</h3>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-blue">未开始</button>

                  <button class="btn-green">进行中</button>

                  <button class="btn-red">已完成</button>

              </div>

          </div>

      </div>

  </div>

{% endblock %}

修改student_add.html页面。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  <div class="form-blue-container-sm">

      <form class="form">

          <div class="input-group-blue">

              <label for="image-title">姓名</label>

              <input type="text"

                     id="image-title"

                     placeholder="请输入姓名" required>

          </div>

          <div class="input-group-blue">

              <label for="image-title">电话</label>

              <input type="text"

                     id="image-title"

                     placeholder="请输入电话" required>

          </div>

          <button type="submit" class="form-blue-btn">添加</button>

      </form>

  </div>

{% endblock %}

修改plan.html页面。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  <div class="container-blue mt1">

      <h1 class="title-white-center">张三进行中的计划</h1>

      <div class="flex-center">

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">部门表改造以后, 权限会跟着变动, 上级部门的权限,

                      应该自动包含下级的权限</h3>

                  <div class="status2">一般</div>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-green">执行</button>

                  <button class="btn-red">删除</button>

              </div>

          </div>

          <div class="col4-card">

              <div class="flex-column">

                  <h3>localstorage存储了用户id和用户名,要考虑存储在pinia中,还要考虑刷新的问题</h3>

                  <div class="status2">一般</div>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-green">执行</button>

                  <button class="btn-red">删除</button>

              </div>

          </div>

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">注册页面优化</h3>

                  <div class="status2">一般</div>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-green">执行</button>

                  <button class="btn-red">删除</button>

              </div>

          </div>

          <div class="col4-card">

              <div class="flex-column">

                  <h3 class="content-white">注册页面优化</h3>

                  <div class="status2">一般</div>

                  <p class="intro-date-gray">2023-10-10 14:30:00</p>

              </div>

              <div class="flex">

                  <button class="btn-green">执行</button>

                  <button class="btn-red">删除</button>

              </div>

          </div>

      </div>

  </div>

{% endblock %}

修改plan_add.html页面。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  <div class="form-blue-container-sm">

      <form class="form">

          <div class="input-group-blue">

              <label for="image-description">计划</label>

              <textarea id="image-description"

                        placeholder="请输入计划内容"

                        rows="6"></textarea>

          </div>

          <button type="submit" class="form-blue-btn">添加</button>

      </form>

  </div>

{% endblock %}

效果预览

此时通过浏览器访问相关的页面,确保页面还是正常的。

http://127.0.0.1:8000/plan/

在这里插入图片描述

http://127.0.0.1:8000/plan/add/

在这里插入图片描述

http://127.0.0.1:8000/student/

在这里插入图片描述

http://127.0.0.1:8000/student/add/

在这里插入图片描述考虑为空的情况

修改student.html页面,如果没有学生,就显示空空如也。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  {% if students %}

      <div class="container-blue mt1">

          <h1 class="title-white-center">学生列表</h1>

          <div class="flex-center">

              <div class="col4-card">

                  <div class="flex-column">

                      <h3 class="content-white">张三</h3>

                      <p class="intro-date-gray">2023-10-10 14:30:00</p>

                  </div>

                  <div class="flex">

                      <button class="btn-blue">未开始</button>

                      <button class="btn-green">进行中</button>

                      <button class="btn-red">已完成</button>

                  </div>

              </div>

              <div class="col4-card">

                  <div class="flex-column">

                      <h3 class="content-white">李四</h3>

                      <p class="intro-date-gray">2023-10-10 14:30:00</p>

                  </div>

                  <div class="flex">

                      <button class="btn-blue">未开始</button>

                      <button class="btn-green">进行中</button>

                      <button class="btn-red">已完成</button>

                  </div>

              </div>

              <div class="col4-card">

                  <div class="flex-column">

                      <h3 class="content-white">王五</h3>

                      <p class="intro-date-gray">2023-10-10 14:30:00</p>

                  </div>

                  <div class="flex">

                      <button class="btn-blue">未开始</button>

                      <button class="btn-green">进行中</button>

                      <button class="btn-red">已完成</button>

                  </div>

              </div>

              <div class="col4-card">

                  <div class="flex-column">

                      <h3 class="content-white">赵六</h3>

                      <p class="intro-date-gray">2023-10-10 14:30:00</p>

                  </div>

                  <div class="flex">

                      <button class="btn-blue">未开始</button>

                      <button class="btn-green">进行中</button>

                      <button class="btn-red">已完成</button>

                  </div>

              </div>

          </div>

      </div>

  {% else %}

      <div class="container-blue-empty">

          <div class="empty-blue">

              空空如也

          </div>

      </div>

  {% endif %}

{% endblock %}

设计模型

模型就是数据库表,和咱们之前学面向对象的时候的类是差不多的概念,模型就是一种可以关联数据库表的特殊类。

我们来创建学生模型和计划模型,每个学生有自己的计划。

from django33.db import models

# 在这里创建你的模型

class Student(models.Model):

  name = models.CharField(max_length=20, verbose_name='姓名')

  add_time = models.DateTimeField(auto_now_add=True, verbose_name='添加时间')

  class Meta:

      verbose_name = '学生'

      verbose_name_plural = verbose_name

      db_table = 'student'

  def __str__(self):

      return self.name

  def __repr__(self):

      return self.__str__()

class Plan(models.Model):

  name = models.CharField(max_length=20, verbose_name='计划名称')

  # 1未开始 2进行中 3已完成

  status = models.SmallIntegerField(default=1, verbose_name='计划状态')

  add_time = models.DateTimeField(auto_now_add=True, verbose_name='添加时间')

  student = models.ForeignKey(Student, on_delete=models.CASCADE, verbose_name='学生')

  class Meta:

      verbose_name = '计划'

      verbose_name_plural = verbose_name

      db_table = 'plan'

  def __str__(self):

      return self.name

  def __repr__(self):

      return self.__str__()

迁移模型:

python manage.py makemigrations

python manage.py migrate

迁移模型就是生成数据库表的过程,迁移之后,我们就可以通过模型类来操作数据库表了。

查询所有学生

修改学生列表的视图方法,我们查询所有学生并传递到模板中。

先导入模型。

from .models import Student, Plan

然后再编写视图函数。

def student(request):

  students = Student.objects.all()

  context = {'students': students}

  return render(request, 'student.html', context)

不过此时我们是没有学生数据的,所以页面上应该会显示空空如也。

浏览器访问 http://127.0.0.1:8000/student/ 测试一下。

添加学生

首先,在导航中,添加一个链接,点击链接跳转到添加学生界面。

然后,修改一下首页,首页默认就是学生列表界面。

from django33.urls import path

from . import views

app_name = 'index'

urlpatterns = [

  path('plan/', views.plan, name='plan'),

  path('plan/add/', views.plan_add, name='plan_add'),

  path('', views.student, name='student'),

  path('student/add/', views.student_add, name='student_add'),

]

此时,点击导航上的新学生,会跳转到添加学生页面。

修改添加学生的表单。

{% csrf_token %}

<div class="input-group-blue">

  <label for="name">姓名</label>

  <input type="text"

         id="name"

         name="name"

         placeholder="请输入姓名" required>

</div>

<button type="submit" class="form-blue-btn">添加</button>

这里的{% csrf_token %}是用来防止CSRF跨站攻击的,是django内置的功能,如果是表单,一定要加。

这里的action="{% url 'index:student_add' %}"表示会把请求发送到添加学生的视图函数里面去。

接着我们修改添加学生的视图函数,实现添加的逻辑。

def student_add(request):

  if request.method == 'POST':

      name = request.POST.get('name')

      student = Student(name=name)

      student.save()

      return redirect(reverse('index:student'))

  return render(request, 'student_add.html')

此时我们去添加学生,就能够自动跳转到学生列表页面了,也就是首页。

动态渲染学生列表

我们添加了学生以后,就可以动态渲染学生列表了,我们使用for循环进行渲染。

{% for student in students %}

<div class="flex-column">

  <h3 class="content-white">{{ student.name }}</h3>

  <p class="intro-date-gray">{{ student.add_time }}</p>

</div>

<div class="flex">

  <button class="btn-blue">未开始</button>

  <button class="btn-green">进行中</button>

  <button class="btn-red">已完成</button>

  <button class="btn-yellow">新计划</button>

  <button class="btn-green">编辑</button>

  <button class="btn-red">删除</button>

</div>

{% endfor %}

此时,首页就会动态渲染我们添加的学生列表信息了。

修改学生信息

如果学生的信息不小心填错了,该怎么修改呢?

我们可以通过超链接重定向到添加学生的页面,并携带学生的id,后端根据id查询学生的信息并渲染添加页面,添加页面中,渲染学生的默认值。

接下来在学生列表页面中,添加编辑的超链接。

<a

 class="btn-green"

 href="{% url 'index:student_add' %}?id={{ student.id }}"

 >

编辑

然后我们去修改添加学生的视图函数,如果传递了学生id过来,就根据id查询学生。

def student_add(request):

  if request.method == 'POST':

      name = request.POST.get('name')

      student = Student(name=name)

      student.save()

      return redirect(reverse('index:student'))

  sid = request.GET.get('id')

  student = Student()

  if sid:

      student = Student.objects.filter(id=sid).first()

  context = {'student': student}

  return render(request, 'student_add.html', context)

然后我们还要去修改添加学生的表单,如果有id,要把id作为隐藏框,如果有name,需要把name的值显示出来。

{% csrf_token %}

<div class="input-group-blue">

  <label for="name">姓名</label>

  {% if student.id %}

  <input type="hidden" name="id" value="{{ student.id }}">

  <input type="text"

         id="name"

         name="name"

         value="{{ student.name }}"

         placeholder="请输入姓名" required>

  {% else %}

  <input type="text"

         id="name"

         name="name"

         placeholder="请输入姓名" required>

  {% endif %}

</div>

<button type="submit" class="form-blue-btn">添加</button>

如果有学生信息,这填充id和学生姓名,如果没有,则使用空数据。

最后,我们还得修改添加学生的视图函数,如果获取到了id,说明是修改,此时执行修改的逻辑。

def student_add(request):

  if request.method == 'POST':

      id = request.POST.get('id')

      name = request.POST.get('name')

      if id:

          student = Student.objects.filter(id=id).first()

          student.name = name

          student.save()

      else:

          student = Student(name=name)

          student.save()

      return redirect(reverse('index:student'))

  sid = request.GET.get('id')

  student = Student()

  if sid:

      student = Student.objects.filter(id=sid).first()

  context = {'student': student}

  return render(request, 'student_add.html', context)

这样我们添加和修改学生的逻辑就完成了。

关于删除学生的功能我们不在前端实现,因为比较危险,可以通过自带的后台管理系统或者数据库实现删除的功能。

添加计划

点击每个学生的“新计划”按钮,这跳转到添加计划的页面,同时要携带这个学生的信息。

首先,我们修改新计划的链接。

新计划

然后我们修改添加计划的视图函数。

def plan_add(request):

  student_id = request.GET.get('id')

  if not student_id:

      return redirect(reverse('index:not_found'))

  context= {"student_id": student_id}

  return render(request, 'plan_add.html', context)

这个not_found我们现在还没有待会儿再实现,现在先继续实现添加计划的逻辑。接下来修改一下添加计划的表单。

{% csrf_token %}

<input type="hidden" name="student_id" value="{{ student_id }}">

<div class="input-group-blue">

  <label for="plan">计划</label>

  <textarea id="plan"

            placeholder="请输入计划内容"

            name="name"

            rows="6"></textarea>

</div>

<button type="submit" class="form-blue-btn">添加</button>

接着我们再继续修改添加计划的视图函数,如果是POST请求,这实现添加计划的逻辑。

def plan_add(request):

  if request.method == 'POST':

      id = request.POST.get('id')

      name = request.POST.get('name')

      student_id = request.POST.get('student_id')

      if id:

          plan = Plan.objects.filter(id=id).first()

          plan.name = name

          plan.student_id = student_id

          plan.save()

      else:

          plan = Plan(name=name, student_id=student_id)

          plan.save()

      return redirect(reverse('index:plan'))

  student_id = request.GET.get('id')

  if not student_id:

      return redirect(reverse('index:not_found'))

  context= {"student_id": student_id}

  return render(request, 'plan_add.html', context)

这段代码和添加学生的代码差不多,也是如果能获取到id则为修改,否则就是新增。

添加或者修改完毕以后,重定向到计划列表页面。

错误404页面

之前我们有一个找不到学生就重定向到404页面的逻辑,现在我们来实现一下该视图函数和页面。

添加error404.html作为页面模板。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  <div class="error404-blue">

      <h1 class="title-white-center">资源不存在</h1>

  </div>

{% endblock %}

添加视图函数:

def error404(request):

  return render(request, 'error404.html')

添加路由:

path('404/', views.error404, name='not_found'),

浏览器访问: http://127.0.0.1:8000/404/

在这里插入图片描述调整导航

在继续开发之前,我们先调整一下导航,这样方便在页面之间跳转。

<a href="{% url 'index:student' %}">首页</a>

<a href="{% url 'index:student_add' %}">新学生</a>

动态渲染计划列表

接下来,我们就像动态渲染学生列表一样,动态渲染计划列表,默认显示状态为1的未开始的计划。

首先也是先添加模板。

{% extends 'base.html' %}

{% load static %}

{% block content %}

  {% if plans %}

      <div class="container-blue mt1">

          <h1 class="title-white-center">{{ student.name }}{{ status_text }}的计划</h1>

          <div class="flex-center">

              {% for plan in plans %}

                  <div class="col4-card">

                      <div class="flex-column">

                          <h3 class="content-white">{{ plan.name }}</h3>

                          <p class="intro-date-gray">{{ plan.add_time }}</p>

                      </div>

                      <div class="flex">

                          <button class="btn-green">执行</button>

                          <button class="btn-red">删除</button>

                      </div>

                  </div>

              {% endfor %}

          </div>

      </div>

  {% else %}

      <div class="container-blue-empty">

          <div class="empty-blue">

              空空如也

          </div>

      </div>

  {% endif %}

{% endblock %}

然后是修改视图函数。

def plan(request):

  student_id = request.GET.get('sid')

  if not student_id:

      return redirect('index:not_found')

  student = Student.objects.filter(id=student_id).first()

  if not student:

      return redirect('index:not_found')

  status = 1

  status_text = '未开始'

  plans = Plan.objects.filter(status=status).all()

  context = {

      'student': student,

      'plans': plans,

      'status_text': status_text,

  }

  return render(request, 'plan.html', context)

同时,添加计划的视图函数也要改一下,要把学生的id传过来。

def plan_add(request):

  if request.method == 'POST':

      id = request.POST.get('id')

      name = request.POST.get('name')

      student_id = request.POST.get('student_id')

      if id:

          plan = Plan.objects.filter(id=id).first()

          plan.name = name

          plan.student_id = student_id

          plan.save()

      else:

          plan = Plan(name=name, student_id=student_id)

          plan.save()

      return redirect(f"{reverse('index:plan')}?sid={student_id}")

  student_id = request.GET.get('id')

  if not student_id:

      return redirect(reverse('index:not_found'))

  context = {"student_id": student_id}

  return render(request, 'plan_add.html', context)

此时给李四添加一个计划,你就会发现自动跳转了: http://127.0.0.1:8000/plan/?sid=2

在这里插入图片描述删除计划

点击删除按钮的时候,我们发送一个删除请求,为了安全,我们用form包裹,传递post请求。

删除一行,重新回到当前页面。

首先修改模板。

接着修改视图函数。

执行计划

点击执行按钮,将计划修改为2,也就是进行中,然后渲染进行中的计划。

修改模板,将执行按钮改为:

此时我们还是使用post请求,不过传递的是uid,u表示update,更新。

然后我们修改视图函数。

def plan(request):

  # 查询计划

  status = 1

  status_text = '未开始'

  # 执行删除

  if request.method == 'POST':

      did = request.GET.get('did')

      uid = request.GET.get('uid')

      # 删除

      if did:

          plan = Plan.objects.filter(id=did).first()

          plan.delete()

      # 修改

      elif uid:

          plan = Plan.objects.filter(id=uid).first()

          plan.status = 2

          plan.save()

          # 查询进行中的计划

          status = 2

          status_text = '进行中'

  # 查询学生

  student_id = request.GET.get('sid')

  if not student_id:

      return redirect('index:not_found')

  student = Student.objects.filter(id=student_id).first()

  if not student:

      return redirect('index:not_found')

  plans = Plan.objects.filter(status=status).all()

  context = {

      'student': student,

      'plans': plans,

      'status': status,

      'status_text': status_text,

  }

  return render(request, 'plan.html', context)

这里我们把status向模板传递,模板根据status的值,动态的渲染一个按钮是“执行”还是“完成”,或者是否加一个撤销?

完成计划

我们修改一下模板,添加一个完成按钮,如果status为2,也就是进行中,则渲染这个按钮,点击按钮,将状态改为已完成。

我们根据状态值动态的切换,动态的渲染不同的按钮。然后加了一个status的隐藏输入框,通过模板主动传递状态值。

此时我们修改视图函数。

查看学生计划

在学生列表页面,通过按钮的点击,能够快速跳转到不同状态下的计划。

然后修改计划视图函数,让status支持通过查询参数传递过来。

def plan(request):

  # 查询计划

  status = None

  try:

      status = int(request.GET.get('status'))

  except:

      status = 1

  status_text = status_dict.get(status)

此时重新访问学生列表页面,你就可以点击各种状态按钮跳转到不同状态下的计划列表页面了。

总结

本课主要讲解了一个计划看板的实战项目,这是可以投入真实使用的项目,也是我们学习django33全栈开发第一个能够正式使用的项目。

完整的代码我已经打包发布到星球,感兴趣的同学可以去星球的源代码专栏进行下载。

最近一直在上直播课,喜欢学编程的同学欢迎来试听一下。

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O3UIZTH6Jftd3a3XlEisNk7w0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券