心蓝的博客 心蓝的博客
首页
  • 零基础

    • python零基础入门
  • 专项

    • 正则表达式
  • web框架

    • django框架
    • drf
技术
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档

心蓝

人生苦短,我用python
首页
  • 零基础

    • python零基础入门
  • 专项

    • 正则表达式
  • web框架

    • django框架
    • drf
技术
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • 零基础

  • 专项

  • web框架

    • django框架

      • web框架介绍
      • python虚拟环境
      • 创建django项目与应用
      • django中的路由系统
      • web框架设计模式
      • 模板
      • 静态文件引用
      • web应用开发模式
      • 请求和响应
      • 视图
      • ORM与模型
      • 数据库操作
      • 项目实战一
        • 需求
        • 学生列表功能
          • 接口设计
          • 代码
          • 视图
          • 路由
          • 模板
        • 学生添加功能
          • 学生添加页面
          • 接口设计
          • 代码
          • 学生添加
          • 接口设计
          • 代码
        • 学生修改功能
          • 学生修改页面
          • 接口设计
          • 代码
          • 学生修改
          • 接口设计
          • 代码
        • 学生删除功能
          • 接口设计
          • 代码
      • 表单
      • RESTful API
      • 项目实战二
      • djangoadmin
    • drf

  • python
  • web框架
  • django框架
心蓝
2022-12-21
目录

项目实战一

# 项目实战一

# 需求

以前后端不分离的方式实现学生的增删改查操作

# 学生列表功能

# 接口设计

url:/students/

请求方法:get

参数:

  • 格式:查询参数
参数名 类型 是否必传 说明
page int 否 页码,默认为1
size init 否 每页数据条数默认为10
name str 否 根据姓名过滤
age int 否 根据年龄过滤
sex int 否 根据性别过滤
phone str 否 根据手机过滤
channel_id int 否 根据渠道过滤

响应:html

# 代码

# 视图

import math
from urllib.parse import urlencode


def student_list(request):
    """学生列表视图"""

    # 1. 获取查询参数
    query_params = {key: value for key, value in request.GET.items()}
    # 2. 获取分页参数
    page = int(query_params.pop('page', 1))
    size = int(query_params.pop('size', 10))
    # 3. 获取查询集
    queryset = Student.objects.all()
    for key, value in query_params.items():
        try:
            queryset = queryset.filter(**{key: value})
        except:
            pass
    # 4. 分页处理
    # 数据总条数
    total_num = queryset.count()
    # 总页数
    total_page = math.ceil(total_num/size)
    # 下一页
    if page < total_page:
        query_params.update({'page': page+1, 'size': size})
        next_page_query_params = urlencode(query_params)
    else:
        next_page_query_params = None

    # 上一页
    if page > 1:
        query_params.update({'page': page - 1, 'size': size})
        pre_page_query_params = urlencode(query_params)
    else:
        pre_page_query_params = None
    # 分页过滤
    queryset = queryset[(page-1)*size:page*size]

    # 5. 渲染模板并返回响应
    return render(request, 'crm/student_list.html', context={
        'queryset': queryset,
        'pre_page': pre_page_query_params,
        'next_page': next_page_query_params,
        'page': page,
        'total_num': total_num,
        'total_page': total_page
    })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 路由

path('students/', views.student_list, name='student-list'),
1

# 模板

<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>学生列表</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
          integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
    <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
