赞
踩
Django 是用python语言写的开源web开发框架,并遵循MVC设计。
Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用,多个组件可以很方便的以"插件"形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNotRepeatYourself)原则。
对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速。
提供项目工程管理的自动化脚本工具
数据库ORM支持(对象关系映射,英语:Object Relational Mapping)
模板
表单
Admin管理站点
文件管理
认证权限
session机制
- 缓存
有一种程序设计模式叫MVC,其核心思想是分工、解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容。
MVC的全拼为Model-View-Controller,最早由TrygveReenskaug在1978年提出,是施乐帕罗奥多研究中心(Xerox PARC)在20世纪80年代为程序语言Smalltalk发明的一种软件设计模式,是为了将传统的输入(input)、处理(processing)、输出(output)任务运用到图形化用户交互模型中而设计的。随着标准输入输出设备的出现,开发人员只需要将精力集中在业务逻辑的分析与实现上。后来被推荐为Oracle旗下Sun公司Java EE平台的设计模式,并且受到越来越多的使用ColdFusion和PHP的开发者的欢迎。现在虽然不再使用原来的分工方式,但是这种分工的思想被沿用下来,广泛应用于软件工程中,是一种典型并且应用广泛的软件架构模式。后来,MVC的思想被应用在了Web开发方面,被称为Web MVC框架。
Django的MVT
Django框架(入门超细笔记 创建项目步骤 )_django创建项目-CSDN博客
pip install virtualenv
进入到DjangoSpace\venv\Scripts目录下执行:pip install Django
安装成功后
django-admin startproject
(项目名称)
例如:创建一个学生管理系统
django-admin startproject studentsystem
与项目同名的目录,此处为studentsystem。
settings.py 是项目的整体配置文件。
urls.py是项目的URL配置文件。
wsgi.py是项目与WSGI兼容的Web服务器入口。
manage.py是项目管理文件,通过它管理项目
在开发阶段,为了能够快速预览到开发的效果,django提供了一个纯python编写的轻量级web服务器,仅在开发阶段使用。
先设置用户:
python manage.py migrate
运行服务器命令:
python manage.py runserver ip:端口
或:python manage.py runserver
注意:如果电脑有酷狗音乐一定要关掉,它有可能会占8000这个端口
django默认工作在调式Debug模式下,如果增加、修改、删除文件,服务器会自动重启
按ctrl+c停止服务器
在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该模块代码整体复制过去,达到复用。
Django的视图编写是放在子应用中的。
在Django中,创建子应用模块的仍然可以通过命令来操作,即
python manage.py startapp 子应用名称
manage.py为上述创建工程时自动生成的管理文件。
例如,在刚才创建的studentsystem工程中,想要创建一个用户student子应用模块,可执行:
python manage.py startapp student
查看此时的工程目录,结构如下:
admin.py 文件跟网站的后台管理站点配置相关。
apps.py 文件用于配置当前子应用的相关信息。
migrations 目录用于存放数据库迁移历史文件。
models.py 文件用户保存数据库模型类。
tests.py 文件用于开发测试用例,编写单元测试。
views.py 文件用于编写Web应用视图函数。
创建出来的子应用目录文件虽然被放到了工程项目目录中,但是django工程并不能立即直接使用该子应用,需要注册安装后才能使用。
在工程配置文件settings.py中,INSTALLED_APPS项保存了工程中已经注册安装的子应用,初始工程中的INSTALLED_APPS如下:
注册安装一个子应用的方法,即是将子应用的配置信息文件apps.py中的Config类添加到INSTALLED_APPS列表中。
例如,将刚创建的book子应用添加到工程中,可在INSTALLED_APPS列表中添加'book.apps.BookConfig'。
用pycharm打开
创建好后
创建项目之前首先要安装Django和pymysql
pip install Django
pip install pymysql
- 当前项目的开发, 都是数据驱动的。
以下为书籍信息管理的数据关系:书籍和人物是 :一对多关系
要先分析出项目中所需要的数据, 然后设计数据库表.
书籍信息表
字段名 | 字段类型 | 字段说明 |
---|---|---|
id | AutoField | 主键 |
name | CharField | 书名 |
id | name |
---|---|
1 | 西游记 |
2 | 三国演义 |
人物信息表
字段名 | 字段类型 | 字段说明 |
---|---|---|
id | AutoField | 主键 |
name | CharField | 人名 |
gender | BooleanField | 性别 |
book | ForeignKey | 外键 |
id | name | gender | book |
---|---|---|---|
1 | 孙悟空 | False | 1 |
2 | 白骨精 | True | 1 |
3 | 曹操 | False | 2 |
4 | 貂蝉 | True | 2 |
使用Django进行数据库开发的提示
MVT
设计模式中的Model
, 专门负责和数据库交互.对应(models.py)
- 由于
Model
中内嵌了ORM框架
, 所以不需要直接面向数据库编程.- 而是定义模型类, 通过
模型类和对象
完成数据库表的增删改查
.ORM框架
就是把数据库表的行与相应的对象建立关联, 互相转换.使得数据库的操作面向对象.
定义模型类
- 模型迁移
- 操作数据库
- 模型类:BookInfo
- 书籍名称字段:name
- 模型类:PeopleInfo
- 人物姓名字段:name
- 人物性别字段:gender
- 外键约束:book
- 外键要指定所属的模型类
book = models.ForeignKey(BookInfo)
- 书籍-人物的关系为一对多. 一本书中可以有多个英雄.
- 不需要定义主键字段, 在生成表时会自动添加, 并且值为自增长.
根据数据库表的设计
- 在
models.py
中定义模型类,继承自models.Model
from django.db import models # Create your models here. ''' 1.定义模型类 2.模型迁移 2.1 先生成迁移文件(不会在数据库中生成表,只会创建一个数据表和模型的对应关系) python manage.py makemigrations 2.2 再迁移(会在数据库中生成表) 3.操作数据库 在哪里定义模型 模型继承自谁就可以 ORM对应的关系 表-->类 字段-->属性 ''' # 准备书籍列表信息的模型类 class BookInfo(models.Model): # 创建字段,字段类型... # 1、主键 当前会自动生成 # 2、属性 name = models.CharField(max_length=10) # 准备人物列表信息的模型类 class PeopleInfo(models.Model): # 姓名 name = models.CharField(max_length=10) # 性别 gender = models.BooleanField() # 外键约束:人物属于哪本书 book = models.ForeignKey(BookInfo,on_delete=models.CASCADE)
迁移由两步完成
生成迁移文件:根据模型类生成创建表的语句
python manage.py makemigrations
执行迁移:根据第一步生成的语句在数据库中创建表
迁移前
迁移后
提示:默认采用
sqlite3
数据库来存储数据
注意事项
解决办法
注意事项
看不到数据库表解决办法
运行
中文显示(改后刷新页面)
内容发布
和公共访问
两部分Django
能够根据定义的模型类自动地生成管理模块Django
的管理模块, 需要按照如下步骤操作 :
- 1.管理界面本地化
- 2.创建管理员
- 3.注册模型类
- 4.发布内容到数据库
- 本地化是将显示的语言、时间等使用本地的习惯,这里的本地化就是进行中国化.
- 中国大陆地区使用
简体中文
, 时区使用亚洲/上海时区
, 注意这里不使用北京时区.本地化前
本地化后
创建管理员的命令
python manage.py createsuperuser
按提示输入用户名、邮箱、密码
注意:要用超级管理员 admin Zw23456..!
重置密码
python manager.py changepassword 用户名
登陆站点 http://127.0.0.1:8000/admin
需要服务器是启动状态
登陆站点成功
站点界面中没有书籍和人物管理入口,因为没有注册模型类
在应用
的admin.py
文件中注册模型类
需要导入模型模块 from book.models import BookInfo,PeopleInfo
注册模型后
注册模型成功后, 就可以在站点管理界面方便快速的管理数据.
- 站点管理页面做好了, 接下来就要做
公共访问
的页面了.- 对于
Django
的设计框架MVT
.
- 用户在URL中请求的是视图.
- 视图接收请求后进行处理.
- 并将处理的结果返回给请求者.
- 使用视图时需要进行两步操作
- 1.定义视图
- 2.配置URLconf
- 视图就是一个
Python
函数,被定义在应用
的views.py
中.- 视图的第一个参数是
HttpRequest
类型的对象reqeust
,包含了所有请求信息
.- 视图必须返回
HttpResponse对象
,包含返回给请求者的响应信息
.需要导入
HttpResponse
模块 :from django.http import HttpResponse
定义视图函数 : 响应字符串
OK!
给客户端
思考 : 如何才能让请求找到视图?
查找视图的过程 :
- 1.请求者在浏览器地址栏中输入URL, 请求到网站.
- 2.网站获取URL信息.
- 3.然后与编写好的URLconf逐条匹配.
- 4.如果匹配成功则调用对应的视图.
5.如果所有的URLconf都没有匹配成功.则返回404错误.
URLconf
入口需要两步完成
URLconf
配置
- 1.在
项目
中定义URLconf
- 2.在
应用
中定义URLconf
在
项目
中定义URLconf
"""bookmanager URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/3.2/topics/http/urls/ Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path,include ''' 1、urlpatterns 是固定写法,值是列表 2、浏览器中输入的路径会和urlpatterns 中的每一项顺序进行匹配 如果匹配成功,则直接引导到相应的模块 如果匹配不成功,则直接返回404 3、urlpatterns中的元素是 url url 的第一个参数是:正则 r 转义 ^ 严格的开始 $ 严格的结尾 4、浏览器中输入的路由 中 哪些部分参与正则匹配 http://ip:port/path/?key=value http://ip:port/ 和 get post 参数不参与正则匹配 5、如果和当前的某一项匹配成功,则引导到子应用中继续匹配 如果匹配成功,则停止匹配返回相应的视图 如果匹配不成功,则继承和后边的工程中的url的每一项继续匹配,直到匹配每一项 6、 ''' urlpatterns = [ # http://127.0.0.1:8000/admin/ # http://127.0.0.1:8000 # admin/?a=100 # admin/ path('admin/', admin.site.urls), # 添加一项 path('',include('book.urls')) ]
在
应用
中定义URLconf
提示:一条
URLconf
包括URL规则、视图两部分
- URL规则使用正则表达式定义.
视图就是在
views.py
中定义的视图函数
from django.urls import path from book.views import index urlpatterns = [ # index/ # url的第一个参数是:正则 # url的第二个参数是:视图函数名 path('index/',index), ]url匹配过程
http://127.0.0.1:8000/index
视图处理过程如下图:
使用视图时需要进行两步操作,两步操作不分先后
- 配置
URLconf
- 在
应用/views.py
中定义视图
思考 : 网站如何向客户端返回一个漂亮的页面呢?
提示 :
- 漂亮的页面需要
html
、css
、js
.- 可以把这一堆字段串全都写到视图中, 作为
HttpResponse()
的参数,响应给客户端.问题 :
- 视图部分代码臃肿, 耦合度高.
- 这样定义的字符串是不会出任何效果和错误的.
- 效果无法及时查看.有错也不容易及时发现.
设想 :
- 是否可以有一个专门定义前端页面的地方, 效果可以及时展示,错误可以及时发现,并且可以降低模块间耦合度!
解决问题 :模板
MVT
设计模式中的T
,Template
在
Django
中, 将前端的内容定义在模板中, 然后再把模板交给视图调用, 各种漂亮、炫酷的效果就出现了.
- 1.创建模板
- 2.设置模板查找路径
- 3.模板接收视图传入的数据
- 4.模板处理数据
- 在
应用
同级目录下创建模板文件夹templates
. 文件夹名称固定写法.- 在
templates
文件夹下, 创建应用
同名文件夹. 例,Book
在
应用
同名文件夹下创建网页模板
文件. 例 :index.html
视图模板加载
总结
- 1.创建视图
- 2.创建模板
- 3.配置URLconf
- 查询数据库数据
- 构造上下文
传递上下文到模板
- from django.shortcuts import render
- from django.http import HttpRequest,HttpResponse
- from book.models import BookInfo
- # Create your views here.
-
- def index(request):
-
- # 实现业务逻辑
- # 1、先把所有书籍查询出来
- # ORM 实现
- books = BookInfo.objects.all()
- # books = [BookInfo(),]
- # 2、组织数据
-
- context = {
- 'books':books
- }
- # 3、将组织好的数据传递给模板
- return render(request,'index.html',context)
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
-
- <body>
- <a href="#">书籍列表</a>
- <ul>
- {% for book in books %}
- <li>{{ book.name }}</li>
- {% endfor %}
-
- </ul>
- </body>
- </html>
进入应用
中的urls.py
文件
- from django.urls import path
- from book.views import index
- urlpatterns = [
- path('index/',index),
- ]
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
当前工程的根目录,Django会依此来定位工程内的相关文件,我们也可以使用该参数来构造文件路径。
调试模式,创建工程后初始值为True,即默认工作在调试模式下。
作用:
修改代码文件,程序自动重启
Django程序出现异常时,向前端显示详细的错误追踪信息,例如
而非调试模式下,仅返回Server Error (500)
注意:部署线上运行的Django不要运行在调式模式下,记得修改DEBUG=False和ALLOW_HOSTS。
项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
为了提供静态文件,需要配置两个参数:
- STATICFILES_DIRS存放查找静态文件的目录
- STATIC_URL访问静态文件的URL前缀
示例
1) 在项目根目录下创建static目录来保存静态文件。
2) 在bookmanager/settings.py中修改静态文件的两个参数为
- STATIC_URL = '/static/'
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, 'static'),
- ]
3)此时在static添加的任何静态文件都可以使用网址/static/文件在static中的路径来访问了。
例如,我们向static目录中添加一个index.html文件,在浏览器中就可以使用127.0.0.1:8000/static/index.html来访问。
或者我们在static目录中添加了一个子目录和文件book/detail.html,在浏览器中就可以使用127.0.0.1:8000/static/book/detail.html来访问。
在每个应用目录中都包含了apps.py文件,用于保存该应用的相关信息。
在创建应用时,Django会向apps.py文件中写入一个该应用的配置类,如
- from django.apps import AppConfig
- class BookConfig(AppConfig):
- name = 'book'
我们将此类添加到工程settings.py中的INSTALLED_APPS列表中,表明注册安装具备此配置属性的应用。
AppConfig.name属性表示这个配置类是加载到哪个应用的,每个配置类必须包含此属性,默认自动生成。
AppConfig.verbose_name属性用于设置该应用的直观可读的名字,此名字在Django提供的Admin管理站点中会显示,如
- from django.apps import AppConfig
-
- class UsersConfig(AppConfig):
- name = 'book'
- verbose_name = '后台相关'
重点
数据的增删改
增:book = BookInfo() book.save()
和 BookInfo.objects.create()
删:book.delete()
和BookInfo.objects.get().delete()
改:book.name='xxx' book.save()
和 BookInfo.objects.get().update(name=xxx)
数据的查询
基础查询
F对象和Q对象
关联查询
查询集QuerySet
django-admin startproject bookmanager01
先设置用户: python manage.py migrate
python manage.py startapp book
运行服务器命令: python manage.py runserver ip:端口
或:python manage.py runserver
- INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- #添加子应用
- 'book.apps.BookConfig'
- ]
- #设置中文
- LANGUAGE_CODE = 'zh-Hans'
- #亚洲上海时区
- TIME_ZONE = 'Asia/Shanghai'
创建管理员命令:python manage.py createsuperuser
在应用同级目录下,创建templates
模板文件夹
- TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [BASE_DIR/'templates'],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
- ]
正则 : 路径只要不是admin/
就算匹配成功。并包含到应用中的urls.py
- from django.contrib import admin
- from django.urls import path,include
-
- urlpatterns = [
- path('admin/', admin.site.urls),
-
- path('',include('book.urls'))
- ]
- # 定义视图:提供书籍列表信息
- def bookList(request):
-
- return HttpResponse('OK!')
urls.py
应用中创建
urls.py
正则 : 路径中包含
booklist/
,就调用视图中对应的bookList
函数
- from django.urls import path
- from book.views import bookList
- urlpatterns = [
- path('booklist/',bookList),
- ]
进入项目文件中, 开启项目对应的服务器
python manage.py runserver浏览器中输入网址 http://127.0.0.1:8000/booklist/
在settings.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库。
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
- }
- }
pip install PyMySQL
在Django的工程同名子目录的__init__.py文件中添加如下语句
- import pymysql
-
- pymysql.install_as_MySQLdb()
作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。
修改DATABASES配置信息
- # sqlite 主要是一个嵌入式的关系型数据库
- # 主要用在移动端
- # sqlite 属于小型的关系型数据库
- # 中型数据库 mysql(甲骨文) sqlserver(微软)
- # 大型数据 oracle DB2
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'HOST': '127.0.0.1', # 数据库主机
- 'PORT': '3306', # 数据库端口
- 'USER': 'root', # 数据库用户名
- 'PASSWORD': '123456', # 数据库用户密码
- 'NAME': 'book', # 数据库名字
- }
- }
在MySQL中创建数据库
create database book charset=utf8;
在models.py 文件中定义模型类。
- from django.db import models
-
- # Create your models here.
- '''
- 1、ORM
- 表 --》 类
- 字段 --》 属性
-
- 2、模型类需要继承models.Model
-
- 3、模型类会自动为我们添加(生成)一个主键
-
- 4、属性名 = 属性类型(选项)
- 属性名:不要使用关键,不要使用连续的下划线
- 属性类型:和mysql的类型相似
- 选项:charfiled 必须设置 max_length
- varchar(m)
- null 是否为空
- unique 唯一
- default 设置默认值
- verbose_name 主要是 admin 后台显示
-
- 书籍表:
- id,name,pub_date,readcount,commentcount,is_delete
-
- '''
- # 准备书籍列表信息的模型类
- class BookInfo(models.Model):
- # 属性名 = 属性类型(选项)
- name = models.CharField(10,unique=True,verbose_name='名字')
- # 发布日期
- pub_date = models.DateField(null=True)
- # 阅读量
- readcount = models.IntegerField(default=0)
- # 评论量
- commentcount = models.IntegerField(default=0)
- # 是否逻辑删除
- is_delete = models.BooleanField(default=False)
-
- class Meta:
- # 改表名
- db_table = 'bookinfo'
- # 修改后台admin的显示信息的配置
- verbose_name = 'admin'
-
-
- def __str__(self):
- return self.name
-
-
- # 准备人物列表信息的模型类
- class PeopleInfo(models.Model):
- # 有序字典
- GENDER_CHOICES = (
- (0, 'male'),
- (1, 'female')
- )
- name = models.CharField(max_length=20, verbose_name='名称')
- gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
- description = models.CharField(max_length=200, null=True, verbose_name='描述信息')
- # on_delete 联级
- book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
- is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
-
- class Meta:
- db_table = 'peopleinfo'
- verbose_name = '人物信息'
-
- def __str__(self):
- return self.name
模型类如果未指明表名,Django默认以小写app应用名_小写模型类名为数据库表名。
可通过db_table指明数据库表名。
django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。
默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。
定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
属性 = models.字段类型(选项)
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串,参数max_length表示最大字符个数 |
TextField | 大文本字段,一般超过4000个字符时使用 |
IntegerField | 整数 |
DecimalField | 十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数 |
FloatField | 浮点数 |
DateField | 日期, 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add和auto_now是相互排斥的,组合将会发生错误 |
TimeField | 时间,参数同DateField |
DateTimeField | 日期时间,参数同DateField |
FileField | 上传文件字段 |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
选项 | 说明 |
---|---|
null | 如果为True,表示允许为空,默认值是False |
blank | 如果为True,则该字段允许为空白,默认值是False |
db_column | 字段的名称,如果未指定,则使用属性的名称 |
db_index | 若值为True, 则在表中会为此字段创建索引,默认值是False |
default | 默认 |
primary_key | 若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用 |
unique | 如果为True, 这个字段在表中必须有唯一值,默认值是False |
null是数据库范畴的概念,blank是表单验证范畴的
在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:
CASCADE级联,删除主表数据时连通一起删除外键表中数据
PROTECT保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
SET_NULL设置为NULL,仅在该字段null=True允许为null时可用
SET_DEFAULT设置为默认值,仅在该字段设置了默认值时可用
SET()设置为特定值或者调用特定方法
DO_NOTHING不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
将模型类同步到数据库中。
python manage.py makemigrations
python manage.py migrate
- insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values
- ('射雕英雄传', '1980-5-1', 12, 34, 0),
- ('天龙八部', '1986-7-24', 36, 40, 0),
- ('笑傲江湖', '1995-12-24', 20, 80, 0),
- ('雪山飞狐', '1987-11-11', 58, 24, 0);
- insert into peopleinfo(name, gender, book_id, description, is_delete) values
- ('郭靖', 1, 1, '降龙十八掌', 0),
- ('黄蓉', 0, 1, '打狗棍法', 0),
- ('黄药师', 1, 1, '弹指神通', 0),
- ('欧阳锋', 1, 1, '蛤蟆功', 0),
- ('梅超风', 0, 1, '九阴白骨爪', 0),
- ('乔峰', 1, 2, '降龙十八掌', 0),
- ('段誉', 1, 2, '六脉神剑', 0),
- ('虚竹', 1, 2, '天山六阳掌', 0),
- ('王语嫣', 0, 2, '神仙姐姐', 0),
- ('令狐冲', 1, 3, '独孤九剑', 0),
- ('任盈盈', 0, 3, '弹琴', 0),
- ('岳不群', 1, 3, '华山剑法', 0),
- ('东方不败', 0, 3, '葵花宝典', 0),
- ('胡斐', 1, 4, '胡家刀法', 0),
- ('苗若兰', 0, 4, '黄衣', 0),
- ('程灵素', 0, 4, '医术', 0),
- ('袁紫衣', 0, 4, '六合拳', 0);
Django的manage工具提供了shell命令,帮助我们配置好当前工程的运行环境(如连接好数据库等),以便可以直接在终端中执行测试python语句。
通过如下命令进入shell
python manage.py shell
导入两个模型类,以便后续使用
from book.models import BookInfo,PeopleInfo
BookInfo.objects.all()
查看mysql数据库日志可以查看对数据库的操作记录。 mysql日志文件默认没有产生,需要做如下配置:
vi /etc/mysql/mysql.conf.d/mysqld.cnf
把68,69行前面的#去除,然后保存并使用如下命令重启mysql服务。
service mysql restart
使用如下命令打开mysql日志文件。
tail -f /var/log/mysql/mysql.log # 可以实时查看数据库的日志内容
# 如提示需要sudo权限,执行
# sudo tail -f /var/log/mysql/mysql.log
增加数据有两种方法。
通过创建模型类对象,执行对象的save()方法保存到数据库中。
- from book.models import BookInfo
- # 方式一
- # 会把新生成的对象返回给我们
- book = BookInfo(
- name = 'python',
- pub_date = '2010-1-1'
- )
-
- # 需要手动待用saver方法
- book.save()
通过模型类.objects.create()保存。
- # 方式二 直接入库
- # objects 模型的管理类
- # 我们对模型的 增删改查 都用它
- BookInfo.objects.create(
- name = 'java',
- pub_date = '2023-1-1'
- )
修改更新有两种方法
修改模型类对象的属性,然后执行save()方法
- from book.models import BookInfo
- # 1、先查询数据
- book = BookInfo.objects.get(id = 1)
- # 2、修改实例属性
- book.readcount = 20
- # 3、调用save方法
- book.save()
使用模型类.objects.filter().update(),会返回受影响的行数
- # 方法二 直接更新
- # filter 过滤
- BookInfo.objects.filter(id = 2).update(
- readcount = 200,
- commentcount = 300
- )
删除有两种方法
- # 方法一
- # 先查出数据
- book = BookInfo.objects.get(name='python')
- # 调用删除方法
- book.delete()
BookInfo.objects.filter(name='java').delete()
get 查询单一结果,如果不存在会抛出模型类.DoesNotExist异常。
all 查询多个结果。
count 查询结果数量。
- # get 得到某一个数据
- # all 获取所有的
- # count 个数
-
- from book.models import BookInfo
-
- # get返回一个对象
- book = BookInfo.objects.get(id=1)
- # 查询不存在的数据会抛出异常 # 完美写法
- try:
- book = BookInfo.objects.get(id=2)
- except BookInfo.DoesNotExist:
- pass
-
-
- # all 返回所有结果是列表
- BookInfo.objects.all()
-
-
- # count
- BookInfo.objects.all().count()
- BookInfo.objects.count()
实现SQL中的where功能,包括
filter 过滤出多个结果
exclude 排除掉符合条件剩下的结果
get 过滤单一结果
对于过滤条件的使用,上述三个方法相同,故仅以filter进行讲解。
过滤条件的表达语法如下:
# 语法形式: filter(属性名称__比较运算符 = 值) # 属性名称和比较运算符间使用两个下划线,所以属性名不能包括多个下划线
exact:表示判等。
例:查询编号为1的图书。
- BookInfo.objects.get(id__exact = 1) # exact 精确的 就是等于
- BookInfo.objects.get(id= 1)
-
- BookInfo.objects.filter(id=1)
- BookInfo.objects.filter(id__exact = 1)
contains:是否包含。
说明:如果要包含%无需转义,直接写即可。
例:查询书名包含'湖'的图书。
- # 查询书名包含'湖'的图书
- # contains 包含
- BookInfo.objects.filter(name__contains='湖')
startswith、endswith:以指定值开头或结尾。
例:查询书名以'部'结尾的图书
- # 查询书名以'部'结尾的图书
- BookInfo.objects.filter(name__endswith='部')
以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith
isnull:是否为null。
例:查询书名为空的图书。
- # 查询书名为空的图书
- BookInfo.objects.filter(name__isnull=True)
in:是否包含在范围内。
例:查询编号为1或3或5的图书
- # 查询编号为1或3或5的图书
- BookInfo.objects.filter(id__in=[1,3,5])
gt 大于 (greater then)
gte 大于等于 (greater then equal)
lt 小于 (less then)
lte 小于等于 (less then equal)
例:查询编号大于3的图书
BookInfo.objects.filter(id__gt=3)
不等于的运算符,使用exclude()过滤器。
例:查询编号不等于3的图书
- # 查询编号不等于3的图书
- BookInfo.objects.exclude(id=3)
year、month、day、week_day、hour、minute、second:对日期时间类型的属性进行运算。
例:查询1980年发表的图书。
- # 查询1980年发表的图书
- BookInfo.objects.filter(pub_date__year=1980)
例:查询1990年1月1日后发表的图书。
- # 查询1990年1月1日后发表的图书
- BookInfo.objects.filter(pub_date__gt='1990-1-1')
注意事项
之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢?
答:使用F对象,被定义在django.db.models中。
语法: F(属性名)
例:查询阅读量大于等于评论量的图书。
- # 查询阅读量大于等于评论量的图书
- from django.db.models import F
- BookInfo.objects.filter(readcount__gt=F('commentcount'))
可以在F对象上使用算数运算。
例:查询阅读量大于2倍评论量的图书。
- # 查询阅读量大于2倍评论量的图书
- BookInfo.objects.filter(readcount__gt=F('commentcount')*2)
多个过滤器逐个调用表示逻辑与关系,同sql语句中where部分的and关键字。
例:查询阅读量大于20,并且编号小于3的图书。
- # 查询阅读量大于20,并且编号小于3的图书。
- BookInfo.objects.filter(readcount__gt=20,id__lt=3)
-
- BookInfo.objects.filter(readcount__gt=20).filter(id__lt=3)
如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符,Q对象被义在django.db.models中。
语法: Q(属性名__运算符=值)
例:查询阅读量大于20的图书,改写为Q对象如下。
- # 查询阅读量大于20的图书,改写为Q对象如下。
- from django.db.models import Q
-
- BookInfo.objects.filter(Q(readcount__gt=20))
Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或。
例:查询阅读量大于20,或编号小于3的图书,只能使用Q对象实现
- # 查询阅读量大于20,或编号小于3的图书,只能使用Q对象实现
- BookInfo.objects.filter(Q(readcount__gt=20)|Q(id__lt=3))
Q对象前可以使用~操作符,表示非not。
例:查询编号不等于3的图书。
- # 查询编号不等于3的图书。
- BookInfo.objects.filter(~Q(id=3))
使用aggregate()过滤器调用聚合函数。聚合函数包括:Avg平均,Count数量,Max最大,Min最小,Sum求和,被定义在django.db.models中。
例:查询图书的总阅读量。
- from django.db.models import Sum
- # 查询图书的总阅读量。
- BookInfo.objects.aggregate(Sum('readcount'))
注意aggregate的返回值是一个字典类型,格式如下:
{'属性名__聚合类小写':值}
如:{'readcount__sum': 298}
使用count时一般不使用aggregate()过滤器。
例:查询图书总数。
- # 查询图书总数。
- BookInfo.objects.count()
注意count函数的返回值是一个数字。
使用order_by对结果进行排序
- # 默认升序
- BookInfo.objects.all().order_by('readcount')
-
- # 降序
- BookInfo.objects.all().order_by('-readcount')
查询书籍为1的所有人物信息
查询人物为1的书籍信息
由一到多的访问语法:
一对一的模型类对象.多对一的模型类名小写_set
例:
- # 查询书籍为1的所有人物信息 ==》 通过书籍 查询人物
-
- # 查询书籍
- book = BookInfo.objects.get(id=1)
-
- # 根据书籍关联人物信息
- book.peopleinfo_set.all()
由多到一的访问语法:
多对应的模型类对象.多对应的模型类中的关系类属性名
例:
- # 查询人物为1的书籍信息 ==》 根据书籍查询人物
- from book.models import PeopleInfo
- # 1、查询人物
- person = PeopleInfo.objects.get(id=1)
- # 2、根据人物关联查询数据
- person.book
-
- person.book.name
- person.book.readcount
- person
访问一对一的模型类关联对象的id语法:
多对一的模型类对象.关联类属性_id
例:
- person = PeopleInfo.objects.get(id=1)
- person.book_id
由多模型类条件查询一模型类数据:
语法如下:
关联模型类名小写__属性名__条件运算符=值
注意:如果没有"__运算符"部分,表示等于。
例:
查询图书,要求图书人物为"郭靖"
- book = BookInfo.objects.filter(peopleinfo__name='郭靖')
- book
查询图书,要求图书中人物的描述包含"八"
- book = BookInfo.objects.filter(peopleinfo__description__contains='八')
- book
由一模型类条件查询多模型类数据:
语法如下:
一模型类关联属性名__一模型类属性名__条件运算符=值
注意:如果没有"__运算符"部分,表示等于。
例:
查询书名为“天龙八部”的所有人物。
- # 查询书名为“天龙八部”的所有人物。
- people = PeopleInfo.objects.filter(book__name='天龙八部')
- people
查询图书阅读量大于30的所有人物
- # 查询图书阅读量大于30的所有人物
- people = PeopleInfo.objects.filter(book__readcount__gt=30)
- people
Django的ORM中存在查询集的概念。
查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。
当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):
all() 返回所有数据。
filter() 返回满足条件的数据。
exclude() 返回满足条件之外的数据。
order_by() 对结果进行排序。
对查询集可以再次调用过滤器进行过滤,如
- books = BookInfo.objects.filter(readcount__gt=30).order_by('pub_date')
- books
也就意味着查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果。
从SQL的角度讲,查询集与select语句等价,过滤器像where、limit、order by子句。
判断某一个查询集中是否有数据:
exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。
1)惰性执行
创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用
例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集books
books = BookInfo.objects.all()
继续执行遍历迭代操作后,才真正的进行了数据库的查询
- for book in books:
- print(book.name)
2)缓存
使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。
情况一:如下是两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载。
- from book.models import BookInfo
-
- [book.id for book in BookInfo.objects.all()]
-
- [book.id for book in BookInfo.objects.all()]
-
- # 上面写发没有使用缓存
情况二:经过存储后,可以重用查询集,第二次使用缓存中的数据。
- # 优化后的代码
-
- books=BookInfo.objects.all()
-
- [book.id for book in books]
-
- [book.id for book in books]
可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句。
注意:不支持负数索引。
对查询集进行切片后返回一个新的查询集,不会立即执行查询。
如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()如果没有数据引发DoesNotExist异常。
示例:获取第1、2项,运行查看。
- # 获取前两条数据
- books = BookInfo.objects.all()[0:2]
- books = BookInfo.objects.all()[:2] # 和上面写发一样
- books
- #查询数据
- books = BookInfo.objects.all()
- #导入分页类
- from django.core.paginator import Paginator
- #创建分页实例
- paginator=Paginator(books,2)
- #获取指定页码的数据
- page_skus = paginator.page(1)
- #获取分页数据
- total_page=paginator.num_pages
重点
HttpResponse
HttpResponse
JsonResponse
redirect
类视图
类视图的定义和使用
类视图装饰器(难点)
- 视图就是
应用
中views.py
文件中的函数- 视图的第一个参数必须为
HttpRequest对象
,还可能包含下参数如
- 通过正则表达式组获取的位置参数
- 通过正则表达式组获得的关键字参数
- 视图必须返回一个
HttpResponse对象
或子对象
作为响应
- 子对象:
JsonResponse
HttpResponseRedirect
- 视图负责接受Web请求
HttpRequest
,进行逻辑处理,返回Web响应HttpResponse
给请求者
- 响应内容可以是
HTML内容
,404错误
,重定向
,json数据
...视图处理过程如下图:
使用视图时需要进行两步操作,两步操作不分先后
- 配置
URLconf
- 在
应用/views.py
中定义视图
创建项目+创建应用+安装应用+配置模板路径+本地化+mysql数据库+URLconf+视图
安装应用
- INSTALLED_APPS = [
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'book.apps.BookConfig'
- ]
mysql数据库使用之前的book
- DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'HOST': '127.0.0.1', # 数据库主机
- 'PORT': 3306, # 数据库端口
- 'USER': 'root', # 数据库用户名
- 'PASSWORD': 'mysql', # 数据库用户密码
- 'NAME': 'book' # 数据库名字
- }
- }
URLconf
settings.py
中:指定url配置
- ROOT_URLCONF = 'bookmanager01.urls'
-
- TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [BASE_DIR / 'templates'],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
- ]
项目中urls.py
:只要不是admin/
就匹配成功,包含到应用中的urls.py
- from django.contrib import admin
- from django.urls import path,include
-
- urlpatterns = [
- path('admin/', admin.site.urls),
-
- path('',include('book.urls'))
- ]
应用中urls.py
:匹配testproject/
成功就调用views
中的testproject
函数,测试项目逻辑
- from django.conf.urls import url
- import views
-
- urlpatterns = [
- # 匹配`testproject/`成功就调用`views`中的`testproject`函数
- url(r'^testproject/$', views.testproject),
- ]
-
- from django.urls import path
- from book.views import bookList
-
- urlpatterns = [
- path('booklist/',bookList),
- ]
视图:测试项目逻辑
- from django.http import HttpResponse
-
- # 测试项目逻辑
- def testproject(request):
- return HttpResponse('测试项目逻辑')
-
- from django.shortcuts import render
- from book.models import BookInfo
-
- def bookList(request):
- # 1、到数据库中查询数据
- books = BookInfo.objects.all()
- # 2、组织数据
- context = {
- 'books':books
- }
- # 3、传递给模板
- return render(request,'booklist.html',context=context)
在models.py 文件中定义模型类
- from django.db import models
-
- # Create your models here.
- # 准备书籍列表信息的模型类
- class BookInfo(models.Model):
- # 创建字段,字段类型...
- name = models.CharField(max_length=20, verbose_name='名称')
- pub_date = models.DateField(verbose_name='发布日期',null=True)
- readcount = models.IntegerField(default=0, verbose_name='阅读量')
- commentcount = models.IntegerField(default=0, verbose_name='评论量')
- is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
-
- class Meta:
- db_table = 'bookinfo' # 指明数据库表名
- verbose_name = '图书' # 在admin站点中显示的名称
-
- def __str__(self):
- """定义每个数据对象的显示信息"""
- return self.name
-
- # 准备人物列表信息的模型类
- class PeopleInfo(models.Model):
- GENDER_CHOICES = (
- (0, 'male'),
- (1, 'female')
- )
- name = models.CharField(max_length=20, verbose_name='名称')
- gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
- description = models.CharField(max_length=200, null=True, verbose_name='描述信息')
- book = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
- is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
-
- class Meta:
- db_table = 'peopleinfo'
- verbose_name = '人物信息'
-
- def __str__(self):
- return self.name
1)生成迁移文件
python manage.py makemigrations
2)同步到数据库中
python manage.py migrate
3)添加测试数据
- insert into bookinfo(name, pub_date, readcount,commentcount, is_delete) values
- ('射雕英雄传', '1980-5-1', 12, 34, 0),
- ('天龙八部', '1986-7-24', 36, 40, 0),
- ('笑傲江湖', '1995-12-24', 20, 80, 0),
- ('雪山飞狐', '1987-11-11', 58, 24, 0);
- insert into peopleinfo(name, gender, book_id, description, is_delete) values
- ('郭靖', 1, 1, '降龙十八掌', 0),
- ('黄蓉', 0, 1, '打狗棍法', 0),
- ('黄药师', 1, 1, '弹指神通', 0),
- ('欧阳锋', 1, 1, '蛤蟆功', 0),
- ('梅超风', 0, 1, '九阴白骨爪', 0),
- ('乔峰', 1, 2, '降龙十八掌', 0),
- ('段誉', 1, 2, '六脉神剑', 0),
- ('虚竹', 1, 2, '天山六阳掌', 0),
- ('王语嫣', 0, 2, '神仙姐姐', 0),
- ('令狐冲', 1, 3, '独孤九剑', 0),
- ('任盈盈', 0, 3, '弹琴', 0),
- ('岳不群', 1, 3, '华山剑法', 0),
- ('东方不败', 0, 3, '葵花宝典', 0),
- ('胡斐', 1, 4, '胡家刀法', 0),
- ('苗若兰', 0, 4, '黄衣', 0),
- ('程灵素', 0, 4, '医术', 0),
- ('袁紫衣', 0, 4, '六合拳', 0);
注意:改变IP地址方式
- # ALLOWED_HOSTS 允许以那个主机的形式访问后端
- # 默认是 127.0.0.1
- # ALLOWED_HOSTS = [192.168.127.128]
- ALLOWED_HOSTS = []
浏览者 通过在浏览器的地址栏中输入网址请求网站
对于Dja ngo开发的网站,由哪一个视图进行处理请求,是由url匹配找到的
1.settings.py
中
指定url配置
ROOT_URLCONF = '项目.urls'
2.项目中urls.py
匹配成功后,包含到应用的urls.py
url(正则, include('应用.urls'))
3.应用中urls.py
匹配成功后,调用views.py
对应的函数
url(正则, views.函数名)
4.提示
1. 正则部分推荐使用 r,表示字符串不转义,这样在正则表达式中使用 \ 只写一个就可以
2. 不能在开始加反斜杠,推荐在结束加反斜杠
正确:path/
正确:path
错误:/path
错误:/path/3. 请求的url被看做是一个普通的python字符串,进行匹配时不包括域名、get或post参数
3.1 如请求地址如:http://127.0.0.1:8000/18/?a=10
3.2 去掉域名和参数部分后,只剩下如下部分与正则匹配 18/
说明:
虽然路由结尾带/能带来上述好处,但是却违背了HTTP中URL表示资源位置路径的设计理念。
是否结尾带/以所属公司定义风格为准。
在定义路由的时候,可以为路由命名,方便查找特定视图的具体路径信息。
1) 在使用include函数定义路由时,可以使用namespace参数定义路由的命名空间,如
url(r'^',include('book.urls',namespace='book'))
命名空间表示,凡是book.urls中定义的路由,均属于namespace指明的book名下。
命名空间的作用:避免不同应用中的路由使用了相同的名字发生冲突,使用命名空间区别开。
2) 在定义普通路由时,可以使用name参数指明路由的名字,如
- urlpatterns = [
- url(r'^$',index),
- # 匹配书籍列表信息的URL,调用对应的bookList视图
- url(r'^booklist/$',bookList,name='index'),
- url(r'^testproject/$',views.testproject,name='test'),
- ]
使用reverse函数,可以根据路由名称,返回具体的路径,如:
- from django.core.urlresolvers import reverse
- #或者
- from django.urls import reverse
-
- def testproject(request):
-
- return HttpResponse("OK")
-
- # 定义视图:提供书籍列表信息
- def bookList(request):
-
- url = reverse('book:test')
- print(url)
- return HttpResponse('index')
- 对于未指明namespace的,reverse(路由name)
- 对于指明namespace的,reverse(命名空间namespace:路由name)
利用HTTP协议向服务器传参有几种途径?
- 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取;
- 查询字符串(query string),形如key1=value1&key2=value2;
- 请求体(body)中发送的数据,比如表单数据、json、xml;
- 在http报文的头(header)中。
- 如果想从URL中获取值,需要在正则表达式中使用
分组
,- 获取值分为两种方式
- 位置参数
- 参数的位置不能错
- 关键字参数
- 参数的位置可以变,跟关键字保持一致即可
- 注意:两种参数的方式不要混合使用,在一个正则表达式中只能使用一种参数方式
- 分别使用以上两种获取URL值的方式提取出
18 188
http://127.0.0.1:8000/18/188/
位置参数(推荐使用)
应用中
urls.py
urlpatterns = [ path('booklist/',bookList), url(r'^(\d+)/(\d+)/$',detail), ]视图中函数: 参数的位置不能错
def detail(request,value1,value2): context = {'v1': value1, 'v2': value2} print(value1,value2) return HttpResponse("deft")
关键字参数
应用中
urls.py
- 其中
?P<value1>
部分表示为这个参数定义的名称为value1
可以是其它名称,起名要做到见名知意
urlpatterns = [ path('booklist/',bookList), # 位置参数 # url(r'^(\d+)/(\d+)/$',detail), # 关键字参数 url(r'^(?P<value1>\d+)/(?P<value2>\d+)/$',detail),
视图中函数: 参数的位置可以变,跟关键字保持一致即可
def detail(request,value2,value1): context = {'v1': value1, 'v2': value2} print(context) return HttpResponse("deft")
HttpRequest对象的属性GET、POST都是QueryDict类型的对象
与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况
方法get():根据键获取值
如果一个键同时拥有多个值将获取最后一个值
如果键不存在则返回None值,可以设置默认值进行后续处理
get('键',默认值)
方法getlist():根据键获取值,值以列表返回,可以获取指定键的所有值
如果键不存在则返回空列表[],可以设置默认值进行后续处理
getlist('键',默认值)
获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2),可以通过request.GET属性获取,返回QueryDict对象。
- # http://127.0.0.1:8000/4/100/?aaa=1111&bbb=2222&aaa=333
-
- def detail(request,value2,value1):
-
- context = {'v1': value1, 'v2': value2}
- print(context)
-
- query_params = request.GET
- print(query_params)
- # <QueryDict: {'aaa': ['1111'], 'bbb': ['2222']}>
-
- aaa = query_params['aaa']
- bbb = query_params['bbb']
- print(aaa,bbb)
-
- alist = query_params.getlist('aaa')
- print(alist)
-
- return HttpResponse("deft")
重要:查询字符串不区分请求方式,即假使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串数据。
注意:QueryDict 已普通的字典形式来获取 一键多值的时候 只能获取最后的一个
请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。
可以发送请求体数据的请求方式有 POST、PUT、PATCH、DELETE。
Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如:
前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。
- def detail(request,value2,value1):
-
-
- data = request.POST
- print(data)
-
- alist = data.getlist('aaa')
- print(alist)
-
- return HttpResponse("deft")
非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。request.body返回bytes类型。
例如要获取请求体中的如下JSON数据
{"a": 1, "b": 2}
可以进行如下方法操作:
- import json
-
- def detail(request):
-
- json_str = request.body # 元组
- print(json_str)
- json_str2 = json_str.decode() # 转换 json形式的 字符串
- print(json_str2)
- req_data = json.loads(json_str2) # 转换成字典
- print(req_data)
- print(req_data['aaa'])
- print(req_data['bbb'])
-
- return HttpResponse('OK')
可以通过request.META属性获取请求头headers中的数据,request.META为字典类型。
常见的请求头如:
CONTENT_LENGTH
– The length of the request body (as a string).CONTENT_TYPE
– The MIME type of the request body.HTTP_ACCEPT
– Acceptable content types for the response.HTTP_ACCEPT_ENCODING
– Acceptable encodings for the response.HTTP_ACCEPT_LANGUAGE
– Acceptable languages for the response.HTTP_HOST
– The HTTP Host header sent by the client.HTTP_REFERER
– The referring page, if any.HTTP_USER_AGENT
– The client’s user-agent string.QUERY_STRING
– The query string, as a single (unparsed) string.REMOTE_ADDR
– The IP address of the client.REMOTE_HOST
– The hostname of the client.REMOTE_USER
– The user authenticated by the Web server, if any.REQUEST_METHOD
– A string such as"GET"
or"POST"
.SERVER_NAME
– The hostname of the server.SERVER_PORT
– The port of the server (as a string).
具体使用如:
- def get_headers(request):
- print(request.META['CONTENT_TYPE'])
- return HttpResponse('OK')
- method:一个字符串,表示请求使用的HTTP方法,常用值包括:'GET'、'POST'。
- user:请求的用户对象。
- path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
encoding:一个字符串,表示提交的数据的编码方式。
- 如果为None则表示使用浏览器的默认设置,一般为utf-8。
- 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。
FILES:一个类似于字典的对象,包含所有的上传文件。
视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。
可以使用django.http.HttpResponse来构造响应对象。
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
也可通过HttpResponse对象属性来设置响应体、响应体数据类型、状态码:
- content:表示返回的内容。
- status_code:返回的HTTP响应状态码。
响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置:
- response = HttpResponse()
- response['itcast'] = 'Python' # 自定义响应头Itcast, 值为Python
-
- return response
示例:
- from django.http import HttpResponse
-
- def response(request):
-
- # return HttpResponse('hello python', status=400)
- # 或者
- data = {"hello python"}
- response = HttpResponse(content=data)
- response.status_code = 400
- return response
Django提供了一系列HttpResponse的子类,可以快速设置状态码
- HttpResponseRedirect 301
- HttpResponsePermanentRedirect 302
- HttpResponseNotModified 304
- HttpResponseBadRequest 400
- HttpResponseNotFound 404
- HttpResponseForbidden 403
- HttpResponseNotAllowed 405
- HttpResponseGone 410
- HttpResponseServerError 500
若要返回json数据,可以使用JsonResponse来构造响应对象,作用:
- 帮助我们将数据转换为json字符串
- 设置响应头Content-Type为application/json
- from django.http import JsonResponse
-
- def response(request):
- return JsonResponse({'city': 'beijing', 'subject': 'python'})
- from django.shortcuts import redirect
-
- def response(request):
- return redirect('/booklist')
- # return redirect('http://www.baidu.com')
- 浏览器请求服务器是无状态的。
- 无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求。
- 无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后,会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
- 有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
- 实现状态保持主要有两种方式:
- 在客户端存储信息使用
Cookie
- 在服务器端存储信息使用
Session
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型记住用户名。
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用。
可以通过HttpResponse对象中的set_cookie方法来设置cookie。
HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)
示例:
- def cookie(request):
- response = HttpResponse('ok')
- response.set_cookie('hello', 'python1') # 临时cookie
- response.set_cookie('hello', 'python2', max_age=3600) # 有效期一小时
- return response
-
- def set_cookie(request):
- # 1、先假设没有cookie信息
- cook = request.COOKIES
- print(cook)
-
- # 2、获取用户名
- username = request.GET.get('username')
-
- # 3、向服务器设置cookie信息
- response = HttpResponse('set_cookie')
- response.set_cookie('username',username)
-
- # 4、返回响应
- return response
可以通过HttpResponse对象的COOKIES属性来读取本次请求携带的cookie值。request.COOKIES为字典类型。
- def cookie(request):
- cookie1 = request.COOKIES.get('username')
- print(cookie1)
- return HttpResponse('OK')
可以通过HttpResponse对象中的delete_cookie方法来删除。
response.delete_cookie('username')
注意:
Session 依赖于cookie,如果浏览器禁用了cookie,则session不能实现
0.概念 1.流程 第一次请求: ① 我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息 ② 当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息 ③ 在设置session信息的同时(session信息保存在服务器端).服务器会在响应头中设置一个 sessionid 的cookie信息(由服务器自己设置的,不是我们设置的) ④ 客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存sessionid的信息) 第二次及其之后的请求: ⑤ 第二次及其之后的请求都会携带 session id信息 ⑥ 当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端) 2.效果 3.从原理(http)角度 第一次请求: ① 第一次请求,在请求头中没有携带任何cookie信息 ② 我们在设置session的时候,session会做2件事. #第一件: 将数据保存在数据库中 #第二件: 设置一个cookie信息,这个cookie信息是以 sessionid为key value为 xxxxx cookie肯定会以响应的形式在相应头中出现 第二次及其之后的: ③ 都会携带 cookie信息,特别是 sessionid
Django项目默认启用Session。
可以在settings.py文件中查看,如图所示
如需禁用session,将上图中的session中间件注释掉即可。
在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。
存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。
SESSION_ENGINE='django.contrib.sessions.backends.db'
如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。
数据库中的表如图所示
表结构如下
由表结构可知,操作Session包括三个数据:键,值,过期时间。
存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。
SESSION_ENGINE='django.contrib.sessions.backends.cache'
优先从本机内存中存取,如果没有则从数据库中存取。
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。
1) 安装扩展
pip install django-redis
2)配置
在settings.py文件中做如下设置
- CACHES = {
- 'default': {
- 'BACKEND': 'django_redis.cache.RedisCache',
- 'LOCATION': 'redis://127.0.0.1:6379/1',
- 'OPTIONS': {
- 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
- }
- }
- }
- SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
- SESSION_CACHE_ALIAS = 'default'
注意
如果redis的ip地址不是本地回环127.0.0.1,而是其他地址,访问Django时,可能出现Redis连接错误,如下:
解决方法:修改redis的配置文件,添加特定ip地址。
打开redis的配置文件
vim /etc/redis/redis.conf
在如下配置项进行修改(如要添加10.211.55.5地址)
重新启动redis服务
service redis-server restart
通过HttpRequest对象的session属性进行会话的读写操作。
1) 以键值对的格式写session。
request.session['键']=值
2)根据键读取值。
request.session.get('键',默认值)
3)清除所有session,在存储中删除值部分。
request.session.clear()
4)清除session数据,在存储中删除session的整条数据。
request.session.flush()
5)删除session中的指定键及值,在存储中只删除某个键及对应的值。
del request.session['键']
6)设置session的有效期
request.session.set_expiry(value)
- def set_session(request):
- """
- 第一次请求:
- ① 我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
- ② 当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session
- 信息
- ③ 在设置session信息的同时(session信息保存在服务器端).服务器会在响应头中设置一个 sessionid 的cookie信息
- ④ 客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
- """
- # 1.cookie中没有任何信息
- print(request.COOKIES)
-
- #2.对用户名和密码进行验证
- # 假设认为 用户名和密码正确
- user_id=6666
-
- #3.设置session信息
- # request.session 理解为字典
- # 设置session的时候其实 session做了2件事
- #第一件: 将数据保存在数据库中
- #第二件: 设置一个 cookie信息,这个cookie信息是以 sessionid为key
- request.session['user_id']=user_id
-
- #4. 返回响应
- return HttpResponse('set_session')
-
- def get_session(request):
- """
- 第二次及其之后的请求:
- ⑤ 第二次及其之后的请求都会携带 session id信息
- ⑥ 当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,
- 验证成功,则可以获取 session信息(session信息保存在服务器端)
- """
- #1. 请求都会携带 session id信息
- print(request.COOKIES)
-
- #2. 会获取到sessionid信息,然后进行验证,
- # 验证成功,可以获取 session信息(
-
- # request.session 字典
- user_id=request.session['user_id']
- user_id=request.session.get('user_id')
-
- #3.返回响应
- return HttpResponse('get_session')
第二次请求:
问题1: 我换了浏览器,还能获取到 session信息吗? 不可以 问题2: 我不换浏览器,删除session id ,则获取不到session数据 问题3: 再去执行 set_sesison 的时候 会重新生成session id
思考:一个视图,是否可以处理两种逻辑?比如get和post请求逻辑。
如何在一个视图中处理get和post请求
注册视图处理get和post请求
以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
- """
- 登陆页面
- GET 请求是获取 登陆的页面
- POST 请求是 验证登陆 (用户名和密码是否正确)
- """
-
- # GET 请求是获取 登陆的页面
- def show_login(request):
- return render(request)
-
- #POST 请求是 验证登陆 (用户名和密码是否正确)
- def veri_login(request):
- return redirect('首页')
-
- # 我想由2个视图 变为 1个视图
- def login(request):
- #我们需要区分业务逻辑
- if request.method == 'GET':
- # GET 请求是获取 登陆的页面
- return render(request)
-
- else:
- # POST 请求是 验证登陆 (用户名和密码是否正确)
- return redirect('首页')
类视图使用
在Django中也可以使用类来定义一个视图,称为类视图。
使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义
- """
- 面向对象
- 类视图 是采用的面向对象的思路
- 1.定义类试图
- ① 继承自 View (from django.views import View)
- ② 不同的请求方式 有不同的业务逻辑
- 类试图的方法 就直接采用 http的请求方式的名字 作为我们的函数名.例如: get ,post,put,delete
- ③ 类试图的方法的第二个参数 必须是请求实例对象
- 类试图的方法 必须有返回值 返回值是HttpResopnse及其子类
- 2.类试图的url引导
- """
-
- from django.views import View
- class BookView(View):
-
- def get(self,request):
- return HttpResponse('get')
-
- def post(self,request):
- return HttpResponse('post')
-
- def put(self,request):
- return HttpResponse('put')
-
- def oooo(self,request):
- return HttpResponse('oooo')
类视图的好处:
- 代码可读性好
- 类视图相对于函数视图有更高的复用性 , 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
定义类视图需要继承自Django提供的父类View,可使用from django.views.generic import View
或者from django.views.generic.base import View
导入,定义方式如上所示。
配置路由时,使用类视图的as_view()
方法来添加。
- urlpatterns = [
- # 视图函数:注册
- # url(r'^register/$', views.register, name='register'),
- # 类视图:注册
- url(r'^register/$', views.RegisterView.as_view(), name='register'),
- ]
-
-
- from django.urls import path
- from django.conf.urls import url
- from book.views import bookList,detail,set_cookie,set_session,get_session,BookView
-
- urlpatterns = [
- path('booklist/',bookList),
-
- # 位置参数
- # url(r'^(\d+)/(\d+)/$',detail),
- # 关键字参数
- url(r'^(?P<value1>\d+)/(?P<value2>\d+)/$',detail),
-
- path('set_cookie/',set_cookie),
-
- path('set_session/',set_session),
-
- path('get_session/',get_session),
-
- path('bookview/',BookView.as_view())
-
- ]
类视图的多继承重写dispatch
使用面向对象多继承的特性。
先找LoginRequiredMixin,后找View
- """
- 个人中心页面 -- 必须登陆才能显示
- 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('个人中心修改')
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
定义一个中间件工厂函数,然后返回一个可以被调用的中间件。
中间件工厂函数需要接收一个可以调用的get_response对象。
返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。
- # 每次请求和响应的时候都会用到
-
- def simple_middleware(get_response):
- # 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。
-
- def middleware(request):
- # 此处编写的代码会在每个请求处理视图前被调用。
-
- response = get_response(request)
-
- # 此处编写的代码会在每个请求处理视图之后被调用。
-
- return response
-
- return middleware
例如,在book应用中新建一个middleware.py文件,
- def my_middleware(get_response):
- print('init 被调用')
- def middleware(request):
- print('before request 被调用')
- response = get_response(request)
- print('after response 被调用')
- return response
- return middleware
定义好中间件后,需要在settings.py 文件中添加注册中间件
- # 中间件
- MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- # 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
- 'book.middleware.my_middleware', # 添加中间件
- ]
定义一个视图进行测试
- def middleware(request):
- print('view 视图被调用')
- return HttpResponse('OK')
执行结果
注意:Django运行在调试模式下,中间件init部分有可能被调用两次。
示例:
定义两个中间件
- def my_middleware(get_response):
- print('init 被调用')
- def middleware(request):
- print('before request 被调用')
- response = get_response(request)
- print('after response 被调用')
- return response
- return middleware
-
- def my_middleware2(get_response):
- print('init2 被调用')
- def middleware(request):
- print('before request 2 被调用')
- response = get_response(request)
- print('after response 2 被调用')
- return response
- return middleware
注册添加两个中间件
- 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',
- 'users.middleware.my_middleware', # 添加
- 'users.middleware.my_middleware2', # 添加
- ]
执行结果
重点
Jinja2模板
CSRF
原理
如何防范
在工程中创建模板目录templates。
在settings.py配置文件中修改TEMPLATES配置项的DIRS值:
- TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [os.path.join(BASE_DIR, 'templates')], # 此处修改
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
- ]
在templates目录中新建一个模板文件,如index.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- {{ city }}
- </body>
- </html>
调用模板分为两步骤:
找到模板 loader.get_template(模板文件在模板目录中的相对路径) -> 返回模板对象
渲染模板 模板对象.render(context=None, request=None) -> 返回渲染后的html文本字符串 context 为模板变量字典,默认值为None request 为请求对象,默认值为None
例如,定义一个视图
- from django.http import HttpResponse
- from django.template import loader
-
-
- def bookList(request):
- # 1.获取模板
- template = loader.get_template('booklist.html')
- # 到数据库中查询数据
- books = BookInfo.objects.all()
- # 2、组织数据
- context = {
- 'books':books
- }
- # 3.渲染模板
- return HttpResponse(template.render(context))
Django提供了一个函数render可以简写上述代码。
render(request对象, 模板文件路径, 模板数据字典)
- from django.shortcuts import render
-
- # 写法二
- def bookList(request):
- # 1、到数据库中查询数据
- books = BookInfo.objects.all()
- # 2、组织数据
- context = {
- 'books':books
- }
- # 3、传递给模板
- return render(request,'booklist.html',context=context)
-
变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。
语法如下: {{变量}}
模板变量可以使python的内建类型,也可以是对象。
- def bookList(request):
-
- context = {
- 'city': '北京',
- 'adict': {
- 'name': '西游记',
- 'author': '吴承恩'
- },
- 'alist': [1, 2, 3, 4, 5]
- }
-
- return render(request,'booklist.html',context=context)
-
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
- <h1>{{ city }}</h1>
- <h1>{{ adict }}</h1>
- <h1>{{ adict.name }}</h1>
- <h1>{{ alist }}</h1>
- <h1>{{ alist.0 }}</h1>
- </body>
- </html>
1)for循环:
2)if条件:
<hr> {% if age > 10 %} 大于10 {% else %} 等于10 {% endif %} <hr>
语法如下:
- 使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。
- 如果过滤器需要参数,则使用冒号:传递参数。
变量|过滤器:参数
列举几个如下:
safe,禁用转义,告诉模板这个变量是安全的,可以解释执行
length,长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数。
default,默认值,如果变量不存在时则返回默认值。
data|default:'默认值'
date,日期,用于对日期类型的值进行字符串格式化,常用的格式化字符如下:
- Y表示年,格式为4位,y表示两位的年。
- m表示月,格式为01,02,12等。
- d表示日, 格式为01,02等。
- j表示日,格式为1,2等。
- H表示时,24进制,h表示12进制的时。
- i表示分,为0-59。
- s表示秒,为0-59。
value|date:"Y年m月j日 H时i分s秒"
Jinja2:是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于 Django 的模板引擎,并扩展了其语法和一系列强大的功能,尤其是Flask框架内置的模板语言
由于django默认模板引擎功能不齐全,速度慢,所以我们也可以在Django中使用jinja2, jinja2宣称比django默认模板引擎快10-20倍。
Django主流的第三方APP基本上也都同时支持Django默认模板及jinja2,所以要用jinja2也不会有多少障碍。
pip install jinja2
- from jinja2 import Environment
-
- def environment(**options):
- env = Environment(**options)
-
- return env
2.在settings.py文件
- TEMPLATES = [
- {
- 'BACKEND': 'django.template.backends.jinja2.Jinja2',#修改1
- 'DIRS': [os.path.join(BASE_DIR, 'templates')],
- 'APP_DIRS':True,
- 'OPTIONS':{
- 'environment': 'jinja2_env.environment',# 修改2
- 'context_processors':[
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
- ],
- },
- },
- ]
在jinja2_env.py文件中自定义过滤器
- from jinja2 import Environment
-
- def environment(**options):
- env = Environment(**options)
-
- # 2.将自定义的过滤器添加到 环境中
- env.filters['do_listreverse'] = do_listreverse
-
- return env
-
- # 1.自定义过滤器
- def do_listreverse(li):
- if li == "B":
- return "哈哈"
CSRF
拼为Cross Site Request Forgery
,译为 跨站请求伪造。CSRF
指攻击者盗用了你的身份,以你的名义发送恶意请求。
- 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......
- 造成的问题:个人隐私泄露以及财产安全。
- #定义路由
- from django.conf.urls import url
- from pay import views
- urlpatterns = [
- url(r'^$',views.LoginView.as_view(),name='index'), #登录路由
- url(r'^transfer/$',views.TransferView.as_view(),name='transfer'), #转账路由
- ]
-
- #定义视图
- class LoginView(View):
-
- def post(self,request):
-
- # 取到表单中提交上来的参数
- username = request.POST.get("username")
- password = request.POST.get("password")
-
- if not all([username, password]):
- print('参数错误')
- else:
- print(username, password)
- if username == 'laowang' and password == '1234':
- # 状态保持,设置用户名到cookie中表示登录成功
- response = redirect(reverse('transfer'))
- response.set_cookie('username', username)
- return response
- else:
- print('密码错误')
- return render(request,'login.html')
- def get(self,request):
- return render(request,'login.html')
-
- class TransferView(View):
-
-
- def post(self,request):
- # 从cookie中取到用户名
- username = request.COOKIES.get('username', None)
- # 如果没有取到,代表没有登录
- if not username:
- return redirect(reverse('index'))
-
-
- to_account = request.POST.get("to_account")
- money = request.POST.get("money")
-
- print('假装执行转操作,将当前登录用户的钱转账到指定账户')
- return HttpResponse('转账 %s 元到 %s 成功' % (money, to_account))
-
- def get(self, request):
- # 渲染转换页面
- response = render(request, 'transfer.html')
-
- return response
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>登录</title>
- </head>
- <body>
-
- <h1>我是网站A,登录页面</h1>
-
- <form method="post">
- <label>用户名:</label><input type="text" name="username" placeholder="请输入用户名"><br/>
- <label>密码:</label><input type="password" name="password" placeholder="请输入密码"><br/>
- <input type="submit" value="登录">
- </form>
-
- </body>
- </html>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>转账</title>
- </head>
- <body>
- <h1>我是网站A,转账页面</h1>
-
- <form method="post">
- <label>账户:</label><input type="text" name="to_account" placeholder="请输入要转账的账户"><br/>
- <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/>
- <input type="submit" value="转账">
- </form>
-
- </body>
- </html>
运行测试,如果在未登录的情况下,不能直接进入转账页面,测试转账是成功的
- #定义路由
- from django.conf.urls import url
- from ads import views
-
- urlpatterns = [
- url(r'^$',views.AdsView.as_view()),
- ]
-
- #定义视图
- class AdsView(View):
-
- def get(self,request):
-
- return render(request,'index.html')
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>Title</title>
- </head>
- <body>
-
- <h1>我是网站B</h1>
-
- <form method="post" action="http://127.0.0.1:9000/transfer/">
- <input type="hidden" name="to_account" value="黑客">
- <input type="hidden" name="money" value="190000" hidden>
- <input type="submit" value="点击领取优惠券">
- </form>
-
- </body>
- </html>
运行测试,在用户登录网站A的情况下,点击网站B的按钮,可以实现伪造访问
- from django.middleware.csrf import get_token
- csrf_token = get_token(request)
在渲染转账页面的,做以下几件事情:
- def get(self, request):
- # 生成csrf_token
- from django.middleware.csrf import get_token
- csrf_token = get_token(request)
-
- # 渲染转换页面,传入 csrf_token 到模板中
- response = render(request, 'transfer.html',context={'csrf_token':csrf_token})
-
- # 设置csrf_token到cookie中,用于提交校验
- response.set_cookie('csrf_token', csrf_token)
-
- return response
在转账模板表单中添加 csrf_token 隐藏字段
- <head>
- <meta charset="UTF-8">
- <title>转账</title>
- </head>
- <body>
- <h1>我是网站A,转账页面</h1>
-
- <form method="post">
- <input type="hidden" name="csrftoken" value="{{ csrf_token }}">
- <label>账户:</label><input type="text" name="to_account" placeholder="请输入对方账户"><br/>
- <label>金额:</label><input type="number" name="money" placeholder="请输入转账金额"><br/>
- <input type="submit" value="转账">
- </form>
-
- </body>
- </html>
运行测试,进入到转账页面之后,查看 cookie 和 html 源代码
- # 取出表单中的 csrf_token
- form_csrf_token = request.POST.get("csrftoken")
- # 取出 cookie 中的 csrf_token
- cookie_csrf_token = request.COOKIES.get('csrf_token')
- # 进行对比
- if cookie_csrf_token != form_csrf_token:
- return HttpResponse('token校验失败,可能是非法操作')
运行测试,用户直接在网站 A 操作没有问题,再去网站B进行操作,发现转账不成功,因为网站 B 获取不到表单中的 csrf_token 的隐藏字段,而且浏览器有同源策略,网站B是获取不到网站A的 cookie 的,所以就解决了跨站请求伪造的问题
Django默认是开启CSRF的
模板中设置 CSRF 令牌
- {% csrf_token %}
- 或者
- <input type="hidden" value="{{ csrf_token }}">
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。