2005年发布,采用Python语言编写的开源web框架
早期的时候Django主做新闻和内容管理的
一个重量级的 Python Web框架,Django 配备了常用的大部分组件
Django的用途
Django的版本
Django的官网
查看已安装的版本
>>> import django
>>> print(django.VERSION)
(2, 2, 12, 'final', 0)
安装
$ sudo pip3 install django
安装django的最新版本$ sudo pip3 install django[==版本]
安装django的指定版本$ tar -xvf Django-2.2.12.tar.gz
$ cd Django-2.2.12
$ sudo python3 setup.py install
Django的卸载
$ pip3 uninstall django
Django 的开发环境
$ django-admin startproject 项目名称
如:
新建项目 django-admin startproject 项目名
启动项目 先找到mange.py 文件 python3 manage.py runserver
$ cd mysite1
$ python3 manage.py runserver
# 或
$ python3 manage.py runserver 5000 # 指定只能本机使用127.0.0.1的5000端口访问本机
示例:
$ django-admin startproject mysite1
$ tree mysite1/
mysite1/
├── manage.py
└── mysite1
├── __init__.py
├── settings.py
├── urls.py
└── wsgi.py
1 directory, 5 files
项目目录结构解析:
manage.py
包含项目管理的子命令, 如:
python3 manage.py runserver
启动服务python3 manage.py startapp
创建应用python3 manage.py migrate
数据库迁移...
__init__.py
wsgi.py
urls.py
settings.py
settings.py
文件介绍https://docs.djangoproject.com/en/2.2/ref/settings/
BASE_DIR
DEBUG
调试模式
(用于开发中)生产环境中
(不启用调试)ALLOWED_HOSTS
127.0.0.1
, localhost
能访问本项目 - DEBUG = True时生效python3 manage.py runserver 0.0.0.0:5000
# 指定网络设备如果内网环境下其他主机想正常访问该站点,需加`ALLOWED_HOSTS = [‘内网ip’]INSTALLED_APPS
MIDDLEWARE
TEMPLATES
DATABASES
LANGUAGE_CODE
"en-us"
"zh-Hans"
TIME_ZONE
"UTC"
"Asia/Shanghai"
ROOT_URLCONF
ROOT_URLCONF = 'mysite1.urls'
注: 此模块可以通过
from django.conf import settings
导入和使用
URL 即统一资源定位符 Uniform Resource Locator
作用:
说明:
URL的一般语法格式为:
protocol :// hostname[:port] / path [?query][#fragment]
如:
http://tts.tmooc.cn/video/showVideo?menuId=657421&version=AID201908#subject
说明:
protocol(协议)
HTTP://
HTTPS://
file:///
hostname(主机名)
port(端口号)
path(路由地址)
query(查询)
fragment(信息片断)
注: [] 代表其中的内容可省略
浏览器 http://127.0.0.1:8000/page/2003/
1,Django 从配置文件中 根据 ROOT_URLCONF 找到 主路由文件;默认情况下,该文件在 项目同名目录下的urls; 例如 mysite1/mysite1/urls.py
2,Django 加载 主路由文件中的 urlpatterns 变量
3,依次匹配 urlpatterns 中的 URL, 匹配到第一个合适的中断后续匹配
4,匹配成功 - 调用对应的视图函数处理请求,返回响应
5,匹配失败 - 返回404响应
主路由-urls.py样例
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls)
path('page/2003/', views.page_2003),
path('page/2004/', views.page_2004),
]
视图函数是用于接收一个浏览器请求并通过HttpResponse对象返回数据的函数。此函数可以接收浏览器请求并根据业务逻辑返回相应的内容给浏览器
视图处理的函数的语法格式:
def xxx_view(request[, 其它参数...]):
return HttpResponse对象
参数:
返回值
HttpResponse的对象;Django会提供一系列的response对象;
示例:
views.py
# file : <项目同名文件夹下>/views.py
from django.http import HttpResponse
def page1_view(request):
html = "这是第1个页面
"
return HttpResponse(html)
ROOT_URLCONF
指定了主路由配置列表urlpatterns的文件位置 # file : <项目同名文件夹>/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
... # 此处配置主路由
]
```
用于描述路由与视图函数的对应关系
模块
from django.urls import path
语法:
练习
语法: <转换器类型:自定义名>
作用:若转换器类型匹配到对应类型的数据,则将数据按照关键字传参的方式传递给视图函数
转换器 | 效果 | 案例 |
---|---|---|
str | 匹配除了 '/' 之外的非空字符串 |
"v1/users/ |
int | 匹配0或任何正整数。返回一个 int | “page/ |
slug | 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签 | “detail/ |
path | 匹配非空字段,包括路径分隔符 '/' |
"v1/users/ |
定义一个路由的格式为:
从路由中提取数据,做相应的操作后返回给浏览器
如:
输入: 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
在url 的匹配过程中可以使用正则表达式进行精确匹配
语法:
(?Ppattern)
;匹配提取参数后用关键字传参方式传递给视图函数示例:
# file : <项目同名文件夹>/urls.py
# 以下示例匹配
# 可匹配 http://127.0.0.1:8000/20/mul/40
# 不可匹配 http://127.0.0.1:8000/200/mul/400
urlpatterns = [
path('admin/', admin.site.urls),
re_path(r'^(?P\d{1,2})/(?P\w+)/(?P\d{1,2})$' ,views.cal_view),
]
练习:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nv4wVYA2-1618040510637)(images/request_response.png)]
POST /v1/tokens HTTP/1.1 -> 起始行
Host: 127.0.0.1:8000 -> headers
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:74.0) Gecko/20100101 Firefox/74.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 58
Origin: http://127.0.0.1:7000
Connection: keep-alive
Referer: http://127.0.0.1:7000/dadashop/templates/login.html
{"username":"guoxiaonao"} -> body
根据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对象
HTTP/1.0 200 OK -> 起始行
Date: Sat, 21 Mar 2020 09:44:15 GMT -> headers
Server: WSGIServer/0.2 CPython/3.6.8
Content-Type: application/json
X-Frame-Options: SAMEORIGIN
Content-Length: 217
Vary: Origin
Access-Control-Allow-Origin: *
{"code": 200, "username": "guoxiaonao"} -> body
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头用以响应浏览器的请求。
HTTP状态码的英文为HTTP Status Code。
下面是常见的HTTP状态码:
HTTP状态码分类
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:
分类 | 分类描述 |
---|---|
1** | 信息,服务器收到请求,需要请求者继续执行操作 |
2** | 成功,操作被成功接收并处理 |
3** | 重定向,需要进一步的操作以完成请求 |
4** | 客户端错误,请求包含语法错误或无法完成请求 |
5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
Django中的响应对象HttpResponse:
构造函数格式:
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
作用:
参数:
'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)是指多用途互联网邮件扩展类型。
HttpResponse 子类
类型 | 作用 | 状态码 |
---|---|---|
HttpResponseRedirect | 重定向 | 302 |
HttpResponseNotModified | 未修改 | 304 |
HttpResponseBadRequest | 错误请求 | 400 |
HttpResponseNotFound | 没有对应的资源 | 404 |
HttpResponseForbidden | 请求被禁止 | 403 |
HttpResponseServerError | 服务器错误 | 500 |
无论是GET还是POST,统一都由视图函数接收请求,通过判断request.method 区分具体的请求动作
样例:
if request.method == 'GET':
处理GET请求时的业务逻辑
elif request.method == 'POST':
处理POST请求的业务逻辑
else:
其他请求业务逻辑
GET请求动作,一般用于向服务器获取数据
能够产生GET请求的场景:
GET请求方式中,如果有数据需要传递给服务器,通常会用查询字符串(Query String)传递 【注意:不要传递敏感数据】
URL 格式: xxx?参数名1=值1&参数名2=值2...
http://127.0.0.1:8000/page1?a=100&b=200
服务器端接收参数
获取客户端请求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
POST请求动作,一般用于向服务器提交大量数据
客户端通过表单等POST请求将数据传递给服务器端,如:
<form method='post' action="/login">
姓名:<input type="text" name="username">
<input type='submit' value='登陆'>
form>
form 表单的name属性
在form表单控件提交数据时,会自动搜索本表单控件内部的子标签的name属性及相应的值,再将这些名字和值以键-值对的形式提交给action指定的服务器相关位置
在form内能自动搜集到的name属性的标签的控件有
<input name='xxx'>
<select name='yyy'>select>
<textarea name='zzz'>textarea>
服务器端接收参数
if request.method == 'POST':
处理POST请求的数据并响应
else:
处理非POST 请求的响应
使用post方式接收客户端数据
request.POST['参数名'] # request.POST 绑定QueryDict
request.POST.get('参数名','')
request.POST.getlist('参数名')
取消csrf验证,否则Django将会拒绝客户端发来的POST请求
取消 csrf 验证
MIDDLEWARE = [
...
# 'django.middleware.csrf.CsrfViewMiddleware',
...
]
什么是模板
模板的配置
<项目名>/templates
templates
文件夹中搜索模板文件默认的模块文件夹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目录
...
},
]
from django.template import loader
# 1.通过loader加载模板
t = loader.get_template("模板文件名")
# 2.将t转换成 HTML 字符串
html = t.render(字典数据)
# 3.用响应对象将转换的字符串内容返回给浏览器
return HttpResponse(html)
from django.shortcuts import render
return render(request,'模板文件名', 字典数据)
t = loader.get_template('xxx.html')
html = t.render(字典数据)
return HttpResponse(html)
return render(request,'xxx.html',字典数据)
{{ 变量名 }}
{{ 变量名.index }}
{{ 变量名.key}}
{{ 对象.方法 }}
{{ 函数名 }}
2.视图函数中必须将变量封装到字典中才允许传递到模板上
def xxx_view(request)
dic = {
"变量1":"值1",
"变量2":"值2",
}
return render(request, 'xxx.html', dic)
def xxx_view(request)
变量1 = 值1
变量2 = 值2
...
return render(request, 'xxx.html', locals())
定义:XSS全称是Cross Site Scripting即跨站脚本
原理:将恶意HTML/JavaScript代码注入到受害用户浏览的网页上,从而达到攻击目的
危害:盗取用户信息,破坏网站正常运行等
分类:
定义:发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,故叫反射型XSS
样例:
以查询字符串形式提交 xss代码
http://127.0.0.1:8000/test_html?t=<script>alert(11)</script>
后端接到查询字符串的值后,显示在页面中
定义:提交的XSS代码会存储在服务器端(数据库,内存,文件系统等),其他用户请求目标页面时即被攻击
样例:
博客发表文章时,提交XSS代码,服务器存储代码后,其他用户访问该文章时,被XSS攻击
定义:DOM XSS的代码无需跟服务器交互,在前端直接触发攻击
样例:
地址栏提交#内容,例如-http://127.0.0.1:8000/test_html#javascript:alert(11)
页面中添加JS:
<script>
var hash = location.hash;
if(hash){
var url = hash.substring(1);
location.href = url;
}
</script>
文档可参见:https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#built-in-tag-reference
作用
标签语法
{% 标签 %}
...
{% 结束标签 %}
if 标签
{% if 条件表达式1 %}
...
{% elif 条件表达式2 %}
...
{% elif 条件表达式3 %}
...
{% else %}
...
{% endif %}
if 标签里的布尔运算符
练习
写一个简单的计算器页面,能够在服务端进行简单加减乘除计算
+加 -减 *乘 /除 = 3参考代码
{% for 变量 in 可迭代对象 %}
... 循环语句
{% empty %}
... 可迭代对象无数据时填充的语句
{% endfor %}
变量 | 描述 |
---|---|
forloop.counter | 循环的当前迭代(从1开始索引) |
forloop.counter0 | 循环的当前迭代(从0开始索引) |
forloop.revcounter | counter值得倒序 |
forloop.revcounter0 | revcounter值的倒序 |
forloop.first | 如果这是第一次通过循环,则为真 |
forloop.last | 如果这是最后一次循环,则为真 |
forloop.parentloop | 当嵌套循环,parentloop 表示外层循环 |
作用
语法
常用的过滤器
过滤器 | 说明 |
---|---|
lower | 将字符串转换为全部小写。 |
upper | 将字符串转换为大写形式 |
safe | 默认不对变量内的字符串进行html转义 |
add: “n” | 将value的值增加 n |
truncatechars:‘n’ | 如果字符串字符多于指定的字符数量,那么会被截断。 截断的字符串将以可翻译的省略号序列(“…”)结尾。 |
… |
文档参见:
模板继承可以使父模板的内容重用,子模板直接继承父模板的全部内容并可以覆盖父模板中相应的块
定义父模板中的块 block
标签
{% block block_name %}
定义模板块,此模板块可以被子模板重新定义的同名块覆盖
{% endblock block_name %}
继承模板 extends
标签(写在模板文件的第一行)
{% extends '父模板名称' %}
{% extends 'base.html' %}
{% block block_name %}
子模板块用来覆盖父模板中 block_name 块的内容
{% endblock block_name %}
参考文档
https://docs.djangoproject.com/en/2.2/ref/templates/language/#for-template-blocks
模板的继承示例:
url 反向解析是指在视图或模板中,用path定义的名称来查找或计算出相应的路由
path 函数的语法
path() 的name
关键字参数
作用:
name=
关键字传参给 url确定了个唯一确定的名字,在模板或视图中,可以通过这个名字反向推断出此url信息在模板中 ->通过url标签实现地址的反向解析
{% url '别名' %}
{% url '别名' '参数值1' '参数值2' %}
ex:
{% url 'pagen' '400' %}
{% url 'person' age='18' name='gxn' %}
在视图函数中 ->可调用 django中的 reverse 方法进行反向解析
from django.urls import reverse
reverse('别名', args=[], kwargs={})
ex:
print(reverse('pagen',args=[300]))
print(reverse('person',kwargs={'name':'xixi','age':18}))
同名找最合适的【参数一致的】;参数一致时,找最后一个;
练习:
写一个有四个自定义页面的网站,对应路由:
/ 主页
/page1 页面1
/page2 页面2
/page3 页面3
功能: 主页加 三个页面的连接分别跳转到一个 页面,三个页面每个页面加入一个链接用于返回主页
配置静态文件的访问路径
配置静态文件的存储路径 STATICFILES_DIRS
示例:
# file: setting.py
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
使用静态文件的访问路径进行访问
<img src="/static/images/lena.jpg">
<img src="http://127.0.0.1:8000/static/images/lena.jpg">
通过 {% static %}标签访问静态文件
{% static %}
表示的就是静态文件访问路径{% load static %}
{% static '静态资源路径' %}

创建步骤
python3 manage.py startapp 应用名称
如:python3 manage.py startapp music
INSTALLED_APPS = [
# ....
'user', #用户信息模块
'music', #音乐模块
]
migrations
文件夹
__init__.py
admin.py
apps.py
models.py
tests.py
views.py
作用:
函数格式
模块
app命字/url模块名.py
文件件里必须有urlpatterns 列表
使用前需要使用from django.conf.urls import include
导入此函数
练习:
1.创建两个应用
1.创建 sport 应用,并注册
2.创建 news 应用,并注册
2.创建分布式路由系统
1. http://127.0.0.1:8000/sport/index
交给 sport 应用中的 index_view() 函数处理
2. http://127.0.0.1:8000/news/index
交给 news 应用中的 index_view() 函数处理
应用内部可以配置模板目录
1,应用下手动创建 templates 文件夹
2,settings.py中确认 TEMPLATE 配置项中 的 'APP_DIRS' 值
例如 : 'APP_DIRS': True
#应用下templates 和 外层templates 都存在时,django得查找模板规则
1,优先查找外层templates目录下的模板
2,按INSTALLED_APPS配置下的 应用顺序 逐层查找
安装前确认ubuntu是否已安装 python3-dev 和 default-libmysqlclient-dev
sudo apt list --installed|grep -E ‘libmysqlclient-dev|python3-dev’
若命令无输出则需要安装 - sudo apt-get install python3-dev default-libmysqlclient-dev
create database 数据库名 default charset utf8 ;
create database mywebdb default charset utf8 collate utf8_general_ci;
# file: settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
DATABASES = {
'default' : {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mywebdb', # 数据库名称,需要自己定义
'USER': 'root',
'PASSWORD': '123456', # 管理员密码
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
ENGINE
'django.db.backends.mysql'
'django.db.backends.sqlite3'
'django.db.backends.oracle'
'django.db.backends.postgresql'
NAME
'NAME': 'mywebdb'
USER
'USER':'root'
PASSWORD
'PASSWORD':'123456'
HOST
'HOST':'127.0.0.1'
PORT
'PORT':'3306'
此示例为添加一个 bookstore_book 数据表来存放图书馆中书目信息
添加一个 bookstore 的 app
$ python3 manage.py startapp bookstore
注册app
# file : setting.py
INSTALLED_APPS = [
...
'bookstore',
]
添加模型类
# 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)
market_price = models.DecimalField('零售价',max_digits=5,decimal_places=2,default=0.0)
pub = models.CharField('出版社',max_length=50,default='')
is_active = models.BooleanField('是否活跃',default=True)
数据库的迁移
将每个应用下的models.py文件生成一个中间文件,并保存在migrations文件夹中
python3 manage.py makemigrations
执行迁移脚本程序
执行迁移程序实现迁移。将每个应用下的migrations目录中的中间文件同步回数据库
python3 manage.py migrate
注: 每次修改完模型类再对服务程序运行之前都需要做以上两步迁移操作。
模型类需继承自django.db.models.Model
Models的语法规范
from django.db import models
class 模型类名(models.Model):
字段名 = models.字段类型(字段选项)
模型类名是数据表名的一部分,建议类名首字母大写
字段名又是当前类的类属性名,此名称将作为数据表的字段名
字段类型用来映射到数据表中的字段的类型
字段选项为这些字段提供附加的参数信息
BooleanField()
CharField()
DateField()
DateTimeField()
DecimalField()
数据库类型:decimal(x,y)
编程语言中:使用小数表示该列的值
在数据库中:使用小数
参数:
示例:
money=models.DecimalField(
max_digits=7,
decimal_places=2,
default=0.0
)
FloatField()
EmailField()
IntegerField()
URLField()
ImageField()
TextField()
示例:
# 创建一个属性,表示用户名称,长度30个字符,必须是唯一的,不能为空,添加索引
name = models.CharField(max_length=30, unique=True, null=False, db_index=True)
文档参见:
使用内部 Meta类 来给模型赋予属性,Meta类下有很多内建的类属性,可对模型类做一些控制
示例:
# 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)
market_price = models.DecimalField('零售价',max_digits=5,decimal_places=2,default=0.0)
pub = models.CharField('出版社',max_length=50,default='')
is_active = models.BooleanField('是否活跃',default=True)
class Meta:
db_table = 'book' #可改变当前模型类对应的表名
class Author(models.Model):
name = models.CharField("姓名",max_length=20)
age = models.IntegerField("年龄",default=18)
email = models.EmailField("邮箱",null=True)
python3 manage.py makemigrations
python manage.py migrate
当执行 $ python3 manage.py makemigrations
出现如下迁移错误时的处理方法
You are trying to add a non-nullable field 'des' to book 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) Quit, and let me add a default in models.py
Select an option:
您试图添加一个非空字段'des'来预订没有默认;我们不能这样做(数据库需要填充现有行)
请选择修复:
1)现在提供一次性默认值(将对所有现有行设置此列的空值)
2)退出,让我在models.py中添加一个默认值
选择一个选项:
default=XXX
的缺省值(推荐使用)数据库的迁移文件混乱的解决办法
__init__.py
除外)class MyModel(models.Model):
...
MyModel.objects.create(...) # objects 是管理器对象
obj = MyModel(属性=值,属性=值)
obj.属性=值
obj.save()
在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 - DecimalField 图书定价 总长6位/小数点2位
4. market_price - 图书零售价 总长6位/小数点2位
2. Author - 作者
1. name - CharField 姓名,非空
2. age - IntegerField, 年龄,非空,缺省值为1
3. email - EmailField, 邮箱,允许为空
书名 | 定价 | 零售价 | 出版社 |
---|---|---|---|
Python | 20.00 | 25.00 | 清华大学出版社 |
Django | 70.00 | 75.00 | 清华大学出版社 |
JQuery | 90.00 | 85.00 | 机械工业出版社 |
Linux | 80.00 | 65.00 | 机械工业出版社 |
HTML5 | 90.00 | 105.00 | 清华大学出版社 |
姓名 | 年龄 | 邮箱 |
---|---|---|
王老师 | 28 | [email protected] |
吕老师 | 31 | [email protected] |
祁老师 | 30 | [email protected] |
python manage.py shell
from bookstore.models import Book,Author
Book.objects.create(title=“python3”,price=50,market_price=55,pub=“清华大学出版社”)
Book.objects.create(title=“linux”,price=60,market_price=65,pub=“清华大学出版社”)
Book.objects.create(title=“html5”,price=70,market_price=75,pub=“清华大学出版社”)
Book.objects.create(title=“Django”,price=100,market_price=110,pub=“北京大学出版社”)
Book.objects.create(title=“python爬虫”,price=80,market_price=85,pub=“北京大学出版社”)
Author.objects.create(name=‘qilaoshi’,age=25,email=“[email protected]”)
Author.objects.create(name=‘lvlaoshi’,age=26,email=“[email protected]”)
Author.objects.create(name=‘shilaoshi’,age=20,email=“[email protected]”)
方法 | 说明 |
---|---|
all() | 查询全部记录,返回QuerySet查询对象 |
get() | 查询符合条件的单一记录 |
filter() | 查询符合条件的多条记录 |
exclude() | 查询符合条件之外的全部记录 |
… |
all()方法
from bookstore.models import Book
books = 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 , 零售价:%s" % (self.title, self.pub, self.price, self.market_price)
from bookstore.models import Book
books = Book.objects.all()
for book in books:
print(book)
书名: python3, 出版社: 清华大学出版社, 定价: 50.00,零售价:55.00
书名: linux, 出版社: 清华大学出版社, 定价: 60.00,零售价:65.00
书名: html5, 出版社: 清华大学出版社, 定价: 70.00,零售价:75.00
书名: Django, 出版社: 北京大学出版社, 定价: 100.00,零售价:110.00
```
查询返回指定列-----------------------(字典表示)
方法: values(‘列1’, ‘列2’)
用法: MyModel.objects.values(…)
作用: 查询部分列的数据并返回
返回值: QuerySet
示例:
from bookstore.models import Book
books = Book.objects.values("title", "pub")
for book in books:
print("书名", book["title"], '出版社:', book['pub'])
print("book=", book)
print(book)
打印:
{‘title’: ‘python3’, ‘pub’: ‘清华大学出版社’}
{‘title’: ‘linux’, ‘pub’: ‘清华大学出版社’}
{‘title’: ‘html5’, ‘pub’: ‘清华大学出版社’}
{‘title’: ‘Django’, ‘pub’: ‘北京大学出版社’}
{‘title’: ‘python爬虫’, ‘pub’: ‘北京大学出版社’}
查询返回指定列-----------------------(元组表示)
方法:values_list(‘列1’,‘列2’)
用法:MyModel.objects.values_list(…)
作用:
返回值: QuerySet容器对象,内部存放 元组
示例:
from bookstore.models import Book
books = Book.objects.values_list("title", "pub")
for book in books:
print("书名", book[0], '出版社:', book[1])
print("book=", book) # ('Python', '清华大学出版社')...
排序查询
方法:order_by
用法:MyModel.objects.order_by(‘-列’,‘列’)
作用:
说明:
默认是按照升序排序,降序排序则需要在列前增加’-'表示
示例:
from bookstore.models import Book
books = Book.objects.order_by("price")
for book in books:
print("书名:", book.title, '定价:', book.price)
print(book)
得到价格从低到高排序
书名: python3, 出版社: 清华大学出版社, 定价: 50.00,零售价:55.00
书名: linux, 出版社: 清华大学出版社, 定价: 60.00,零售价:65.00
书名: html5, 出版社: 清华大学出版社, 定价: 70.00,零售价:75.00
书名: python爬虫, 出版社: 北京大学出版社, 定价: 80.00,零售价:85.00
书名: Django, 出版社: 北京大学出版社, 定价: 100.00,零售价:110.00
- 示例:
```python
from bookstore.models import Book
books = Book.objects.order_by("-price") #加一个减号,价格降序排列
for book in books:
print("书名:", book.title, '定价:', book.price)
print(book)
```
打印:
书名: Django, 出版社: 北京大学出版社, 定价: 100.00,零售价:110.00
书名: python爬虫, 出版社: 北京大学出版社, 定价: 80.00,零售价:85.00
书名: html5, 出版社: 清华大学出版社, 定价: 70.00,零售价:75.00
书名: linux, 出版社: 清华大学出版社, 定价: 60.00,零售价:65.00
书名: python3, 出版社: 清华大学出版社, 定价: 50.00,零售价:55.00
条件查询 - filter
方法: filter(条件)
语法:
MyModel.objects.filter(属性1=值1, 属性2=值2)
返回值:
说明:
Books.objects.filter(price=20, pub="清华大学出版社")
返回定价为20 且
出版社为"清华大学出版社"的全部图书示例:
# 查询书中出版社为"清华大学出版社"的图书
from bookstore.models import Book
books = Book.objects.filter(pub="清华大学出版社")
for book in books:
print(“书名:”, book.title)
Book.objects.filter(pub=“清华大学出版社”,price=60)
# 查询Author实体中name为王老师并且age是28岁的
authors=Author.objects.filter(name='王老师',age=28)
```
条件查询 - exclude
方法: exclude(条件)
语法:
作用:
条件
的 全部的数据集示例:
清华大学出版社,定价等于50
以外的全部图书books = Book.objects.exclude(pub="清华大学出版社", price=50)
for book in books:
print(book)
条件查询 - get
方法: get(条件)
语法:
作用:
说明:
示例:
from bookstore.models import Book
book = Book.objects.get(id=1)
print(book.title)
__exact
: 等值匹配
Author.objects.filter(id__exact=1)
# 等同于select * from author where id = 1
__contains
: 包含指定值
Author.objects.filter(name__contains='w')
# 等同于 select * from author where name like '%w%'
Book.objects.filter(title__contains="5")
<QuerySet [<Book: 书名: html5, 出版社: 清华大学出版社, 定价: 70.00,零售价:75.00>]>
__startswith
: 以 XXX 开始
__endswith
: 以 XXX 结束
__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/2.2/ref/models/querysets/#field-lookups
示例
MyModel.objects.filter(id__gt=4)
# 等同于 SELECT ... WHERE id > 4;
练习:
查询Book表中price大于等于50的信息
Book.objects.filter(price__gte=50)
查询Author表中姓王的人的信息
Author.objects.filter(name__contains=“王”)
查询Author表中Email中包含"w "的人的信息
Author.objects.filter(Email__contains=“w”)
from bookstore.models import Book
abook = Book.objects.get(id=10)
abook.market_price = "10.5"
abook.save()
直接调用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)
try:
auth = Author.objects.get(id=1)
auth.delete()
except:
print(删除失败)
# 删除全部作者中,年龄大于65的全部信息
auths = Author.objects.filter(age__gt=65)
auths.delete()
不带分组的聚合查询是指导将全部数据进行集中统计查询
聚合函数【需要导入】:
from django.db.models import *
语法:
返回结果:
示例:
# 得到所有书的平均价格
from bookstore.models import Book
from django.db.models import Avg
result = Book.objects.aggregate(myAvg=Avg('price'))
print("平均价格是:", result['myAvg'])
print("result=", result) # {"myAvg": 58.2}
# 得到数据表里有多少本书
from django.db.models import Count
result = Book.objects.aggregate(mycnt=Count('title'))
print("数据记录总个数是:", result['mycnt'])
print("result=", result) # {"mycnt": 10}
print(result["mycnt"]) #10
分组聚合是指通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
语法:
用法步骤:
通过先用查询结果MyModel.objects.values 查找查询要分组聚合的列
pub_set = Book.objects.values('pub')
print(pub_set) #
```
通过返回结果的 QuerySet.annotate 方法分组聚合得到分组结果
pub_count_set = pub_set.annotate(myCount=Count('pub'))
print(pub_count_set) #
Book.objects.values("pub").annotate(mycount=Count("id")).filter(mycount__gt=2)
示例:
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('请查看服务器端控制台获取结果')
作用:
用法
from django.db.models import F
语法:
from django.db.models import F
F('列名')
说明:
示例1
Book.objects.all().update(market_price=F('market_price')+10)
'UPDATE `bookstore_book` SET `market_price` = (`bookstore_book`.`market_price` + 10)
# 以上做法好于如下代码
books = Book.objects.all()
for book in books:
book.market_price=book.marget_price+10
book.save()
#在原定价高10元
Book.objects.all().update(market_price=F('price')+10)
示例2
from django.db.models import F
from bookstore.models import Book
books = Book.objects.filter(market_price__gt=F('price'))
'SELECT * FROM `bookstore_book` WHERE `bookstore_book`.`market_price` > (`bookstore_book`.`price`)
for book in books:
print(book.title, '定价:', book.price, '现价:', book.market_price)
当在获取查询结果集 使用复杂的逻辑或 |
、 逻辑非 ~
等操作时可以借助于 Q对象进行操作
如: 想找出定价低于20元 或 清华大学出版社的全部书,可以写成
Book.objects.filter(Q(price__lt=20)|Q(pub="清华大学出版社"))
Q对象在 数据包 django.db.models 中。需要先导入再使用
from django.db.models import Q
作用
运算符:
语法
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的书
Book.objects.filter(Q(market_price__lt=50) | Q(pub_house='清华大学出版社'))
# 查找不是机械工业出版社的书且价格低于50的书
Book.objects.filter(Q(market_price__lt=50) & ~Q(pub_house='机械工业出版社'))
语法:
MyModel.objects.raw(sql语句,[拼接参数])
用法
MyModel.objects.raw('sql语句', [拼接参数])
返回值:
示例
books = Book.objects.raw('select * from bookstore_book')
for book in books:
print(book)
#sql注入问题
s1 = Book.objects.raw('select * from bookstore_book where id=%s'%('1 or 1=1'))
s2 = Book.objects.raw('select * from bookstore_book where id=%s',['1 or 1=1'])
使用django中的游标cursor对数据库进行 增删改查 操作
在Django中跨国模型类直接操作数据库
使用步骤:
from django.db import connection
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;')
-练习:
2.urls.py
```python
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path(‘admin/’, admin.site.urls),
path(‘bookstore/’,include(‘bookstore.urls’)),
]
```
2.bookstore/ urls.py
```python
from django.urls import path
from . import views
urlpatterns =[
path(‘all_book’,views.all_book),
path(‘delete_book’,views.delete_book),
path(‘update_book/int:bid’,views.update_book),
]
```
3. views.py
```python
from django.http import HttpResponse,HttpResponseRedirect
from django.shortcuts import render
from .models import Book
def all_book(request):
all_book = Book.objects.filter(is_active = True)
return render(request,‘bookstore/all_book.html’,locals())
def delete_book(request):
bid = request.GET.get(‘bid’)
if not bid:
return HttpResponse(‘–bid is error–’)
try:
book = Book.objects.get(id = bid,is_active=True)
except Exception as e:
print(‘–no book! – %s’ %e)
return HttpResponse(‘–book is error–’)
book.is_active = False
book.save()
#302
return HttpResponseRedirect(‘/bookstore/all_book’)
def update_book(request,bid):
try:
book = Book.objects.get(id = bid,is_active = True)
except Exception:
return HttpResponse(‘no this book’)
if request.method == “GET”:
return render(request,‘bookstore/update_book.html’,locals())
elif request.method == “POST”:
price = request.POST.get(‘price’)
market_price = request.POST[‘market_price’]
book.price = price
book.market_price = market_price
book.save()
return HttpResponseRedirect(‘/bookstore/all_book’)
```
4. update_book.html
```html
更新图书
书名:
出版社:
定价:
零售价:
```
5. all_book.html
```html
图书列表 {% for b in all_book %} {% endfor %}
编号 | 书名 | 出版社 | 定价 | 零售价 | 操作 |
---|---|---|---|---|---|
{{ b.id }} | {{ b.title }} | {{ b.pub }} | {{ b.price }} | {{ b.market_price }} | 更新 删除 |
```
$ python3 manage.py createsuperuser
$ python3 manage.py createsuperuser
Username (leave blank to use 'tarena'): tarena # 此处输入用户名
Email address: [email protected] # 此处输入邮箱
Password: # 此处输入密码(密码要复杂些,否则会提示密码太简单)
Password (again): # 再次输入重复密码
Superuser created successfully.
$
/admin
后台管理界中显示和管理,需要将自己的类注册到后台管理界面admin.site.register(自定义模型类)
方法进行注册
from .models import Book
from django.contrib import admin
admin.site.register(自定义模型类)
# file: bookstore/admin.py
from django.contrib import admin
# Register your models here.
from . import models
...
admin.site.register(models.Book) # 将Book类注册为可管理页面
from django.contrib import admin
from .models import Book
class BookManage(admin.ModelAdmin):
list_display = [‘title’,‘pub’,‘price’,‘market_price’]
list_display_links = [‘title’,‘pub’]
search_fields = [‘title’,‘pub’]
admin.site.register(Book,BookManage)
```
XXXX object
类型的记录,不便于阅读和判断def __str__(self):
方法解决显示问题,如:
class Book(models.Model):
...
def __str__(self):
return "书名" + self.title
作用:
说明:
django.contrib.admin
里的 ModelAdmin
类模型管理器的使用方法:
<应用app>/admin.py
里定义模型管理器类class XXXXManager(admin.ModelAdmin):
......
from django.contrib import admin
from .models import *
admin.site.register(YYYY, XXXXManager) # 绑定 YYYY 模型类与 管理器类 XXXXManager
# file : bookstore/admin.py
from django.contrib import admin
from .models import Book
class BookManager(admin.ModelAdmin):
list_display = ['id', 'title', 'price', 'market_price']
admin.site.register(Book, BookManager)
模型管理器类ModelAdmin中实现的高级管理功能
通过Meta内嵌类 定义模型类的属性
模型类可以通过定义内部类class Meta 来重新定义当前模型类和数据表的一些属性信息
用法格式如下:
class Book(models.Model):
title = CharField(....)
class Meta:
1. db_table = '数据表名'
- 该模型所用的数据表的名称。(设置完成后需要立马更新同步数据库)
2. verbose_name = '单数名'
- 给模型对象的一个易于理解的名称(单数),用于显示在/admin管理界面中
3. verbose_name_plural = '复数名'
- 该对象复数形式的名称(复数),用于显示在/admin管理界面中
from django.db import models
from django.db.models import F
class Book(models.Model):
title=models.CharField(“书名”,max_length=50,default=‘’)
price=models.DecimalField(‘定价’,max_digits=7,decimal_places=2,default=0.0)
market_price=models.DecimalField(‘零售价’,max_digits=5,decimal_places=2,default=0.0)
pub=models.CharField(‘出版社’,max_length=50,default=‘’)
is_active=models.BooleanField(‘是否活跃’,default=True)
class Meta:
db_table='book' # 可改变当前模型类对应的表名
verbose_name = '图书' #单数是图书
verbose_name_plural = '图书' #复数也是
def __str__(self):
return "书名: %s, 出版社: %s, 定价: %s,零售价:%s" % (self.title,self.pub,self.price,self.market_price)
class Author(models.Model):
name=models.CharField(“姓名”,max_length=20)
age=models.IntegerField(“年龄”,default=18)
email=models.EmailField(“邮箱”,null=True)
```
语法
class A(model.Model):
...
class B(model.Model):
属性 = models.OneToOneField(A, on_delete=xxx)
外键类字段选项
特殊字段参数【必须项】:
其余常用的字段选项【非必须项】;如:
用法示例
# 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, on_delete=models.CASCADE) # 增加一对一属性
from .models import *
author1 = Author.objects.create(name='王老师')
wife1 = Wife.objects.create(name='王夫人', author=author1) # 关联王老师
author2 = Author.objects.create(name='小泽老师') # 一对一可以没有数据对应的数据
正向查询
# 通过 wife 找 author
from .models import Wife
wife = Wife.objects.get(name='王夫人')
print(wife.name, '的老公是', wife.author.name)
反向查询
实例对象.引用类名(小写)
,如作家的反向引用为作家对象.wife
# 通过 author.wife 关联属性 找 wife,如果没有对应的wife则触发异常
author1 = Author.objects.get(name='王老师')
print(author1.name, '的妻子是', author1.wife.name)
author2 = Author.objects.get(name='小泽老师')
try:
print(author2.name, '的妻子是', author2.wife.name)
except:
print(author2.name, '还没有妻子')
作用: 垂直分表
主要是解决常用数据不常用数据的存储问题,把经常加载的一个数据放在主表中,不常用数据放在另一个副表中,这样在访问主表数据时不需要加载副表中的数据以提高访问速度提高效率和节省内存空间,如经常把书的内容和书名建成两张表,因为在网站上经常访问书名等信息,但不需要得到书的内容。
语法
class A(model.Model):
...
class B(model.Model):
属性 = models.ForeignKey("一"的模型类, on_delete=xx)
用法示例
清华大学出版社
有书
北京大学出版社
有书
创建模型类
# file: otm/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 = ForeignKey(Publisher, on_delete=models.CASCADE)
创建数据
#先创建 '一' ,再创建 '多'
from .models import *
pub1 = Publisher.objects.create(name='清华大学出版社')
Book.objects.create(title='C++', publisher=pub1)
Book.objects.create(title='Java', publisher_id=1)
#高级创建 - 利用 反向属性
pub2 = Publisher.objects.create(name='北京大学出版社')
pub2.book_set.create(title='西游记')
数据查询
通过 Book 查询 Publisher【正向】
通过 publisher 属性查询即可
book.publisher
abook = Book.objects.get(id=1)
print(abook.title, '的出版社是:', abook.publisher.name)
通过 Publisher 查询 对应的所有的 Book 【反向】
Django会在Publisher中增加一个属性来表示对对应的Book们的查询引用
属性:book_set 等价于 objects
# 通过出版社查询对应的书
pub1 = Publisher.objects.get(name='清华大学出版社')
books = pub1.book_set.all() # 通过book_set 获取pub1对应的多个Book数据对象
#books = Book.objects.filter(publisher=pub1) # 也可以采用此方式获取
print("清华大学出版社的书有:")
for book in books:
print(book.title)
语法
属性 = models.ManyToManyField(MyModel)
用法示例
class Author(models.Model):
...
class Book(models.Model):
...
authors = models.ManyToManyField(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)
authors = models.ManyToManyField(Author)
def __str__(self):
return self.title
创建数据
方案1 先创建 author 再关联 book
author1 = Author.objects.create(name='吕老师')
author2 = Author.objects.create(name='王老师')
# 吕老师和王老师同时写了一本Python
book11 = author1.book_set.create(title="Python")
author2.book_set.add(book11)
方案2 先创建 book 再关联 author
book = Book.objects.create(title='python1')
#郭小闹和吕老师都参与了 python1 的 创作
author3 = book.authors.create(name='guoxiaonao')
book.authors.add(author1)
数据查询
book.authors.all() -> 获取 book 对应的所有的author的信息
book.authors.filter(age__gt=80) -> 获取book对应的作者中年龄大于80岁的作者的信息
author.book_set.all()
author.book_set.filter()
Cookies和Session就是为了保持会话状态而诞生的两个存储技术
cookies是保存在客户端浏览器上的存储空间
Chrome 浏览器 可能通过开发者工具的 Application
>> Storage
>> Cookies
查看和操作浏览器端所有的 Cookies 值
火狐浏览器 可能通过开发者工具的 存储 -> Cookie
cookies 在浏览器上是以键-值对的形式进行存储的,键和值都是以ASCII字符串的形存储(不能是中文字符串)
cookies中的数据是按域存储隔离的,不同的域之间无法访问
cookies 的内部的数据会在每次访问此网址时都会携带到服务器端,如果cookies过大会降低响应速度
在Django 设置浏览器的COOKIE 必须通过 HttpResponse 对象来完成
添加、修改COOKIE
删除COOKIE
获取cookie
通过 request.COOKIES 绑定的字典(dict) 获取客户端的 COOKIES数据
value = request.COOKIES.get('cookies名', '默认值')
print("cookies名 = ", value)
示例
# 为浏览器添加键为 my_var1,值为123,过期时间为1个小时的cookie
responds = HttpResponse("已添加 my_var1,值为123")
responds.set_cookie('my_var1', 123, 3600)
return responds
# 为浏览器添加键为 my_var1,修改值为456,过期时间为2个小时的cookie
responds = HttpResponse("已修改 my_var1,值为456")
responds.set_cookie('my_var1', 456, 3600*2)
return responds
# 删除浏览器键为 my_var1的cookie
responds = HttpResponse("已删除 my_var1")
responds.delete_cookie('my_var1')
return responds
# 获取浏览器中 my_var变量对应的值
value = request.COOKIES.get('my_var1', '没有值!')
print("cookie my_var1 = ", value)
return HttpResponse("my_var1:" + value)
session又名会话控制,是在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据
实现方式
Django中配置Session
INSTALLED_APPS = [
# 启用 sessions 应用
'django.contrib.sessions',
]
MIDDLEWARE = [
# 启用 Session 中间件
'django.contrib.sessions.middleware.SessionMiddleware',
]
session的基本操作:
保存 session 的值到服务器
request.session['KEY'] = VALUE
获取session的值
VALUE = request.session['KEY']
VALUE = request.session.get('KEY', 缺省值)
删除session的值
在 settings.py 中有关 session 的设置
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
注: 当使用session时需要迁移数据库,否则会出现错误
python3 manage.py migrate
django 原生session 问题:
1,django_session表是 单表设计; 且该表数据量持续增持【浏览器故意删掉sessionid&过期数据未删除】
2,可以每晚执行 python3 manage.py clearsessions 【该命令可删除已过期的session数据】
存储位置:
C- 浏览器中 s- 服务器中【mysql】
安全性:
C - 不安全 s- 相对安全一些
不管C还是S , 不要存储敏感数据 【密码】
##接下来是Django 网络云笔记项目
缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存
视图渲染有一定成本,对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数
案例分析
from django.shortcuts import render
def index(request):
# 时间复杂度极高的渲染
book_list = Book.objects.all() #-> 此处假设耗时2s
return render(request, 'index.html', locals())
优化思想
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
1,博客列表页
2,电商商品详情页
3,缓存导航及页脚
Django中提供多种缓存方式,如需使用需要在settings.py中进行配置
1,数据库缓存 mysite7 改配置 migrate , 添加缓存配置项 createcachetable
Django可以将其缓存的数据存储在您的数据库中
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
'TIMEOUT': 300, #缓存保存时间 单位秒,默认值为300,
'OPTIONS':{
'MAX_ENTRIES': 300, #缓存最大数据条数
'CULL_FREQUENCY': 2,#缓存条数达到最大值时 删除1/x的缓存数据
}
}
}
创建缓存表
python3 manage.py createcachetable
2,文件系统缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache',#这个是文件夹的路径
#'LOCATION': 'c:\test\cache',#windows下示例
}
}
3, 本地内存缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake'
}
}
在视图View中使用cache
from django.views.decorators.cache import cache_page
@cache_page(30) -> 单位s
def my_view(request):
...
在路由中使用
from django.views.decorators.cache import cache_page
urlpatterns = [
path('foo/', cache_page(60)(my_view) ),
]
在模板中使用
{% load cache %}
{% cache 500 sidebar username %}
.. sidebar for logged in user ..
{% endcache %}
缓存api
作用:局部缓存部分结果
使用:
#指定配置引入
from django.core.cache import caches
cache1 = caches['myalias']
cache2 = caches['myalias_2']
#默认配置引入【指的配置中的default项】 等同于 caches['default']
from django.core.cache import cache
#常规命令 set
#key: 字符串类型
#value: Python对象
#timeout:缓存存储时间 默认值为settings.py CACHES对应配置的TIMEOUT
#返回值:None
cache.set('my_key', 'myvalue', 30)
#常规命令 get
#返回值:为key的具体值,如果没有数据,则返回None
cache.get('my_key')
#可添加默认值,如果没取到返回默认值
cache.get('my_key', 'default值')
#常规命令 add 只有在key不存在的时候 才能设置成功
#返回值 True or False
cache.add('my_key', 'value') #如果my_key已经存在,则此次赋值失效
#常规命令 get_or_set 如果未获取到数据 则执行set操作
#返回值 key的值
cache.get_or_set('my_key', 'value', 10)
#常规命令 get_many(key_list) set_many(dict,timeout)
#返回值 set_many:返回插入不成功的key数组
# get_many:取到的key和value的字典
>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
#常规命令 delete
#返回值 None
cache.delete('my_key')
#常规命令 delete_many
#返回值 成功删除的数据条数
cache.delete_many(['a', 'b', 'c'])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-umYrJ0Bl-1618040510665)(images\浏览器缓存.png)]
浏览器缓存分类:
不会向服务器发送请求,直接从缓存中读取资源
1,Expires
缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
Expires:Thu, 02 Apr 2030 05:14:08 GMT
Expires 是 HTTP/1 的产物,受限于本地时间,如 果修改了本地时间,可能会造成缓存失效
2, Cache-Control
在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当Cache-Control:max-age=120
代表请求创建时间后的120秒,缓存失效
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程
1,Last-Modified和If-Modified-Since
第一次访问时,服务器会返回
Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT
浏览器下次请求时 携带If-Modified-Since这个header , 该值为 Last-Modified
服务器接收请求后,对比结果,若资源未发生改变,则返回304, 否则返回200并将新资源返回给浏览器
缺点:只能精确到秒,容易发生单秒内多次修改,检测不到
2,ETag和If-None-Match
Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成
流程同上
对比 Last-Modified VS ETag
1,精度不一样 - Etag 高
2,性能上 - Last-Modifi 高
3,优先级 - Etag 高
中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。
每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。
中间件类:
django.utils.deprecation.MiddlewareMixin
类def process_request(self, request):
执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象def process_view(self, request, callback, callback_args, callback_kwargs):
调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象def process_response(self, request, response):
所有响应返回浏览器 被调用,在每个请求上调用,返回HttpResponse对象def process_exception(self, request, exception):
当处理过程中抛出异常时调用,返回一个HttpResponse对象def process_template_response(self, request, response):
在视图函数执行完毕且试图返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象编写中间件类:
# file : middleware/mymiddleware.py
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class MyMiddleWare(MiddlewareMixin):
def process_request(self, request):
print("中间件方法 process_request 被调用")
def process_view(self, request, callback, callback_args, callback_kwargs):
print("中间件方法 process_view 被调用")
def process_response(self, request, response):
print("中间件方法 process_response 被调用")
return response
# file : settings.py
MIDDLEWARE = [
...
]
中间件的执行过程
练习
跨站请求伪造攻击
说明:
CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。
作用:
防范步骤:
{% csrf_token %}
如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
django.core.paginator
模块中。负责分页数据整体的管理
对象的构造方法
Paginator属性
Paginator方法
Paginator异常exception
负责具体某一页的数据的管理
创建对象
Paginator对象的page()方法返回Page对象
page = paginator.page(页码)
Page对象属性
Page对象方法
说明:
Page 对象是可迭代对象,可以用 for 语句来 访问当前页面中的每个对象
参考文档 https://docs.djangoproject.com/en/2.2/topics/pagination/
分页示例:
from django.core.paginator import Paginator
def book(request):
bks = Book.objects.all()
paginator = Paginator(bks, 10)
cur_page = request.GET.get('page', 1) # 得到默认的当前页
page = paginator.page(cur_page)
return render(request, 'bookstore/book.html', locals())
模板设计
<html>
<head>
<title>分页显示title>
head>
<body>
{% for b in page %}
<div>{{ b.title }}div>
{% endfor %}
{% if page.has_previous %}
<a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页a>
{% else %}
上一页
{% endif %}
{% for p in paginator.page_range %}
{% if p == page.number %}
{{ p }}
{% else %}
{{ p }}
{% endif %}
{% endfor %}
{% if page.has_next %}
下一页
{% else %}
下一页
{% endif %}
```
Django可直接在视图函数中生成csv文件 并响应给浏览器
import csv
from django.http import HttpResponse
from .models import Book
def make_csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="mybook.csv"'
all_book = Book.objects.all()
writer = csv.writer(response)
writer.writerow(['id', 'title'])
for b in all_book:
writer.writerow([b.id, b.title])
return response
Content-Disposition
标头,其中包含CSV文件的名称。它将被浏览器用于“另存为…”对话框writer.writerow
,传递一个可迭代对象,如列表或元组。文件上传必须为POST提交方式
表单中文件上传时必须有带有
enctype="multipart/form-data"
时才会包含文件内容数据。
表单中用标签上传文件
xxx
对应request.FILES['xxx']
对应的内存缓冲文件流对象。可通能过request.FILES['xxx']
返回的对象获取上传文件数据file=request.FILES['xxx']
file 绑定文件流对象,可以通过文件流对象的如下信息获取文件数据上传文件的表单书写方式
<html>
<head>
<meta charset="utf-8">
<title>文件上传title>
head>
<body>
<h3>上传文件h3>
<form method="post" action="/test_upload" enctype="multipart/form-data">
<input type="file" name="myfile"/><br>
<input type="submit" value="上传">
form>
body>
html>
在setting.py 中设置MEDIA相关配置;Django把用户上传的文件,统称为media资源
# file : settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
在当前项目文件夹下创建 media
文件夹
$ mkdir media
上传文件的视图处理函数 方案1 传统写入
# file views.py
from django.http import HttpResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import os
@csrf_exempt
def upload_view(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == "POST":
a_file = request.FILES['myfile']
print("上传文件名是:", a_file.name)
filename =os.path.join(settings.MEDIA_ROOT, a_file.name)
with open(filename, 'wb') as f:
data = a_file.file.read()
f.write(data)
return HttpResponse("接收文件:" + a_file.name + "成功")
上传文件的视图处理函数 方案2 借助orm
#test_upload/models.py
from django.db import models
# Create your models here.
class Content(models.Model):
desc = models.CharField(max_length=100)
myfile = models.FileField(upload_to='myfiles')
#test_upload/views.py
from test_upload.models import *
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def upload_view_dj(request):
if request.method == 'GET':
return render(request, 'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
a_file = request.FILES['myfile']
Content.objects.create(desc=title,myfile=a_file)
return HttpResponse('----upload is ok-----')
若要在浏览器中访问 上传的资源,runserver环境下,需要在项目得主路由下添加media路由的绑定
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
浏览器可以访问 http://127.0.0.1:8000/media/xxxx
Django带有一个用户认证系统。 它处理用户账号、组、权限以及基于cookie的用户会话。
作用:
文档参见
https://docs.djangoproject.com/en/2.2/topics/auth/
User模型类
位置: from django.contrib.auth.models import User
默认user的基本属性有:
属性名 | 类型 | 是否必选 |
---|---|---|
username | 用户名 | 是 |
password | 密码 | 是 |
邮箱 | 可选 | |
first_name | 名 | |
last_name | 姓 | |
is_superuser | 是否是管理员帐号(/admin) | |
is_staff | 是否可以访问admin管理界面 | |
is_active | 是否是活跃用户,默认True。一般不删除用户,而是将用户的is_active设为False。 | |
last_login | 上一次的登录时间 | |
date_joined | 用户创建的时间 |
from django.contrib.auth.models import User
user = User.objects.create_user(username='用户名', password='密码', email='邮箱',...)
from django.contrib.auth.models import User
user = User.objects.create_superuser(username='用户名', password='密码', email='邮箱',...)
from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.is_active = False # 记当前用户无效
user.save()
print("删除普通用户成功!")
except:
print("删除普通用户失败")
from django.contrib.auth.models import User
try:
user = User.objects.get(username='xiaonao')
user.set_password('654321')
user.save()
return HttpResponse("修改密码成功!")
except:
return HttpResponse("修改密码失败!")
from django.contrib.auth.models import User
try:
user = User.objects.get(username='xiaonao')
if user.check_password('654321'): # 成功返回True,失败返回False
return HttpResponse("密码正确")
else:
return HttpResponse("密码错误")
except:
return HttpResponse("没有此用户!")
如果需要在默认auth表上扩展新的字段,如phone
1,添加新的应用
2,定义模型类 继承 AbstractUser
3,settings.py中 指明 AUTH_USER_MODEL = '应用名.类名'
#models.py案例
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class UserInfo(AbstractUser):
phone = models.CharField(max_length=11, default='')
#settings.py添加配置
AUTH_USER_MODEL = 'user.UserInfo'
#添加用户
from user.models import UserInfo
UserInfo.objects.create_user(username='guoxiao', password='123456', phone='13488871101')
QQ邮箱->设置->帐户->“POP3/IMAP......服务”
# 发送邮件设置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 固定写法
EMAIL_HOST = 'smtp.qq.com' # 腾讯QQ邮箱 SMTP 服务器地址
EMAIL_PORT = 25 # SMTP服务的端口号
EMAIL_HOST_USER = '[email protected]' # 发送邮件的QQ邮箱
EMAIL_HOST_PASSWORD = '******' # 在QQ邮箱->设置->帐户->“POP3/IMAP......服务” 里得到的在第三方登录QQ邮箱授权码
EMAIL_USE_TLS = True # 与SMTP服务器通信时,是否启动TLS链接(安全链接)默认false
视图函数中
from django.core import mail
mail.send_mail(
subject, #题目
message, # 消息内容
from_email, # 发送者[当前配置邮箱]
recipient_list=['[email protected]'], # 接收者邮件列表
)
在安装机器上安装和配置同版本的环境
django 项目迁移
$ sudo scp 当前项目源代码 远程主机地址和文件夹
sudo scp /home/tarena/django/mysite1 [email protected]:/home/root/xxx
请输入root密码:
用 uwsgi 替代python3 manage.py runserver
方法启动服务器
配置 nginx 反向代理服务器
用nginx 配置静态文件路径,解决静态路径问题
WSGI (Web Server Gateway Interface)Web服务器网关接口,是Python应用程序或框架和Web服务器之间的一种接口,被广泛使用
使用 python manage.py runserver
通常只在开发和测试环境中使用。
当开发结束后,完善的项目代码需要在一个高效稳定的环境中运行,这时可以使用WSGI
uWSGI是WSGI的一种, 它实现了 http协议 WSGI协议 以及 uwsgi协议
安装uWSGI
终端输入如下命令
sudo pip3 install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/
检查是否安装成功
sudo pip3 freeze|grep -i 'uwsgi'
#如果成功安装,则会输出
uWSGI==2.0.18
配置uWSGI
项目同名文件夹/uwsgi.ini
[uwsgi]
# 套接字方式的 IP地址:端口号
# socket=127.0.0.1:8000
# Http通信方式的 IP地址:端口号
http=127.0.0.1:8000
# 项目当前工作目录
chdir=/home/tarena/.../my_project 这里需要换为项目文件夹的绝对路径
# 项目中wsgi.py文件的目录,相对于当前工作目录
wsgi-file=my_project/wsgi.py
# 进程个数
process=4
# 每个进程的线程个数
threads=2
# 服务的pid记录文件
pidfile=uwsgi.pid
# 服务的目志文件位置
daemonize=uwsgi.log
# 开启主进程管理模式
master=true
```
修改settings.py将 DEBUG=True 改为DEBUG=False
修改settings.py 将ALLOWED_HOSTS = [] 改为ALLOWED_HOSTS = [‘网站域名’] 或者 [‘服务监听的ip地址’]
uWSGI的运行管理
启动 uwsgi
$ 进入到项目同名文件夹下 【即settings.py所在目录】
$ sudo uwsgi --ini uwsgi.ini
停止 uwsgi
$ 进入到项目同名文件夹下 【即settings.py所在目录】
$ sudo uwsgi --stop uwsgi.pid
说明:
当uwsgi 启动后,当前django项目的程序已变成后台守护进程,在关闭当前终端时此进程也不会停止。
若执行 stop 操作失败,则需要执行如下操作杀死进程
ps aux|grep 'uwsgi' -> 查看uwsgi进程
tarena 103408 0.0 0.9 137172 39984 ? S 10:02 0:01 uwsgi --ini uwsgi.ini
tarena 103410 0.0 0.9 436200 38552 ? Sl 10:02 0:00 uwsgi --ini uwsgi.ini
ps -ef | grep 'uwsgi' | grep -v grep | awk '{print $2}' | xargs sudo kill -9
测试:
Nginx是轻量级的高性能Web服务器,提供了诸如HTTP代理和反向代理、负载均衡、缓存等一系列重要特性,在实践之中使用广泛。
C语言编写,执行效率高
nginx 作用
原理:
客户端请求nginx,再由nginx 将请求转发 uWSGI 运行的django
ubuntu 下 nginx 安装
$ sudo apt install nginx
vim /etc/apt/sources.list
更改国内源
sudo apt-get update
nginx 配置
# 在server节点下添加新的location项,指向uwsgi的ip与端口。
server {
...
location / {
uwsgi_pass 127.0.0.1:8000; # 重定向到127.0.0.1的8000端口
include /etc/nginx/uwsgi_params; # 将所有的参数转到uwsgi下
}
...
}
nginx服务控制
$ sudo /etc/init.d/nginx start|stop|restart|status
# 或
$ sudo service nginx start|stop|restart|status
通过 start,stop,restart,status 可能实现nginx服务的启动、停止、重启、操作
修改uWSGI配置
项目同名文件夹/uwsgi.ini
下的Http通信方式改为socket通信方式[uwsgi]
# 去掉如下
# http=127.0.0.1:8000
# 改为
socket=127.0.0.1:8000
进入到 项目同名文件夹下
$ sudo uwsgi --stop uwsgi.pid
$ sudo uwsgi --ini uwsgi.ini
在浏览器端输入http://127.0.0.1 进行测试
注意 :
1,此时端口号为80(nginx默认值)
2,Django中有任何修改 需要重启 uwsgi , 否则修改不生效
创建新路径-主要存放Django所有静态文件 如: /home/tarena/项目名_static/
在Django settings.py 中添加新配置
STATIC_ROOT = '/home/tarena/项目名_static/static
#注意 此配置路径为 存放所有正式环境中需要的静态文件
进入项目,执行 python3 manage.py collectstatic 。执行该命令后,Django将项目重所有静态文件 复制到 STATIC_ROOT 中 ,包括Django内建的静态文件【如admin后台的样式】
Nginx配置中添加新配置
# file : /etc/nginx/sites-enabled/default
# 新添加location /static 路由配置,重定向到指定的 第一步创建的路径即可
server {
...
location /static {
# root 第一步创建文件夹的绝对路径,如:
root /home/tarena/项目名_static;
}
...
}
from django.http import Http404
def xxx_view( ):
raise Http404 # 直接返回404
当正式服务器上代码运行有报错时,可将错误追溯信息发至指定的邮箱
配置如下 settings.py中
#在基础邮件配置之后 添加如下
#关闭调试模式
DEBUG = False
#错误报告接收方
ADMINS = [('guoxiaonao', '[email protected]'), ('wanglaoshi', '[email protected]')]
#发送错误报告方,默认为 root@localhost账户,多数邮件服务器会拒绝
SERVER_EMAIL = 'email配置中的邮箱'
报错邮件中会显示一些错误的追踪,这些错误追踪中会出现如 password等敏感信息,Django已经将配置文件中的敏感信息 过滤修改为 多个星号,但是用户自定义的视图函数需要用户手动过滤敏感信息
1,视图函数中的局部变量
from django.views.decorators.debug import sensitive_variables
@sensitive_variables('user', 'pw', 'cc')
def process_info(user):
pw = user.pass_word
cc = user.credit_card_number
name = user.name
...
#注意:
#1 若报错邮件中牵扯到user,pw,cc等局部变量的值,则会将其替换成 *****, 而 name 变量还显示其真实值
#2 多个装饰器时,需要将其放在最顶部
#3 若不传参数,则过滤所有局部变量的值
2,POST提交中的数据
from django.views.decorators.debug import sensitive_post_parameters
@sensitive_post_parameters('password', 'username')
def index(request):
s = request.POST['username'] + request.POST['abcd']
#'abcd' 并不存在,此时引发error
#POST中 username 及 password的值会被替换成 ******