Django Web框架
第一章 Django框架的介绍
- 2005年发布,采用Python语言编写的开源web框架
- 早期的时候Django主做新闻和内容管理的
- 一个重量级的 Python Web框架,Django 配备了常用的大部分组件
- 基本配置
- 路由系统
- 原生HTML模板系统
- 视图 view
- Model模型,数据库连接和ORM数据库管理
- 中间件
- Cookie & Seesion
- 分页
- 数据库后台管理系统admin
- Django的用途
- 网站后端开发
- 微信公众号、微信小程序等后台开发
- 基于HTTP/HTTPS协议的后台服务器开发
- 在线语音/图像识别服务器
- 在线第三方身份验证服务器等
- Django的版本
- 最新版本:3.2.x
- Django的官网
- 官方网址: http://www.djangoproject.com
- 中文文档(第三方):
1.1 Django的安装
-
查看已安装的版本
>>> import django >>> print(django.VERSION) (3,0, 6, 'final', 0)
-
安装
- 在线安装
$ sudo pip3 install django
安装django的最新版本- 或
$ sudo pip3 install django[==版本]
安装django的指定版本- 如:
$ sudo pip3 install django==3.0.6
- 离线安装
- 下载安装包:
- 安装离线包
$ tar -xvf Django-3.0.6.tar.gz
$ cd Django-3.0.6
$ sudo python3 setup.py install
- 用wheel离线安装
- 下载安装包:
pip download -d /home/tarena/django_packs django==3.0.6
- 安装离线包
- $ pip install Django-3.0.6.whl
- 下载安装包:
- 在线安装
-
Django的卸载
-
$ pip uninstall django
1.2 Django框架开发
创建项目的指令
-
$ django-admin startproject 项目名称
-
如:
-
$ django-admin startproject mysite1
-
-
运行
$ cd mysite1 $ python3 manage.py runserver # 或 $ python3 manage.py runserver 5000 # 指定只能本机使用127.0.0.1的5000端口访问本机
Django项目的目录结构
-
示例:
$ django-admin startproject mysite1 $ tree mysite1/ mysite1/ ├── manage.py └── mysite1 ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py 1 directory, 5 files
-
项目目录结构解析:
-
manage.py
- 此文件是项目管理的主程序,在开发阶段用于管理整个项目的开发运行的调式
manage.py
包含项目管理的子命令, 如:python3 manage.py runserver
启动服务python3 manage.py startapp
创建应用python3 manage.py migrate
数据库迁移...
-
mysite1 项目包文件夹
- 项目包的主文件夹(默认与项目名称一致)
__init__.py
- 包初始化文件,当此项目包被导入(import)时此文件会自动运行
wsgi.py
- WSGI 即 Web Server Gateway Interface
- WEB服务网关接口的配置文件,仅部署项目时使用
urls.py
- 项目的基础路由配置文件,所有的动态路径必须先走该文件进行匹配
settings.py
- Django项目的配置文件, 此配置文件中的一些全局变量将为Django框架的运行传递一些参数
- setting.py 配置文件,启动服务时自动调用,
- 此配置文件中也可以定义一些自定义的变量用于作用全局作用域的数据传递
-
-
settings.py
文件介绍https://docs.djangoproject.com/en/3.0/ref/settings/
-
BASE_DIR
- 用于绑定当前项目的绝对路径(动态计算出来的), 所有文件都可以依懒此路径
-
DEBUG
- 用于配置Django项目的启用模式, 取值:
- True 表示开发环境中使用
调试模式
(用于开发中) - False 表示当前项目运行在
生产环境中
(不启用调试)
- True 表示开发环境中使用
- 用于配置Django项目的启用模式, 取值:
-
ALLOWED_HOSTS
-
设置允许访问到本项目的网络地址列表,取值:
- [] 空列表,表示只有
127.0.0.1
,localhost
能访问本项目 - ['*'],表示任何网络地址都能访问到当前项目
- ['192.168.1.3', '192.168.3.3'] 表示只有当前两个主机能访问当前项目
- 注意:
- 如果要在局域网其它主机也能访问此主机,启动方式应使用如下模式:
- [] 空列表,表示只有
-
python3 manage.py runserver 0.0.0.0:5000
# 指定网络设备所有主机都可以通过5000端口访问(需加ALLOWED_HOSTS = ['*']
)
-
-
INSTALLED_APPS
- 指定当前项目中安装的应用列表
-
MIDDLEWARE
- 用于注册中间件
-
TEMPLATES
- 用于指定模板的配置信息
-
DATABASES
- 用于指定数据库的配置信息
-
LANGUAGE_CODE
- 用于指定语言配置
- 取值:
- 英文 :
"en-us"
- 中文 :
"zh-Hans"
- 英文 :
-
TIME_ZONE
- 用于指定当前服务器端时区
- 取值:
- 世界标准时间:
"UTC"
- 中国时区 :
"Asia/Shanghai"
- 世界标准时间:
-
ROOT_URLCONF
- 用于配置根级 url 配置 'mysite1.urls'
- 如:
ROOT_URLCONF = 'mysite1.urls'
注: 此模块可以通过
from django.conf import settings
导入和使用 -
URL 介绍
-
url 即统一资源定位符 Uniform Resource Locator
-
作用:
- 用来表示互联网上某个资源的地址。
-
说明:
- 互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
-
URL的一般语法格式为:
protocol :// hostname[:port] / path [?query][#fragment]
-
如:
http://tts.tmooc.cn/video/showVideo?menuId=657421&version=AID201908#subject
-
说明:
- protocol(协议)
- http 通过 HTTP 访问该资源。 格式
HTTP://
- https 通过安全的 HTTPS 访问该资源。 格式
HTTPS://
- file 资源是本地计算机上的文件。格式:
file:///
- ...
- http 通过 HTTP 访问该资源。 格式
- hostname(主机名)
- 是指存放资源的服务器的域名系统(DNS) 主机名、域名 或 IP 地址。
- port(端口号)
- 整数,可选,省略时使用方案的默认端口;
- 各种传输协议都有默认的端口号,如http的默认端口为80。https默认 443
- path(路由地址)
- 由零或多个“/”符号隔开的字符串,一般用来表示主机上的一个目录或文件地址。路由地址决定了服务器端如何处理这个请求
- query(查询)
- 可选,用于给动态网页传递参数,可有多个参数,用“&”符号隔开,每个参数的名和值用“=”符号隔开。
- fragment(信息片断)
- 字符串,用于指定网络资源中的片断。例如一个网页中有多个名词解释,可使用fragment直接定位到某一名词解释。
- 注: [] 代表其中的内容可省略
- protocol(协议)
视图函数(view)
-
视图函数是用于接收一个浏览器请求并通过HttpResponse对象返回数据的函数。此函数可以接收浏览器请求并根据业务逻辑返回相应的内容给浏览器
-
视图处理的函数的语法格式:
def xxx_view(request[, 其它参数...]): return HttpResponse对象
-
参数:
- request用于绑定HttpRequest对象,通过此对象可以获取浏览器的参数和数据
-
示例:
-
视图处理函数
views.py
# file : <项目名>/views.py from django.http import HttpResponse def page1_view(request): html = "<h1>这是第1个页面</h1>" return HttpResponse(html)
-
1.3 Django 中的路由配置
-
settings.py 中的
ROOT_URLCONF
指定了主路由配置列表urlpatterns的文件位置 -
urls.py 主路由配置文件
# file : <项目名>/urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), ... # 此处配置主路由 ]
urlpatterns 是一个路由-视图函数映射关的列表,此列表的映射关系由url函数来确定
url() 函数
-
用于描述路由与视图函数的对应关系
-
模块
-
from django.conf.urls import url
-
-
语法:
-
url(regex, views, name=None)
-
参数:
- regex: 字符串类型,匹配的请求路径,允许是正则表达式
- views: 指定路径所对应的视图处理函数的名称
- name: 为地址起别名,在模板中地址反向解析时使用
-
每个正则表达式前面的r表示
'\'
不转义的原始字符串
-
练习
-
建立一个小网站:
- 输入网址: http://127.0.0.1:8000, 在网页中输出 : 这是我的首页
- 输入网址: http://127.0.0.1:8000/page1, 在网页中输出 : 这是编号为1的网页
- 输入网址: http://127.0.0.1:8000/page2, 在网页中输出 : 这是编号为2的网页
提示: 主页路由的正则是
r'^$'
- 思考
- 建立如上一百个网页该怎么办?
-
带有分组的路由和视图函数
-
在视图函数内,可以用正则表达式分组
()
提取参数后用函数位置传参传递给视图函数 -
一个分组表示一个参数,多个参数需要使用多个分组,并且使用个/隔开
- 定义一个路由的格式为:
- 从路由中提取数据,做相应的操作后返回给浏览器
- 如:
输入: 127.0.0.1:8000/100/add/200 页面显示结果:300 输入: 127.0.0.1:8000/100/sub/200 页面显示结果:-100 输入: 127.0.0.1:8000/100/mul/200 页面显示结果:20000
-
示例:
-
视图函数
def cal_view(request, x, op, y): x = int(x) y = int(y) if op not in ['mul', 'add', 'sub']: return HttpResponse('Sorry~Your canshu is wrong !!') #开始计算 res = None if op == 'add': res = x + y elif op == 'mul': res = x * y else: res = x - y #返回浏览器 具体结果 return HttpResponse(res)
-
路由配置文件
# file : <项目名>/urls.py # 以下示例匹配 # http://127.0.0.1:8000/person/xiaoming/20 # http://127.0.0.1:8000/person/xiaohong/29 # http://127.0.0.1:8000/person/xiaolan/9 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^(\d+)/(\w+)/(\d+)', views.cal_view), ]
-
带有命名分组的路由和视图函数
-
在url 的正则表达式中可以使用命名分组(捕获分组)
-
说明:
- 在视图函数内,可以用正则表达式分组
(?P<name>pattern)
提取参数后用函数关键字传参传递给视图函数
- 在视图函数内,可以用正则表达式分组
-
示例:
-
视图函数
def person_view(request, name, age): res = "姓名: " + name res += " 年龄: " + age return HttpResponse(res)
-
路由配置文件
# file : <项目名>/urls.py # 以下示例匹配 # http://127.0.0.1:8000/person/xiaoming/20 # http://127.0.0.1:8000/person/xiaohong/29 # http://127.0.0.1:8000/person/xiaolan/9 urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^person/(?P<name>\w+)/(?P<age>\d{1,2})',views.person_view), ]
-
-
练习:
- 访问地址:
- 最终输出: 生日为: xxxx年xx月xx日
- 如: 输入网址: http://127.0.0.1:8000/birthday/2015/12/11 显示为: 生日为:2015年12月11日 输入网址: http://127.0.0.1:8000/birthday/2/28/2008 显示为: 生日为:2008年2月28日
1.4 HTTP协议的请求和响应
- 请求是指浏览器端通过HTTP协议发送给服务器端的数据
- 响应是指服务器端接收到请求后做相应的处理后再回复给浏览器端的数据
HTTP 请求
-
根据HTTP标准,HTTP请求可以使用多种请求方法。
-
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法(最常用)
-
HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
-
HTTP1.1 请求详述
序号 方法 描述 1 GET 请求指定的页面信息,并返回实体主体。 2 HEAD 类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头 3 POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。 4 PUT 从客户端向服务器传送的数据取代指定的文档的内容。 5 DELETE 请求服务器删除指定的页面。 6 CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 7 OPTIONS 允许客户端查看服务器的性能。 8 TRACE 回显服务器收到的请求,主要用于测试或诊断。 -
HttpRequest对象
- 视图函数的第一个参数是HttpRequest对象
- 服务器接收到http协议的请求后,会根据请求数据报文创建HttpRequest对象
- HttpRequest属性
- path:字符串,表示请求的路由信息
- path_info: URL字符串
- method:字符串,表示HTTP请求方法,常用值:'GET'、'POST'
- encoding:字符串,表示提交的数据的编码方式
- 如果为None则表示使用浏览器的默认设置,一般为'utf-8'
- 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值
- GET:QueryDict查询字典的对象,包含get请求方式的所有数据
- POST:QueryDict查询字典的对象,包含post请求方式的所有数据
- FILES:类似于字典的对象,包含所有的上传文件信息
- COOKIES:Python字典,包含所有的cookie,键和值都为字符串
- session:似于字典的对象,表示当前的会话,
- body: 字符串,请求体的内容(POST或PUT)
- environ: 字符串,客户端运行的环境变量信息
- scheme : 请求协议('http'/'https')
- request.get_full_path() : 请求的完整路径
- request.get_host() : 请求的主机
- request.META : 请求中的元数据(消息头)
- request.META['REMOTE_ADDR'] : 客户端IP地址
HTTP 响应
-
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头用以响应浏览器的请求。
-
HTTP状态码的英文为HTTP Status Code。
-
下面是常见的HTTP状态码:
- 200 - 请求成功
- 301 - 资源(网页等)被永久转移到其它URL
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
-
HTTP状态码分类
-
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:
分类 分类描述 1** 信息,服务器收到请求,需要请求者继续执行操作 2** 成功,操作被成功接收并处理 3** 重定向,需要进一步的操作以完成请求 4** 客户端错误,请求包含语法错误或无法完成请求 5** 服务器错误,服务器在处理请求的过程中发生了错误
-
-
Django中的响应对象HttpResponse:
-
构造函数格式:
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
-
作用:
- 向客户端浏览器返回响应,同时携带响应体内容
-
参数:
-
content:表示返回的内容。
-
status_code:返回的HTTP响应状态码(默认为200)。
-
content_type:指定返回数据的的MIME类型(默认为"text/html")。浏览器会根据这个属性,来显示数据。如果是text/html,那么就会解析这个字符串,如果text/plain,那么就会显示一个纯文本。
- 常用的Content-Type如下:
'text/html'
(默认的,html文件)'text/plain'
(纯文本)'text/css'
(css文件)'text/javascript'
(js文件)'multipart/form-data'
(文件提交)'application/json'
(json传输)
'application/xml'
(xml文件)
注: 关键字MIME(Multipurpose Internet Mail Extensions)是指多用途互联网邮件扩展类型。
- 常用的Content-Type如下:
-
-
-
HttpResponse 子类
类型 作用 状态码 HttpResponseRedirect 重定响 302 HttpResponseNotModified 未修改 304 HttpResponseBadRequest 错误请求 400 HttpResponseNotFound 没有对应的资源 404 HttpResponseForbidden 请求被禁止 403 HttpResponseServerError 服务器错误 500
GET方式传参
-
GET请求方式中可以通过查询字符串(Query String)将数据传递给服务器
-
URL 格式:
xxx?参数名1=值1&参数名2=值2...
- 如:
http://127.0.0.1:8000/page1?a=100&b=200
- 如:
-
服务器端接收参数
-
判断 request.method 的值判断请求方式是否是get请求
if request.method == 'GET': 处理GET请求时的业务逻辑 else: 处理其它请求的业务逻辑
-
获取客户端请求GET请求提交的数据
-
语法
request.GET['参数名'] # QueryDict request.GET.get('参数名','默认值') request.GET.getlist('参数名') # mypage?a=100&b=200&c=300&b=400 # request.GET=QueryDict({'a':['100'], 'b':['200','400'], 'c':['300']}) # a = request.GET['a'] # b = request.GET['b'] # Error
-
能够产生get请求方式的场合
-
地址栏手动输入, 如: http://127.0.0.1:8000/mypage?a=100&b=200
-
form表单中的method为get
#当前form 提交请求至 http://127.0.0.1:8000/user/login <form method='get' action="/user/login"> 姓名:<input type="text" name="uname"> </form>
-
-
-
一般查询字符串的大小会受到浏览器的的限制(不建议超过2048字节)
- 练习:
- 访问地址:http://127.0.0.1:8000/sum?start=整数&stop=整数&step整=字
- 输出结果为sum(range(start, step, stop)) 和:
- 如:
- 输入网址: http://127.0.0.1:8000/sum?start=1&stop=101&step=1
- 页面显示: 结果: 5050
- 输入网址: http://127.0.0.1:8000/sum?stop=101&step=2
- 页面显示: 结果: 2550
- 输入网址: http://127.0.0.1:8000/sum?start=1&stop=101&step=2
- 页面显示: 结果: 2500
- 练习:
- 访问地址:http://127.0.0.1:8000/birthday?year=四位整数&month=整数&day=整数
- 最终输出: 生日为: xxxx年xx月xx日
- 如:
- 输入网址: http://127.0.0.1:8000/birthday?year=2015&month=12&day=11
- 显示为: 生日为:2015年12月11日
POST传递参数
- 客户端通过表单等POST请求将数据传递给服务器端,如:
<form method='post' action="/login">
姓名:<input type="text" name="username">
<input type='submit' value='登陆'>
</form>
-
服务器端接收参数
- 通过 request.method 来判断是否为POST请求,如:
if request.method == 'POST': 处理POST请求的数据并响应 else: 处理非POST 请求的响应
-
使用post方式接收客户端数据
- 方法
request.POST['参数名'] # request.POST 绑定QueryDict request.POST.get('参数名','') request.POST.getlist('参数名')
-
取消csrf验证,否则Django将会拒绝客户端发来的POST请求
-
取消 csrf 验证
- 删除 settings.py 中 MIDDLEWARE 中的 CsrfViewsMiddleWare 的中间件
MIDDLEWARE = [ ... # 'django.middleware.csrf.CsrfViewMiddleware', ... ]
-
form 表单的name属性
-
在form表单控件提交数据时,会自动搜索本表单控件内部的子标签的name属性及相应的值,再将这些名字和值以键-值对的形式提交给action指定的服务器相关位置
-
key1=value1&key2=value2&key3=value3
-
在form内能自动搜集到的name属性的标签的控件有
<input name='xxx'> <select name='yyy'></select> <textarea name='zzz'></textarea>
- 如:
<form action="/page1" method="POST"> <input name="title" type="text" value="请输入"> <select name="gender"> <option value=1>男</option> <option value=0>女</option> </select> <textarea name="comment" rows="5" cols="10">附言...</textarea> <input type="submit" value="提交"> </form>
注意事项:
1,若启动django时【python3 manage.py runserver】报如下错误:
Error: That port is already in use.
解决方案:
分析:当前有其他进程占用8000端口,
方案1:ps aux|grep 'manage' -> sudo kill -9 进程id
方案2:更换端口启动 python3 manage.py runserver 8080
第二章 Django的框架设计模式
2.1 MVC与MTV
-
MVC 设计模式
-
MVC 代表 Model-View-Controller(模型-视图-控制器) 模式。
-
作用: 降低模块间的耦合度(解耦)
-
MVC
- M 模型层(Model), 主要用于对数据库层的封装
- V 视图层(View), 用于向用户展示结果
- C 控制(Controller ,用于处理请求、获取数据、返回结果(重要)
-
MVC模式如图:
-
-
MTV 模式 MTV 代表 Model-Template-View(模型-模板-视图) 模式。这种模式用于应用程序的分层开发
- 作用:
- 降低模块间的耦合度(解耦)
- MTV
- M -- 模型层(Model) 负责与数据库交互
- T -- 模板层(Template) 负责呈现内容到浏览器
- V -- 视图层(View) 是核心,负责接收请求、获取数据、返回结果
- MTV模式如图:
- 作用:
2.2 模板 Templates
- 什么是模板
- 模板是可以根据字典数据动态变化的html网页
- 模板可以根据视图中传递的字典数据动态生成相应的HTML网页。
- 模板的配置
- 创建模板文件夹
<项目名>/templates
- 在 settings.py 中有一个 TEMPLATES 变量
- BACKEND : 指定模板的引擎
- DIRS : 模板的搜索目录(可以是一个或多个)
- APP_DIRS : 是否要在应用中的
templates
文件夹中搜索模板文件 - OPTIONS : 有关模板的选项
- 创建模板文件夹
- 默认的模块文件夹
templates
- 修改settings.py文件,设置TEMPLATES的DIRS值为
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# file: settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 'DIRS': [],
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 添加模板路径
'APP_DIRS': True, # 是否索引各app里的templates目录
...
},
]
2.3 Django 模板语言
模板的传参
-
模板传参是指把数据形成字典,传参给模板,为模板渲染提供数据
-
通过 loader 获取模板,通过HttpResponse进行响应
from django.template import loader # 1.通过loader加载模板 t = loader.get_template("模板文件名") # 2.将t转换成 HTML 字符串 html = t.render(字典数据) # 3.用响应对象将转换的字符串内容返回给浏览器 return HttpResponse(html)
-
使用 render() 直接加载并响应模板
from django.shortcuts import render return render(request,'模板文件名', 字典数据)
-
模板的变量
-
在模板中使用变量语法
{{ 变量名 }}
{{ 变量名.index }}
{{ 变量名.key}}
{{ 对象.方法 }}
{{ 函数名 }}
-
视图函数中必须将变量封装到字典中才允许传递到模板上
def xxx_view(request) dic = { "变量1":"值1", "变量2":"值2", } return render(request, 'xxx.html', dic)
-
练习
-
写一个简单的计算器页面,能够在服务端进行简单加减乘除计算
= 3
-
参考代码
<form action='/mycal' method='POST'> <input type='text' name="x" value="{{ x }}"> <select name='op'> <option value="add"> +加 </option> <option value="sub"> -减 </option> <option value="mul"> *乘 </option> <option value="div"> /除 </option> </select> <input type='text' name="y" value="2"> = <span>3</span> <div> <input type="submit" value='开始计算'> <div> </form>
视图函数:
def cal_view(request): #GET or POST if request.method == 'GET': #拿页面显示给 用户 return render(request, 'cal.html') elif request.method == 'POST': # 浏览器会用form POST请求提交如下数据 x=x_val&op=op_val&y=y_val #处理数据 +-×/ # form text框 空提交时 浏览器会带上具体text框的name及空值一并提交到服务器 #x = int(request.POST.get('x', 100)) x = request.POST.get('x') if not x: #错误处理 将提示信息返给浏览器 error = 'Please give me x!!' dic = {'error':error} return render(request, 'cal.html', dic) try: x = int(x) except Exception as e: print('----x is error ----') print(x) try: x = int(float(x)) except Exception as e: error = 'The x is must be number !!' dic = {'error': error} return render(request, 'cal.html', dic) #TODO 检查 y 值;方法同上 op = request.POST.get('op') y = int(request.POST.get('y')) result = 999999 if op == 'add': result = x + y elif op == 'sub': result = x - y elif op == 'mul': result = x * y elif op == 'div': result = x / y return render(request, 'cal.html', locals())
-
模板的标签
-
作用
- 将一些服务器端的功能嵌入到模板中
-
标签语法
{% 标签 %} ... {% 结束标签 %}
-
if 标签
{% if 条件表达式1 %} ... {% elif 条件表达式2 %} ... {% elif 条件表达式3 %} ... {% else %} ... {% endif %}
-
if 标签里的布尔运算符
- if 条件表达式里可以用的运算符 ==, !=, <, >, <=, >=, in, not in, is, is not, not、and、or
- 在if标记中使用实际括号是无效的语法。 如果您需要它们指示优先级,则应使用嵌套的if标记。
-
locals函数的使用
locals() 返回当前函数作用域内全部局部变量形成的字典。
-
for 标签
-
语法
{% for 变量 in 可迭代对象 %} ... 循环语句 {% empty %} ... 可迭代对象无数据时填充的语句 {% endfor %}
-
内置变量 - forloop
变量 描述 forloop.counter 循环的当前迭代(从1开始索引) forloop.counter0 循环的当前迭代(从0开始索引) forloop.revcounter 循环结束的迭代次数(从1开始索引) forloop.revcounter0 循环结束的迭代次数(从0开始索引) forloop.first 如果这是第一次通过循环,则为真 forloop.last 如果这是最后一次循环,则为真 forloop.parentloop 当嵌套循环,parentloop 表示外层循环 -
遍历字典的 key 和 value
1, 只遍历key
{% for i in dic %} {{ i }} {% endfor %}
2, 遍历 key 和 value
{% for k, v in dic.item %} 键:{{ k }} 值: {{ v }} {% endfor %}
-
过滤器
-
作用
- 在变量输出时对变量的值进行处理
- 您可以通过使用 过滤器来改变变量的输出显示。
-
语法
- {{ 变量 | 过滤器1:参数值1 | 过滤器2:参数值2 ... }}
-
常用的过滤器
过滤器 说明 lower 将字符串转换为全部小写。 upper 将字符串转换为大写形式 safe 默认不对变量内的字符串进行html转义 add: "n" 将value的值增加 n truncatechars:'n' 如果字符串字符多于指定的字符数量,那么会被截断。 截断的字符串将以可翻译的省略号序列(“...”)结尾。 ... -
文档参见:
模板的继承
-
模板继承可以使父模板的内容重用,子模板直接继承父模板的全部内容并可以覆盖父模板中相应的块
-
定义父模板中的块
block
标签-
标识出哪些在子模块中是允许被修改的
-
block标签:在父模板中定义,可以在子模板中覆盖
{% block block_name %} 定义模板块,此模板块可以被子模板重新定义的同名块覆盖 {% endblock block_name %}
-
-
继承模板
extends
标签(写在模板文件的第一行)-
子模板继承语法标签
-
{% extends '父模板名称' %}
-
如:
{% extends 'base.html' %}
-
-
子模板 重写父模板中的内容块
{% block block_name %} 子模板块用来覆盖父模板中 block_name 块的内容 {% endblock block_name %}
- 重写的覆盖规则
- 不重写,将按照父模板的效果显示
- 重写,则按照重写效果显示
- 注意
- 模板继承时,服务器端的动态内容无法继承
-
-
参考文档
-
模板的继承示例:
url 反向解析
-
url 反向解析是指在视图或模板中,用为url定义的名称来查找或计算出相应的路由
-
url 函数的语法
-
url(regex, views, kwargs=None, name="别名")
-
例如:
-
url(r'^user_login$', views.login_view, name="login")
-
-
-
url() 的
name
关键字参数-
作用:
- 根据url 列表中的
name=
关键字传参给 url确定了个唯一确定的名字,在模板中,可以通过这个名字反向推断出此url信息
- 根据url 列表中的
-
在模板中通过别名实现地址的反向解析
{% url '别名' %} {% url '别名' '参数值1' '参数值2' %}
-
-
练习:
写一个有四个自定义页面的网站,对应路由: / 主页 /page1 页面1 /page2 页面2 /page3 页面3 功能: 主页加 三个页面的连接分别跳转到一个 页面,三个页面每个页面加入一个链接用于返回主页
2.4 静态文件
-
什么是静态文件
- 不能与服务器端做动态交互的文件都是静态文件
- 如:图片,css,js,音频,视频,html文件(部分)
-
静态文件配置
- 在 settings.py 中配置一下两项内容:
-
配置静态文件的访问路径
- 通过哪个url地址找静态文件
- STATIC_URL = '/static/'
- 说明:
- 指定访问静态文件时是需要通过 /static/xxx或 127.0.0.1:8000/static/xxx
- xxx 表示具体的静态资源位置
-
配置静态文件的存储路径
STATICFILES_DIRS
- STATICFILES_DIRS保存的是静态文件在服务器端的存储位置
-
示例:
# file: setting.py STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), )
-
访问静态文件
-
使用静态文件的访问路径进行访问
-
访问路径: STATIC_URL = '/static/'
-
示例:
<img src="/static/images/lena.jpg"> <img src="http://127.0.0.1:8000/static/images/lena.jpg">
-
-
通过 {% static %}标签访问静态文件
{% static %}
表示的就是静态文件访问路径
-
加载 static
{% load static %}
-
使用静态资源时
-
语法:
{% static '静态资源路径' %}
-
示例:
-
<img src="{% static 'images/lena.jpg' %}">
-
-
-
第三章 Django中的应用 - app
- 应用在Django项目中是一个独立的业务模块,可以包含自己的路由,视图,模板,模型
3.1 创建应用app
-
创建步骤
- 用manage.py 中的子命令 startapp 创建应用文件夹
- 在settings.py 的 INSTALLED_APPS 列表中配置安装此应用
-
创建应用的子命令
- python3 manage.py startapp 应用名称(必须是标识符命令规则)
- 如:
- python3 manage.py startapp music
-
Django应用的结构组成
migrations
文件夹- 保存数据迁移的中间文件
__init__.py
- 应用子包的初始化文件
admin.py
- 应用的后台管理配置文件
apps.py
- 应用的属性配置文件
models.py
- 与数据库相关的模型映射类文件
tests.py
- 应用的单元测试文件
views.py
- 定义视图处理函数的文件
-
配置安装应用
-
在 settings.py 中配置应用, 让此应用能和整个项目融为一体
# file : settings.py INSTALLED_APPS = [ ... ..., '自定义应用名称' ]
-
如:
INSTALLED_APPS = [ # .... 'user', # 用户信息模块 'music', # 收藏模块 ]
-
3.2 应用的分布式路由
- Django中,基础路由配置文件(urls.py)可以不处理用户具体路由,基础路由配置文件的可以做请求的分发(分布式请求处理)。具体的请求可以由各自的应用来进行处理
- 127.0.0.1:8000/news/get_news/today -> news应用下 urls.py -> get_news/today -> 找哪个news应用下的 views.函数
- 127.0.0.1:8000/music/jielun/11111123 -> music应用下的urls.py ->......
include 函数
-
作用:
- 用于分发将当前路由转到各个应用的路由配置文件的 urlpatterns 进行分布式处理
-
函数格式
- include('app命字.url模块名')
模块
app命字/url模块名.py
文件件里必须有urlpatterns 列表 使用前需要使用from django.conf.urls import include
导入此函数from django.conf.urls import url from . import views #files music/urls.py urlpatterns = [ #http://127.0.0.1:8000/music/index url(r'^index$', views.index) ]
-
练习:
1.创建四个应用 1.创建 index 应用,并注册 2.创建 sport 应用,并注册 3.创建 news 应用,并注册 注册即在settings.py中登记该应用 4.创建 music 应用,并注册 2.创建分布式路由系统 主路由配置只做分发 每个应用中处理具体访问路径和视图 1. 127.0.0.1:8000/music/index 交给 music 应用中的 index_view() 函数处理 2. 127.0.0.1:8000/sport/index 交给 sport 应用中的 index_view() 函数处理 3. 127.0.0.1:8000/news/index 交给 news 应用中的 index_view() 处理处理
注意:
1,先创建应用 再注册应用; 否则执行 python3 manage.py startapp 应用名时 会抛出 no module named 应用名 错误
2,一定要创建完应用后 即刻注册 【settings.py 里 添加自定义应用】
3,app下可以创建应用内部的templates文件夹,该文件夹可供开发人员存储当前应用下的html ; 如果settings.py中 TEMPLATES 配置了 DIRS属性
【即配置了外部的html集中存储文件夹】, 则优先查找外部的存储文件夹,其次再查找应用内部的templates~
4,如果出现 TemplatesDoesNotExisted 则尝试重启进程 触发加载配置
5,当每个应用下都有templates时,请注意
有可能当前应用加载了 其他应用中的 同名html
解决方案:
-
app下的templates下创建同应用名的子文件夹,将该应用所有html转至该文件夹; ex: mysite3/music/templates/music/index.html
-
music视图函数中, renturn render(request, 'music/index.html')
第四章 数据库和模型
4.1 Django下配置使用 mysql 数据库
-
安装 pymysql包
-
用作 python 和 mysql 的接口
-
$ sudo pip3 install pymysql
-
-
安装 mysql 客户端(非必须)
$ sudo pip3 install mysqlclient
-
-
创建 和 配置数据库
-
创建数据库
- 创建
create database 数据库名 default charset utf8 collate utf8_general_ci;
create database mywebdb default charset utf8 collate utf8_general_ci;
- 创建
-
数据库的配置
-
sqlite 数据库配置
# file: settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
-
mysql 数据库配置
DATABASES = { 'default' : { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mywebdb', # 数据库名称,需要自己定义 'USER': 'root', 'PASSWORD': '123456', # 管理员密码 'HOST': '127.0.0.1', 'PORT': 3306, } }
-
-
关于数据为的SETTING设置
-
ENGINE
- 指定数据库的后端引擎
'django.db.backends.mysql' 'django.db.backends.sqlite3' 'django.db.backends.oracle' 'django.db.backends.postgresql'
-
mysql引擎如下:
-
'django.db.backends.mysql'
-
-
NAME
- 指定要连接的数据库的名称
'NAME': 'mywebdb'
-
USER
- 指定登录到数据库的用户名
'USER':'root'
-
PASSWORD
- 接数据库时使用的密码。
'PASSWORD':'123456'
-
HOST
- 连接数据库时使用哪个主机。
'HOST':'127.0.0.1'
-
PORT
- 连接数据库时使用的端口。
'PORT':'3306'
-
-
添加 mysql 支持
-
安装pymysql 模块
-
$ sudo pip install pymysql
-
-
修改项目中init.py 加入如下内容来提供pymysql引擎的支持
import pymysql pymysql.install_as_MySQLdb()
-
-
4.2 模型(Models)
- 模型是一个Python类,它是由django.db.models.Model派生出的子类。
- 一个模型类代表数据库中的一张数据表
- 模型类中每一个类属性都代表数据表中的一个字段。
- 模型是数据交互的接口,是表示和操作数据库的方法和方式
4.3 Django 的 ORM框架
- ORM(Object Relational Mapping)即对象关系映射,它是一种程序技术,它允许你使用类和对象对数据库进行操作,从而避免通过SQL语句操作数据库
- ORM框架的作用
- 建立模型类和表之间的对应关系,允许我们通过面向对象的方式来操作数据库。
- 根据设计的模型类生成数据库中的表格。
- 通过简单的配置就可以进行数据库的切换。
- ORM 好处:
- 只需要面向对象编程, 不需要面向数据库编写代码.
- 对数据库的操作都转化成对类属性和方法的操作.
- 不用编写各种数据库的sql语句.
- 实现了数据模型与数据库的解耦, 屏蔽了不同数据库操作上的差异.
- 不在关注用的是mysql、oracle...等数据库的内部细节.
- 通过简单的配置就可以轻松更换数据库, 而不需要修改代码.
- 只需要面向对象编程, 不需要面向数据库编写代码.
- ORM 缺点
- 相比较直接使用SQL语句操作数据库,有性能损失.
- 根据对象的操作转换成SQL语句,根据查询的结果转化成对象, 在映射过程中有性能损失.
- ORM 示意
-
模型示例:
-
此示例为添加一个 bookstore_book 数据表来存放图书馆中书目信息
-
添加一个 bookstore 的 app
$ python3 manage.py startapp bookstore
-
添加模型类并注册app
# file : bookstore/models.py from django.db import models class Book(models.Model): title = models.CharField("书名", max_length=50, default='') price = models.DecimalField('定价', max_digits=7, decimal_places=2, default=0.0)
-
注册app
# file : setting.py INSTALLED_APPS = [ ... 'bookstore', ]
-
-
数据库的迁移
- 迁移是Django同步您对模型所做更改(添加字段,删除模型等) 到您的数据库模式的方式
- 生成或更新迁移文件
- 将每个应用下的models.py文件生成一个中间文件,并保存在migrations文件夹中
python3 manage.py makemigrations
- 执行迁移脚本程序
- 执行迁移程序实现迁移。将每个应用下的migrations目录中的中间文件同步回数据库
python3 manage.py migrate
-
每次修改完模型类再对服务程序运行之前都需要做以上两步迁移操作。
-
生成迁移脚本文件
bookstore/migrations/0001_initial.py
并进行迁移$ python3 manage.py makemigrations $ python3 manage.py migrate
-
-
编写模型类Models
-
模型类需继承自
django.db.models.Model
-
Models的语法规范
from django.db import models class 模型类名(models.Model): 字段名 = models.字段类型(字段选项)
模型类名是数据表名的一部分,建议类名首字母大写 字段名又是当前类的类属性名,此名称将作为数据表的字段名 字段类型用来映射到数据表中的字段的类型 字段选项为这些字段提供附加的参数信息
-
-
-
字段类型
-
BooleanField()
- 数据库类型:tinyint(1)
- 编程语言中:使用True或False来表示值
- 在数据库中:使用1或0来表示具体的值
-
CharField()
- 数据库类型:varchar
- 注意:
- 必须要指定max_length参数值
-
DateField()
- 数据库类型:date
- 作用:表示日期
- 编程语言中:使用字符串来表示具体值
- 参数:
- DateField.auto_now: 每次保存对象时,自动设置该字段为当前时间(取值:True/False)。
- DateField.auto_now_add: 当对象第一次被创建时自动设置当前时间(取值:True/False)。
- DateField.default: 设置当前时间(取值:字符串格式时间如: '2019-6-1')。
- 以上三个参数只能多选一
-
DateTimeField()
- 数据库类型:datetime(6)
- 作用:表示日期和时间
- models.DateTimeField(auto_now_add=True, )
- default='2019-10-1 18:15:20'
-
DecimalField()
-
数据库类型:decimal(x,y)
-
编程语言中:使用小数表示该列的值
-
在数据库中:使用小数
-
参数:
- DecimalField.max_digits: 位数总数,包括小数点后的位数。 该值必须大于等于decimal_places.
- DecimalField.decimal_places: 小数点后的数字数量
-
示例:
money=models.DecimalField( max_digits=7, decimal_places=2, default=0.0 )
-
-
FloatField()
- 数据库类型:double
- 编程语言中和数据库中都使用小数表示值
-
EmailField()
- 数据库类型:varchar
- 编程语言和数据库中使用字符串
-
IntegerField()
- 数据库类型:int
- 编程语言和数据库中使用整数
-
URLField()
- 数据库类型:varchar(200)
- 编程语言和数据库中使用字符串
-
ImageField()
-
数据库类型:varchar(100)
-
作用:在数据库中为了保存图片的路径
-
编程语言和数据库中使用字符串
-
示例:
image=models.ImageField( upload_to="static/images" )
-
upload_to:指定图片的上传路径 在后台上传时会自动的将文件保存在指定的目录下
-
-
TextField()
- 数据库类型:longtext
- 作用:表示不定长的字符数据
-
-
字段选项FIELD_OPTIONS
- 字段选项, 指定创建的列的额外的信息
- 允许出现多个字段选项,多个选项之间使用,隔开
- primary_key
- 如果设置为True,表示该列为主键,如果指定一个字段为主键,则此数库表不会创建id字段
- blank
- 设置为True时,字段可以为空。设置为False时,字段是必须填写的。
- null
- 如果设置为True,表示该列值允许为空。
- 默认为False,如果此选项为False建议加入default选项来设置默认值
- default
- 设置所在列的默认值,如果字段选项null=False建议添加此项
- db_index
- 如果设置为True,表示为该列增加索引
- unique
- 如果设置为True,表示该字段在数据库中的值必须是唯一(不能重复出现的)
- db_column
- 指定列的名称,如果不指定的话则采用属性名作为列名
- verbose_name
- 设置此字段在admin界面上的显示名称。
-
示例:
# 创建一个属性,表示用户名称,长度30个字符,必须是唯一的,不能为空,添加索引 name = models.CharField(max_length=30, unique=True, null=False, db_index=True)
数据库迁移的错误处理方法
-
当执行
$ python3 manage.py makemigrations
出现如下迁移错误时的处理方法-
错误信息
$ python3 manage.py makemigrations You are trying to change the nullable field 'title' on book to non-nullable without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration) 3) Quit, and let me add a default in models.py Select an option:
-
翻译为中文如下:
$ python3 manage.py makemigrations 您试图将图书上的可空字段“title”更改为非空字段(没有默认值);我们不能这样做(数据库需要填充现有行)。 请选择修复: 1)现在提供一次性默认值(将对所有现有行设置此列的空值) 2)暂时忽略,让我自己处理空值的现有行(例如,因为您在以前的数据迁移中添加了RunPython或RunSQL操作来处理空值) 3)退出,让我在models.py中添加一个默认值 选择一个选项:
-
错误原因
- 当将如下代码
class Book(models.Model): title = models.CharField("书名", max_length=50, null=True)
- 去掉 null=True 改为如下内容时会出现上述错误
class Book(models.Model): title = models.CharField("书名", max_length=50)
- 原理是 此数据库的title 字段由原来的可以为NULL改为非NULL状态,意味着原来这个字段可以不填值,现在改为必须填定一个值,那填什么值呢?此时必须添加一个缺省值。
-
处理方法:
- 选择1 手动给出一个缺省值,在生成 bookstore/migrations/000x_auto_xxxxxxxx_xxxx.py 文件时自动将输入的值添加到default参数中
- 暂时忽略,以后用其它的命令处理缺省值问题(不推荐)
- 退出当前生成迁移文件的过程,自己去修改models.py, 新增加一个
default=XXX
的缺省值(推荐使用)
-
-
数据库的迁移文件混乱的解决办法
- 删除 所有 migrations 里所有的 000?_XXXX.py (
__init__.py
除外) - 删除 数据表
- sql> drop database mywebdb;
- 重新创建 数据表
- sql> create datebase mywebdb default charset...;
- 重新生成migrations里所有的 000?_XXXX.py
- python3 manage.py makemigrations
- 重新更新数据库
- python3 manage.py migrate
- 删除 所有 migrations 里所有的 000?_XXXX.py (
4.4 数据库的基本操作
- 数据库的基本操作包括增删改查操作,即(CRUD操作)
- CRUD是指在做计算处理时的增加(Create)、读取查询(Read)、更新(Update)和删除(Delete)
管理器对象
-
每个继承自 models.Model 的模型类,都会有一个 objects 对象被同样继承下来。这个对象叫管理器对象
-
数据库的增删改查可以通过模型的管理器实现
class MyModel(models.Model): ... MyModel.objects.create(...) # objects 是管理器对象
创建数据对象
-
Django 使用一种直观的方式把数据库表中的数据表示成Python 对象
-
创建数据中每一条记录就是创建一个数据对象
-
MyModel.objects.create(属性1=值1, 属性2=值1,...)
- 成功: 返回创建好的实体对象
- 失败: 抛出异常
-
创建 MyModel 实例对象,并调用 save() 进行保存
obj = MyModel(属性=值,属性=值) obj.属性=值 obj.save() 无返回值,保存成功后,obj会被重新赋值
-
Django shell 的使用
-
在Django提供了一个交互式的操作项目叫
Django Shell
它能够在交互模式用项目工程的代码执行相应的操作 -
利用 Django Shell 可以代替编写View的代码来进行直接操作
-
在Django Shell 下只能进行简单的操作,不能运行远程调式
-
启动方式:
$ python3 manage.py shell
-
练习:
在 bookstore/models.py 应用中添加两个model类 1. Book - 图书 1. title - CharField 书名,非空,唯一 2. pub - CharField 出版社,字符串,非空 3. price - 图书定价,, 4. market_price - 图书零售价 2. Author - 作者 1. name - CharField 姓名,非空 2. age - IntegerField, 年龄,非空,缺省值为1 3. email - EmailField, 邮箱,允许为空
-
然后用 Django Shell 添加如下数据
-
图书信息
书名 定价 零售价 出版社 Python 20.00 25.00 清华大学出版社 Python3 60.00 65.00 清华大学出版社 Django 70.00 75.0 0 清华大学出版社 JQuery 90.00 85.00 机械工业出版社 Linux 80.00 65.00 机械工业出版社 Windows 50.00 35.00 机械工业出版社 HTML5 90.00 105.00 清华大学出版社 -
作者信息:
姓名 年龄 邮箱 王老师 28 wangweichao@tedu.cn 吕老师 31 lvze@tedu.cn 祁老师 30 qitx@tedu.cn
-
-
In [1]: from bookstore.models import Book
In [2]: b1 = Book.objects.create(title = 'python',pub = '清华大学出版社',price = 20,market_price=25)
查询数据
-
数据库的查询需要使用管理器对象进行
-
通过 MyModel.objects 管理器方法调用查询接口
方法 说明 all() 查询全部记录,返回QuerySet查询对象 get() 查询符合条件的单一记录 filter() 查询符合条件的多条记录 exclude() 查询符合条件之外的全部记录 ...
-
all()方法
-
方法: all()
-
用法: MyModel.objects.all()
-
作用: 查询MyModel实体中所有的数据
- 等同于
- select * from tabel
- 等同于
-
返回值: QuerySet容器对象,内部存放 MyModel 实例
-
示例:
from bookstore import models
books = models.Book.objects.all()
for book in books: print("书名", book.title, '出版社:', book.pub)
-
-
在模型类中定义
def __str__(self):
方法可以将自定义默认的字符串class Book(models.Model): title = ... def __str__(self): return "书名: %s, 出版社: %s, 定价: %s" % (self.title, self.pub, self.price)
-
查询返回指定列(字典表示)
-
方法: values('列1', '列2')
-
用法: MyModel.objects.values(...)
-
作用: 查询部分列的数据并返回
- select 列1,列2 from xxx
-
返回值: QuerySet
- 返回查询结果容器,容器内存字典,每个字典代表一条数据,
- 格式为:
-
示例:
from bookstore import models books = models.Book.objects.values("title", "pub") for book in books: print("书名", book["title"], '出版社:', book['pub']) print("book=", book)
-
-
查询返回指定列(元组表示)
-
方法:values_list('列1','列2')
-
用法:MyModel.objects.values_list(...)
-
作用:
- 返回元组形式的查询结果
-
返回值: QuerySet容器对象,内部存放
元组
- 会将查询出来的数据封装到元组中,再封装到查询集合QuerySet中
-
示例:
from bookstore import models books = models.Book.objects.values_list("title", "pub") for book in books: print("book=", book) # ('Python', '清华大学出版社')...
-
-
排序查询
-
方法:order_by
-
用法:MyModel.objects.order_by('-列','列')
-
作用:
- 与all()方法不同,它会用SQL 语句的ORDER BY 子句对查询结果进行根据某个字段选择性的进行排序
-
说明:
-
默认是按照升序排序,降序排序则需要在列前增加'-'表示
-
示例:
from bookstore import models books = models.Book.objects.order_by("price") for book in books: print("书名:", book.title, '定价:', book.price)
-
-
根据条件查询多条记录
-
方法: filter(条件)
-
语法:
MyModel.objects.filter(属性1=值1, 属性2=值2)
-
返回值:
- QuerySet容器对象,内部存放 MyModel 实例
-
说明:
- 当多个属性在一起时为"与"关系,即当
Books.objects.filter(price=20, pub="清华大学出版社")
返回定价为20且
出版社为"清华大学出版社"的全部图书
- 当多个属性在一起时为"与"关系,即当
-
示例:
# 查询书中出版社为"清华大学出版社"的图书 from bookstore import models books = models.Book.objects.filter(pub="清华大学出版社") for book in books: print("书名:", book.title)
2. 查询Author实体中id为1并且isActive为True的
- authors=Author.objects.filter(id=1,isActive=True)
-
字段查找
-
字段查询是指如何指定SQL语句中 WHERE 子句的内容。
-
字段查询需要通过QuerySet的filter(), exclude() and get()的关键字参数指定。
-
非等值条件的构建,需要使用字段查询
-
示例:
# 查询作者中年龄大于30 Author.objects.filter(age__gt=30) # 对应 # SELECT .... WHERE AGE > 30;
查询谓词
- 每一个查询谓词是一个独立的查询功能
-
__exact
: 等值匹配Author.objects.filter(id__exact=1) Author.objects.filter(id=1) # 等同于select * from author where id = 1
-
__contains
: 包含指定值Author.objects.filter(name__contains='w') # 等同于 select * from author where name like '%w%'
-
__startswith
: 以 w 开始 like w% -
__endswith
: 以 w 结束 like %w -
__gt
: 大于指定值Author.objects.filer(age__gt=50) # 等同于 select * from author where age > 50
-
__gte
: 大于等于 -
__lt
: 小于 -
__lte
: 小于等于 -
__in
: 查找数据是否在指定范围内- 示例
Author.objects.filter(country__in=['中国','日本','韩国']) # 等同于 select * from author where country in ('中国','日本','韩国')
-
__range
: 查找数据是否在指定的区间范围内# 查找年龄在某一区间内的所有作者 Author.objects.filter(age__range=(35,50)) # 等同于 SELECT ... WHERE Author BETWEEN 35 and 50;
-
详细内容参见: https://docs.djangoproject.com/en/3.0/ref/models/querysets/#field-lookups
-
示例
MyModel.objects.filter(id__gt=4) # 等同于 SELECT ... WHERE id > 4;
-
练习:
- 查询Book表中price大于等于50的信息
- 查询Author表中姓王的人的信息
- 查询Author表中Email中包含"w"的人的信息
-
不等的条件筛选
-
语法: MyModel.objects.exclude(条件)
-
作用:
- 返回不包含此
条件
的 全部的数据集
- 返回不包含此
-
示例:
- 查询
清华大学出版社,定价大于50
以外的全部图书
books = models.Book.objects.exclude(pub="清华大学出版社", price__gt=50) for book in books: print(book)
- 查询
-
-
查询指定的一条数据
-
语法: MyModel.objects.get(条件)
-
作用:
- 返回满足条件的唯一一条数据
-
返回值:
- MyModel 对象
-
说明:
- 该方法只能返回一条数据
- 查询结果多余一条数据则抛出,Model.MultipleObjectsReturned异常
- 查询结果如果没有数据则抛出Model.DoesNotExist异常
-
示例:
from bookstore import models book = models.Book.objects.get(id=1) print(book.title)
-
修改数据记录
-
修改单个实体的某些字段值的步骤:
- 查
- 通过 get() 得到要修改的实体对象
- 改
- 通过 对象.属性 的方式修改数据
- 保存
- 通过 对象.save() 保存数据
-
如:
from bookstore import models abook = models.Book.objects.get(id=10) abook.market_price = "10.5" abook.save()
- 查
-
通过 QuerySet 批量修改 对应的全部字段
-
直接调用QuerySet的update(属性=值) 实现批量修改
-
如:
# 将 id大于3的所有图书价格定为0元 books = Book.objects.filter(id__gt=3) books.update(price=0) # 将所有书的零售价定为100元 books = Book.objects.all() books.update(market_price=100)
-
练习:修改图书得零售价
路由: /bookstore/mod/5
删除记录
- 删除记录是指删除数据库中的一条或多条记录
- 删除单个MyModel对象或删除一个查询结果集(QuerySet)中的全部对象都是调用 delete()方法
-
删除单个对象
-
步骤
- 查找查询结果对应的一个数据对象
- 调用这个数据对象的delete()方法实现删除
-
示例:
try: auth = Author.objects.get(id=1) auth.delete() except: print(删除失败)
-
-
删除查询结果集
-
步骤
- 查找查询结果集中满足条件的全部QuerySet查询集合对象
- 调用查询集合对象的delete()方法实现删除
-
示例:
# 删除全部作者中,年龄大于65的全部信息 auths = Author.objects.filter(age__gt=65) auths.delete()
-
聚合查询
- 聚合查询是指对一个数据表中的一个字段的数据进行部分或全部进行统计查询,查bookstore_book数据表中的全部书的平均价格,查询所有书的总个数等,都要使用聚合查询
-
不带分组聚合
-
不带分组的聚合查询是指导将全部数据进行集中统计查询
-
聚合函数:
- 定义模块:
django.db.models
- 用法:
from django.db.models import *
- 聚合函数:
- Sum, Avg, Count, Max, Min
- 定义模块:
-
语法:
- MyModel.objects.aggregate(结果变量名=聚合函数('列'))
-
返回结果:
- 由 结果变量名和值组成的字典
- 格式为:
- `
-
示例:
# 得到所有书的平均价格 from bookstore import models from django.db.models import Count result = models.Book.objects.aggregate(myAvg=Avg('price')) print("平均价格是:", result['myAvg']) print("result=", result) # {"myAvg": 58.2} # 得到数据表里有多少本书 from django.db.models import Count result = models.Book.objects.aggregate(mycnt=Count('title')) print("数据记录总个数是:", result['mycnt']) print("result=", result) # {"mycnt": 10}
-
-
分组聚合
-
分组聚合是指通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
-
语法:
- QuerySet.annotate(结果变量名=聚合函数('列'))
-
用法步骤:
-
通过先用查询结果MyModel.objects.value. 查找查询要分组聚合的列
-
MyModel.objects.value('列1', '列2')
-
如:
pub_set = models.Book.objects.values('pub') print(books) # <QuerySet [{'pub': '清华大学出版社'}, {'pub': '清华大学出版社'}, {'pub_hou {'pub': '机械工业出版社'}, {'pub': '清华大学出版社'}]>
-
-
通过返回结果的 QuerySet.annotate 方法分组聚合得到分组结果
-
QuerySet.annotate(名=聚合函数('列'))
-
返回 QuerySet 结果集,内部存储结果的字典
-
如:
pub_count_set = pub_set.annotate(myCount=Count('pub')) print(pub_count_set) # <QuerySet [{'pub': '清华大学出版社', 'myCount': 7}, {'pub': '机械工业出版社', 'myCount': 3}]>
.values('查询列名')
-
-
-
示例:
- 得到哪儿个出版社共出版多少本书
def test_annotate(request): from django.db.models import Count from . import models # 得到所有出版社的查询集合QuerySet pub_set = models.Book.objects.values('pub') # 根据出版社查询分组,出版社和Count的分组聚合查询集合 pub_count_set = pub_set.annotate(myCount=Count('pub')) # 返回查询集合 for item in pub_count_set: print("出版社:", item['pub'], "图书有:", item['myCount']) return HttpResponse('请查看服务器端控制台获取结果')
-
F对象
- 一个F对象代表数据库中某条记录的字段的信息
-
作用:
- 通常是对数据库中的字段值在不获取的情况下进行操作
- 用于类属性(字段)之间的比较。
-
用法
- F对象在数据包 django.db.models 中,使用时需要先导入
from django.db.models import F
- F对象在数据包 django.db.models 中,使用时需要先导入
-
语法:
from django.db.models import F F('列名')
-
说明:
- 一个 F() 对象代表了一个model的字段的值
- F对象通常是对数据库中的字段值在不加载到内存中的情况下直接在数据库服务器端进行操作
-
示例1
- 更新Book实例中所有的零售价涨10元
models.Book.objects.all().update(market_price=F('market_price')+10) #好于如下代码 books = models.Book.objects.all() for book in books: book.update(market_price=book.marget_price+10) book.save()
-
示例2
- 对数据库中两个字段的值进行比较,列出哪儿些书的零售价高于定价?
from django.db.models import F from bookstore import models books = models.Book.objects.filter(market_price__gt=F('price')) for book in books: print(book.title, '定价:', book.price, '现价:', book.market_price)
Q对象
-
当在获取查询结果集 使用复杂的逻辑或
|
、 逻辑非~
等操作时可以借助于 Q对象进行操作 -
如: 想找出定价低于20元 或 清华大学出版社的全部书,可以写成
models.Book.objects.filter(Q(price__lt=20)|Q(pub="清华大学出版社"))
-
Q对象在 数据包 django.db.models 中。需要先导入再使用
-
from django.db.models import Q
-
-
作用
- 在条件中用来实现除 and(&) 以外的 or(|) 或 not(~) 操作
-
运算符:
- & 与操作
- | 或操作
- 〜 非操作
-
语法
from django.db.models import Q Q(条件1)|Q(条件2) # 条件1成立或条件2成立 Q(条件1)&Q(条件2) # 条件1和条件2同时成立 Q(条件1)&~Q(条件2) # 条件1成立且条件2不成立 ...
-
示例
from django.db.models import Q # 查找清华大学出版社的书或价格低于50的书 models.Book.objects.filter(Q(market_price__lt=50) | Q(pub_house='清华大学出版社')) # 查找不是机械工业出版社的书且价格低于50的书 models.Book.objects.filter(Q(market_price__lt=50) & ~Q(pub_house='机械工业出版社'))
原生的数据库操作方法
-
使用MyModel.objects.raw()进行 数据库查询操作查询
- 在django中,可以使用模型管理器的raw方法来执行select语句进行数据查询
- 语法:
MyModel.objects.raw(sql语句)
- 用法
MyModel.objects.raw('sql语句')
- 返回值:
- QuerySet 集合对象
-
示例
books = models.Book.objects.raw('select * from bookstore_book') for book in books: print(book)
-
使用django中的游标cursor对数据库进行 增删改操作
-
在Django中可以使用 如UPDATE,DELETE等SQL语句对数据库进行操作。
-
在Django中使用上述非查询语句必须使用游标进行操作
-
使用步骤:
-
导入cursor所在的包
-
Django中的游标cursor定义在 django.db.connection包中,使用前需要先导入
-
如:
-
from django.db import connection
-
-
-
用创建cursor类的构造函数创建cursor对象,再使用cursor对象,为保证在出现异常时能释放cursor资源,通常使用with语句进行创建操作
-
如:
from django.db import connection with connection.cursor() as cur: cur.execute('执行SQL语句')
-
-
示例
# 用SQL语句将id 为 10的 书的出版社改为 "XXX出版社" from django.db import connection with connection.cursor() as cur: cur.execute('update bookstore_book set pub_house="XXX出版社" where id=10;') with connection.cursor() as cur: # 删除 id为1的一条记录 cur.execute('delete from bookstore_book where id=10;')
-
-
第五章 admin 后台数据库管理
-
django 提供了比较完善的后台管理数据库的接口,可供开发过程中调用和测试使用
-
django 会搜集所有已注册的模型类,为这些模型类提拱数据管理界面,供开发者使用
-
使用步骤:
-
创建后台管理帐号:
-
后台管理--创建管理员帐号
$ python3 manage.py createsuperuser
- 根据提示完成注册,参考如下:
$ python3 manage.py createsuperuser Username (leave blank to use 'tarena'): tarena # 此处输入用户名 Email address: laowei@tedu.cn # 此处输入邮箱 Password: # 此处输入密码(密码要复杂些,否则会提示密码太简单) Password (again): # 再次输入重复密码 Superuser created successfully.
-
-
用注册的帐号登陆后台管理界面
- 后台管理的登录地址:
-
5.1 自定义后台管理数据表
-
若要自己定义的模型类也能在
/admin
后台管理界中显示和管理,需要将自己的类注册到后台管理界面 -
添加自己定义模型类的后台管理数据表的,需要用
admin.site.register(自定义模型类)
方法进行注册-
配置步骤如下:
-
在应用app中的admin.py中导入注册要管理的模型models类, 如:
from . import models
-
调用 admin.site.register 方法进行注册,如:
from django.contrib import admin admin.site.register(自定义模型类)
-
-
如: 在 bookstore/admin.py 添加如下代码对Book类进行管理
-
示例:
# file: bookstore/admin.py from django.contrib import admin # Register your models here. from . import models ... admin.site.register(models.Book) # 将Book类注册为可管理页面
-
5.2 修改后台Models的展现形式
-
在admin后台管理数据库中对自定义的数据记录都展示为
XXXX object
类型的记录,不便于阅读和判断 -
在用户自定义的模型类中可以重写
def __str__(self):
方法解决显示问题,如:- 在 自定义模型类中重写 str(self) 方法返回显示文字内容:
class Book(models.Model): ... def __str__(self): return "书名" + self.title
5.3 模型管理器类
-
作用:
- 为后台管理界面添加便于操作的新功能。
-
说明:
- 后台管理器类须继承自
django.contrib.admin
里的ModelAdmin
类
- 后台管理器类须继承自
-
模型管理器的使用方法:
-
在
<应用app>/admin.py
里定义模型管理器类class XXXX_Manager(admin.ModelAdmin): ......
-
注册管理器与模型类关联
from django.contrib import admin from . import models admin.site.register(models.YYYY, XXXX_Manager) # 注册models.YYYY 模型类与 管理器类 XXXX_Manager 关联
-
示例:
# file : bookstore/admin.py from django.contrib import admin from . import models class BookAdmin(admin.ModelAdmin): list_display = ['id', 'title', 'price', 'market_price'] admin.site.register(models.Book, BookAdmin)
- 进入http://127.0.0.1:8000/admin/bookstore/book/ 查看显示方式和以前有所不同
-
-
模型管理器类ModelAdmin中实现的高级管理功能
- list_display 去控制哪些字段会显示在Admin 的修改列表页面中。
- list_display_links 可以控制list_display中的字段是否应该链接到对象的“更改”页面。
- list_filter 设置激活Admin 修改列表页面右侧栏中的过滤器
- search_fields 设置启用Admin 更改列表页面上的搜索框。
- list_editable 设置为模型上的字段名称列表,这将允许在更改列表页面上进行编辑。
- 其它参见https://docs.djangoproject.com/en/3.0/ref/contrib/admin/
数据库表管理
-
修改模型类字段的显示名字
-
模型类各字段的第一个参数为 verbose_name,此字段显示的名字会在后台数据库管理页面显示
-
通过 verbose_name 字段选项,修改显示名称示例如下:
title = models.CharField( max_length = 30, verbose_name='显示名称' )
-
-
通过Meta内嵌类 定义模型类的属性及展现形式
-
模型类可以通过定义内部类class Meta 来重新定义当前模型类和数据表的一些属性信息
-
用法格式如下:
class Book(models.Model): title = CharField(....) class Meta: 1. db_table = '数据表名' - 该模型所用的数据表的名称。(设置完成后需要立马更新同步数据库) python3 manage.py makemigrations python3 manage.py migrate 2. verbose_name = '单数名' - 给模型对象的一个易于理解的名称(单数),用于显示在/admin管理界面中 3. verbose_name_plural = '复数名' - 该对象复数形式的名称(复数),用于显示在/admin管理界面中
-
- 练习:
- 将Book模型类 和 Author 模型类都加入后台管理
- 制作一个AuthorManager管理器类,让后台管理Authors列表中显示作者的ID、姓名、年龄信息
- 用后台管理程序 添加三条 Author 记录
- 修改其中一条记录的年龄 - Author
- 删除最后一条添加的记录 - Author
- 将bookstore_author 表名称改为myauthor (需要重新迁移数据库)
第六章 数据表关联关系映射
- 在关系型数据库中,通常不会把所有数据都放在同一张表中,这样做会额外占用内存空间,
- 在关系列数据库中通常用表关联来解决数据库。
- 常用的表关联方式有三种:
- 一对一映射
- 如: 一个身份证对应一个人
- 一对多映射 A(一) B(多)
- 如: 一个班级可以有多个学生
- 多对多映射
- 如: 一个学生可以报多个课程,一个课程可以有多个学生学习
- 一对一映射
6.1 一对一映射
- 一对一是表示现实事物间存在的一对一的对应关系。
- 如:一个家庭只有一个户主,一个男人有一个妻子,一个人有一个唯一的指纹信息等
-
语法
class A(model.Model): ... class B(model.Model): 属性 = models.OneToOneField(A)
-
用法示例
-
创建作家和作家妻子类
# file : xxxxxxxx/models.py from django.db import models class Author(models.Model): '''作家模型类''' name = models.CharField('作家', max_length=50) class Wife(models.Model): '''作家妻子模型类''' name = models.CharField("妻子", max_length=50) author = models.OneToOneField(Author) # 增加一对一属性
-
查询
- 在 Wife 对象中,通过 author 属性找到对应的author对象
- 在 Author 对象中,通过 wife 属性找到对应的wife对象
-
创始一对一的数据记录
from . import models author1 = models.Author.objects.create(name='王老师') wife1 = models.Wife.objects.create(name='王夫人', author=author1) # 关联王老师 author2 = models.Author.objects.create(name='小泽老师') # 一对一可以没有数据对应的数据
-
一对一数据的相互获取
-
正向查询
- 直接通过关联属性查询即可
# 通过 wife 找 author from . import models wife = models.Wife.objects.get(name='王夫人') print(wife.name, '的老公是', wife.author.name)
-
反向查询
- 通过反向关联属性查询
- 反向关联属性为
实例对象.引用类名(小写)
,如作家的反向引用为作家对象.wife
- 当反向引用不存在时,则会触发异常
# 通过 author.wife 关联属性 找 wife,如果没有对应的wife则触发异常
author1 = models.Author.objects.get(name='王老师') print(author1.name, '的妻子是', author1.wife.name) author2 = models.Author.objects.get(name='小泽老师') try: print(author2.name, '的妻子是', author2.wife.name) except: print(author2.name, '还没有妻子')
-
-
- 作用:
- 主要是解决常用数据不常用数据的存储问题,把经常加载的一个数据放在主表中,不常用数据放在另一个副表中,这样在访问主表数据时不需要加载副表中的数据以提高访问速度提高效率和节省内存空间,如经常把书的内容和书名建成两张表,因为在网站上经常访问书名等信息,但不需要得到书的内容。
- 练习:
- 创建一个Wife模型类,属性如下
- name
- age
- 在Wife类中增加一对一关联关系,引用 Author
- 同步回数据库并观察结果
- 创建一个Wife模型类,属性如下
6.2 一对多映射
- 一对多是表示现实事物间存在的一对多的对应关系。
- 如:一个学校有多个班级,一个班级有多个学生, 一本图书只能属于一个出版社,一个出版社允许出版多本图书
-
用法语法
- 当一个A类对象可以关联多个B类对象时
class A(model.Model): 一 ... class B(model.Model): 多 属性 = models.ForeignKey(多对一中"一"的模型类, ...)
-
外键类ForeignKey
主表有数据
从表1 CASCADE - 主表删/更新数据,从表也跟着删除/更新数据
从表2 强硬版 - 如果从表有主表想动的数据,则主表不能修改或删除
从表3 SET NULL- 主表删除/更新 , 从表外键变为 NULL
- 构造函数:
```python
ForeignKey(to, on_delete, **options)
```
- 常用参数:
- on_delete
1. models.CASCADE 级联删除。 Django模拟SQL约束ON DELETE CASCADE的行为,并删除包含ForeignKey的对象。
```python
mysql 里面 依旧还是 强硬版
django A主id=1 B从-f_id=1
A想删除 id=1 数据; django先帮您把从表数据删除,再回来删除主表的数据
```
2. models.PROTECT 抛出ProtectedError 以阻止被引用对象的删除;
3. SET_NULL 设置ForeignKey null;需要指定null=True
4. SET_DEFAULT 将ForeignKey设置为其默认值;必须设置ForeignKey的默认值。
5. ... 其它参请参考文档 <https://docs.djangoproject.com/en/3.0/ref/models/fields/#foreignkey> ForeignKey部分
- `**options` 可以是常用的字段选项如:
1. null
2. unique等
3. ...
-
示例
- 有二个出版社对应五本书的情况.
清华大学出版社
有书- C++
- Java
- Python
北京大学出版社
有书- 西游记
- 水浒
-
定义一对多类
# file: one2many/models.py from django.db import models class Publisher(models.Model): '''出版社''' name = models.CharField('名称', max_length=50, unique=True) class Book(models.Model): title = models.CharField('书名', max_length=50) publisher = models.ForeignKey(Publisher, null=True)
-
创建一对多的对象
# file: xxxxx/views.py from . import models pub1 = models.Publisher.objects.create(name='清华大学出版社') models.Book.objects.create(title='C++', publisher=pub1) models.Book.objects.create(title='Java', publisher=pub1) models.Book.objects.create(title='Python', publisher=pub1) pub2 = models.Publisher.objects.create(name='北京大学出版社') models.Book.objects.create(title='西游记', publisher=pub2) models.Book.objects.create(title='水浒', publisher=pub2)
-
查询:
- 通过多查一
# 通过一本书找到对应的出版社 abook = models.Book.objects.get(id=1) print(abook.title, '的出版社是:', abook.publisher.name)
- 通过一查多
# 通过出版社查询对应的书 pub1 = models.Publisher.objects.get(name='清华大学出版社') books = pub1.book_set.all() # 通过book_set 获取pub1对应的多个Book数据对象 # books = models.Book.objects.filter(publisher=pub1) # 也可以采用此方式获取 print("清华大学出版社的书有:") for book in books: print(book.title)
- 有二个出版社对应五本书的情况.
- 练习:
- 完成Book 和 Publisher 之间的一对多
- 查看数据库效果
- 登录到后台,查看Book实体
-
数据查询
-
通过 Book 查询 Publisher
通过 publisher 属性查询即可 练习: 查询 西游记 对应的出版社信息,打印在终端上
-
通过 Publisher 查询 对应的所有的 Books
Django会在Publisher中增加一个属性来表示对对应的Book们的查询引用 属性:book_set(MyModel.objects)
-
6.3 多对多映射
- 多对多表达对象之间多对多复杂关系,如: 每个人都有不同的学校(小学,初中,高中,...),每个学校都有不同的学生...
-
语法
- 在关联的两个类中的任意一个类中,增加:
属性 = models.ManyToManyField(MyModel)
-
示例
- 一个作者可以出版多本图书
- 一本图书可以被多名作者同时编写
class Author(models.Model): ... class Book(models.Model): ... authors = models.ManyToManyField(Author)
-
数据查询
-
通过 Book 查询对应的所有的 Authors
book.authors.all() -> 获取 book 对应的所有的author的信息 book.authors.filter(age__gt=80) -> 获取book对应的作者中年龄大于80岁的作者的信息
-
通过 Author 查询对应的所有的Books
- Django会生成一个关联属性 book_set 用于表示对对应的book的查询对象相关操作
author.book_set.all() author.book_set.filter() author.book_set.create(...) # 创建新书并联作用author author.book_set.add(book) # 添加已有的书为当前作者author author.book_set.clear() # 删除author所有并联的书
-
-
示例:
- 多对多模型
class Author(models.Model): '''作家模型类''' name = models.CharField('作家', max_length=50) def __str__(self): return self.name class Book(models.Model): title = models.CharField('书名', max_length=50) author = models.ManyToManyField(Author) def __str__(self): return self.title
- 多对多视图操作
from django.http import HttpResponse from . import models def many2many_init(request): # 创建两人个作者 author1 = models.Author.objects.create(name='吕泽') author2 = models.Author.objects.create(name='王老师') # 吕择和王老师同时写了一本Python book11 = author1.book_set.create(title="Python") author2.book_set.add(book11) # # 王老师还写了两本书 book21 = author2.book_set.create(title="C") # 创建一本新书"C" book22 = author2.book_set.create(title="C++") # 创建一本新书"C++" return HttpResponse("初始化成功") def show_many2many(request): authors = models.Author.objects.all() for auth in authors: print("作者:", auth.name, '发出版了', auth.book_set.count(), '本书: ') for book in books: print(' ', book.title) print("----显示书和作者的关系----") books = models.Book.objects.all() for book in books: auths = book.author.all() print(book.title, '的作者是:', '、'.join([str(x.name) for x in auths])) return HttpResponse("显示成功,请查看服务器端控制台终端")
- 多对多最终的SQL结果
mysql> select * from many2many_author; +----+-----------+ | id | name | +----+-----------+ | 11 | 吕泽 | | 12 | 王老师 | +----+-----------+ 2 rows in set (0.00 sec) mysql> select * from many2many_book; +----+--------+ | id | title | +----+--------+ | 13 | Python | | 14 | C | | 15 | C++ | +----+--------+ 3 rows in set (0.00 sec) mysql> select * from many2many_book_author; +----+---------+-----------+ | id | book_id | author_id | +----+---------+-----------+ | 17 | 13 | 11 | | 20 | 13 | 12 | | 18 | 14 | 12 | | 19 | 15 | 12 | +----+---------+-----------+ 4 rows in set (0.00 sec)
第七章 session 会话
7.1 cookies
-
cookies是保存在客户端浏览器上的存储空间,通常用来记录浏览器端自己的信息和当前连接的确认信息
-
cookies 在浏览器上是以键-值对的形式进行存储的,键和值都是以ASCII字符串的形存储(不能是中文字符串)
-
cookies 的内部的数据会在每次访问此网址时都会携带到服务器端,如果cookies过大会降低响应速度
-
在Django 服务器端来设置 设置浏览器的COOKIE 必须通过 HttpResponse 对象来完成
-
HttpResponse 关于COOKIE的方法
- 添加、修改COOKIE
- HttpResponse.set_cookie(key, value='', max_age=None, expires=None)
- key:cookie的名字
- value:cookie的值
- max_age:cookie存活时间,秒为单位
- expires:具体过期时间
- 当不指定max_age和expires 时,关闭浏览器时此数据失效
- HttpResponse.set_cookie(key, value='', max_age=None, expires=None)
- 删除COOKIE
- HttpResponse.delete_cookie(key)
- 删除指定的key 的Cookie。 如果key 不存在则什么也不发生。
- 添加、修改COOKIE
-
Django中的cookies
-
使用 响应对象HttpResponse 等 将cookie保存进客户端
-
方法1
from django.http import HttpResponse resp = HttpResponse() resp.set_cookie('cookies名', cookies值, 超期时间)
- 如:
resp = HttpResponse() resp.set_cookie('myvar', "weimz", 超期时间)
-
方法二, 使用render对象
from django.shortcuts import render resp = render(request,'xxx.html',locals()) resp.set_cookie('cookies名', cookies值, 超期时间)
-
-
获取cookie
-
通过 request.COOKIES 绑定的字典(dict) 获取客户端的 COOKIES数据
value = request.COOKIES.get('cookies名', '没有值!') print("cookies名 = ", value)
-
-
注:
- Chrome 浏览器 可能通过开发者工具的
Application
>>Storage
>>Cookies
查看和操作浏览器端所有的 Cookies 值
- Chrome 浏览器 可能通过开发者工具的
-
-
cookies 示例
-
以下示例均在视图函数中调用
-
添加cookie
# 为浏览器添加键为 my_var1,值为123,过期时间为1个小时的cookie responds = HttpResponse("已添加 my_var1,值为123") responds.set_cookie('my_var1', 123, 3600) return responds
-
修改cookie
# 为浏览器添加键为 my_var1,修改值为456,过期时间为2个小时的cookie responds = HttpResponse("已修改 my_var1,值为456") responds.set_cookie('my_var1', 456, 3600*2) return responds
-
删除cookie
# 删除浏览器键为 my_var1的cookie responds = HttpResponse("已删除 my_var1") responds.delete_cookie('my_var1') return responds
-
获取cookie
# 获取浏览器中 my_var变量对应的值 value = request.COOKIES.get('my_var1', '没有值!') print("cookie my_var1 = ", value) return HttpResponse("my_var1:" + value)
-
-
综合练习:
- 实现用户注册功能,界面如下:
- 注册界面
- 实现用户注册功能,界面如下:
-
-
要求 :
- 创建一个 user 应用 实现注册逻辑,如:
python3 manage.py startapp user
- 如果用户注册成功,则用当前浏览器的cookies记录当前成功注册的用户名 [cookies存储一天]
- 注册时如果用户输入数据合法,则在数据库记中记录用户的用户名密码等数据
- 创建一个 user 应用 实现注册逻辑,如:
-
模型类
-
用户模型类
class User(models.Model): username = models.CharField("用户名", max_length=30, unique=True) password = models.CharField("密码", max_length=30) def __str__(self): return "用户" + self.username
-
-
登陆设计规范(在user应用中写代码)
路由正则 视图函数 模板位置 说明 /user/reg def reg_view(request): templates/user/register.html 用户注册
-
作用:
1, 保持会话状态【保持用户登录状态】
2, 购物车 - 未登录状态下的购物车记录
3, 搜索引擎 - 记录当前您的搜索记录
缺点:
1,数据存储在客户/用户端
2,每次请求网站,自动提交当前网站的cookie
3,网络带宽
7.2 session 会话控制
-
什么是session
-
session又名会话控制,是在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据
-
session的起源
- http协议是无状态的:每次请求都是一次新的请求,不会记得之前通信的状态
- 实现状态保持的方式:在客户端或服务器端存储与会话有关的数据
- 推荐使用sesison方式,所有数据存储在服务器端
-
实现方式
- 使用 session 需要在浏览器客户端启动 cookie,且用在cookie中存储sessionid
- 每个客户端都可以在服务器端有一个独立的Session
- 注意:不同的请求者之间不会共享这个数据,与请求者一一对应
-
Django启用Session
-
在 settings.py 文件中
-
向 INSTALLED_APPS 列表中添加:
INSTALLED_APPS = [ # 启用 sessions 应用 'django.contrib.sessions', ]
-
向 MIDDLEWARE_CLASSES 列表中添加:
MIDDLEWARE = [ # 启用 Session 中间件 'django.contrib.sessions.middleware.SessionMiddleware', ]
-
-
session的基本操作:
- session对于象是一个在似于字典的SessionStore类型的对象, 可以用类拟于字典的方式进行操作
- session 只能够存储能够序列化的数据,如字典,列表等。
- 保存 session 的值到服务器
request.session['KEY'] = VALUE
- 获取session的值
VALUE = request.session['KEY']
VALUE = request.session.get('KEY', 缺省值)
- 删除session的值
del request.session['KEY']
- 在 settings.py 中有关 session 的设置
- SESSION_COOKIE_AGE
- 作用: 指定sessionid在cookies中的保存时长(默认是2周),如下:
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
- SESSION_EXPIRE_AT_BROWSER_CLOSE = True 设置只要浏览器关闭时,session就失效(默认为False)
- SESSION_COOKIE_AGE
- session 缺省配置
- 模块
import django.conf.global_settings
- 模块
-
注: 当使用session时需要迁移数据库,否则会出现错误
$ python3 manage.py makemigrations
$ python3 manage.py migrate
作用
存储会话状态-登录状态
缺点
单表【点】问题
Cookies 对比 Session
1,存储位置不一样
2,存储大小不一样
3,Cookies可自动提交
创建项目 mysite6 绑定数据库 mysite6
创建应用 index
启用分布式路由 浏览器访问127.0.0.1:8000/index/set_cookies 视图函数输出return 'set ok'
session 单表问题 -> django所有session数据存储在单个表中,表名为django_session; 并且该表没有自动回收【过期的session】,可执行python3 manage.py clearsessions 命令进行 过期session的删除
第八章 中间件
8.1 定义
- 中间件是Django请求/响应处理的钩子框架。它是一个轻量级的、低级的"插件"系统,用于全局改变Django的输入或者输出在这里插入图片描述
- 中间件以类的形式体现
- 每个中间件组件负责做一些特定的功能。例如:Django包含一个中间件组件AuthenticationMiddleware,它使用会话将用户与请求关联起来
8.2 中间件规则
中间件类必须继承自django.utils.deprecation.MiddlewareMixin类
中间件类须实现下列五个方法中的一个或多个:
process_request(self, request)
执行路由之前被调用,在每个请求上调用,返回Node或HttpResponse对象
返回Node说明请求可以继续往下走
返回HttpResponse说明请求终止
process_view(self, request, callback, callback_args, callback_kwargs)
调用视图之前被调用,在每个请求上调用,返回Node或HttpResponse对象
process_response(self, request, response)
所有响应返回浏览器被调用,在每个请求上调用,返回HttpResponse对象
process_exception(self, request, exception)
当处理过程中抛出异常时调用,返回HttpResponse对象
统一异常处理
process_template_response(self, request, response)
在视图函数执行完毕且试图返回的对象中包含render方法时被调用,该方法需要返回实现了render方法的响应对象
【注】中间件中大多数方法在返回None时表示忽略当前操作进入下一项事,当返回HttpResponse对象时表示此请求结束,直接返回给客户端
8.3 新建中间件
项目目录下,新建一个middleware文件夹,新建middleware.py文件。
from django.utils.deprecation import MiddlewareMixin
class MyMW(MiddlewareMixin):
def process_request(self, request):
print("MyMW process_request..")
def process_view(self, request, callback, callback_args, callback_kwargs):
print("MyMW process_view..")
def process_response(self, request, response):
print("MyMW process_response..")
return response
setting.py中需要注册一下自定义的中间件
# file : settings.py
MIDDLEWARE = [
...
]
使用中间件:
8.4 中间件执行顺序
两个中间件
class MyMW(MiddlewareMixin):
def process_request(self, request):
print('MyMW process_request do ---')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('MyMW process_views do ---')
def process_response(self, request, response):
print('MyMW process_response do ---')
return response
class MyMW2(MiddlewareMixin):
def process_request(self, request):
print('MyMW2 process_request do ---')
def process_view(self, request, callback, callback_args, callback_kwargs):
print('MyMW2 process_views do ---')
def process_response(self, request, response):
print('MyMW2 process_response do ---')
return response
注册中间件:
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',
'middleware.mymiddleware.MyMW',
'middleware.mymiddleware.MyMW2',
]
访问效果:
(洋葱):m1–>m2–view—>m2—>m1
注意:配置为数组,中间件被调用时,以’先上到下’ 再 ’先下到上’的顺序调用
8.5 练习
用中间件实现强制某个IP地址只能向/test开头的地址发送5次请求
提示:
request.META['REMOTE_ADDR']
可以得到远程客户端的IP地址request.path_info
可以得到客户端访问的请求路由信息
class VisitLimit(MiddlewareMixin):
visit_times = {}
def process_request(self, request):
ip_address = request.META['REMOTE_ADDR']
path_url = request.path_info
if not re.match('^/test', path_url):
return None
times = self.visit_times.get(ip_address, 0)
print('ip', ip_address, '已经访问', times, '次')
self.visit_times[ip_address] = times + 1
if times < 5:
return None
return HttpResponse('您已经访问过' + str(times) + '次,访问被禁止')
8.6 中间件执行总流程
8.7 跨站请求伪造保护 CSRF
8.7.1 定义
CSRF —— 跨站伪造请求攻击
某些恶意网站上包含链接、表单按钮或者JavaScript,他们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作。这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)
利用Cookies中保存了登录状态且自动提交
8.7.2 CSRF防范
- django采用比对暗号机制防范攻击
- Cookies中存储暗号1,模板中表单里藏着暗号2,用户只有在本网站下提交数据,暗号2才会随表单提交给服务器,django对比两个暗号,对比成功,则认为是合法请求,都则是违法请求—403响应码
8.7.3 配置步骤
setting.py中确认MIDDLEWARE中django.middleware.csrf.CsrfViewMiddleware是否打开
模板中,form标签下添加如下标签
{% csrf_token %}
如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
样例:
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
return HttpResponse('hello world')
第九章 分页
9.1 定义
分页是指在web页面有大量数据需要显示,为了阅读方便在每个页面中只显示部分数据
优点
- 方便阅读
- 减少数据提取量,减轻服务器压力
Django提供了Paginator类可以方便的实现分页功能
Paginator类位于django.core.paginator模块中
9.2 Paginator对象
负责分页数据整体的管理
对象的构造方法
paginator = Paginator(object_list, per_page)
参数
- object_list:需要分类数据的对象列表
- per_page:每页数据个数
返回值
- Paginator对象
Paginator属性
- count:需要分页数据的对象总数
- num_pages:分页后的页面总数
- page_range:从1开始的range对象,用于记录当前页码数
- per_page:每页数据的个数
Paginator方法
paginator对象.page(number)
- 参数number为页码信息(从1开始)
- 返回当前number页对应的页信息
- 如果提供的页码不存在,抛出InvalidPage异常
InvalidPage:总的异常基类,包含以下两个异常子类
- PageNotAnInteger:当向page()传入一个不是整数的值时抛出
- EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出
9.3 Page对象
负责具体某一页的数据管理
创建对象:
- Paginator对象的page()方法返回Page对象
- page = paginator.page(页码)
Page对象属性:
- pbject_list:当前页上所有数据对象的列表
- number:当前页的序号,从1开始
- paginator:当前page对象相关的Paginator对象
Page对象方法:
- has_next():如果有下一页返回True
- has_previous():如果有上一页返回True
- has_other_pages():如果有上一页或下一页返回True
- next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
- previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
9.4 测试
视图函数:
def test_page(request):
# /test_page/4
# /test_page?page=1
page_num = request.GET.get('page', 1)
all_data = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']
# 初始化paginator
paginator = Paginator(all_data, 3)
# 初始化 具体页码对应page对象
c_page = paginator.page(int(page_num))
return render(request, 'test_page.html', locals())
路由:
from django.urls import path
from . import views
urlpatterns = [
path('test_page', views.test_page),
]
界面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页</title>
</head>
<body>
{% for p in c_page %}
<p>{{ p }}</p>
{% endfor %}
{% if c_page.has_previous %}
<a href="/test_page?page={{ previous_page_number }}">上一页</a>
{% else %}
上一页
{% endif %}
{% for p_num in paginator.page_range %}
{% if p_num == c_page.number %}
{{ p_num }}
{% else %}
<a href="/test_page?page={{ p_num }}">{{ p_num }}</a>
{% endif %}
{% endfor %}
{% if c_page.has_next %}
<a href="/test_page?page={{ next_page_number }}">下一页</a>
{% else %}
下一页
{% endif %}
</body>
</html>
效果:
第十章 生成csv文件
10.1 csv定义
逗号分隔值(Comma-Separated Values,CSV,有时也成为字符分隔值,因为分割字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)
说明:可被常见制表工具,如excel等直接进行读取
10.2 python中生成csv文件
Python提供了内建库 - csv;可直接通过该库操作csv文件
案例如下:
import csv
with open('eggs.csv', 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['a', 'b', 'c'])
10.3 csv文件下载
在网站中,实现下载csv,注意如下:
- 相应Content-Type类型需要修改为text/csv 这是告诉浏览器该文档是csv文件,而不是HTML文件
- 响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名 它将被浏览器用于开启"另存问…"对话框
import csv
from django.http import HttpResponse
from .models import Book
def make_csv_view(request):
# 定义响应对象
response = HttpResponse(content_type='text/csv')
# 添加Content-Disposition标头
response['Content-Disposition'] = 'attachment;filename="mybook.csv"'
# 查找数据
all_book = Book.object.all()
# 响应对象传递给writer,往response中写
writer = csv.writer(response)
# 写表头
writer.writerow(['id','title'])
# 写数据
for book in all_book:
writer.writerow([book.id, book.title])
# 返回响应
return response
第十一章 内建用户系统
11.1 定义
- Django带有一个用户认证系统。它处理用户账号、组、权限以及基于cookie的用户会话
- 用户可以直接使用Django自带的用户表
- 官方文档:https://docs.djangoproject.com/en/3.2/topics/auth/
11.2 基本字段
模型类位置 from django.contrib.auth.models import User
字段 | 含义 |
---|---|
username | 用户名 |
password | 密码 |
邮箱 | |
first_name | 名 |
last_name | 姓 |
is_superuser | 是否是管理员账号(/admin) |
is_staff | 是否可以访问admin管理界面 |
is_active | 是否是活跃用户,默认True 一般不删除用户,而是将用户的is_active设为False |
last_login | 上一次的登陆时间 |
date_joined | 用户创建时间 |
11.3 创建用户
11.3.1 创建普通用户
from django.contrib.auth.models import User
user = User.objects.create_user(username='用户名', password='密码', email='邮箱', ...)
11.3.2 创建超级用户
from django.contrib.auth.models import User
user = User.objects.create_superuser(username='用户名', password='密码', email='邮箱', ...)
11.4 删除用户
from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.is_active = False # 记录当前用户无效
user.save()
print("删除普通用户成功!")
except:
print("删除普通用户失败!")
11.5 校验密码
from django.contrib.auth import authenticate
user = authenticate(username=username, password=password)
说明:如果用户名密码校验成功则返回对应的user对象,否则返回None
11.6 修改密码
from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.set_password('新密码')
user.save()
print("修改密码成功!")
except:
print("修改密码失败!")
11.7 登录状态保持
from django.contrib.auth import login, authenticate
def login_view(request):
user = authenticate(username=username, password=password)
# 用户存在,记录登录状态
login(request, user)
11.8 登录状态校验
from django.contrib.auth.decorators import login_required
@login_required
def login_view(request):
# 该视图必须为用户登录状态下才可访问
# 当前登录用户可通过request.user获取
login_user = request.user
...
11.9 登录状态取消
from django.contrib.auth import logout
def logout_view(request):
logout(request)
11.10 字段扩展
方案1:通过建立新表,跟内建表做1对1
方案2:继承内建的抽象user模型类
继承内建抽象类
步骤:
- 添加新的应用
- 定义模型类 继承AbstractUser
- setting.py中指明AUTH_USER_MODEL='应用名.类名'
注意:
此操作要在第一次Migrate之前进行
示例 —— user/models.py 添加user应用
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
# 定义新字段
phone = models.CharField(max_length=11, default='')
setting.py中添加配置
AUTH_USER_MODEL = 'user.UserInfo'
添加用户
from user.models import UserInfo
UserInfo.objects.create_user(username='demo', password='123', phone='15611111111')
第十二章 文件上传
12.1 定义
用户可以通过浏览器将图片等文件传至网站
场景:
- 用户上传头像
- 上传流程性的文档[pdf,txt等]
12.2 上传规范
前端【HTML】
- 文件上传必须为POST提交方式
- 表单
- 表单中用标签上传文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form action="/test_upload" method="post" enctype="multipart/form-data">
<p><input type="text" name="title"></p>
<p><input type="file" name="file"></p>
<p><input type="submit" value="上传"></p>
</form>
</body>
</html>
后端【Django】
获取内容
视图函数中,用request.FILES取文件框的内容
file = request.FILES['xxx']
说明:
- FILES的key对应页面中file框的name值
- file绑定文件流对象
- file.name文件名
- file.file文件的字节流数据
配置路径
配置文件的访问路径和存储路径
在setting.py中设置MEDIA相关配置
Django把用户上传的文件,统称为media资源
# file : setting.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
绑定路径
MEDIA_URL和MEDIA_ROOT需要手动绑定
步骤:主路由中添加路由
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
说明:等价于做了MEDIA_URL开头的路由,Django接到该特征请求后去MEDIA_ROOT路径查找资源
12.3 文件写入方案
传统的open方式
@csrf_exempt
def upload_view(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == 'POST':
# 取文件数据
a_file = request.FILES['file']
print("上传文件名是:", a_file.name)
# 拼接存储绝对路径
filename = os.path.join(settings.MEDIA_ROOT, a_file.name)
with open(filename, 'wb') as f:
# a_file.file 文件数据
# a_file.file.read() 读出来
data = a_file.file.read()
f.write(data)
return HttpResponse("接收文件:" + a_file.name + "成功")
优化 - 借助ORM
借助M层,在数据库中搞一个字段,字段不存储文件本身,字段用来存储文件相对路径,
字段:FileFieId(upload = '子目录名')
def test_upload(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
file = request.FILES['file']
Content.objects.create(title=title, file=file)
return HttpResponse("upload is ok")
12.4 测试
视图函数
def test_upload(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
file = request.FILES['file']
Content.objects.create(title=title, picture=file)
return HttpResponse("upload is ok")
界面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传文件</title>
</head>
<body>
<form action="/test_upload" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p><input type="text" name="title"></p>
<p><input type="file" name="file"></p>
<p><input type="submit" value="上传"></p>
</form>
</body>
</html>
路由
path('test_upload', views.test_upload),
上传后,media目录下生成picture文件夹,上传的文件在内部
且数据库字段也保存路径
如果上传同一名称的图片,会自动添加后缀保存
第十三章 Django发送邮件
13.1 业务场景
- 业务警告
- 邮件验证
- 密码找回
13.2 邮件相关协议
SMTP
- SMTP的全程是"Simple Mail Transfer Protocol",即简单邮件传输协议(25号端口)
- 它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转
- 属于"推送"协议
IMAP
- IMAP的全称是"Internet Message Access Protocol",即交互式邮件访问协议,是一个应用层协议(143号端口)
- 用来从本地邮件客户端(Outlook Express、Foxmail、Mozilla Thunderbird等)访问远程服务器上的邮件
- 属于"拉取"协议
POP3
- POP3的全称是"Post Office Protocol 3",即邮局协议的第三个版本,是TCP/IP协议族中的一员(110号端口)
- 本协议主要用于支持使用客户端远程管理在服务器上的电子邮件
- 属于"拉取"协议
IMAP VS POP3
- 两者均为"拉取"型协议,负责从邮件服务器中下载邮件
- IMAP具备摘要浏览功能,可预览部分摘要,再下载整个邮件
- IMAP为双向协议,客户端操作可反馈给服务器
- POP3必须下载全部邮件,无摘要功能
- POP3为单向协议,客户端操作无法同步服务器
13.3 邮件发送过程
13.4 Django发邮件
Django中配置邮件功能,主要为SMTP协议,负责发邮件
原理:
- 给Django授权一个邮箱
- Django用该邮箱给对应收件人发送邮件
- django.core.mail封装了电子邮件的自动发送SMTP协议
13.5 授权步骤 - 以QQ邮箱为例
申请QQ号
用QQ号登录到QQ邮箱并修改设置
用申请到的QQ号和密码登录到https://mail.qq.com/
修改QQ邮箱 → 设置 → 账户 → "POP3/IMAP...服务"
开启服务,获取授权码
13.6 Django配置
from django.core import mail
mail.send_mail(
subject='xx', # 题目
message='xx', # 消息内容
from_email='xxx@qq.com', # 发送者[当前配置邮箱]
recipient_list=['xxx@qq.com'], # 接收者邮件列表
)
测试:进入shell
$ python3 manage.py shell
13.7 练习 - 邮件警告
用中间件实现抓取视图函数的异常,并以发邮件的形式将异常信息发送给指定联系人
- 邮件主题:‘mysite7异常告警’
- 内容:自定义即可,要求带有异常信息
- 收件人要求可灵活配置
定义中间件
class ExceptionMW(MiddlewareMixin):
def process_response(self, request, exception):
mail.send_mail(
subject='mysite7异常告警',
message=traceback.format_exc(),
from_email='xxx@qq.com',
recipient_list=settings.EX_EMAIL
)
return HttpResponse('网站繁忙')
其中,收件人邮箱在setting中配置
EX_EMAIL = ['1032567790@qq.com']
注册中间件
模拟报错
测试
访问http://127.0.0.1:8000/test_upload会发送报错邮件