十一、项目实战一

news2024/11/15 9:30:00

项目实战一

需求

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

学生列表功能

接口设计

url:/students/

请求方法:get

参数:

  • 格式:查询参数
参数名类型是否必传说明
pageint页码,默认为1
sizeinit每页数据条数默认为10
namestr根据姓名过滤
ageint根据年龄过滤
sexint根据性别过滤
phonestr根据手机过滤
channel_idint根据渠道过滤

响应: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
    })

路由

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

模板

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

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

学生添加功能

学生添加页面

接口设计

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. 路由
path('students/create/', views.StudentCreateView.as_view(), name='student-create')
  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>

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

学生添加

接口设计

url:/students/create/

请求方法:post

参数:

  • 格式:form
参数名类型是否必传说明
namestr姓名
ageint年龄
sexint性别
phonestr手机
channel_idint渠道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'))

打开F12,鼠标移动到“添加”按钮上面,左下角展示路由路径是在原有的路径下面紧跟aaa

 如果a标签里href为以"/"开头的,比如说“/aaa”那就直接从根路由后面紧跟你href的值,如下图

大家看出区别来了吗? 

 那此时添加按钮我可以直接写成创建学生也就是学生详情那个界面的路由吗?当然可以,但不过这样子写就是硬编码。

 我们应该用模板标签


然后我们看渠道是下拉选择框,那这个选择项我们怎么获取?肯定是从数据库查了获取对吧。此时思路就是这样子:

1. 先去数据库查有几个渠道。

2. 查到之后渲染模板。

 

 

学生修改功能

学生修改页面

接口设计

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. 路由
path('students/update/<int:pk>/', views.StudentUpdateView.as_view(), name='student-update')
  1. 模板
同添加功能模板

学生修改

接口设计

url:/students/update/pk/

请求方法:post

参数:

  • 路径参数

  • 格式:form

参数名类型是否必传说明
namestr姓名
ageint年龄
sexint性别
phonestr手机
channel_idint渠道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. 路由

同上

学生删除功能

接口设计

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. 路由
path('students/delete/<int:pk>', views.student_delete, name='student-delete')

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/360122.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅谈安科瑞电力智能运维在高速铁路电力系统的应用分析

摘 要&#xff1a;高速铁路电力智能运维管理系统采用终端感知层、系统网络层、系统平台层的三层网络架构模式&#xff0c;通过集成网关&#xff0c;共享通信传输设备&#xff0c;利用铁路专用运维传输网络通道将各类监测数据上传至运维管理平台数据进行实时分析&#xff0c;建…

k8s二进制部署

目录 一、K8S常见的部署方式 1.1 K8S常见的部署方式 1.2 k8s部署 二进制与高可用的区别 二、Kubernetes二进制部署 2.1 Kubernetes二进制部署准备 ① 服务器准备 ② 签发证书环境准备 2.2 操作系统初始化配置 2.3 部署 etcd 集群 ① 在 master01 节点上操作 ② work…

使用Fabric.js实现贝塞尔曲线波浪特效

一、前言 本文是在此基础上收到启发然后进行的变化&#xff0c;当然&#xff0c;观看与否不会影响接下来的阅读体验。 二、实现思路 其实整个波浪动画其实可以看成&#xff1a;在相对坐标系静止的视角下&#xff0c;一个正弦函数在直角坐标系上匀速平移时我们所观察到的效果…

如何防止订单重复提交

如何防止订单重复提交前言什么是重复下单为什么会重复下单&#xff1f;如何处理重复下单&#xff1f;利用数据库实现幂等利用Redis防重前言 最近在做电商项目&#xff0c;整理一下解决方案并帮助自己巩固知识点&#xff0c;此方案是结合了目前的业务环境&#xff0c;若有更好的…

Linux进程概念讲解

1、进程的基本概念在给进程下定义之前&#xff0c;我们先了解一下进程&#xff1a;我们在编写完代码并运行起来时&#xff0c;在我们的磁盘中会形成一个可执行文件&#xff0c;当我们双击这个可执行文件时&#xff08;程序时&#xff09;&#xff0c;这个程序会加载到内存中&am…

中央计算平台「上车」加速

随着英伟达、高通宣布在2024年开始陆续交付中央计算平台&#xff0c;Tier1正在跨域联合&#xff0c;以寻求在新的行业周期获得先发优势。 本月初&#xff0c;韩国LG电子宣布与高通达成合作协议&#xff0c;在原有座舱计算平台基础上&#xff0c;进一步拓展至智能驾驶领域。而在…

Transformer架构理解

参考&#xff1a; 1. Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N. Gomez, Łukasz Kaiser, and Illia Polosukhin. 2017. Attention is all you need. In Proceedings of the 31st International Conference on Neural Information Pr…

Java 语法糖详解

本文从 Java 编译原理角度&#xff0c;深入字节码及 class 文件&#xff0c;抽丝剥茧&#xff0c;了解 Java 中的语法糖原理及用法&#xff0c;帮助大家在学会如何使用 Java 语法糖的同时&#xff0c;了解这些语法糖背后的原理。 什么是语法糖&#xff1f; 语法糖&#xff08…