</head>
<body>
<div class="container">
    <h1>学生列表</h1>
    <div style="width: 800px">
        {% if queryset %}
        <a class="btn btn-success" style="float: right" href="{% url 'student-create' %}">添加</a>
        <table class="table table-hover table-bordered table-condensed">
            <thead>
            <tr>
                <th>序号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <th>电话</th>
                <th>渠道</th>
                <th>创建时间</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            {% for obj in queryset %}
                <tr>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ obj.name }}</td>
                    <td>{{ obj.age | default:"" }}</td>
                    <td>{% if obj.sex == 1 %}男{% else %}女{% endif %}</td>
                    <td>{{ obj.phone | default:"" }}</td>
                    <td>{% if obj.channel %}{{ obj.channel.title }}{% endif %}</td>
                    <td>{{ obj.c_time }}</td>
                    <td><a class="btn btn-primary" href="{% url 'student-update' obj.id %}">编辑</a>
                        <a class="btn btn-danger" href="{% url 'student-delete' obj.id %}">删除</a></td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
        <nav aria-label="...">
            <ul class="pager">
                <li class="previous {% if not pre_page %}disabled{% endif %}"><a
                        href="{% if pre_page %}?{{ pre_page }}{% else %}#{% endif %}"><span
                        aria-hidden="true">&larr;</span> 上一页</a></li>
                <li class="next {% if not next_page %}disabled{% endif %}"><a
                        href="{% if next_page %}?{{ next_page }}{% else %}#{% endif %}">下一页 <span aria-hidden="true">&rarr;</span></a>
                </li>
            </ul>
        </nav>
        {% else %}
            <p>查不到数据...</p>
        {% endif %}
    </div>
</div>


