Django框架
1.web应用处理流程
2.web框架的意义
- 用于搭建web应用程序
- 免去不同web应用相同代码部分的重复编写,只需关心web应用核心的业务逻辑实现
3.web应用程序本质
- 接收并解析http请求,获取具体的请求信息
- 处理本次http请求,即完成本次请求的业务逻辑处理
- 构造并返回处理结果–http响应
4.web框架
- 如何搭建工程程序
- 工程的组建
- 工程的配置
- 路由的定义
- 视图函数的定义
- 如何获取请求数据(操作requests对象)
- 如何构造响应数据(构造response对象)
- 框架提供的其他功能组建使用
- 数据库
- 模板
- admin
5.Django流程
- 创建Django项目和应用
- django-admin startproject name
- python manager.py startapp name 或 django-admin startapp appname
- 视图和URL
- 视图的请求和响应
- URL的匹配路径
- Django主要目的:
- 简便,快速的开发数据库驱动的网站
- 强调代码复用
- Django特点
- 重量级框架
- 对比于Flask框架,Django原生提供了众多的功能组件,让开发更简便快捷
- 提供项目工程管理的自动化脚本工具
- 数据库ORM支持(对象关系映射 Object Relational Mapping)
- 模板
- 表单
- Admin管理站点
- 文件管理
- 认证权限
- session机制
- 缓存
- 重量级框架
- MVT模式
- mvc核心思想:分工,解耦,让不同代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容
- MVC
- M-Model,主要封装对数据库层面的访问,对数据库中的数据进行增删改查
- V-View,用于封装结果,生成页面展示的html内容
- C-Controller,用于接收请求,处理业务逻辑,与Model和View交互,返回结果
- MVT
- M-Model,与MVC中功能相同,负责和数据库交互,进行数据处理
- V-View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答
- T-Template,与MVC中的V功能相同,负责封装构造要要返回的html
- 虚拟环境搭建
- 为什么要搭建虚拟环境?
- 在同一台电脑上想开发多个不同的项目,需要用到同一个包的不同版本,安装的新版本会覆盖旧版本,其他项目就会无法继续运行
- 虚拟环境可以搭建独立的python运行环境,使得单个项目运行环境与其他项目互不影响
- 安装虚拟环境
- 创建虚拟环境 mkvirtualenv -p python解释器地址 虚拟环境名称
- 查看虚拟环境 workon
- 进入虚拟环境 workon 虚拟环境名称
- 删除虚拟环境 rmvirtualenv 虚拟环境名
- 退出虚拟环境 deactivate
- 为什么要搭建虚拟环境?
6.创建Django项目
- 创建Django项目 django-admin startproject name
- manage.py—管理工具 / 脚本(如创建子应用 管理项目)
- setting.py—项目整体配置文件
- BASE_DIR 表示文件的绝对路径
- DEBUG=True 开发者进行调试用的,当上线时为False
- ALLOWED_HOSTS 允许以哪个主机的形式访问后端 默认为127.0.0.1
- INSTALLED_APPS 注册子应用 book.apps.BookConfig
- ROOT_URLCONF 工程url的配置入口 默认是 工程名.urls
- TEMPLATES 模板相关的配置 ‘DIRS’: [os.path.join(BASE_DIR,‘template’)], 设置模板路径
- DATABASES 数据库配置
- LANGUAGE_CODE = ‘zh-Hans’ admin界面语言
- TIME_ZONE = ‘Asia/Shanghai’ 时区
- STATICFILES_DIRS=[ os.path.join(BASE_DIR,‘static’), ] 告知系统静态文件的位置
- STATIC_URL = ‘/static/’ 用于区分静态资源
- urls.py—路由相关的
- 不能在开始加反斜杠,推荐在结束加反斜杠
- 正确:path/ 正确:path 错误:/path 错误:/path
- 请求的url被看做一个普通的Python字符串,进行匹配时不包括域名,get/post参数
- 虽路由结尾/有好处,但违背了HTTP中url表示资源位置路径的设计理念
- wsgi.py—程序入口(是项目与WSGI兼容的web服务器入口)
- 创建子应用 python manager.py startapp name
- views.py—和视图相关
- tests.py—和测试相关,填写单元测试用例
- models.py—和模型相关,用户保存数据库模型类
- migrations—和迁移相关,存放数据库迁移历史文件
- admin.py—和后台相关
- apps.py—和当前子应用相关
- 启动(默认为8000) python manage.py runserver ip:端口号 启动错误 关闭–ctrl+c
- 在开发阶段,为了能够快速预览到开发效果,django提供了一个纯python编写的轻量级web服务器,仅在开发阶段使用
- 注册子应用 在工程setting.py中的INSTALLED_APPS中添加子应用
# Application definition
#安装/注册子应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 'login'
'login.apps.LoginConfig'#login.apps.LoginConfig==login
#'子应用名'
#'子应用名.apps.子应用名Config'
]
9.模型
当前项目的开发,都是数据驱动的
以书籍信息管理的数据关系:书籍和人物:一对多关系
书籍信息(西游记(孙悟空,猪八戒),三国演义(曹操,吕布))
书籍信息表
id | name |
---|---|
1 | 西游记 |
2 | 三国演义 |
人物信息表
id(主键) | name(人名) | gender(性别) | book(外键) |
---|---|---|---|
1 | 孙悟空 | False | 1 |
2 | 白骨精 | True | 1 |
3 | 曹操 | False | 2 |
4 | 吕布 | False | 2 |
-
使用Django进行数据库开发
- MVT设置模式中的Model,专门负责和数据库交互 对应(models.py)
- 由于Model中内嵌了ORM框架,所以不需要直接面向数据库编程
- 而是定义模型类,通过模型和对象完成数据库表的增删改查
- ORM框架,就是把数据库表的行与相应的对象建立关联,互相转换,使得数据库的操作面向对象
-
使用Django进行数据库开发步骤:
- 定义模型类
- 根据书籍表结构设计模型类
- 模型类:Book
- 书籍名称字段:name
- 根据人物表结构设计模型类:
- 模型类:People
- 人物姓名字段:name
- 人物性别:gender
- 外键约束:book
- 外键要指定所属的模型类 book=models.ForeignKey(Book)
- 根据书籍表结构设计模型类
- 模型迁移
- 生成迁移文件(会创建一个数据表和模型的对应关系)迁移之前要注册相应子应用(No changes detected) 迁移成功后会在迁移文件中生成迁移文件
- Python manage,py makemigrations
- 再迁移(会在数据库中生成表)
- Python manage.py migrate
- ok—代表迁移成功 数据库信息在工程文件中setting.py中的Database中
- 生成迁移文件(会创建一个数据表和模型的对应关系)迁移之前要注册相应子应用(No changes detected) 迁移成功后会在迁移文件中生成迁移文件
- 操作数据库
- 在视图文件中操作
- 定义模型类
###################################新增数据##############################
from book.models import Book
#方式1
"""
# 会把新生成的对象返回给我们
book=Book(
name='python',
pub_date='2020-02-20'
)
#需要手动调用save方法
book.save()
"""
#方式二 直接入库
# objects 模型的管理类 对模型的增删改查都用objects
Book.objects.create(
name='java',
pub_date='2020-1-1'
)
################################修改(查询)数据###########################
from book.models import Book
#方式一
"""
#1.先查询数据 select * from book where id=1
book=Book.objects.get(id=1)
#2.直接修改实例的属性
book.readcount=20
#3.调用save()方法
book.save()
"""
#方式二 直接更新 filter过滤 ==查询
Book.objects.filter(name='python').update(
readcount=100,
commentcount=10
)
#######################删除数据##########################
from book.models import Book
#方式一
"""
#1.先查询数据
book=Book.objects.get(name='java')
#调用删除方法
book.delete()
"""
# 方式二
Book.objects.filter(name='java').delete()
#################基本查询###############################
from book.models import Book
#get 得到耨一个数据
#all 获取所有数据
#count 个数
#返回一个对象 查询不存在的数据会抛出异常
try:
#单一对象
book=Book.objects.get(id=1)
except Book.DoesNotExist:
pass
#返回所有数据 是一个列表
book=Book.objects.all()
#count
Book.objects.all().count()
Book.objects.count()
#两者是等价的
#################filter,get,exclude####################
#相当于where查询
#filter 筛选/过滤 返回 n个结果(n=0/1/n)
#get 返回1个结果
#exclude 排除掉符合剩下的结果 相当于 not
#语法形式:
#以filter(字段名_运算符=值)
# 1.查询编号为1图书
Book.objects.get(id_exact=1)# exact 精确的 准确的
Book.objects.get(id=1)#习惯使用简写形式 返回的是单一对象
Book.objects.filter(id=1)# 返回的是一个列表
# 2.查询书名包含'湖'的图书
Book.objects.filter(name__contains='湖')#contains 包含
#3.查询书名以'部‘结尾的图书
Book.objects.filter(name__endswith='部')#endswith 以什么结尾
# 3.查询书名为空的图书
Book.objects.filter(name__isnull=True)# 书名为空 []
# 4.查询编号为1或3或5的图书
Book.objects.filter(id__in=[1,3,5])
# 5.查询编号大于3的图书
#gt(great)---大于 gte(equal)---大于等于 lt(less than)---小于
Book.objects.filter(id__gt=3)
#5.查询书籍id不为3的图书
Book.objects.exclude(id=3)
# 6.查询1980年发布的图书
Book.objects.filter(pub_date__year='1980')
# 7.查询1990年1月1日后发表的图书
Book.objects.filter(pub_date__gt='1990-1-1')
- F和Q对象
- F对象
- 之前的查询都是属性与常量之间的比较,两个属性怎么比较呢?使用F对象,被定义在django.db.models中
- 语法:F(属性名)
- F对象
####################F和Q对象################
# F对象 两个属性比较
#语法:filter(字段名__运算符=F('字段名'))
#查询阅读量大于等于评论量的图书
from django.db.models import F
Book.objects.filter(readcount__gte=F('commentcount'))
# 可以在F对象上使用算数运算
#查询阅读量大于评论量两倍的图书
Book.objects.filter(readcount__gte=F('commentcount')*3)
2. Q对象
#Q对象
#查询id大于2,并且阅读量大于20的书籍
#方式1
#filter().filter()
Book.objects.filter(id__gt=2).filter(readcount__gt=20)
#方式2
#filter(条件,条件)
Book.objects.filter(id__gt=2,readcount__gt=20)
#查询id大于2或者 阅读量大于20的书籍
from django.db.models import Q
# Q(字段名__运算符=值)
#或 Q()|Q()..
#并且 Q()&Q()..
#not ~Q()
Book.objects.filter(Q(id__gt=2)|Q(readcount__gt=20))
#查询书籍id不等于3的
Book.objects.exclude(id=3)
Book.objects.filter(~Q(id=3))
3. 聚合函数
###########################聚合函数#####################
#Sum Max Avg Count
#聚合函数需要使用 aggregate 语法形式:aggragte(Xxx('字段'))
from django.db.models import Sum,Avg,Max,Min,Count
Book.objects.aggregate(Sum('readcount'))# {'readcount__sum': 234}
4. 排序
###########################排序########################
#默认升序
Book.objects.all().order_by('readcount')
#降序
Book.objects.all().order_by('-readcount')
5. 关联查询
################关联查询###############
#书籍与人物 1:n
#书籍中没有任何人物的字段
#人物中有关于书籍的字段
#语法: 通过书籍查询人物信息(已知 主表信息, 关联查询从表数据)
#主表模型(实例对象).关联模型类名小写_set.all()
# 通过人物查询书籍信息 (已知从表数据,关联查询 主表数据)
# 从表模型(实例对象).外键
#查询书籍为1的所有人物信息
#通过书籍 查询人物
#1.查询书籍
book=Book.objects.get(id=1)
#2.查询书籍关联人物信息
book.people_set.all()
#查询人物为1的书籍信息
#1.查询人物
from book.models import People
person=People.objects.get(id=1)
#2.查询人物关联书籍
person.book # <Book: 射雕英雄传>
# person.book.name/readcount..
#########关联查询筛选########
#书籍和人物关系 1:n
#语法:
# 需要主表数据 已知条件是从表信息
# filter(关联模型类名小写__字段__运算符=值)
#需要从表信息 已知主表信息
# filter(外键__字段__运算符=值)
#查询图书,要求图书人物为“郭靖”
Book.objects.filter(people__name__exact='郭靖')
Book.objects.filter(people__name='郭靖')
#查询图书,要求图书中人物描述包含“八”
Book.objects.filter(people__description__contains='八')
#查询书名为天龙八部的所有人物
People.objects.filter(book__name__exact='天龙八部')
People.objects.filter(book__name='天龙八部')
#查询图书阅读量大于30的所有人
People.objects.filter(book__readcount__gt=30)
6. 查询集QuerysSet
1. 初识
1. Django的ORM中存在查询集的概念
2. 查询集 也称 查询结果集,QuerySet,表示从数据库中获取的对象集合
3. 当调用以下过滤器方法时,Django会返回查询集(而不是简单的列表)
1. all() 返回所有数据
2. exclude() 返回满足条件之外的数据
3. filter() 返回满足条件的数据
4. order_by() 对结果进行排序
4. 对查询结果集可再次调用 过滤器 进行过滤 books=Book.objects.filter(readcount_gt=20).order_by('pub_date')
5. 从SQL角度,查询集 与select语句等价,过滤器等价 where ,limit ,order by
6. 判断某一查询集是否有数据?exits() : 判断查询集中是否有数据,若有返回True
2. 两大特性:
1. 惰性执行
1. 创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代,序列化,与if合用
2. 例此 并未进行数据库查询 只是创建了一个查询集books books=Book.objects.all() 执行遍历 for i in boost: 才进行数据库查询
2. 缓存
1. 使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来。再次使用这个查询集时会会使用缓存的数据,减少了数据库的查询次数
2. 两个查询集 ,无法重用缓存,增加了数据库负载 [book.id for book in Book.objects,all()]
3. 可使用缓存 books=Book.objects.all() [book.id for book in books] ...
3. 限制查询集
1. 可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句 但不支持负数索引
2. [0]与[0:1].get()等同 ,没有数据,[0]会抛出IndexEror异常 [0:1].get() 会抛出DoesNotExist()异常
7. 分页(Pagination)
#################分页#############
#导入分页类
from django.core.paginator import Paginator
#查询数据
books=Book.objects.all()
# objects_list 结果集/列表
# per_page 每页多少条记录
#创建分页实例
p=Paginator(books,2)
#获取指定页数据
books_page=p.page(1)
#获取分页数据
total_page=p.num_pages
分页文档
5. 属性类型
类型 | 说明 |
---|---|
AudioField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True / False |
NullBooleanField | 支持Null.True,False |
CharField | 字符串,参数max_length表示最大字符个数 |
TextField | 大文本字段,一般超过4000个字符时使用 |
IntegerField | 整数 |
DecimalField | 十进制浮点数,,参数max_digits表示总位数,参数decimal_places表示小数位数 |
FloatField | 浮点数 |
DataField | 日期,参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于“最后一次修改时间”的时间戳,它总是使用当前日期,默认为False;参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False;参数auto_now和auto_add是相互排斥的,组合将会发生错误 |
null 是数据库范畴的概念,blank是表单验证范畴的
- 外键
- 在设置外键时,通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量
- CASCADE级联:删除主表数据时同时删除外键表数据
- PROTECT保护:通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
- SET_NULL设置为NULL,仅在该字段null=True允许为null时可用
- SET_DEFAULT设置为默认值,仅在该字段设置了默认值时可用
- SET() 设置为特定值或者调用特定方法
- DO_NOTHING不做任何操作,如果数据库的前置指明级联性,此选项会抛出IntegrityError异常
- 修改数据库引擎
- 数据库:
- 小型数据库(性能有瓶颈):sqlite
- 一个嵌入式的关系型数据库,主要在移动端使用
- 中型数据库:mysql(甲骨文),sqlserver(微软)
- 大型数据库:oracle ,DB2
- 小型数据库(性能有瓶颈):sqlite
- 修改:
- 安装驱动程序 pip install PyMSQL
- 在Django的工程同名子目录的__init__.py文件添加:
- import pymysql
- pymysql.install_as_MySQLdb()
- 作用:让Django的ORM能以mysqldb的方式来调用PyMSQL
- 修改DATABASE配置信息
- ENGINE:引擎
- HOST:主机
- PORT:端口号
- USER:用户名
- PASSWORD:密码
- NAME:指定数据库
- 数据库:
10.admin站点管理
- 管理界面本地化
- 本地化是将显示的语言,时间等使用本地的习惯,也就是中国化
- 修改站点界面为中文—工程文件settings.py—LAMGUAGE_CODE=‘zh-Hans’
- 设置站点用户名,密码,邮箱—Python manage.py createsuperuser
- 添加子应用数据—在子应用admin.py—注册模型admin.site.register(Book)—进入站点管理—添加数据—工程模型文件中—对应的类中显示当应的属性def __ str__:
- 站点分为: 内容发布 和 公共访问
- 内容发布的部分由网站的管理官负责查看,添加,修改,删除数据
- Django能够根据定义的模型类自动的生成管理模块
- 使用Django管理模块,步骤:
- 管理界面本地化
- 创建管理员
- 注册模型
- 发布内容到数据库
11.视图与URL
- 对于Django的设计框架MVT
- 用户在URL中请求的是视图
- 视图接收请求后进行处理
- 并将处理的结果返回给请求者
- 使用视图两步走:
- 定义视图
- 配置URLconf
- 定义视图
- 视图就是一个python函数,被定义在应用的views.py中
- 视图的第一个参数是HttpRequest类型的对象requests,包含素有请求信息
- 视图必须返回HttpResponset对象,包含返回请求者的响应信息
- 需要导入HttpResponse模块:from django.http import HttpResponse
- url
12.模板
- 新建模板文件template
- 配置模板 —项目setting.py中—TEMPLATES—DIRS(设置模板路径)—os.path.join(BASE_DIR,‘template’)
- 修改子应用中的视图函数return render(request,template_name,context=None)
- 步骤:
- 创建模板
- 设置模板查找路径
- 模板接收视图传入参数
- 模板处理数据
13.静态文件
项目中的CSS,图片,js都是静态文件,一般会将静态文件放到一个单独的目录中,以方便管理,在html页面中调用,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径,静态文件可以放在项目根目录下,也可以放在应用目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目根目录下,方便管理。
-
为了提高静态文件,需要配置两个参数:
- STATICAFILES_DIRS 存放查找静态文件的目录
- STATIC_URL 访问静态文件的url前端
- 例:
- 在项目根目录下创建static目录来保存静态文件
- 在项目文件下settings.py中修改静态文件的两个参数
-
Django是如何区分静态和动态资源的?
- 通过STATIC_URL
- 访问静态资源 https://ip:port+STATIC_URL+文件名
14.App应用配置
在每个应用目录中都包含了apps.py文件,用于保存该应用的相关信息
在创建应用时,Django会同apps,py文件中写入一个该应用的配置类,如:
from django.apps import AppConfig
将此类添加到settings.py中的INSTALLED_APPS列表中,表名注册安装具备此配置属性的应用
- AppConfig.name 属性表示这个配置是加载到哪个应用的,每个配置类必须包含此属性,默认自动生成
- AppConfig.verbose_name 属性用于设置该应用的直观可读的名字,此名字在Django提供的Admin管理站点中会显示
15.shell工具
Django的manage工具提供了shell命令,帮助我们配置好当前工程的运行环境(如连接好数据库等),以使可以直接在终端中执行测试python语句
通过此命令进入: python manage.py shell
16.路由命名与reverse反解析(逆向)
- 路由命名
- 在定义时为路由命名 ,可以方便查找特定视图的具体路径信息
- 在使用include函数定义路由时,可以使用namespace参数定义路由命名空间,避免了不同子应用中路由使用相同名字起冲突
- 在定义普通·路由·时,可以使用name参数指明路由名字
- reverse 反解析
- 使用reverse函数,可以根据路由名字,返回具体的路由路径
- 导包 from django.core.urlresolvers import reverse / from django.urls import reverse
- url=reverse(‘namespace:name’)
17.PostMan对请求进行测试
PostMan是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件,可以直接去对我们写出来的路由和视图函数进行调试
- 官网下载桌面版
- 谷歌商店搜索PostMan扩展程序进行安装
- 将下载好的插件文件拖入浏览器
18.HttpRequest对象
-
利用HTTP协议向服务器传参有几种途径?
- 提取URL的特定部分,如/weather/beijing,可以在服务器端的路由中使用正则表示式截取
- 查询字符串,形如key1=value1&key2=value2
- 请求体body中发送数据,如表单数据,json,xml
- 在http报文的头(header)中
-
URL路径参数
- 若想在URL中获取值,需要在正则表达式中使用分组
- 获取值:
- 位置参数
- 参数位置不能错
- 关键字参数
- 参数位置可变,跟关键字保持一致即可
- 位置参数
- 两种参数方式不要混合使用,在一个正则表达式中只能使用一种参数方式
-
Django中的QueryDict对象
- HttpRequest对象的属性GET,POST都是QueryDict类型的对象
- get(‘键’,默认值) ,若一键多值,则获取最后一个值,若值不存在则返回None值,可设置默认值进行后续处理
- getlist(‘键’,默认值):根据键获取值,值以列表返回,可以获取指定键所有值 若键不存在返回空列表 ,可设置默认值进行后续处理
- 查询字符串 Query String
- 获取请求路径中的查询字符串参数,(形如?k1=1&k2=2),可以通过request.GET属性获取,返回QueryDict对象
- 查询字符串不区分请求方式,即若客户端进行POST方式请求,依然可以通过request.GET获取请求中的查询字符串数据
-
表单类型的Form Data
- 前端发送的表单类型请求体数据,可以通过request.POST属性获取,返回QueryDict对象
-
非表单类型 Non-Form Data
- 非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON,XML等)进行解析,request.body返回bytes类型
# 获取url中的key-values
##########GET请求##########
# query_params=request.GET
# print(query_params)# <QueryDict: {'username': ["'python'"], 'password': ["'123'"]}>
#QueryDict query_params.get[''] 以普通的字典形式来获取 一键多值的时候 只能获取最后一个值
#query_params.getlist('') 获取一键多值
#############POST请求##########
# 需要关闭setting.py 文件中的 CSRF 验证机制
# query_params=request.POST
# print(query_params)
############POST json数据##########
# json 是双引号
'''
{
"name":"python_json"
}
json.dumps 将python数据转换为json数据 字典---json形式字符串
json.loads 将json数据转件为python数据 json形式字符串---字典
'''
body=request.body
print(body)# b'{\r\n "name":"python_json",\r\n "password":"123"\r\n}'
body_str=body.decode()# JSON形式的字符串
body_dict=json.loads(body_str)
print(body_dict['name'])
return HttpResponse('detail')
############查询字符串################
'''
get请求 以?分割
? 前 表示路由
?后 表示get方式请求参数 称为查询字符串
'''
-
请求头
-
通过request.META属性获取请求头headers中的数据,request.META为字典类型
- 常见请求头
- CONTENT_LENGTH-请求体的长度(作为字符串)
- CONTENT_TYPE-请求体的MIME类型
- HTTP_ACCEPT-响应可接受的内容类型
- HTTP ACCEPT_ENCODING-响应可接受的编码
- HTTP_ACCEPT_LANGUAGE-响应可接受的语言
- HTTP HOST- 客户端发送的HTTP HOST报头
- HTTP_REFER-引用页,如果有的话
- HTTP USER_AGEN-客户端的用户代理字符串
- QUERY_STRING-查询字符串
- REMOTE_ADDR-客户端的ip地址
- REMOTE_HOST-客户端的主机地址
- REMOTE_USER-Web服务器认证的用户(若有的话)
- REMOTE_METHOD-请求方法,如get / post
- SERVER_NAME-服务器主机名
- SERVER_PORT-服务器端口(字符串形式)
- 常见请求头
-
-
其他常用的HttpRequest对象属性
- method:一个字符串,表示请求使用的HTTP方法,常用 GET POST
- user: 请求的用户对象
- path:一个字符串,表示请求的页面的完整路径,不包含域名,和参数部分
- encoding:一个字符串,表示提交数据的编码格式
- 如果为None则表示使用浏览器的默认值,一般为utf-8
- 此属性是可写的,可以通过修改它来访问表单数据的编码,接下来对属性的任何访问得使用新的encoding值
- FILES:一个类似于字典的对象,包含所有的上传文件
-
请求数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待,可以发送请求体数据的请求方式有 POST,PUT,PATCH,DELETE
-
Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法在settings.py文件中注释掉 CSRF 中间件
19.HttpResponse对象
视图在接收并处理后,必须返回HttpResponse对象或子对象,HttpRequest对象由Django创建,HttpResponse对象由开发人员创建
- HttpResponse
- 可以使用django.http.HttpResponse来构造响应对象
- HttpResponse(content=响应体,content_type=响应体数据类型,status=状态码)
- 也可通过HttpResponse对象属性来设置响应体,响应体数据类型,状态码
- content:表示返回的内容
- status_code:返回的HTTP响应状态码
- 响应头可直接将HttpResponse对象当做字典进行响应头键值对的设置
- 可以使用django.http.HttpResponse来构造响应对象
#############HTTPResponse############
#content 传递字符串 不要字典,对象等数据
#status 100-599 整数 只能适应系统规定的
#content_type 是一个MIME类型 语法: 大类/小类 application/json text/html image/png
# data='python_httpResponse'
# return HttpResponse(data,status=200)
#############JsonResponse##########
# 返回字典类型
from django.http import JsonResponse
data={'name':'python_httpResponse','pass':'123'}
return JsonResponse(data,status=200)
20.重定向
from django.shortcuts import redirect
def detail(request,category_id,book_id):
print(category_id,book_id)
###########重定向############
# return redirect('http://www.baidu.com/')
#重定向到首页
path=reverse('book:index')
return redirect(path)
21.状态保持
浏览器请求服务器是无状态的
无状态:指一次用户请求时,浏览器,服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求
无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会处理页面完毕之后销毁页面对象
有时需要保持用户的浏览状态,比如用户是否登录过,浏览过哪些商品
- 实现状态保持主要有两种方式:
- 在客户端存储信息使用cookie
- 过期时间:max_age 单位为秒,默认为None,若是临时cookie,可将max_age设置为None
- 在客户端存储信息使用cookie
############Cookie################
'''
面试:
你是如何理解cookie的?
1.概念 2.流程(流程,从http角度分析) 3.在开发过程中哪里使用了 4.在开发过程中遇到什么印象深刻的地方
保存在客户端 Cookie cooki在请求头中
* cookie 保存在客户端,换个客户端(浏览器)就没有cookie信息了
* cookie 是基于域名的(ip(启动ip))
1.流程(原理)
第一次 请求过程:
1.浏览器第一次请求服务器时,不会携带任何cookie信息
2.浏览器接收请求后,发现,请求中没有任何cookie信息
3.服务器设置一个cookie,这个cookie设置在响应信息中
4.浏览器接收这个响应后,发现响应中有cookie信息,浏览器会将cookie信息保存起来
第二次请求:
5.浏览器第二次请求服务器及其之后都会携带cookie信息
6.服务器接收到请求之后,会发现请求中携带cookie信息,这样就认识是哪个的请求了
2.看效果
3.从http协议角度深入掌握cookie的流程(原理)
第一次:
1.第一次请求不会携带任何cookie信息,请求头中没有任何cookie信息
2.服务器会为响应设置cookie值,响应头中有set_cookie信息
第二次:
3.第二次及其之后的请求都会携带cookie信息,请求头中cookie信息
4.(可选)在当前代码中,没有在响应头中设置cookie,所以响应头中没有set_cookie信息
保存在服务端 Session
'''
#第一次请求:
def set_cookie(request):
#1.判断是否有cookie信息
#先假设没有
#2.获取用户名
username=request.GET.get('username')
#3.因为假设没有cookie,所以服务器要设置cookie
response=HttpResponse('set_cookie')
#key value
#过期时间:max_age 从服务器接收到这个请求时间 + 秒数 计算之后的时间
response.set_cookie('username',username,max_age=3600)
#删除cookie
response.delete_cookie('username') # 第一种
# resposne.set_cookie('username',username,max_age=0)# 第二种
#4.返回响应
return response
#第二次请求
def get_cookie(request):
#第二次请求及其后面 请求都是携带 cookie 信息
#1.服务器可以接受(查看)cookie信息 cookies就是一个字典
cookies=request.COOKIES
username=cookies.get('username')
print(username)
#得到用户信息就可以进行其他的业务逻辑了
return HttpResponse('get_cookie')
2. 在服务端存储信息使用Session
1.
2. 启用Session
1. Django项目默认启用Seesion,若想禁用,需在工程的setting.py中注释掉 'django.contrib.sessions.middleware.SessionMiddleware',
3. 存储方式
1. 在setting.py文件中,可以设置session数据的存储方式,可以保存在数据库,本地缓存等。
2. 数据库
1. 存储数据库中,如下设置可写,可不写,这是默认存储方式
SESSION_ENGINE='django.contrib.sessions.backends.db'
3. . 若存储在数据库中。需要在INSTALLED_APPS中安装Session应用 'django.contrib.sessions',
4. 清除所有session,在存储中删除值部分 request.session.clear()
5. 清除session数据,在存储中删除session的整条数据 request.session.flush()
6. 删除session中指定的键及值,在存储中只删除某一个键及对应的值 del request.session['键']
7. 设置session的有效期 request.session.set_expiry(vaule)
1. 若value是一个整数,session 将在value秒没有活动后过期
2. 若value是0,那么用户的session的Cookie将在用户的浏览器关闭时过期
3. 若value为None,那session有效期将采用系统默认值,默认值为两周,可以通过setting.py 文件中设置 SESSION_COOKIE_AGE 设置全局默认值
################Session##############
'''
保存在服务器的数据-Session:
问题1:换个浏览器还能获取到session信息吗?不可以(session依赖于cookie)
问题2:不换浏览器,删除sessionid,则获取不到session数据
问题3;再去执行set_session 时,会重新生成sessionid,原有的sessionid等待过期
Session 需要依赖于 cookie (若浏览器使用了 禁用cookie 则Session不能实现)
1.概念
2.流程
第一次请求:
1.第一次请求时可以携带一些信息(用户名/密码)cookie中没有任何信息
2.当服务器接收这个请求后,进行用户名与密码的验证,验证没有问题可以设置Session信息
3.在设置session信息(session信息保存在服务端)的同时,服务器会在响应头中设置一个sessionid 的cookie信息
4。客户端(浏览器)在接收到响应之后,会将cookie信息保存(保存 sessionid 的信息)起来
第二次请求:
5.第二次 及其之后 的请求都会携带 session id 信息
6.当服务器接收这个请求后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(保存在服务器的session信息)
3.效果
4.从原理(http)角度
1.第一次请求是没有任何cookie信息的
2.在响应头中有session设置的cookie信息, cookie信息是 sessionid :xxx
3.第二次及其之后的请求都会携带 cookie 信息, 请求头中的cookie信息中有一项是sessionid
'''
#设置session
#session 信息保存在数据库中django_session
#设置session信息
#1.将数据保存在数据库
#2.设置一个cookie信息,这个cookie信息是以 sessionid 为key
def set_session(request):
#1.Cookie中没有任何信息
print(request.COOKIES)
#2.对用户名与密码进行验证
#假设认为 用户名和密码正确
user_id=250
#3.设置Session信息
#request.session 是字典形式
request.session['user_id']=user_id
# 4.返回响应
return HttpResponse('set_session')
#第二次请求
def get_session(request):
#5.第二次请求会携带 sessionid 信息
print(request.COOKIES)# {'sessionid': 'vm7honblddf0998hj6ulc8banat4mkdn'}
#6.验证成功后 可以获取 session信息
user_id=request.session['user_id']
# user_id=request.session.get('user_id')
print(user_id)# 250
# 返回响应
return HttpResponse('get_session')
- Session–redis(将session保存在缓存中,session包含三条数据:j键,值,过期是世间)
- 本地缓存
- 存储子本机内存,如果丢失则不能找回,比较数据库的方式读写更快
- 本地缓存
SESSION_ENGINE='django.contrib.session.backends.cache'
2. 混合存储
1. 优先从内存中存取,如果没有则从数据库中存取
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
3. Redis
1. 在redis中保存session,需要引入第三方扩展,可以使用django-redis来解决
1. 安装扩展 pip install django-redis
2. 配置
1. 在settings.py文件中做设置:
2.
#views.py
#---------------------------
from django.shortcuts import render
from django.http import HttpResponse
from django.views import View
# Create your views here.
class SetSession(View):
def get(self,request):
#设置session
#从中间件可看出
#request.session = self.SessionStore=redis缓存
#增加数据
request.session['name']='123Python_redis_session'
#删除某一条数据
# del request.session['name']
# del request.session['键']
#删除所有数据 保留key
request.session.clear()
#将数据库或redis中的key删除
request.session.flush()
#session 是有时间的 默认两周 redis中可用ttl key 查看
#设置时间
# request.session.set_expiry(values)
# values=0,用户session的Cookie将在用户的浏览器关闭时过期
#values=None,session有效期将采用系统的默认值,默认为两周,可通过settings.py中设置
#SESSION_COOKIE_AGE来设置全局默认值
request.session.set_expiry(20)
return HttpResponse('abc')
#获取数据
class GetSession(View):
def get(self,request):
# name=request.session['name']
name=request.session.get('name')# 不存在不会报错
print(name)
return HttpResponse('get session')
#settings.py
#----------------------------
#caches 缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
'PASSWORD':'123'
}
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
#中间件文件--middleware.py
#----------------------------------
def __init__(self, get_response=None):
super().__init__(get_response)
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore
def process_request(self, request):
session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME)
request.session = self.SessionStore(session_key)
22.类视图与中间件
- 注册视图处理get和post请求
- 以函数的方式定义的视图为函数视图,函数视图便于理解,但遇到一个视图对应的路径提供了多种不同的HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳
'''面向过程'''
#GET请求 获取登录页面
def show_login(request):
return render(request)
#POST 请求 登录验证
def veri_login(requst):
return redirect('首页')
# 由2个视图变为 1个视图
def login(request):
#需要区分业务逻辑
if request.method=='GET':
#GET 请求 获取登录页面
return render(request)
else:
# POST请求 登录验证
return redirect('首页 ')
- 类视图使用
- 在Django中也可以使用类来定义一个视图,称为类视图
- 使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义
- 类视图的好处:
- 代码可读性好
- 类视图相对于函数视图有更高的复用性,如果有其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
- 类视图继承来自Django提供的父类View,可使用from django.views.generic import View 或from django.views.generic.base import View
- 配置路由是,使用类视图的as_view() 方法添加
#url 第一个参数 正则
#url 第二个参数 视图函数名
url(r'^login/$',LoginView.as_view(),name='login'),
###########类视图############
'''
登录页面:
GET 请求 获取登录页面
POST请求 验证登录(用户名与密码是否正确?)
'''
#Python支持面向过程也支持面向对象
'''面向过程'''
#GET请求 获取登录页面
def show_login(request):
return render(request)
#POST 请求 登录验证
def veri_login(requst):
return redirect('首页')
# 由2个视图变为 1个视图
def login(request):
#需要区分业务逻辑
if request.method=='GET':
#GET 请求 获取登录页面
return render(request)
else:
# POST请求 登录验证
return redirect('首页 ')
'''
面向对象:
类视图 是采用的面向对象的思路
1.定义类视图
1.继承自 View (from django,views import View)
2.不同的请求方式 有不同的业务逻辑
类视图的方法 就直接采用http的请求方式的名字,作为函数名,get post put
3.类视图方法的第二个参数 必须是请求实例对象
类视图的方法,必须有返回值,返回值是HttpResponse及其子类
2.类视图的url引导
#url 第一个参数 正则
#url 第二个参数 视图函数名
url(r'^login/$',LoginView.as_view()),
'''
from django.view import View
class LoginView(View):
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')
def put(self):
return HttpResponse('put')
'''
- 中间件
- Django中的中间件是一个轻量级,底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出,中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Djagno框架的健壮性,可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
- 中间件定义:
- 定义一个中间件工厂函数,然后返回一个可以被调用的中间件
- 中间件工厂函数需要接收一个可以调用的get_response对象
- 返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象
#中间件 位置不限
'''
中间件的定义:
中间件的作用:
每次请求和响应都会调用
例:
判断每次请求中是否携带了cookie的某些信息
多个中间件:
请求前执行顺序==注册顺序
请求/响应后顺序== 逆注册顺序
before_request1,before_request2--请求响应--after_resquest/response2,after_resquest/response1
'''
#外层函数
def simple_middleware(get_response):
# 此处是中间件 第一次调用执行的地方 DEBUG 模式 会执行两次 init
#内层函数
def middleware(request):
username=request.COOKIES.get('username')
if username is None:
print('username is None')
# return HttpResponse('您还没有登录!')
else:
print('username:',username)
#请求前
print('before_request')
response=get_response(request)
#响应后/请求后
print('after_request/response')
return response
return middleware
'''
username is None
before_request
after_request
[10/Oct/2021 13:03:11] "GET /set_cookie/?username=python_cookie HTTP/1.1" 200 10
username: python_cookie
before_request
python_cookie
after_request
'''
23.MRO的继承顺序
- 多继承遵循MRO
##############MRO###########
'''
个人中心页面 --必须登录才能显示
实现判断
from django.contrib.auth.mixins import LoginRequiredmixin
GET 展示个人中心
POST 实现个人中心信息的修改
定义类视图
'''
from django.contrib.auth.mixins import LoginRequiredMixin
class CenterView(LoginRequiredMixin,View):
def get(self,request):
return HttpResponse('个人中心展示')
def post(self,request):
return HttpResponse('个人中心修改')
24.模板(Django自带)
- 配置
- 在工程中创建模板目录 templates
- 在setting.py配置文件中共修改TEMPLATES配置项的DIRS值 ‘DIRS’: [os.path.join(BASE_DIR,‘templates’)],
- 定义模板 在templates目录中新建一个模板文件如:index.html 变量{{。。。}}
- 模板渲染
- 找到模板—loader.get_template(模板文件在模板目录中的相对路径),–返回模板对象
- 渲染模板—模板对象 render(context=None,request=None)—>返回渲染后的html文本字符串 context模板变量字典,默认值为None request为请求对象,默认为None
#################模板####################
#d定义一个视图
from django.http import HttpResponse
from django.template import loader
def index1(request):
#获取模板
template=loader.get_template('index.html')
#组织数据
context={
'username':'中国'
}
return render(request,'index.html',context=context)
-
模板语法
- 模板变量
- 变量名必须由字母,数字,下划线(不能以下划线开头)和点组成
- 语法: {{变量}}
- 模板变量可以是python的内建类型,也可以是对象
- 模板语句 {% %}
- 注意: 运算符左右两侧 不能紧挨变量或常量,必须有空格
- {% if a == 1 %} 正确
- {% if a==1 %} 错误
- {% forloop.counter %}----表示当前是第几次循环,从1开始
- {% empty %} 列表为空或不存在时执行此逻辑
- 注意: 运算符左右两侧 不能紧挨变量或常量,必须有空格
- 注释
- 单行注释 {# #}
- 多行注释使用comment标签:{% comment %} 。。。{% endcomment %}
- 模板变量
-
过滤器
- 语法:
- 使用管道符号 | 来应用过滤器,用于进行计算,转换操作,可以使用在变量、标签中
- 使用过滤器需要修改参数,则使用冒号 : 传递参数
- 变量|过滤器:参数
- 例:
- safe:—禁用转义,告诉模板这个变量是安全的,可以解释执行
- length—长度,返回字符串包含字符的个数,或列表,元组,字典的元素个数
- default—默认值,若变量不存在时则返回默认值 data | default:‘默认值’
- date—日期,用于对日期类型的值进行字符串格式化,常用的格式化字符:
- Y—年,格式为4位,y—2位的年
- m—月,格式为 01,02
- d—日,格式为01,02
- j—日,格式为1,2
- H—时,格式为24进制,h—格式为12进制
- i—分,格式为0-59
- s—秒,格式为0-59
- value | data:'Y年m月j日 H时i分s秒
- 语法:
#views.py
#################模板####################
#d定义一个视图
from django.http import HttpResponse
from django.template import loader
import datetime
def index1(request):
#获取模板
template=loader.get_template('index.html')
#组织数据
context={
'username':'中国',
'age':14,
'birthday':datetime.datetime.now(),
'firends':['python','java','c++','c'],
'money':{
'2019':12000,
'2020':18000,
'2021':23000,
},
'desc':'<script>alert("host")</script>'
}
return render(request,'index.html',context=context)
class HomeView(View):
def get(self,request):
#1.获取数据
username=request.GET.get('username')
#2组织数据
context={
'username':username
}
return render(request,'index.html',context=context)
#index.html
<body>
<a href="#">{{ username }}</a>
<hr>
我的年龄是:{{age }}
<hr>
我的生日是:{{ birthday }}
{#过滤器的使用 #}
<br>
我的生日是:{{ birthday|date:'Y年m月d日' }}
<hr>
我总共有{{ firends|length }} 个朋友<br>
我的朋友是:{{ firends }}
<hr>
我的闺蜜是:{{ firends.0 }}
<hr>
我第一年的月薪:{{ money.2019 }}
流程控制:
{# {% %} #}
<ul>
我的好友列表:
{% for item in firends %}
{# parentloop': {}, 'counter0': 0, 'counter': 1, 'revcounter': 4, 'revcounter0': 3, 'first': True, 'last': False}--python#}
<li>{{ forloop.counter }}--{{ item }}</li>
{% endfor %}
</ul>
<ul>
我的薪资列表:
{# python中为 items()#}
{% for item in money.items %}
<li>{{ item.0 }}--{{ item.1 }}</li>
{% endfor %}
</ul>
<hr>
{% if age > 10 %}
你是大于10岁的
{% else %}
你是不大于10岁的
{% endif %}
<hr>
我的简介是:{{ desc|safe }}
<hr>
我没有定义的变量:{{ avs|default:'~~~~~~' }}
</body>
- 模板继承
- 模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量
- 父模板
- 多个模板中相同的内容归于父模板
- 标签 block:用于在父模板中预留区域,留给子模板填充差异性内容,名字不能相同,为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同,父模板中也可使用上下文中传递过来的数据
- 子模板
- 标签 extends:继承,写在子模板文件的第一行
#base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}父模板{% endblock title %}</title>
</head>
<body>
{# 父模板 #}
{% block header %}
<h1>顶部</h1>
{% endblock header %}
{% block main %}
<h1>主要部分</h1>
{% endblock main %}
{% block footer %}
<h1>底部</h1>
{% endblock footer %}
</body>
</html>
#detail.html
{# 子模板 #}
{#把继承模板写在最上面 #}
{# 需要改哪 重写 block#}
{#继承自 base.html #}
{% extends 'base.html' %}
{# 重写父模板 #}
{% block title %}子模板 详情页面{% endblock title %}
{% block main %}
<a href="https://www.baidu.com">点我有惊喜!</a>
{% endblock main %}
{#覆盖 底部 #}
{% block footer %}{% endblock footer %}
25.模板(Jinja2)
- Jinja2初识:
- Jinja2 是Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,它的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,尤其是Flask框架内置的模板语言
- 由于django默认模板引擎功能不齐全,速度慢,所以在django中使用jinja2,jinja2比django默认模板引擎快 10-20倍
- Django主流的第三方APP基本上也都支持Django默认模板及jinja2,所以jinja2不会有太多障碍
- 安装 pip install jinja2
- Django配置jinja2
#jinja2_env.py
from jinja2 import Environment
from django.template.defaultfilters import date
def environment(**option):
#1.创建Enviroment 实例
env=Environment(**option)
#2.指定(更新)jinja2的函数指向 django 的指定过滤器
env.globals.updata({
'date':date
})
#3.返回Enviroment实例
return env
2. 修改settings.py 文件 'OPTIONS': 'environment':'book.jinja2_env.environment',
- Jinja2中没有{% comment %} {% endcommment %} 多行注释
- Jinja2中过滤器不一样 {{ date(birthday),‘格式’ }}
- Jinja2中没有forloop ----是loop
- Jinja2中字典遍历 不用 dict1.items 可以用 {% for k,v in dict1.items() %}
#jinja2_env.py
from jinja2 import Environment
from django.template.defaultfilters import date
def environment(**option):
#1.创建Enviroment 实例
env=Environment(**option)
#2.指定(更新)jinja2的函数指向 django 的指定过滤器
env.globals.update({
'date':date
})
#将自定义的过滤器添加到 环境中
env.filters['do_lis']=do_lis
#3.返回Enviroment实例
return env
#自定义过滤器
def do_lis(li):
if li=='0':
return 1
#index.html
{#我的生日是:{{ birthday|date:'Y年m月d日' }}#}
{#我的生日是:{{ date(birthday),'Y年 m月 d日' }}#}
{# 此处报错原因是Pycharm 认为有错 实际没错 Pycharm默认的引擎 是django自带的引擎 需告知Pycahrm现在的引擎为jinja2 #}
{# setting--languages--Template--Jinja2#}
我的生日是:{{ date(birthday) }}
{#列表可以用 .index 字典不可#}
我的闺蜜是:{{ firends.0 }}
我第一年的月薪:{{ money['2019'] }}
{% for k,v in money.items() %}
<li>{{ k }}--{{ v }}</li>
{% endfor %}
26.CSRF
CSRF 全拼:Cross Site Forgery 译为跨站请求伪造
CSRF 指攻击者盗用了你的身份,以你的名义发送恶意请求
包括:以你名义发送邮件,发消息,盗取账号,甚至购买商品,虚拟货币转账等
造成问题:个人隐私泄露以及财产安全
- CSRF 攻击示意图
- 客户端访问服务器时没有同服务器做安全验证
- 同源策略
- 源: 协议,域名和端口号
- 同源策略是浏览器的一个安全功能,不同源客户端脚本在没有明确授权下,不能读写对方资源,所以a.com的js脚本采用ajax不能读取b.com里面的文件,会报错
- CSRF的django使用
- 用于验证
#login.html
#-------------------------------
<form action="",method="post">
{% csrf_token %}
<input type="text" name="'money">
<input type='submit' value="提交">
</form>
#views.py
#--------------------------------
class LoginView(View):
def get(self,request):
return render(request,'login.html')
def post(self,request):
return HttpResponse('post')
#中间件文件 csrf.py
#----------------------------------
def process_request(self, request):
csrf_token = self._get_token(request)
if csrf_token is not None:
# Use same token next time.
request.META['CSRF_COOKIE'] = csrf_token
def _get_token(self, request):
if settings.CSRF_USE_SESSIONS:
try:
return request.session.get(CSRF_SESSION_KEY)
except AttributeError:
raise ImproperlyConfigured(
'CSRF_USE_SESSIONS is enabled, but request.session is not '
'set. SessionMiddleware must appear before CsrfViewMiddleware '
'in MIDDLEWARE.'
)
else:
try:
cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME]
except KeyError:
return None
csrf_token = _sanitize_token(cookie_token)
- 金身本无相,万相由心生