金三银四一口气拿下三个offer的软件测试面试宝典,面试必看

说说你们怎么做接口自动化测试的☆☆☆☆☆ 我们是使用pythonrequesunittest来搭建我们的做接口自动化测试框架&#xff0c;使用python语言来调用第三方的类库requests来根据我们接口方法get或者post来发送接口请求&#xff0c;接收响应回来的数据进行适当的格式转换&#xff…

Python+Requests+PyTest+Excel+Allure 接口自动化测试实战

--------UnitTest框架和PyTest框架的简单认识对比与项目实战-------- 定义&#xff1a; Unittest是Python标准库中自带的单元测试框架&#xff0c;Unittest有时候也被称为PyUnit&#xff0c;就像JUnit是Java语言的标准单元测试框架一样&#xff0c;Unittest则是Python语言的标…

Python爬虫进阶 - win和linux下selenium使用代理

目录 Windows selenium配置 下载地址 Chrome Chromedriver 版本对应关系 实践测试 操作元素 浏览器操作 获取元素信息 鼠标操作 实战demo selenium添加代理 Linux selenium配置 检查服务器环境 下载安装第三方库&#xff08;最简单版&#xff09; 实践测试 代码…

【2.20】动态规划 +项目 + 存储引擎

01背包问题 现有一容量为w的背包&#xff0c;有3个物品&#xff0c;每个物品重量不同&#xff0c;价值不同&#xff0c;问&#xff0c;怎样装才能价值最大化&#xff1f; 明确dp数组含义和下标含义&#xff1a;dp[j]表示当前背包的最大价值。j表示背包容量。递推公式&#xf…

【期末复习】例题说明Prim算法与Kruskal算法

点睛Prim与Kruskal算法是用来求图的最小生成树的算法。最小生成树有n个顶点&#xff0c;n-1条边&#xff0c;不能有回路。Prim算法Prim算法的特点是从个体到整体&#xff0c;随机选定一个顶点为起始点出发&#xff0c;然后找它的权值最小的边对应的另一个顶点&#xff0c;这两个…

IEEE学生会员注册

IEEE学生会员注册0、引言IEEE 会员介绍1、IEEE学生会员会费2、加入学生会员2.1、创建/登录 IEEE 账户2.2、填写/维护 个人基本信息2.3、填写/维护 基本教育信息3、选择一个你将要加入的协会或社团4、确认购物车5、付款5.1、付款详情信息页5.2、扫码付款并验证5.3、会员确认邮件…

【前端提效】-- VsCode 实用插件推荐

EditorConfig for VS Code ***** 作用&#xff1a;多人协同开发&#xff0c;规范缩进风格&#xff0c;缩进大小&#xff0c;tab长度以及字符集等&#xff0c;解决不同IDE的编码范设置&#xff0c;在这里配置&#xff08;.editorconfig&#xff09;的代码规范规则优先级高于编辑…

Object.defineproperty方法

Object.defineproperty 的作用就是直接在一个对象上定义一个新属性&#xff0c;或者修改一个已经存在的属性Object.defineproperty可以接收三个参数Object.defineproperty(obj, prop, desc)obj : 第一个参数就是要在哪个对象身上添加或者修改属性prop : 第二个参数就是添加或修…

Ansys Zemax | 如何在存在全内反射 (TIR) 的情况下应用散射

在本文中&#xff0c;我们将展示如何利用虚拟表面来对具有全内反射 (TIR) 的物体进行建模&#xff0c;同时保持其他独特的表面特性&#xff0c;例如粗糙的表面结构。 下载 联系工作人员获取附件 简介 在OpticStudio中&#xff0c;全内反射 (TIR) 在其他表面属性&#xff08…

计网物理层

第一章&#xff1a;物理层 1、物理层的主要作用&#xff1a; ①、在不同的传输媒体上传输比特流 ②、屏蔽各种传输媒体的差异&#xff0c;为上层的数据链路层提供服务&#xff0c;使得上层的数据链路层无需考虑传输媒体是什么 2、传输媒体的种类&#xff1a; ①、导引型传…

python环境配置

python环境配置一、ADB环境配置1、ADB下载路径:2、点击下载3、解压并放到本地磁盘4、配置ADB环境变量二、Python环境配置1、Python下载路径:2、点击下载(默认下载最新的)3、解压并放到本地磁盘4、配置Python环境变量5、配置pip环境变量三、Pycharm安装1、pycharm下载路径:2、点…

“数字乡民”疑云:助农?坑农?

1月2日&#xff0c;“深入实施数字乡村发展行动&#xff0c;推动数字化应用场景研发推广”写入了国务院《关于做好2023年全面推进乡村振兴重点工作的意见》中&#xff0c;2022年中央一号文件也要求“大力推进数字乡村建设&#xff0c;推进智慧农业发展”。 在这之前&#xff0…