<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
{#    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>#}
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
{#    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>#}
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

访问http://127.0.0.1/crm/students/效果图如下:

1661261338575

# 学生添加功能

# 学生添加页面

# 接口设计

url:/students/create/

请求方法:get

响应:html页面

# 代码

  1. 视图
class StudentCreateView(View):
    """
    学生添加视图
    """
    def get(self, request):
        """学生添加页面"""
        # 1. 获取渠道对象
        channels = Channel.objects.all()
        return render(request, 'crm/student_detail.html', context={'channels': channels})

1
2
3
4
5
6
7
8
9
10
  1. 路由
path('students/create/', views.StudentCreateView.as_view(), name='student-create')
1
  1. 模板
<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>{% if obj %}修改{% else %}添加{% endif %}学生</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
    <![endif]-->
  </head>
  <body>
    <div class="container">
       <div style="width: 800px">
    <h1>学生{% if obj %}修改{% else %}添加{% endif %}页面</h1>
    <form class="form-horizontal" method="post">
  <div class="form-group">
    <label for="name" class="col-sm-2 control-label">姓名</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="name" name="name" placeholder="姓名" value="{{ obj.name }}">
    </div>
  </div>
  <div class="form-group">
    <label for="age" class="col-sm-2 control-label">年龄</label>
    <div class="col-sm-10">
      <input type="text" class="form-control" id="age" name="age" placeholder="年龄" value="{{ obj.age|default:"" }}">
    </div>
  </div>
  <div class="form-group">
    <label for="sex" class="col-sm-2 control-label">性别</label>
    <div class="col-sm-10">
        <select name="sex" id="sex" class="form-control">
            <option value="1" {% if obj.sex == 1%}selected{% endif %}>男</option>
            <option value="0" {% if obj.sex == 0%}selected{% endif %}>女</option>
        </select>
    </div>
  </div>
  <div class="form-group">
    <label for="phone" class="col-sm-2 control-label">电话号码</label>
    <div class="col-sm-10">
        <input type="text" class="form-control" id="phone" name="phone" placeholder="phone" value="{{ obj.phone|default:"" }}">
    </div>
  </div>
  <div class="form-group">
    <label for="channel" class="col-sm-2 control-label">渠道</label>
    <div class="col-sm-10">
        <select name="channel" id="channel" class="form-control">
            <option value="">--------</option>
            {% for ch in channels %}
                <option value="{{ ch.id }}" {% if obj.channel.title == ch.title %}selected{% endif %}>{{ ch.title }}</option>
            {% endfor %}
        </select>
    </div>
  </div>

  <div class="form-group">
    <div class="col-sm-offset-2 col-sm-10">
      <button type="submit" class="btn btn-default">{% if obj %}修改{% else %}添加{% endif %}</button>
{#      <button type="submit" class="btn btn-default">添加</button>#}
    </div>
  </div>
</form>
    </div>
    </div>


    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js" integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ" crossorigin="anonymous"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
  </body>
</html>


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

访问http://127.0.0.1/crm/students/create/效果图如下:

1661261450885

# 学生添加

# 接口设计

url:/students/create/

请求方法:post

参数:

  • 格式:form
参数名 类型 是否必传 说明
name str 是 姓名
age int 否 年龄
sex int 否 性别
phone str 否 手机
channel_id int 否 渠道id

响应:重定向到学生列表页面

# 代码

  1. 视图
class StudentCreateView(View):
    """
    学生添加视图
    """
	...
    def post(self, request):
        """添加学生"""
        # 1. 获取前端传递的数据
        data = {key: value for key, value in request.POST.items() if value}
        if data.get('channel', None):
            data['channel_id'] = data.pop('channel')
        # 2. 创建学生
        try:
            obj = Student.objects.create(**data)
        except Exception as e:
            # 粗糙的处理现在就够了
            return HttpResponse(str(e), status=400)
        # 3. 返回响应
        return redirect(reverse('student-list'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  1. 路由

同上

# 学生修改功能

# 学生修改页面

# 接口设计

url:/students/update/

请求方法:get

响应:html页面

# 代码

  1. 视图
from django.shortcuts import get_object_or_404


class StudentUpdateView(View):
    """
    学生更新视图
    """

    def get_obj(self, pk):
        obj = get_object_or_404(Student, pk=pk)
        return obj

    def get(self, request, pk):
        # 1. 获取修改对象
        obj = self.get_obj(pk)
        # 2. 获取渠道对象
        channels = Channel.objects.all()
        # 2. 渲染并返回修改页面
        return render(request, 'crm/student_detail.html', context={'channels': channels, 'obj': obj})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  1. 路由
path('students/update/<int:pk>/', views.StudentUpdateView.as_view(), name='student-update')
1
  1. 模板
同添加功能模板
1

# 学生修改

# 接口设计

url:/students/update/pk/

请求方法:post

参数:

  • 路径参数

  • 格式:form

参数名 类型 是否必传 说明
name str 否 姓名
age int 否 年龄
sex int 否 性别
phone str 否 手机
channel_id int 否 渠道id

响应:重定向到学生列表页面

# 代码

  1. 视图
class StudentUpdateView(View):
    """
    学生更新视图
    """
    ...

    def post(self, request, pk):
        # 1. 获取修改对象
        obj = self.get_obj(pk)
        # 2. 获取前端传递的数据
        data = {key: value for key, value in request.POST.items() if value}
        if data.get('channel', None):
            data['channel_id'] = data.pop('channel')
        # 3. 更新学生
        for key, value in data.items():
            setattr(obj, key, value)
        try:
            obj.save(update_fields=data.keys())
        except Exception as e:
            # 粗糙的处理现在就够了
            return HttpResponse(str(e), status=400)
        # 4. 返回响应
        return redirect(reverse('student-list'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  1. 路由

同上

# 学生删除功能

# 接口设计

url:/students/delete/pk/

请求方法:get

参数:

  • 路径参数

响应:重定向到学生列表页面

# 代码

  1. 视图
def student_delete(request, pk):
    # 1. 获取对象
    obj = get_object_or_404(Student, pk=pk)
    # 2. 删除对象
    obj.delete()
    # 3. 返回响应
    return redirect(reverse('student-list'))
1
2
3
4
5
6
7
  1. 路由
path('students/delete/<int:pk>', views.student_delete, name='student-delete')
1

本文完,感谢你的耐心阅读,如有需要可加我微信,备注「博客」并说明原因,我们一起进步,下次见。

#django
上次更新: 2022/12/26, 16:59:39
数据库操作
表单

← 数据库操作 表单→

最近更新
01
requests让接口测试如此简单 原创
03-31
02
最简明的python正则教程
03-30
03
pycharm激活码
12-30
更多文章>
Theme by Vdoing | Copyright © 2019-2025 心蓝
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式