一、Django项目开发
1.web框架底层
1.1 网络通信
注意:局域网
个人一般写程序,想要让别人访问:阿里云、腾讯云。
- 去云平台租服务器(含公网IP)
- 程序放在云服务器
先以局域网为例
- 我的电脑【服务端】
import socket# 1.监听本机的IP和端口
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8001)) # 我自己的电脑IP,端口8001# 2.让多少人等待
sock.listen(5)while True:# 3.等待连接请求的申请,有人来连接(阻塞)conn, addr = sock.accept()# 4.连接成功后立即发送conn.sendall("欢迎使用xx系统".encode("utf-8"))# 5.断开连接conn.close()# 6.停止服务端程序
sock.close()
- 女朋友的电脑(同一个局域网)【客户端】
import socket# 1. 向指定IP发送连接请求
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('192.168.10.3', 8001))# 2. 接收你发的消息
message = client.recv(1024)
print(message.decode("utf-8"))# 3.断开连接
client.close()
- 姓王的好兄弟【客户端】
import socket# 1. 向指定IP发送连接请求
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('192.168.10.3', 8001))# 2. 接收你发的消息
message = client.recv(1024)
print(message.decode("utf-8"))# 3.断开连接
client.close()
我们自己写时,通过socket模块可以实现网络上的两端进行通信。
1.2 常见软件架构
- bs架构
浏览器:充当客户端
服务器:网站
- cs架构,开发应用程序,例如:QQ、Pycharm、网易云音乐(安装在电脑上的软件)
客户端:安装在电脑上的软件。 网易云音乐
服务端:网易服务器
1.3 快速自己写以为网站(不能用django、flask等)
import socket# 1.监听本机的IP和端口
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('192.168.0.6', 9000)) # 我自己的电脑IP,端口8001# 2.让多少人等待
sock.listen(5)while True:# 3.等待连接请求的申请,有人来连接(阻塞) -> 登录浏览器来连接我conn, addr = sock.accept()# 4.收到浏览器发送的消息buf = conn.recv(2048)print(buf)# 5.给浏览器返回数据conn.send(b"HTTP/1.1 200 OK\r\n\r\n")conn.send(b"Hello, World")# 6.断开连接conn.close()# 6.停止服务端程序
sock.close()
2. web框架
常见的web框架:django、flask、tornado、sanic、fastapi…
web应用程序:
- 用户网络通信的socket
- web框架
- 业务开发
以django为例:
- wsgiref模块、uwsgi、daphne -> 本质上都是socket实现。
- 原来实现了框架
以flask为例:
- werkzurg、uwsgi、…
- flask框架
以tornado为例:
- tornado、werkzurg、uwsgi、…
- 框架
2.1 wsgiref
from wsgiref.simple_server import make_serverdef run_server(environ, start_response):start_response('200 OK', [('Content-Type', 'text/html')])return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]if __name__ == '__main__':httpd = make_server('127.0.0.1', 8000, run_server)httpd.serve_forever()
2.2 werkzeug
pip install werkzeug
from werkzeug.wrappers import Responsedef application(environ, start_response):response = Response('Hello World!', mimetype='text/plain')return response(environ, start_response)if __name__ == '__main__':from werkzeug.serving import run_simplerun_simple('localhost', 4000, application)
2.3 各框架的区别
django、flask、tornado、sanic、fastapi..
- 内部集成功能的多少
- django,内部提供了很多组件。 【相对大】
- flask、tornado、sanic、fastapi… 本身自己功能很少+第三方组件。【相对小】
- 同步框架 vs 异步非阻塞
- 异步非阻塞:tornado、sanic、fastapi、django
- 同步:django、flask、bottle、webpy…
1.同步框架:django、flask
2.tornado,异步非阻塞,特别NB。- 同步:常见应用。- 异步:IO应用 + conroutine装饰器 + redis/MySQL/...
3.sanic,路飞小猿圈平台
4.fastapi- 参考flask- py最新注解- restfulAPI- 异步目前不看好:- 增加编程的难度,功能&效率- 项目中不会有那么IO操作 ---> 100功能/2-IO ---> celery
二、命令行以及Pycharm创建app、创建虚拟环境
1.django框架
1.1 安装
pip install django==3.2
1.2 命令行
- 创建项目
cd 指定目录
django-admin startproject 项目名
mysite
├── manage.py [项目的管理工具]
└── mysite├── __init__.py├── settings.py 【配置文件,只有一部分。程序启动时,先读取django内部配置,再读settings.py】├── urls.py 【主路由,在里面编写 /xxx/xxx/xxx ---> index 】├── asgi.py 【异步】└── wsgi.py 【同步,主】
- 编写代码 urls.py
from django.contrib import admin
from django.urls import pathfrom django.shortcuts import HttpResponsedef info(request):print("请求来执行了")return HttpResponse("xxxx")def xxxx(request):print("请求来执行了")return HttpResponse("。。。。。。")urlpatterns = [# path('admin/', admin.site.urls),path('api/index/', info),path('api/show/', xxxx),
]
- 运行
cd 项目
python3.9 manage.py runserver
python3.9 manage.py runserver 127.0.0.1:8000
python3.9 manage.py runserver 127.0.0.1:9000
- app概念
cd 项目
python manage.py startapp 名字
mysite
├── manage.py [项目的管理工具]
├── web├── __init__.py├── views.py [视图函数]├── models.py [ORM,基于models可以对数据库进行简便的操作]...
└── mysite├── __init__.py├── settings.py 【配置文件,只有一部分。程序启动时,先读取django内部配置,再读settings.py】├── urls.py 【主路由,在里面编写 /xxx/xxx/xxx ---> index 】├── asgi.py 【异步】└── wsgi.py 【同步,主】
mysite
├── manage.py
├── mysite
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── web├── __init__.py├── admin.py├── apps.py├── migrations│ └── __init__.py├── models.py├── tests.py└── views.py
1.3 Pycharm
django-admin startproject 项目名称cd 项目目录
python manage.py startapp
python manage.py runserver
2. 虚拟环境
2.1 创建虚拟环境 - 命令行
- venv,Python官方用于创建虚拟环境的工具。
cd xxx/xxx/crm
python3.9 -m venv ddd
python3.7 -m venv xxxx
python3.7 -m venv /xxx/xxx/xxx/xx/ppp
- virtualenv 【推荐】
pip install virtualenv
cd /xxx/xx/
virtualenv ddd --python=python3.9
virtualenv /xxx/xx/ddd --python=python3.7
操作:
- 在
F:\envs\
创建虚拟环境。
cd F:\envs
virtualenv crm --python=python3.9
- 激活虚拟环境
- win
cd F:\envs\crm\Scripts
activate
- mac
source /虚拟环境目录/bin/activate
- 安装包
pip install 包名
- 创建django项目 `D:\project\crm
cd D:\project
django-admin startproject crm
D:\project\crm
├── manage.py [项目的管理工具]
└── crm├── __init__.py├── settings.py 【配置文件,只有一部分。程序启动时,先读取django内部配置,再读settings.py】├── urls.py 【主路由,在里面编写 /xxx/xxx/xxx ---> index 】├── asgi.py 【异步】└── wsgi.py 【同步,主】
python manage.py startapp xxxx
python manage.py runserver
- 退出虚拟环境
deactivate
2.2 Pycharm项目+虚拟环境
.venv:隐藏文件夹
- 在虚拟环境中安装 requests
pip install requests
2.3 django+虚拟环境【最新】
pip install django
注意:创建django最新版可以。
2.3.1 django+虚拟环境【指定版本】
pip install django==3.2
django-admin startproject Django
这么命令会把Django项目嵌套着多了一层目录放进去,不是我们想要的
django-admin startproject Django .
我们想要的是将manage.py和Django这个目录放在当前这个项目目录
3.关于创建app
- 项目只需要一个app,目录机构的建议。
- 项目只需要一个app,目录结构的建议。
day002.venvday002......manage.pyappswebbackendapi
先在apps目录下面创建api、backend、web三个目录,然后使用下面命令,完成之后修改apps下面的apps.py文件,修改为:name = 'apps.api'
,其他两个也一样
4.关于纯净版
总结
知道如何基于pycharm+虚拟环境+业务场景 -> 创建django项目。
问题:给别人的代码+requirements.txt
三、模板介绍、静态文件、重定向以及相关案例
1.快速上手
-
确保app已注册 【settings.py】
-
编写URL和视图函数对应关系 【urls.py】
-
编写视图函数 【views.py】
-
启动django项目
- 命令行启动
python manage.py runserver
- Pycharm启动
- 命令行启动
1.1 再写一个页面
2. templates模板
2.1 静态文件
2.1.1 static目录
- 在app目录下创建static文件夹
2.1.2 引用静态文件
3. 模板语法
本质上:在HTML中写一些占位符,由数据对这些占位符进行替换和处理。
案例:伪联通新闻中心
4.请求和响应
关于重定向:
案例:用户登录
报上面错误的话要在.html里面加上{% csrf_token %}
就可以了
四、Django操作数据库
1.数据库操作
- MySQL数据库 + pymysql
import pymysql# 1.连接MySQL
conn = pymysql.connect(host="127.0.0.1", port=3306, user='root', passwd="root123", charset='utf8', db='unicom')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)# 2.发送指令
cursor.execute("insert into admin(username,password,mobile) values('wupeiqi','qwe123','15155555555')")
conn.commit()# 3.关闭
cursor.close()
conn.close()
- Django开发操作数据库更简单,内部提供了ORM框架。
1.1 安装第三方模块
pip install mysqlclient
1.2 ORM
- 创建、修改、删除数据库中的表(不用你写SQL语句)。 【无法创建数据库】
- 操作表中的数据(不用写SQL语句)。
1.2.1 自己创建数据库
- 启动MySQL服务
- 自带工具创建数据库
create database gx_day15 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
1.2.2 django连接数据库
在settings.py文件中进行配置和修改
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'gx_day15', # 数据库名字'USER': 'root','PASSWORD': 'root123','HOST': '127.0.0.1', # 那台机器安装了MySQL'PORT': 3306,'OPTIONS': {"init_command": "SET sql_mode='STRICT_TRANS_TABLES'"}}
}
1.2.3 django操作表
- 创建表
- 删除表
- 修改表
创建表:在models.py文件中
create table app01_userinfo(id bigint auto_increment primary key,name varchar(32),password varchar(64),age int
)
执行命令:
python manage.py makemigrations
python manage.py migrate
注意:app需要提前注册。
在表中新增列时,由于已存在列中可能已有数据,所以新增列必须要指定新增列对应的数据
以后在开发中如果想要对表结构进行调整:
- 在models.py文件中操作类即可。
- 命令
python manage.py makemigrations
python manage.py migrate
1.2.4 表中的数据
#### 1.新建 ####
Department.objects.create(title="销售部")
Department.objects.create(title="IT部")
Department.objects.create(title="运营部")
UserInfo.objects.create(name="武沛齐", password="123", age=19)
UserInfo.objects.create(name="朱虎飞", password="666", age=29)
UserInfo.objects.create(name="吴阳军", password="666")#### 2.删除 ####
UserInfo.objects.filter(id=3).delete()
Department.objects.all().delete()#### 3.获取数据 ####
# 3.1 获取符合条件的所有数据
data_list = [对象,对象,对象] QuerySet类型
data_list = UserInfo.objects.all()
for obj in data_list:print(obj.id, obj.name, obj.password, obj.age)data_list = [对象,]
data_list = UserInfo.objects.filter(id=1)
print(data_list)
# 3.1 获取第一条数据【对象】
row_obj = UserInfo.objects.filter(id=1).first()
print(row_obj.id, row_obj.name, row_obj.password, row_obj.age)#### 4.更新数据 ####
UserInfo.objects.all().update(password=999)
UserInfo.objects.filter(id=2).update(age=999)
UserInfo.objects.filter(name="朱虎飞").update(age=999)
五、案例:员工管理系统–部门管理
员工管理系统(部门管理)
1.新建项目
2.创建app
python manage.py startapp app01
2.1 注册app
3. 设计表结构(django)
from django.db import modelsclass Department(models.Model):"""部门表"""title = models.CharField(verbose_name="标题", max_length=32)class UserInfo(models.Model):"""员工表"""name = models.CharField(verbose_name="姓名", max_length=32)password = models.CharField(verbose_name="密码", max_length=64)age = models.IntegerField(verbose_name="年龄")account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)create_time = models.DateTimeField(verbose_name="入职时间")# 1.约束# to:与那一张表关联# to_field: 表中的那一列关联# 2.django内部会把depart自动生成depart_iddepart = models.ForeignKey(to="Department", to_field="id")# 3.部门表被删除# 3.1 级联删除# depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)# 3.2 置空# depart = models.ForeignKey(to="Department", to_field="id", null=True, blank=True, on_delete=models.SET_NULL)# 创建性别# 在django中做的约束,与数据库无关gender_choices = ((1, '男'),(2, '女'),)gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
4. 在MySQL中生成表
create database gx_day16 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
- django命令生成数据库表
python manage.py makemigrations
python manage.py migrate
5. 静态文件管理
static目录
6. 部门管理
体验,最原始方法来做。Django中提供Form和ModelForm组件(方便)
6.1 部门列表
<!--
@version : python 3.8
@author : Stara
@file : xxx.html
@time : 2023-10-28 下午13:20
-->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>部门列表</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.navbar{border-radius: 0;}</style>
</head>
<body>
<nav class="navbar navbar-default"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">联通用户管理系统</a></div><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li><a href="/depart/list/">部门管理</a></li><li><a href="#">link</a> </li></ul><ul class="nav navbar-nav navbar-right"><li><a href="#">登录</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"aria-haspopup="true" aria-expanded="false">Stara<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人资料</a></li><li><a href="#">我的信息</a></li><li role="separator" class="divider"></li><li><a href="#">注销</a></li></ul></li></ul></div></div>
</nav>
<div><div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="#"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建部门</a></div><div class="panel panel-default"><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>部门列表</div><table class="table table-bordered"><thead><tr><th>ID</th><th>名称</th><th>操作</th></tr></thead><tbody><tr><th>1</th><td>销售部</td><td><a class="btn btn-primary btn-xs">编辑</a><a class="btn btn-danger btn-xs">删除</a></td></tr></tbody></table></div></div>
</div><script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>
6.2 添加部门
6.2.1 添加部门页面
第一种:
<!--
@version : python 3.8
@author : Stara
@file : xxx.html
@time : 2023-10-28 下午13:20
-->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>添加部门</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.navbar{border-radius: 0;}</style>
</head>
<body><nav class="navbar navbar-default"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">联通用户管理系统</a></div><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li><a href="/depart/list/">部门管理</a></li><li><a href="#">link</a> </li></ul><ul class="nav navbar-nav navbar-right"><li><a href="#">登录</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"aria-haspopup="true" aria-expanded="false">Stara<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人资料</a></li><li><a href="#">我的信息</a></li><li role="separator" class="divider"></li><li><a href="#">注销</a></li></ul></li></ul></div></div>
</nav>
<div><div class="container"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"> 新建部门 </h3></div><div class="panel-body"><form class="form-horizontal"><div class="form-group"><label class="col-sm-2 control-label">标题</label><div class="col-sm-10"><input type="text" class="form-control" placeholder="标题" name="title"></div></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="submit" class="btn btn-primary">保 存</button></div></div></form></div></div></div>
</div><script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>
第二种:
<!--
@version : python 3.8
@author : Stara
@file : xxx.html
@time : 2023-10-28 下午13:20
-->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>添加部门</title><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.navbar{border-radius: 0;}</style>
</head>
<body><nav class="navbar navbar-default"><div class="container"><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">联通用户管理系统</a></div><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li><a href="/depart/list/">部门管理</a></li><li><a href="#">link</a> </li></ul><ul class="nav navbar-nav navbar-right"><li><a href="#">登录</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"aria-haspopup="true" aria-expanded="false">Stara<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">个人资料</a></li><li><a href="#">我的信息</a></li><li role="separator" class="divider"></li><li><a href="#">注销</a></li></ul></li></ul></div></div>
</nav>
<div><div class="container"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"> <span style="font-weight:bold;">新建部门</span> </h3></div><div class="panel-body"><form><div class="form-group"><labe><span style="font-weight:bold;">标题</span></labe><input type="text" class="form-control" id="exampleInputEmail1" placeholder="标题" name="title"/></div><button type="submit" class="btn btn-primary">提交</button></form></div></div></div>
</div><script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-3.4.1/js/bootstrap.min.js' %}"></script>
</body>
</html>
6.2.2 添加部门
6.2.3 删除部门
6.3 编辑部门
注:
7. 模板的继承
7.1编辑部门
定义模板:layout.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="{% static 'plugin...min.css' %}">{% block css %}{% endblock %}
</head>
<body><h1>标题</h1><div>{% block content %}{% endblock %}</div><h1>底部</h1><script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>{% block js %}{% endblock %}
</body>
</html>
继承模板:
{% extends 'layout.html' %}{% block css %}<link rel="stylesheet" href="{% static 'pluxxx.css' %}"><style>...</style>
{% endblock %}{% block content %}<h1>首页</h1>
{% endblock %}{% block js %}<script src="{% static 'js/jqxxxin.js' %}"></script>
{% endblock %}
六、案例:员工管理系统–用户管理
员工管理系统(用户管理)
{% extends 'layout.html' %}{% block content %}<div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="#"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建用户</a></div><div class="panel panel-default"><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>用户列表</div><table class="table table-bordered"><thead><tr><th>ID</th><th>姓名</th><th>密码</th><th>年龄</th><th>余额</th><th>入职时间</th><th>性别</th><th>所属部门</th><th>操作</th></tr></thead><tbody><tr><th>id</th><td>xxx</td><td>xxx</td><td>xxx</td><td>xxx</td><td>xxx</td><td>xxx</td><td>xxx</td><td><a class="btn btn-primary btn-xs" href="#">编辑</a><a class="btn btn-danger btn-xs" href="#">删除</a></td></tr></tbody></table></div></div>{% endblock %}
1.用户管理
insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("韩超","666",23,100.68,"2020-01-11",2,1);insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("刘东","123",23,100.68,"2010-11-11",1,2);insert into app01_userinfo(name,password,age,account,create_time,gender,depart_id) values("朱虎飞","999",33,9900.68,"2021-05-11",1,1);
+-------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(16) | NO | | NULL | |
| password | varchar(64) | NO | | NULL | |
| age | int(11) | NO | | NULL | |
| account | decimal(10,2) | NO | | NULL | |
| create_time | datetime(6) | NO | | NULL | |
| gender | smallint(6) | NO | | NULL | |
| depart_id | bigint(20) | NO | MUL | NULL | |
+-------------+---------------+------+-----+---------+----------------+
传入HTML模板中:
2 新建用户
2.1 原始方式理思路:不会采用(本质)【麻烦】
{% extends 'layout.html' %}
{% block content %}<div class="container"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"> <span style="font-weight:bold;">新建用户</span> </h3></div><div class="panel-body"><form method="post">{% csrf_token %}<div class="form-group"><labe><span style="font-weight:bold;">姓名</span></labe><input type="text" class="form-control" placeholder="姓名"/></div><div class="form-group"><labe><span style="font-weight:bold;">密码</span></labe><input type="text" class="form-control" placeholder="密码"/></div><div class="form-group"><labe><span style="font-weight:bold;">年龄</span></labe><input type="text" class="form-control" placeholder="年龄"/></div><div class="form-group"><labe><span style="font-weight:bold;">余额</span></labe><input type="text" class="form-control" placeholder="余额"/></div><div class="form-group"><labe><span style="font-weight:bold;">入职时间</span></labe><input type="text" class="form-control" placeholder="入职时间"/></div><div class="form-group"><labe><span style="font-weight:bold;">性别</span></labe><select class="form-control">{% for item in gender_choices %}<option value="{{ item.0 }}">{{ item.1 }}</option>{% endfor %}</select></div><div class="form-group"><labe><span style="font-weight:bold;">部门</span></labe><select class="form-control">{% for item in depart_list %}<option value="{{ item.id }}">{{ item.title }}</option>{% endfor %}</select></div><button type="submit" class="btn btn-primary">提交</button></form></div></div></div>
{% endblock %}
提交:
- 定义name属性
- GET请求POST提交,添加到数据库中,返回到用户列表页面
2.2 Django组件
- Form组件(小简便)
- ModelForm组件(最简便)
2.2.1 初识Form
1. views.py
class MyForm(Form):user = forms.CharField(widget=forms.Input)pwd = form.CharFiled(widget=forms.Input)email = form.CharFiled(widget=forms.Input)account = form.CharFiled(widget=forms.Input)create_time = form.CharFiled(widget=forms.Input)depart = form.CharFiled(widget=forms.Input)gender = form.CharFiled(widget=forms.Input)def user_add(request):if request.method == "GET":form = MyForm()return render(request, 'user_add.html',{"form":form})
2. user_add.html
<form method="post">{% for field in form%}{{ field }}{% endfor %}<!-- <input type="text" placeholder="姓名" name="user" /> -->
</form>
<form method="post">{{ form.user }}{{ form.pwd }}{{ form.email }}<!-- <input type="text" placeholder="姓名" name="user" /> -->
</form>
2.2.2 ModelForm(推荐)
1. models.py
class UserInfo(models.Model):""" 员工表 """name = models.CharField(verbose_name="姓名", max_length=16)password = models.CharField(verbose_name="密码", max_length=64)age = models.IntegerField(verbose_name="年龄")account = models.DecimalField(verbose_name="账户余额", max_digits=10, decimal_places=2, default=0)create_time = models.DateTimeField(verbose_name="入职时间")depart = models.ForeignKey(to="Department", to_field="id", on_delete=models.CASCADE)gender_choices = ((1, "男"),(2, "女"),)gender = models.SmallIntegerField(verbose_name="性别", choices=gender_choices)
2. views.py
class MyForm(ModelForm):xx = form.CharField*("...")class Meta:model = UserInfofields = ["name","password","age","xx"]def user_add(request):if request.method == "GET":form = MyForm()return render(request, 'user_add.html',{"form":form})
3. user_add.html
<form method="post">{% for field in form%}{{ field }}{% endfor %}<!-- <input type="text" placeholder="姓名" name="user" /> -->
</form>
<form method="post">{{ form.user }}{{ form.pwd }}{{ form.email }}<!-- <input type="text" placeholder="姓名" name="user" /> -->
</form>
3.用户——添加(ModelForm)
前端界面user_moduser_model_form_add.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<form method="post">{% csrf_token %}{% for field in form %}{{ field.label }} : {{ field }}{% endfor %}</form></body>
</html>
这样写后我们只需要在views.py和models.py里面添加
{{ field.label }} 是表单字段的文本标签,{{ field }} 则是一个 HTML 元素,表示该字段的表单控件,如输入框、下拉列表等。label 表示表单中每个字段的文本标签
3.1 关联数据
3.2 集成到bootstrip
user_model_form_add.html
{% extends 'layout.html' %}
{% block content %}<div class="container"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"><span style="font-weight:bold;">新建用户</span></h3></div><div class="panel-body"><form method="post">{% csrf_token %}{% for field in form %}<div class="form-group"><labe><span style="font-weight:bold;">{{ field.label }}</span></labe>{{ field }}{# <input type="text" class="form-control" placeholder="姓名" name="user"/>#}</div>{% endfor %}<button type="submit" class="btn btn-primary">提交</button></form></div></div></div>
{% endblock %}
添加样式:
方式一:
方式二:
你也可以某个字段不加:
3.2 添加和错误提示
做更多的校验
Django(六)
4.编辑用户
- 点击编辑,跳转到编辑页面(将编辑行的ID携带过去)。
- 编辑页面(默认数据,根据ID获取并设置到页面中)
- 提交:
- 错误提示
- 数据校验
- 在数据库更新
user_edit.html
{% extends 'layout.html' %}
{% block content %}<div class="container"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"><span style="font-weight:bold;">编辑用户</span></h3></div><div class="panel-body"><form method="post" novalidate>{% csrf_token %}{% for field in form %}<div class="form-group"><labe><span style="font-weight:bold;">{{ field.label }}</span></labe>{{ field }}<span style="color: red">{{ field.errors.0 }}</span></div>{% endfor %}<button type="submit" class="btn btn-primary">提交</button></form></div></div></div>
{% endblock %}
展示默认数据
编辑修改
5.删除
user_list.html
七、员工管理系统-靓号管理
1.靓号管理
1.1 表结构
根据表结构的需求,在models.py中创建类(由类生成数据库中的表)。
class PrettyNum(models.Model):""" 靓号表 """mobile = models.CharField(verbose_name="手机号", max_length=11)# 想要允许为空 null=True, blank=Trueprice = models.IntegerField(verbose_name="价格", default=0)level_choices = ((1, "1级"),(2, "2级"),(3, "3级"),(4, "4级"),)level = models.SmallIntegerField(verbose_name="级别", choices=level_choices, default=1)status_choices = ((1, "已占用"),(2, "未使用"))status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=2)
自己在数据模拟创建一些数据:
insert into app01_prettynum(mobile,price,level,status)values("111111111",19,1,1);
1.2 靓号列表
- URL
- 函数
- 获取所有靓号
- 结合HTML+render将靓号罗列出来
id 号码 价格 级别(中文) 状态(中文)
pretty_list.html
{% extends 'layout.html' %}
{% block content %}<div class="container"><div style="margin-bottom: 10px"><a class="btn btn-success" href="#"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建靓号</a></div><div class="panel panel-default"><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>用户列表</div><table class="table table-bordered"><thead><tr><th>ID</th><th>号码</th><th>价格</th><th>级别</th><th>状态</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.mobile }}</td><td>{{ obj.price }}</td><td>{{ obj.get_level_display }}</td><td>{{ obj.get_status_display}}</td><td><a class="btn btn-primary btn-xs" href="#">编辑</a><a class="btn btn-danger btn-xs" href="#">删除</a></td></tr>{% endfor %}</tbody></table></div></div>
{% endblock %}
layout.html里面添加如下图
1.3 新建靓号
-
列表页面跳转:
/pretty/add/
-
URL
-
ModelForm类
from django import formsclass PrettyModelForm(forms.ModelForm):...
-
函数
- 实例化类的对象
- 通过render将对象传入到HTML中。
- 模板的循环展示所有的字段。
{% extends 'layout.html' %}
{% block content %}<div class="container"><div class="panel panel-default"><div class="panel-heading"><h3 class="panel-title"><span style="font-weight:bold;">新建靓号</span></h3></div><div class="panel-body"><form method="post" novalidate> # 如果没写novalidate浏览器会做校验{% csrf_token %}{% for field in form %}<div class="form-group"><labe><span style="font-weight:bold;">{{ field.label }}</span></labe>{{ field }}<span style="color: red">{{ field.errors.0 }}</span></div>{% endfor %}<button type="submit" class="btn btn-primary">提交</button></form></div></div></div>
{% endblock %}
- 点击提交
- 数据校验
- 保存到数据库
- 跳转回靓号列表
可以为空,输入也能提交但是这样需要格式校验(手机号11位)
1.4 编辑靓号
- 列表页面:`/pretty/数字/edit/
- URL
- 函数
- 根据ID获取当前编辑的对象
- ModelForm配合,默认显示数据。
- 提交修改。
不允许手机号重复
- 添加:【正则表达式】【手机号不能存在】
# [obj,obj,obj] 对象
queryset = models.PrettyNum.objects.filter(mobile="1888888888")obj = models.PrettyNum.objects.filter(mobile="1888888888").first()# True/False
exists = models.PrettyNum.objects.filter(mobile="1888888888").exists() # 钩子方法里面操作
- 编辑:【正则表达式】【手机号不能存在】
排除自己以外,其他的数据是否手机号是否重复?# id!=2 and mobile='1888888888'
models.PrettyNum.objects.filter(mobile="1888888888").exclude(id=2)
1.5 删除靓号
1.6 搜索手机号
models.PrettyNum.objects.filter(mobile="19999999991",id=12)data_dict = {"mobile":"19999999991","id":123}
models.PrettyNum.objects.filter(**data_dict) #如果在filter里面放了空字典就相当于获取所有的
models.PrettyNum.objects.filter(id=12) # 等于12
models.PrettyNum.objects.filter(id__gt=12) # 大于12
models.PrettyNum.objects.filter(id__gte=12) # 大于等于12
models.PrettyNum.objects.filter(id__lt=12) # 小于12
models.PrettyNum.objects.filter(id__lte=12) # 小于等于12data_dict = {"id__lte":12}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(mobile="999") # 等于
models.PrettyNum.objects.filter(mobile__startswith="1999") # 筛选出以1999开头
models.PrettyNum.objects.filter(mobile__endswith="999") # 筛选出以999结尾
models.PrettyNum.objects.filter(mobile__contains="999") # 筛选出包含999data_dict = {"mobile__contains":"999"}
models.PrettyNum.objects.filter(**data_dict)
1.7 分页
queryset = models.PrettyNum.objects.all()queryset = models.PrettyNum.objects.filter(id=1)[0:10]# 第1页
queryset = models.PrettyNum.objects.all()[0:10]# 第2页
queryset = models.PrettyNum.objects.all()[10:20]# 第3页
queryset = models.PrettyNum.objects.all()[20:30]
- 分页的逻辑和处理规则
- 封装分页类
- 从头到尾开发
- 写项目用【pagination.py】公共组件。
pagination.py
"""
自定义的分页组件,以后如果想要使用这个分页组件,你需要做如下几件事:在视图函数中:def pretty_list(request):# 1.根据自己的情况去筛选自己的数据queryset = models.PrettyNum.objects.all()# 2.实例化分页对象page_object = Pagination(request, queryset)context = {"queryset": page_object.page_queryset, # 分完页的数据"page_string": page_object.html() # 生成页码}return render(request, 'pretty_list.html', context)在HTML页面中{% for obj in queryset %}{{obj.xx}}{% endfor %}<ul class="pagination">{{ page_string }}</ul>"""from django.utils.safestring import mark_safeclass Pagination(object):def __init__(self, request, queryset, page_size=10, page_param="page", plus=5):""":param request: 请求的对象:param queryset: 符合条件的数据(根据这个数据给他进行分页处理):param page_size: 每页显示多少条数据:param page_param: 在URL中传递的获取分页的参数,例如:/etty/list/?page=12:param plus: 显示当前页的 前或后几页(页码)"""from django.http.request import QueryDictimport copyquery_dict = copy.deepcopy(request.GET)query_dict._mutable = Trueself.query_dict = query_dictself.page_param = page_parampage = request.GET.get(page_param, "1")if page.isdecimal():page = int(page)else:page = 1self.page = pageself.page_size = page_sizeself.start = (page - 1) * page_sizeself.end = page * page_sizeself.page_queryset = queryset[self.start:self.end]total_count = queryset.count()total_page_count, div = divmod(total_count, page_size)if div:total_page_count += 1self.total_page_count = total_page_countself.plus = plusdef html(self):# 计算出,显示当前页的前5页、后5页if self.total_page_count <= 2 * self.plus + 1:# 数据库中的数据比较少,都没有达到11页。start_page = 1end_page = self.total_page_countelse:# 数据库中的数据比较多 > 11页。# 当前页<5时(小极值)if self.page <= self.plus:start_page = 1end_page = 2 * self.plus + 1else:# 当前页 > 5# 当前页+5 > 总页面if (self.page + self.plus) > self.total_page_count:start_page = self.total_page_count - 2 * self.plusend_page = self.total_page_countelse:start_page = self.page - self.plusend_page = self.page + self.plus# 页码page_str_list = []self.query_dict.setlist(self.page_param, [1])page_str_list.append('<li><a href="?{}">首页</a></li>'.format(self.query_dict.urlencode()))# 上一页if self.page > 1:self.query_dict.setlist(self.page_param, [self.page - 1])prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode())else:self.query_dict.setlist(self.page_param, [1])prev = '<li><a href="?{}">上一页</a></li>'.format(self.query_dict.urlencode())page_str_list.append(prev)# 页面for i in range(start_page, end_page + 1):self.query_dict.setlist(self.page_param, [i])if i == self.page:ele = '<li class="active"><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)else:ele = '<li><a href="?{}">{}</a></li>'.format(self.query_dict.urlencode(), i)page_str_list.append(ele)# 下一页if self.page < self.total_page_count:self.query_dict.setlist(self.page_param, [self.page + 1])prev = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode())else:self.query_dict.setlist(self.page_param, [self.total_page_count])prev = '<li><a href="?{}">下一页</a></li>'.format(self.query_dict.urlencode())page_str_list.append(prev)# 尾页self.query_dict.setlist(self.page_param, [self.total_page_count])page_str_list.append('<li><a href="?{}">尾页</a></li>'.format(self.query_dict.urlencode()))search_string = """<li><form style="float: left;margin-left: -1px" method="get"><input name="page"style="position: relative;float:left;display: inline-block;width: 80px;border-radius: 0;"type="text" class="form-control" placeholder="页码"><button style="border-radius: 0" class="btn btn-default" type="submit">跳转</button></form></li>"""page_str_list.append(search_string)page_string = mark_safe("".join(page_str_list))return page_string
pretty_list.html
{% extends 'layout.html' %}{% block content %}<div class="container"><div style="margin-bottom: 10px" class="clearfix"><a class="btn btn-success" href="/pretty/add/"><span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>新建靓号</a><div style="float: right;width: 300px;"><form method="get"><div class="input-group"><input type="text" name="q" class="form-control" placeholder="Search for..."value="{{ search_data }}"><span class="input-group-btn"><button class="btn btn-default" type="submit"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></button></span></div></form></div></div><div class="panel panel-default"><!-- Default panel contents --><div class="panel-heading"><span class="glyphicon glyphicon-th-list" aria-hidden="true"></span>靓号列表</div><!-- Table --><table class="table table-bordered"><thead><tr><th>ID</th><th>号码</th><th>价格</th><th>级别</th><th>状态</th><th>操作</th></tr></thead><tbody>{% for obj in queryset %}<tr><th>{{ obj.id }}</th><td>{{ obj.mobile }}</td><td>{{ obj.price }}</td><td>{{ obj.get_level_display }}</td><td>{{ obj.get_status_display }}</td><td><a class="btn btn-primary btn-xs" href="/pretty/{{ obj.id }}/edit/">编辑</a><a class="btn btn-danger btn-xs" href="/pretty/{{ obj.id }}/delete/">删除</a></td></tr>{% endfor %}</tbody></table></div><div class="clearfix"><ul class="pagination">{{ page_string }}</ul></div></div>
{% endblock %}
修改以下用户管理分页
修改部门管理分页
1.7.1 分页的关键步骤
1.8 时间插件
<link rel="stylesheet" href="static/plugins/bootstrap-3.4.1/css/bootstrap.css">
<link rel="stylesheet" href="static/plugins/bootstrap-datepicker/css/bootstrap-datepicker.css"><input type="text" id="dt" class="form-control" placeholder="入职日期"><script src="static/js/jquery-3.6.0.min.js"></script>
<script src="static/plugins/bootstrap-3.4.1/js/bootstrap.js"></script>
<script src="static/plugins/bootstrap-datepicker/js/bootstrap-datepicker.js"></script>
<script src="static/plugins/bootstrap-datepicker/locales/bootstrap-datepicker.zh-CN.min.js"></script><script>$(function () {$('#dt').datepicker({format: 'yyyy-mm-dd',startDate: '0',language: "zh-CN",autoclose: true});})
</script>
在ModelForm界面:
1.9 优化ModelForm和BootStrap
- ModelForm可以帮助我们生成HTML标签。
例如:
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password",]form = UserModelForm()
在HTML中只是生成了普通的input框没有bootstrap样式:
{{form.name}} 普通的input框
{{form.password}} 普通的input框
- 定义插件
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password",]widgets = {"name": forms.TextInput(attrs={"class": "form-control"}),"password": forms.PasswordInput(attrs={"class": "form-control"}),"age": forms.TextInput(attrs={"class": "form-control"}),}
也可以这样写:class UserModelForm(forms.ModelForm):name = forms.CharField(min_length=3,label="用户名",widget=forms.TextInput(attrs={"class": "form-control"}))class Meta:model = models.UserInfofields = ["name", "password", "age"]
在HTML展示是可以有bootstrap样式的
{{form.name}} BootStrap的input框
{{form.password}} BootStrap的input框
问题在于这么写太繁琐了,所有我们重新定义init方法
- 重新定义的init方法,批量设置
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password", "age",]def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 循环ModelForm中的所有字段,给每个字段的插件设置for name, field in self.fields.items():field.widget.attrs = {"class": "form-control", "placeholder": field.label}
优化:
class UserModelForm(forms.ModelForm):class Meta:model = models.UserInfofields = ["name", "password", "age",]def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 循环ModelForm中的所有字段,给每个字段的插件设置for name, field in self.fields.items():# 字段中有属性,保留原来的属性,没有属性,才增加。if field.widget.attrs:field.widget.attrs["class"] = "form-control"field.widget.attrs["placeholder"] = field.labelelse:field.widget.attrs = {"class": "form-control", "placeholder": field.label}
再优化:
自定义类
class BootStrapModelForm(forms.ModelForm):def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 循环ModelForm中的所有字段,给每个字段的插件设置for name, field in self.fields.items():# 字段中有属性,保留原来的属性,没有属性,才增加。if field.widget.attrs:field.widget.attrs["class"] = "form-control"field.widget.attrs["placeholder"] = field.labelelse:field.widget.attrs = {"class": "form-control", "placeholder": field.label}
让它继承:
class UserEditModelForm(BootStrapModelForm):class Meta:model = models.UserInfofields = ["name", "password", "age",]
1.9.1 操作
- 提取公共的类
- 在
vews.py
中找到所有的modelform
继承并拆分出来
- 将
vews.py
文件中的业务按照类型拆分斌删除vews.py
文件
- 关键点:修改urls.py文件
八、案例:员工管理系统–管理员
1. 管理员操作
1.1 添加
from django.shortcuts import render, redirectfrom app01 import models
from app01.utils.pagination import Paginationfrom django import forms
from django.core.exceptions import ValidationError
from app01.utils.bootstrap import BootStrapModelForm
from app01.utils.encrypt import md5class AdminModelForm(BootStrapModelForm):confirm_password = forms.CharField(label="确认密码",widget=forms.PasswordInput(render_value=True))class Meta:model = models.Adminfields = ["username", 'password', "confirm_password"]widgets = {"password": forms.PasswordInput(render_value=True)}def clean_password(self):pwd = self.cleaned_data.get("password")return md5(pwd)def clean_confirm_password(self):pwd = self.cleaned_data.get("password")confirm = md5(self.cleaned_data.get("confirm_password"))if confirm != pwd:raise ValidationError("密码不一致")# 返回什么,此字段以后保存到数据库就是什么。return confirmdef admin_add(request):""" 添加管理员 """title = "新建管理员"if request.method == "GET":form = AdminModelForm()return render(request, 'change.html', {'form': form, "title": title})form = AdminModelForm(data=request.POST)if form.is_valid():form.save()return redirect('/admin/list/')return render(request, 'change.html', {'form': form, "title": title})
1.2 编辑删除
1.3 重置密码
class AdminResetModelForm(BootStrapModelForm):confirm_password = forms.CharField(label="确认密码",widget=forms.PasswordInput(render_value=True))class Meta:model = models.Adminfields = ['password', 'confirm_password']widgets = {"password": forms.PasswordInput(render_value=True)}def clean_password(self): # 钩子方法pwd = self.cleaned_data.get("password")md5_pwd = md5(pwd)# 去数据库校验当前密码和新输入的密码是否一致exists = models.Admin.objects.filter(id=self.instance.pk, password=md5_pwd).exists()if exists:raise ValidationError("不能与以前的密码相同")return md5_pwddef clean_confirm_password(self): # 钩子方法pwd = self.cleaned_data.get("password")confirm = md5(self.cleaned_data.get("confirm_password"))if confirm != pwd:raise ValidationError("密码不一致")# 返回什么,此字段以后保存到数据库就是什么。return confirm
def admin_reset(request, nid):""" 重置密码 """# 对象 / Nonerow_object = models.Admin.objects.filter(id=nid).first()if not row_object:return redirect('/admin/list/')title = "重置密码 - {}".format(row_object.username) # 重置谁的密码if request.method == "GET":form = AdminResetModelForm()return render(request, 'change.html', {"form": form, "title": title})form = AdminResetModelForm(data=request.POST, instance=row_object)if form.is_valid():form.save()return redirect('/admin/list/')return render(request, 'change.html', {"form": form, "title": title})
九、用户登录cookie和session、中间件处理以及动态生成图片验证码
1. 用户登录-Cookie和Session
什么是cookie和session?
cookie:
1.保存在浏览器上的键值对
2.发送请求时,自动携带
Session:
服务端存储信息,是一个概念,可以存在数据库等地
- 发送HTTP请求或者HTTPS请求(无状态&短连接)
http://127.0.0.1:8000/admin/list/
https://127.0.0.1:8000/admin/list/
- http无状态短连接:一次请求响应之后断开连接,再发请求重新连接服务端网站就不再知道是那个人了(新人)
account.py
from django.shortcuts import render, HttpResponse, redirect
from django import forms
from app01.utils.bootstrap import BootStrapForm
from app01.utils.encrypt import md5
from app01 import modelsclass LoginForm(BootStrapForm):username = forms.CharField(label="用户名",widget=forms.TextInput,required=True, # 必填不能为空)password = forms.CharField(label="密码",widget=forms.PasswordInput(render_value=True), # render_value=True:保留输入错误的密码required=True,)def clean_password(self): # 钩子方法pwd = self.cleaned_data.get("password")return md5(pwd)def login(request):""" 登录 """if request.method == "GET":form = LoginForm()return render(request, 'login.html', {'form': form})form = LoginForm(data=request.POST)if form.is_valid():# 1.验证成功,获取到的用户名和密码# {'username': 'stara', 'password': '20010511'}# print(form.cleaned_data)# 2.去数据库校验用户名和密码是否正确(数据库密码是个密文,上面定义钩子方法),获取用户对象错误为None# {'username': 'stara', 'password': '72ed2c732c585b70e3d699a710623e07'}# print(form.cleaned_data)# admin_object = models.Admin.objects.filter(username=form.cleaned_data['username'], password=form.cleaned_data['password']).first() # 这样写可以但是我们已经知道是字典了admin_object = models.Admin.objects.filter(**form.cleaned_data).first()if not admin_object:# 展示错误信息form.add_error("password", "用户名或密码错误")# form.add_error("username", "用户名或密码错误")return render(request, 'login.html', {'form': form})# 用户名和密码正确# 网站生成一个随机字符串,写到用户浏览器的cookie中,在写到session中;request.session["info"] = {"id": admin_object.id, "name": admin_object.username}return redirect("/admin/list/")# return HttpResponse("提交成功")return render(request, 'login.html', {'form': form})
login.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1/css/bootstrap.min.css' %}"><style>.account {width: 400px;border: 1px solid #dddddd;border-radius: 5px;box-shadow: 5px 5px 20px #aaa;margin-left: auto;margin-right: auto;margin-top: 100px;padding: 20px 40px;}.account h2 {margin-top: 10px;text-align: center;}</style>
</head>
<body>
<div class="account"><h2>用户登录</h2><form method="post" novalidate>{% csrf_token %}<div class="form-group"><label>用户名</label>{{ form.username }}<span style="color: red;">{{ form.username.errors.0 }}</span></div><div class="form-group"><label>密码</label>{{ form.password }}<span style="color: red;">{{ form.password.errors.0 }}</span></div><input type="submit" value="登 录" class="btn btn-primary"></form>
</div></body>
</html>
1.1 登录
登录成功后:
- cookie,随机字符串
- session,用户信息
在其他需要登录才能访问的页面中,都需要加入:
def index(request):info = request.session.get("info")if not info:return redirect('/login/')
2.用户登录-中间件处理
正常执行:
在类里面经过中间件,中间件其实是一个类,类里面经过中间件的时候进来的是process_request方法,当请求进来之后会找到三个类一次执行process_request;如果都执行完了,在执行三个类里面的process_reaponse方法。
不正常执行:
如果执行到第二个中间件,不允许往后走,那么就会在第二个直接返回过去,用户就根本到达不了视图函数了,如果我们用户来访问的时候在第三个中间件前面写了一个中间件的类,在这个类里面写了一个process_request方法,我们在process_request里面判断(第二个类里面的process_request方法)当前用户是否已经登录了,如果登录继续往后走,没有登录让process_response(第二个类的方法)返回登录页面
- 定义中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponseclass M1(MiddlewareMixin):""" 中间件1 """def process_request(self, request):# 如果方法中没有返回值(返回None),继续向后走# 如果有返回值 HttpResponse、render 、redirectprint("M1.process_request")return HttpResponse("无权访问")def process_response(self, request, response):print("M1.process_response")return responseclass M2(MiddlewareMixin):""" 中间件2 """def process_request(self, request):print("M2.process_request")def process_response(self, request, response):print("M2.process_response")return response
- 应用中间件
setings.py
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware','app01.middleware.auth.M1','app01.middleware.auth.M2',
]
- 在中间件的process_request方法
# 如果方法中没有返回值(返回None),继续向后走
# 如果有返回值 HttpResponse、render 、redirect,则不再继续向后执行。
2.1 中间件实现登录校验
- 编写中间件
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse, redirectclass AuthMiddleware(MiddlewareMixin):def process_request(self, request):# 0.排除那些不需要登录就能访问的页面# request.path_info 获取当前用户请求的URL /login/if request.path_info == "/login/":return# 1.读取当前访问的用户的session信息,如果能读到,说明已登陆过,就可以继续向后走。info_dict = request.session.get("info")print(info_dict)if info_dict:return# 2.没有登录过,重新回到登录页面return redirect('/login/')
- 应用中间件
MIDDLEWARE = ['django.middleware.security.SecurityMiddleware','django.contrib.sessions.middleware.SessionMiddleware','django.middleware.common.CommonMiddleware','django.middleware.csrf.CsrfViewMiddleware','django.contrib.auth.middleware.AuthenticationMiddleware','django.contrib.messages.middleware.MessageMiddleware','django.middleware.clickjacking.XFrameOptionsMiddleware','app01.middleware.auth.AuthMiddleware',
]
3.用户认证-注销
def logout(request):""" 注销 """request.session.clear()return redirect('/login/')
4.获取登录后的用户信息
第一种方式:
第二种方式:在模板layout.html中
5.图片验证码
- 基本逻辑代码(参考)
import randomdef check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):code = []img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))draw = ImageDraw.Draw(img, mode='RGB')def rndChar():"""生成随机字母 :return:"""return chr(random.randint(65, 90))def rndColor():"""生成随机颜色:return:"""return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))# 写文字font = ImageFont.truetype(font_file, font_size)for i in range(char_length):char = rndChar()code.append(char)h = random.randint(0, 4)draw.text([i * width / char_length, h], char, font=font, fill=rndColor())# 写干扰点for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())# 写干扰圆圈for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())x = random.randint(0, width)y = random.randint(0, height)draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())# 画干扰线for i in range(5):x1 = random.randint(0, width)y1 = random.randint(0, height)x2 = random.randint(0, width)y2 = random.randint(0, height)draw.line((x1, y1, x2, y2), fill=rndColor())img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)return img,''.join(code)if __name__ == '__main__':# 1. 直接打开# img,code = check_code()# img.show()# 2. 写入文件# img,code = check_code()# with open('code.png','wb') as f:# img.save(f,format='png')# 3. 写入内存(Python3)# from io import BytesIO# stream = BytesIO()# img.save(stream, 'png')# stream.getvalue()# 4. 写入内存(Python2)# import StringIO# stream = StringIO.StringIO()# img.save(stream, 'png')# stream.getvalue()pass
5.1 生成图片
login.html加上下面代码
<div class="form-group"><label for="id_code">图片验证码</label><div class="row"><div class="col-xs-7"><input type="text" name="code" class="form-control" placeholder="图片验证码" required="" id="id_code"><span style="color: red;">{{ form.code.errors.0 }}</span></div><div class="col-xs-5"><img id="image_code" src="{% static 'img/code.jpg' %}" style="width: 125px;"></div></div></div>
5.2 动态生成图片
pip install pillow
首先Python生成随机验证码,需要使用PIL模块,接着,我们需要下载并准备适应验证码的字体文件。网络上有许多免费的字体可供选择,你可以选择一个合适的字体并进行下载。将字体文件保存在与你的Python脚本相同的目录下。然后,我们可以开始编写代码来生成随机验证码
博主自己下载了一些文件字体需要者可取,下载链接如下:
https://download.csdn.net/download/m0_69402477/88820707
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilterdef check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28): # 字体文件:Monaco.ttfcode = []img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) # 创建图片draw = ImageDraw.Draw(img, mode='RGB')def rndChar():"""生成随机字母:return:"""return chr(random.randint(65, 90))def rndColor():"""生成随机颜色:return:"""return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))# 写文字font = ImageFont.truetype(font_file, font_size)for i in range(char_length):char = rndChar()code.append(char)h = random.randint(0, 4)draw.text([i * width / char_length, h], char, font=font, fill=rndColor())# 写干扰点for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())# 写干扰圆圈for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())x = random.randint(0, width)y = random.randint(0, height)draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())# 画干扰线for i in range(5):x1 = random.randint(0, width)y1 = random.randint(0, height)x2 = random.randint(0, width)y2 = random.randint(0, height)draw.line((x1, y1, x2, y2), fill=rndColor())img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)return img, ''.join(code)if __name__ == '__main__':img, code_str = check_code() # 写在图片上的文字code_strprint(code_str)with open('app01/static/img/code.png', 'wb') as f:img.save(f, format='png')
5.2.1在Django中调用
- 在utils下创建一个文件
code.py
将随机生成验证码代码粘贴进去,把之前创建的.py文件删除,图片也删除,只留下字体
code.py修改
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilterdef check_code(width=120, height=30, char_length=5, font_file='Monaco.ttf', font_size=28): # 字体文件:Monaco.ttfcode = []img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255)) # 创建图片draw = ImageDraw.Draw(img, mode='RGB')def rndChar():"""生成随机字母:return:"""return chr(random.randint(65, 90))def rndColor():"""生成随机颜色:return:"""return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))# 写文字font = ImageFont.truetype(font_file, font_size)for i in range(char_length):char = rndChar()code.append(char)h = random.randint(0, 4)draw.text([i * width / char_length, h], char, font=font, fill=rndColor())# 写干扰点for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())# 写干扰圆圈for i in range(40):draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())x = random.randint(0, width)y = random.randint(0, height)draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())# 画干扰线for i in range(5):x1 = random.randint(0, width)y1 = random.randint(0, height)x2 = random.randint(0, width)y2 = random.randint(0, height)draw.line((x1, y1, x2, y2), fill=rndColor())img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)return img, ''.join(code)
- 写url
5.2.2 图片验证码校验
十、Ajax的运用
1. Ajax请求
浏览器向网站发送请求时:URL 和 表单的形式提交。
- GET
- POST
特点:页面刷新。
除此之外,也可以基于Ajax向后台发送请求(偷偷的发送请求)。
- 依赖jQuery
- 编写ajax代码
$.ajax({url:"发送的地址",type:"get",data:{n1:123,n2:456},success:function(res){console.log(res);}
})
这里只是console了一下,我们想要的是点击发送一个请求
发送一个请求:
1.1 GET请求
$.ajax({url: '/task/ajax/', //发送的地址type: "get", //发get请求data: {n1: 123,n2: 456},success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);}
})
后台获取数据
def task_ajax(request):print(request.GET)return HttpResponse("成功了")
1.2 POST请求
$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: {n1: 123,n2: 456},success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);}
})
免除csrf_token认证
后台获取数据
from django.shortcuts import render, HttpResponse
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def task_ajax(request):print(request.GET)print(request.POST)return HttpResponse("成功了")
1.3 修改前端代码jQuery形式关闭绑定事件
{% extends 'layout.html' %}{% block content %}<div class="container"><h1>Ajax学习</h1><h3>示例一</h3><input id="btn1" type="button" class="btn-primary" value="点击"></div>{% endblock %}
{% block js %}<script type="text/javascript">$(function () {// 页面框架加载完成之后直接执行bindBtn1Event();})function bindBtn1Event() {$("#btn1").click(function () {$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: {n1: 123,n2: 456},success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);}})})}</script>
{% endblock %}
1.4 ajax请求的返回值
一般会返回一个JSON格式(字符串里面套这个列表啥的)
前端界面可以获取到值:(前端得到字符串,后端是一个json数据)前端js里面针对json格式反序列化成js里面的对象
{% extends 'layout.html' %}{% block content %}<div class="container"><h1>Ajax学习</h1><h3>示例一</h3><input id="btn1" type="button" class="btn-primary" value="点击"></div>{% endblock %}
{% block js %}<script type="text/javascript">$(function () {// 页面框架加载完成之后直接执行bindBtn1Event();})function bindBtn1Event() {$("#btn1").click(function () {$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: {n1: 123,n2: 456},dataType:"JSON",success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);console.log(res.status);console.log(res.data);}})})}</script>
{% endblock %}
import jsonfrom django.shortcuts import render, HttpResponse
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exemptdef task_list(request):"""任务列表"""return render(request, 'task_list.html')@csrf_exempt
def task_ajax(request):print(request.GET)print(request.POST)data_dict = {"status": True, "data": [11, 22, 33, 44]}return HttpResponse(json.dumps(data_dict))# return JsonResponse(data_dict)
1.5 案例2把input框里面的数据提交到后台
代码
<h3>示例2</h3>
<input type="text" id="txtUser" placeholder="姓名"/>
<input type="text" id="txtAge" placeholder="年龄"/>
<input id="btn2" type="button" class="btn-primary" value="点击">
<script type="text/javascript">$(function () {// 页面框架加载完成之后直接执行bindBtn2Event();})function bindBtn2Event() {$("#btn2").click(function () { //绑定click事件$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: {name: $("#txtUser").val(), //数据不能写死,需要获取到值传入到后台age: $("#txtAge").val()},dataType: "JSON",success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);console.log(res.status);console.log(res.data);}})})}
</script>
1.6 示例3
Ajax部分源代码
:
{% extends 'layout.html' %}{% block content %}<div class="container"><h1>Ajax学习</h1><h3>示例1</h3><input id="btn1" type="button" class="btn-primary" value="点击1"><h3>示例2</h3><input type="text" id="txtUser" placeholder="姓名"/><input type="text" id="txtAge" placeholder="年龄"/><input id="btn2" type="button" class="btn-primary" value="点击2"><h3>示例3</h3><form id="form3"><input type="text" name="user" placeholder="姓名"/><input type="text" name="age" placeholder="年龄"/><input type="text" name="email" placeholder="邮箱"/><input type="text" name="more" placeholder="介绍"/></form><input id="btn3" type="button" class="btn-primary" value="点击3"></div>{% endblock %}
{% block js %}<script type="text/javascript">$(function () {// 页面框架加载完成之后直接执行bindBtn1Event();bindBtn2Event();bindBtn3Event();})function bindBtn1Event() {$("#btn1").click(function () { //绑定click事件$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: {n1: 123,n2: 456},dataType: "JSON",success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);console.log(res.status);console.log(res.data);}})})}function bindBtn2Event() {$("#btn2").click(function () { //绑定click事件$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: {name: $("#txtUser").val(), //数据不能写死,需要获取到值传入到后台age: $("#txtAge").val()},dataType: "JSON",success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);console.log(res.status);console.log(res.data);}})})}function bindBtn3Event() {$("#btn3").click(function () { //绑定click事件$.ajax({url: '/task/ajax/', //发送的地址type: "post", //发get请求data: $("#form3").serialize(), //自动将表单里面的所有输入框值全部获取到并且打包后发送到Django后台dataType: "JSON",success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值console.log(res);console.log(res.status);console.log(res.data);}})})}</script>
{% endblock %}
创建一个任务,写一个面板里面写几个表单,将任务数据拿到后保存到数据库
1.创建面板
<div class="container"><div class="panel panel-default"><div class="panel-heading">表单</div><div class="panel-body">Panel content</div></div>
2.创表
class Task(models.Model):"""任务"""level_choice = ((1, "紧急"),(2, "重要"),(3, "临时"))level = models.SmallIntegerField(verbose_name="级别", choices=level_choice, default=1)title = models.CharField(verbose_name="标题", max_length=64)detail = models.TextField(verbose_name="详细信息")user = models.ForeignKey(verbose_name="负责人", to="Admin", on_delete=models.CASCADE)
3.展示ModelForm
from app01.utils.bootstrap import BootStrapModelForm
from app01 import models
class TaskModelForm(BootStrapModelForm):class Meta:model = models.Taskfields = "__all__"widgets = {"detail": forms.TextInput # 修改页面中详细信息插件,之前是TextField自动生成Textarea标签,不想这样生成自己定义导入form换成TextInput或者# "detail": forms.Textarea}def task_list(request):"""任务列表"""form = TaskModelForm() # 直接在列表页面生成表单return render(request, 'task_list.html', {"form": form})
显示负责人在前端界面
前端页面展示表单信息
<div class="container"><div class="panel panel-default"><div class="panel-heading">表单</div><div class="panel-body"><form method="post" novalidate><div class="clearfix"> <!--把父级撑起来,样式的调整-->{% for field in form %}<div class="col-xs-6"> <!--删格6,每一个占6格--><div class="form-group"><labe><span style="font-weight:bold;">{{ field.label }}</span></labe>{{ field }}</div></div>{% endfor %}<div class="col-xs-12"><button type="submit" class="btn btn-primary">提交</button></div></div></form></div></div>
用户输入并且提交到后台(提交不能以submit提交,自己写一个button提交,再写一个Ajax请求把这些数据获取到)
后端拿到数据做相应的校验
@csrf_exempt
def task_add(request):# print(request.POST)# < QueryDict: {'level': ['1'], 'title': ['中方与以色列方通话'], 'detail': ['bug'], 'user': ['2']} ># 1.用户发送过来的数据进行校验(ModelForm进行校验)# 创建一个ModelForm对象传入对象form = TaskModelForm(data=request.POST)if form.is_valid():form.save()data_dict = {"status": True}return HttpResponse(json.dumps(data_dict))# print(type(form.errors.as_json)) # 查看错误信息类型# # form django.forms.utils import ErrorDictdata_dict = {"status": False, 'error': form.errors}return HttpResponse(json.dumps(data_dict, ensure_ascii=False))
<div class="container"><div class="panel panel-default"><div class="panel-heading">表单</div><div class="panel-body"><form id="formAdd"><div class="clearfix"> <!--把父级撑起来,样式的调整-->{% for field in form %}<div class="col-xs-6"> <!--删格6,每一个占6格--><div class="form-group" style="position: relative;margin-bottom: 20px;"><!--相对的一个定位--><labe><span style="font-weight:bold;">{{ field.label }}</span></labe>{{ field }}<span class="error-msg" style="color:red;position: absolute;"></span><!--绝对的定位--></div></div>{% endfor %}<div class="col-xs-12"><button id="btnAdd" type="button" class="btn btn-primary">提交</button></div></div></form></div></div><script type="text/javascript">$(function () {// 页面框架加载完成之后直接执行bindBtnAddEvent();})function bindBtnAddEvent() {$("#btnAdd").click(function () { //绑定click事件$(".error-msg").empty(); // 把所有的错误信息清空$.ajax({url: '/task/add/', //发送的地址type: "post", //发get请求data: $("#formAdd").serialize(), //自动将表单里面的所有输入框值全部获取到并且打包后发送到Django后台dataType: "JSON",success: function (res) { //登陆成功之后,自动执行success后面的函数,res是返回的值if (res.status) {alert("添加成功")} else {console.log(res.error)$.each(res.error, function (name, data) {console.log(name, data);$("#id_" + name).next().next().text(data[0]);})}}})})}</script>