Bootstrap

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

前言

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

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

抽离基础模板

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

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <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">
</head>
<body class="container-full-dark">
<div class="navbar-blue">
    <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>
</div>
{% block content %}
{% endblock %}

</body>
</html>

修改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/ 测试一下。

添加学生

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

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

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

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'),
]

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

修改添加学生的表单。

<form class="form" method="post" action="{% url 'index: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>
</form>

这里的{% 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="col4-card">
  <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>
</div>
{% endfor %}

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

修改学生信息

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

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

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

<a
   class="btn-green"
   href="{% url 'index:student_add' %}?id={{ student.id }}"
   >
  编辑
</a>

然后我们去修改添加学生的视图函数,如果传递了学生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的值显示出来。

<form class="form" method="post" action="{% url 'index:student_add' %}">
  {% 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>
</form>

如果有学生信息,这填充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)

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

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

添加计划

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

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

<a class="btn-yellow" href="{% url 'index:plan_add' %}?id={{ student.id }}">
  新计划
</a>

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

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我们现在还没有待会儿再实现,现在先继续实现添加计划的逻辑。接下来修改一下添加计划的表单。

<form class="form" method="post" action="{% url 'index:plan_add' %}">
  {% 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>
</form>

接着我们再继续修改添加计划的视图函数,如果是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' %}" class="logo">Python私教</a>
<div class="nav-links">
  <a href="{% url 'index:student' %}">首页</a>
  <a href="{% url 'index:student_add' %}">新学生</a>
</div>

动态渲染计划列表

接下来,我们就像动态渲染学生列表一样,动态渲染计划列表,默认显示状态为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请求。

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

首先修改模板。

<form action="{% url 'index:plan' %}?did={{ plan.id }}&sid={{ student.id }}"
      method="post">
  {% csrf_token %}
  <button class="btn-red" type="submit">删除</button>
</form>

接着修改视图函数。

def plan(request):
    # 执行删除
    if request.method == 'POST':
        id = request.GET.get('did')
        if id:
            plan = Plan.objects.filter(id=id).first()
            plan.delete()

    # 查询学生
    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)

执行计划

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

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

<form action="{% url 'index:plan' %}?uid={{ plan.id }}&sid={{ student.id }}"
      method="post">
  {% csrf_token %}
  <input type="hidden" name="status" value="2">
  <button type="submit" class="btn-green">执行</button>
</form>

此时我们还是使用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,也就是进行中,则渲染这个按钮,点击按钮,将状态改为已完成。

{% if status == 1 %}
<form action="{% url 'index:plan' %}?uid={{ plan.id }}&sid={{ student.id }}"
      method="post">
  {% csrf_token %}
  <input type="hidden" name="status" value="2">
  <button type="submit" class="btn-green">执行</button>
</form>
{% elif status == 2 %}
<form action="{% url 'index:plan' %}?uid={{ plan.id }}&sid={{ student.id }}"
      method="post">
  {% csrf_token %}
  <input type="hidden" name="status" value="3">
  <button type="submit" class="btn-green">完成</button>
</form>
<form action="{% url 'index:plan' %}?uid={{ plan.id }}&sid={{ student.id }}"
      method="post">
  {% csrf_token %}
  <input type="hidden" name="status" value="1">
  <button type="submit" class="btn-yellow">暂停</button>
</form>
{% endif %}

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

此时我们修改视图函数。

status_dict = {
    1: '未开始',
    2: '进行中',
    3: '已完成',
    4: '已取消',
    5: '已删除',
}


def plan(request):
    # 查询计划
    status = 1
    status_text = status_dict.get(status)

    # 执行删除
    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:
            try:
                status = int(request.POST.get('status'))
                status_text = status_dict.get(status)
            except:
                pass
            plan = Plan.objects.filter(id=uid).first()
            plan.status = status
            plan.save()

    # 查询学生
    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)

查看学生计划

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

<a href="{% url 'index:plan' %}?sid={{ student.id }}&status=1" class="btn-blue">未开始</a>
<a href="{% url 'index:plan' %}?sid={{ student.id }}&status=2" class="btn-green">进行中</a>
<a href="{% url 'index:plan' %}?sid={{ student.id }}&status=3" class="btn-red">已完成</a>

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

def plan(request):
    # 查询计划
    status = None
    try:
        status = int(request.GET.get('status'))
    except:
        status = 1
    status_text = status_dict.get(status)

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

总结

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

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

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

;