Django框架学习与开发
Django学习
常用命令:
1、新建一个项目
1 |
|
2、新建app (一个项目可以有多个app,通用的app也可以在多个项目中使用)
1 |
|
3、创建数据库表 或 更改数据库表或字段(可在SQL等数据库中创建与models.py代码对应的表)
创建更改的文件
1 |
|
将生成的py文件应用到数据库
1 |
|
4、运行Django自带开发服务器
1 |
|
公网访问:
1 |
|
本地访问:
1 |
|
5、清空数据库
1 |
|
6、创建超级管理员
在创建前,得先执行python manage.py migrate
1 |
|
修改用户密码
1 |
|
7、导入导出数据
导入:
1 |
|
导出:
1 |
|
8、django 项目环境终端
1 |
|
9、数据库命令行
1 |
|
视图层:
1.base64编码:
1 |
|
2.uuid :
uuid - 匹配一个格式化的 UUID 。
为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回 一个 UUID 实例。
1 |
|
3.读取django配置:
1 |
|
4.关于request对象:
request 是当用户请求django时,由wsgi.py中的application Web应用对象,会自动帮我们解析http请求的文本内容,并把对应的对象,赋值给request对象的.
request 对象是 django.core.handers.wsgi.WSGIRequest请求处理类的实例对象
WSGIRequest的父类是django.http.request.HttpRequest
在日常开发中,我们在django视图中使用request里面的绝大部分的属性方法都是继承于HttpRequest
1 |
|
5.会话控制:
从打开浏览器访问一个网站,到关闭浏览器结束此次访问,称之为一次会话
HTTP协议是无状态的,导致会话状态难以保持
Cookies和Session就是为了保持会话状态而诞生的两个存储技术
Cookies
cookies是保存在客户端浏览器上的存储空间,然后可以通过浏览器的开发者工具的application可以查看cookies的值
- cookies在浏览器上是以键-值对的形式进行存储的,键和值都是以ASCII字符串的形式存储(不能是中文字符串)
- 存储的数据带有生命周期
- cookies中的数据是按域存储隔离的,不同的域之间无法访问
- cookies的内部的数据会在每次访问此网址时都会携带到服务器端,如果cookies过大会降低响应速度
Session
session是在服务器上开辟一段空间用于保留浏览器和服务器交互时的重要数据
实现方式:
- 使用session需要在浏览器客户端启动cookie,且在cookie中存储sessionid
- 每个客户端都可以在服务器端有一个独立的Session
- 注意:不同的请求者之间不会共享这个数据,与请求者一一对应
实现会话控制的几种技术类型:
- url地址栏记录用户身份的参数[少见,很古老的技术了]
- cookie:在浏览器中由浏览器自动读写保存用户信息的一种小文件,能够存储的数据有限。10M左右[过时了,谷歌在2021年开始慢慢关闭这个cookie技术]
- session:在服务端中保存用户信息的一种小文件,能够存储的数据有限,根据服务端配置而定(需要掌握)
- token令牌:就是一段可以记录和识别用户身份的字符串,通过客户端语言[js/安卓/ios]保存在客户端的一种技术
cookie(了解就行):
1.**response.set_cookie()的参数列表:
1 |
|
2.设置cookie:
1 |
|
3.获取cookie:
1 |
|
4.删除cookie:
1 |
|
session:
1.设置session:
1 |
|
2.获取session数据:
1 |
|
3.删除session:
1 |
|
6.获取http请求数据:
1.获取查询字符串:
1 |
|
2.获取请求体数据:
1 |
|
3.获取请求头数据:
1 |
|
7.路由绑定:
当我们创建的视图和应用程序越来越多时,我们就需要用到include来进行路由绑定,使得我们不容易弄混
include用法:
#前提是你得在子目录中有建立存储关于子目录相关的url地址
在urls.py文件的urlpatterns写入:
from django.urls import path,include #要导入include的包
例子模板:path(“公共url地址(又叫路由前缀)”,include(“子应用目录名.路由模块”))
1.路由进阶:
1 |
|
2.路径转换器(内置的):
- str - 匹配除了 ‘/‘ 之外的非空字符串。如果表达式内不包含转换器,则会默认匹配字符串。
- int - 匹配 0 或任何正整数。返回一个 int 。
- slug - 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。比如,building-your-1st-django-site 。
- uuid - 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID 实例。
- path - 匹配非空字段,包括路径分隔符 ‘/‘ 。它允许你匹配完整的 URL 路径而不是像 str 那样匹配 URL 的一部分。
(1).在子路由文件下添加路由:
1 |
|
(2).视图层实例:
1 |
|
3.注册自定义的路径转换器:
(1).在目录下写入注册自定义转换器:
1 |
|
(2).在目录中的urls.py文件进行导入自定义路由转换器,进行编写变量:
1 |
|
(3).在视图层中编写视图函数使用自定义的路由转换器:
1 |
|
8.限制http请求:
常用的http请求:
1 | post | 添加/上传 |
---|---|---|
2 | get | 获取/下载 |
3 | put/patch | 修改,其中put表示修改整体数据,而patch表示修改部分数据 |
4 | delete | 删除/废弃 |
可以限制http请求方法,来防止其他请求访问视图
- 网页中的状态代码一般应该显示2xx的数字表示ok
- 1xx表示特殊情况,表示请求没完成
- 3xx表示该页面被别人跳转了
- 4xx表示我们的客户端请求有问题
- 5xx表示我们请求的服务端出错了
例子:
1 |
|
9.响应数据:
1.响应html内容:
1 |
|
2.响应json数据:
1 |
|
3.返回图片信息:
1 |
|
4.下载文件信息:
1 |
|
5.页面跳转:
1 |
|
6.站内跳转:
1 |
|
模板层:
1.django的数据库操作:
1,得先在命令行或者数据库图形化工具下进行创建用于django项目的数据库
1 |
|
2,得在model文件下定义数据库中的表类:
1 |
|
3.进行数据表的生成和迁移
1 |
|
模板语法
‘’’ 变量 ‘’’:
‘’’ 注释 ‘’’:
多行注释:
1
2
3{% comment %} 注释开始语
中间为注释
{% endcomment %} 注释结束语‘’’ 标签 ‘’’:
1
{% 标签名 %}
模板层中显示变量
视图层代码:
1 |
|
html代码:
1 |
|
if判断标签语法
视图层代码:
1 |
|
html代码:
1 |
|
1 |
|
过滤器
过滤器本质就是函数[可以是python函数,也可以是开发者自定义函数]
1 |
|
filter(过滤器)本质就是一个函数,这种函数,允许我们直接在模板中使用。使用过滤器的情况一般就是希望在模板中对数据进行格式化处理或对数据进行规范输出和调整
文档:https://docs.djangoproject.com/zh-hans/3.2/ref/templates/builtins/#ref-templates-builitins-tags
内置过滤器
safe:
safe声明当前视图提供的数据中,html代码不需要进行实体字符转义
如果来自客户端的留言等信息,在是否使用safe时,一定要考虑清楚,可以提前清楚script等不安全的标签
视图层代码
1 |
|
html代码
1 |
|
内置过滤器
1 |
|
示例代码:
视图层代码:
1 |
|
html代码:
1 |
|
自定义过滤器
customer filter
要声明自定义过滤器并且能在模板中正常使用,需要3个步骤。
确保当前需要使用过滤器的子应用已经注册到了INSTALLED_APPS中。
1
2
3
4
5
6
7
8
9
10
11
12INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'home',
'user',
'tem', #模板引擎
'login',
]编写过滤器,过滤器必须在子应用下的templatetags包(如果子应用目录下没有,就自己创建)里面创建对应的python文件中。tem.templatetags.my_filters.py的代码如下:
1
2
3
4
5
6
7
8
9
10from django import template
register=template.Library()
#自定义过滤器
#@register.filter("过滤器别名")
@register.filter("mobile")
def moblie_filter(content,flag="****"): #过滤器必须要有一个以上的参数,提供给模板调用
return content[:3]+flag+content[-3:]
#必须要有返回值,否则模板中没有内容显示在模板中通过load标签加载当前子应用已经声明的过滤器函数,load标签的使用最好写在模板的第一行
1
2
3
4
5
6
7
8
9
10
11{% load my_filter %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>{{ content3 | mobile:"新年快乐" }}</p> {# 传入变量给过滤器中进行自定义更换隐私字符 #}
</body>
</html>
模板继承
模板分离
1 |
|
视图层代码
1 |
|
模板层:
- 公共页面代码(header)
1 |
|
分离的独特内容页面代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20{% include "header.html" %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.main{
height:200px;
text-align: center;
border: 1px solid #f00;
}
</style>
</head>
<body>
<div class="main">
列表页独有的内容
</div>
</body>
</html>
其实模板分离,这种方式,虽然达到了页面代码复用的效果,但是由此也会带来大量的碎片化模板,导致维护模板的成本上升。
因此,大部分框架中除了提供这种模板分离技术以外,还并行的提供了模板继承给开发者。
模板继承(主要使用)
模板继承可以用block标签把该文件页面独有的代码(子模板)继承公共部分(父模版),这样就不用因为公共部分(父模版)的随意变化导致的文件过多
显示子模版的时候,继承父模板的公共内容
1
{% extends "base.html" %} {# base.html 为父模板 #}
视图层代码:
1
2
3def index7(request):
"""模板继承-用户页面"""
return render(request,'index7.html',locals())模板层代码:
父模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.header{
height: 100px;
background-color: orange;
text-align: center;
}
.main{
height: 300px;
background-color: lightsteelblue;
text-align: center;
}
.footer{
height: 15px;
background-color: #ff0;
text-align: center;
}
</style>
</head>
<body>
<div class="header">
公共头部
</div>
<div class="main">
{% block main %} {# 子模版可以通过声明block的变量名进行覆盖这片区域 #}
主体内容
{% endblock main %}
</div>
<div class="footer">
公共脚部
</div>
</body>
</html>子模版:
1
2
3
4{% extends "base.html" %}
{% block main %}
index7.html的主题内容
{% endblock main %}也可以在父模板中用block标签写出默认值内容,然后再在子模版中用block标签把独特的内容将默认值覆盖,也可以用block进行标记,然后如果需要添加内容,也可以通过block添加内容
block.super
block标签中的super可以进行在block标签中引用没被覆盖时的内容(必须得用两个{})
1
2
3
4
5
6{% extends "base.html" %}
{% block main %}
<-- 可以通过{{block.super}}重新获取被覆盖的父模板内容 -->
{{ block.super }}
index7.html的主题内容
{% endblock main %}1
2
3
4
5<div class="main">
{% block main %} {# 子模版可以通过block的名字进行覆盖这片区域 #}
主体内容
{% endblock main %}
</div>
使用模板层的三个步骤
django框架中使用模板引擎把视图中的数据更好的展示给客户端,需要三个步骤:
- 在项目配置文件中指定保存模板文件的模板目录。(一般模板目录都是设置在项目根目录或者主应用目录下)
- 在视图中基于django提供的渲染函数绑定模板文件和需要展示的数据变量
- 在模板目录下创建对应的模板文件,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据
1.配置模板目录:
手动在djdemo中创建一个普通目录templates
然后在settings.py中的模板引擎配置TEMPLATES中模板目录列表’DIRS’中添加templates的路径,用BASE_DIR/进行拼接
1
2
3'DIRS': [
BASE_DIR/'templates',
],在子应用定义列表INSTALLED_APPS 中注册子应用
2.绑定模板文件:
在子应用目录下手动创建urls.py子路由文件,并往里面写入几行代码:
1
2
3
4
5
6from django.urls import path,re_path
from . import views
urlpatterns=[
]在总路由文件中写入子路由文件变量
1
path('tem/',include('tem.urls'))
创建一个存储模板(html)文件的目录,并根据模板引擎内置的模板语法,填写输出视图传递过来的数据
模板文件:1
2
3
4
5
6
7
8
9
10
11<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
{# django模板中的注释内容 #}
{{ title }}
</body>
</html>视图函数编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20from django.shortcuts import render
from django.template.loader import get_template
from django.http.response import HttpResponse
# Create your views here.
def index1(request):
#模板引擎的基本使用
data={}
title="模板引擎的基本使用"
data["title"]=title
#return render(request,"index1.html",context={"title":title})
#return render(request,"index1.html",data)
#return render(request,"index1.html",locals()) #通过locals()获取当前函数中所有信息,收集成一个字典,并传参给模板
#基于settings,py中配置的模板引擎获取DIRS配置的模板文件,转换成Template模板对象
template=get_template("index1.html")
#调用模板对象提供的render函数,把本次客户端的请求对象和视图中数据data传参到模板文件中,进行渲染
#将来如果要对模板中的内容进行数据缓存[cache],可以对content进行保存起来,将来如果直接读取content的话,
# 则django就不需要操作数据库或者进行渲染,因为模板渲染的过程就是正则替换的过程
content=template.render(data,request)
return HttpResponse(content)
静态文件
开发中在开启了debug模式时,django可以通过配置,允许用户通过对应的url地址访问django的静态文件。(必须手动创建)
settings.py 中代码:
1 |
|
注意:
项目上线以后,关闭debug模式时,django默认是不提供静态文件的访问支持,项目部署的时候,我们会通过收集静态文件使用nginx这种web服务器来提供静态文件的访问支持。
视图进阶
新建一给子应用,cbv。接下来在这个子应用下面,我们学习类视图的操作。
1 |
|
在setting.py配置文件中注册子应用,setting.py, 代码:
1 |
|
创建子应用的路由文件,cbv.urls,代码:
1 |
|
在总路由中进行子路由文件的注册。 ‘djdemo.urls ‘,代码
1 |
|
类视图
Class Base View,简称”CBV”,与我们之前编写的视图函数不同,类视图是类的结构编写视图代码的。可以让我们实现相关业务代码的整合。同时还在函数视图的基础上,可以实现对于客户端访问的http请求进行分流和限制。
类视图的基本定义
1 |
|
类视图的路由绑定
调用as_view的本质,就是调用内部的view视图函数
进入内部通过dispatch函数进行路由分发,通过请求方法,获取到请求的视图函数的同名方法
1 |
|
视图基类的基本使用
1 |
|
视图基类的相关扩展
1 |
|
中间件
MiddleWare,是通过Django请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变Django的输入输出。[输入指代的就是客户端向服务端django发送数据,输出指代django根据客户端要求处理数据的结果返回给客户端]
钩子就是编程开发的一个术语,hook,钩子可以理解为一段代码(要么是类,要么是函数),它的作用就类似日常生活中墙上的钩子,不需要的时候,挂在墙上不会占用房子的空间,但是需要的时候我们可以把一些物件挂在上面。
这种中间件在平时不使用情况下不会耗费任何的性能,如果编写了中间件以后,可以在特定的条件下,全局执行!!
文档:https://docs.djangoproject.com/zh-hans/3.2/topics/http/middleware
内置中间件
django框架内部声明了很多的中间件,这些中间件有着各种各样的用途,有些没有被使用,有些被默认开启使用了。而被开始使用的中间件,都是settings.py的MIDDLEWARE中注册使用的
1 |
|
Csrf攻击
跨站请求伪造,Cross-site request forgery,利用用户在不知情的情况下实现伪造表单提交给服务端进行攻击的手段。
csrf攻击原理
django中提供了一个Csrfmiddleware的中间件给开发者用于防止网站用户遭到这种攻击收发。
中间件主要是每次客户端通过post,patch,put等方式提交数据操作时,判断当前表单是否是隐藏了一个csrf_token的随机字符串令牌。如果有这个随机字符串。则中间件则会判断这个随机字符串是否由服务端提供的。
我们开发者只需要在每一个表单页面中,内置一个隐藏的输入框里面填写csrf_token则可以让当前页面的表单顺利提交数据到后台
csrf_token的生成是每次都是基于服务端的密钥进行随机生成的,所以如果没有密钥的情况下,则生成的随机token令牌则会轻易被django识别到。
1 |
|
GZIP压缩
压缩所有现代浏览器的响应,节省宽带和传输时间
注意,GzipMiddleware目前被认为是一种安全风险,并且容易TSL/SSL提供的保护无效的攻击。
setting.py,代码:
1 |
|
自定义中间件
django中提供了2种不同的中间件声明方式。主要在django1.9或者2.0出现
函数式中间件
在项目的主应用目录下创建一个专门存放中间件函数的模块 主应用djdemo/my_middleware.py 代码:
1 |
|
因为中间件一旦注册了以后,会在全局生效,所以我们访问任何一个视图都可以看到中间件执行的过程。
类中间件
1 |
|
数据库
常用数据库两大阵营:
关系数据库[RDB]: 数据库中存储数据的表之间存在某种内在的关联关系,因为这种关系,所以我们称这一类型数据为关系型数据库。
共同的特点: 都使用了SQL语句进行数据库操作。
常见的数据库: mysql[MariaDB], PostgreSQL, Oracle, MSServer, DB2. sqlite, Access
非关系型数据库[Nosql]: 泛指那些不适用SQL语句进行数据库操作的所有其他数据库。Redis,MongoDB,Memcached,Elasticsearch/Sphinx/Solr,HBase/CouchDB
耦合性:类似成语里面的藕断丝连,表示2段程序之间的关联性和依赖性的强度。
工作中要追求代码的低藕性,就是降低2段程序之间的关联性和依赖性。 高内聚低耦合
ORM框架
O是object,也就是类对象的意思。
R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思
M是mapping,是映射的意思。
ORM框架会帮我们把类对象和数据表进行一对一的映射,让我们可以通过类对象来操作对应的数据表。
ORM框架还可以根据我们设计的类自动帮我们生成数据库中的表格,省去了自己建表的过程。
django中内嵌了ORM框架,不需要直接编写SQL语句进行数据库操作,而是通过定义模型类,操作模型类来完成对数据库中表中的数据的增删改查和数据表创建等操作。
ORM的优点
- 数据模型类都在一个地方定义,更容易更新和维护,也有利于重用代码。
- ORM有现成的工具,很多功能都可以自动完成,比如数据消除、预处理、事务等等。
- 它迫使你使用MVC架构,ORM就是天然的Model,最终使代码更清晰。
- 基于ORM的业务代码比较简单,代码最少,语义性好,容易理解。
- 新手对于复杂业务容易写出性能不佳的SQL,有了ORM不必编写复杂的SQL语句,只需要通过操作模型对象即可同步修改数据表中的数据。
- 开发中应用ORM将来如果要切换数据库,只需要切换ORM底层对接数据库的驱动【修改配置文件的连接地址即可】
ORM缺点
- ORM库不是轻量级工具,需要花很多精力学习和设置,甚至不同的框架,会存在不同操作的ORM。
- 对于复杂的业务查询,ORM表达起来比原生的SQL要更加困难和复杂。
- ORM操作数据库的i性能要比原生的SQL差。[ORM内部要拼接SQL语句]
- ORM抽象掉了数据库层,开发者无法了解底层的数据库操作,也无法定制一些特殊的SQL。【自己使用pymysql另外操作即可,用了ORM并表示当前项目不能使用别的数据库操作工具了。】
配置数据库连接
在setting.py中保存了数据库的连接配置信息,Django默认初始配置使用sqlite数据库。
我们可以通过一下步骤来使用django的数据库操作
1 |
|
使用MySQL数据库首先需要安装驱动程序
1
pip install pymysql
在django的主应用目录的_init_.py文件中添加如下语句
1
2
3from pymysql import install_as_MySQLdb
install_as_MySQLdb()#让pymysql以MySQLDB的运行模式和Django的ORM对接运行作用是让Django的ORM能以mysqldb的方式调用pymysql
修改database配置信息,setting.py配置文件配置数据库连接信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'register_info',
'USER':'root',
'PASSWORD':'Acky16140563.',
'HOST':'localhost',
'POST':'3306',
'POOL_OPTIONS':{ #数据库连接池配置
'POOL_SIZE': 10, #默认情况下,打开的数据库连接的数量
'MAX_OVERFLOW': 10 #负载情况下,允许溢出的连接数量
}
}
}在mysql中创建数据库
1
2create database school; #mysql8.0默认就是utf8mb4
create database school default charset=utf8mb4; #mysql8.0之前的版本mysql5.7,django3.0以下版本,错误解决
数据库兼容问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22#错误提示
django.db.utils.ProgrammingError:(1064,"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED' at line 1")
#解决方案
#在settings.py的database选项中每一个数据库连接加上OPTIONS选项配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'register_info',
'USER':'root',
'PASSWORD':'Acky16140563.',
'HOST':'localhost',
'POST':'3306',
'POOL_OPTIONS':{ #数据库连接池配置
'POOL_SIZE': 10, #默认情况下,打开的数据库连接的数量
'MAX_OVERFLOW': 10 #负载情况下,允许溢出的连接数量
},
'OPTIONS':{
'isolation_level': None
}
}
}
定义模型类
- 模型类被定义在”子应用/models.py”文件中
- 模型类必须直接或者间接继承自django.db.models.Model类
在子应用目录下的models.py文件中定义模型类
1 |
|
1 |
|
db_table 用于设置当前模型类对应的数据表是哪一张,如果不设置,则表名对应为**”子应用名_模型类名”.lower()**
2)关于主键
django会为模型自动声明一个自动增长的主键列,每个模型只能有一个主键列
如果使用选项设置某个字段的约束属性为主键列后,django不会再创建自动增长的主键列
默认创建的主键列属性为id,可以用pk代替
3)属性命名规则
不能是python的关键字或保留字
不允许使用连续的2个下划线,这是由django的查询方式决定的。__是关键字来的,不能使用!!!
定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
1
2
3属性名=models.字段类型(约束选项,verbose_name="注释")
#在mysql数据表中的字段名如果在python是一个关键字/保留字。则选项中需要通过db_column()来进行关联绑定
4)字段类型
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时django会自动创建属性名为id的自动增长属性,并且提供一个PK属性与主键进行关联 |
BooleanField | 布尔类型,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串,参数max_length表示最大字符个数,对应mysql中的varchar |
TextField | 大文本字段,一般大段文本(超过4000个字符)才使用 |
IntegerField | 整数 |
DecimalField | 十进制浮点数,参数max_digits表示总位数,参数decimal_places表示小数位数,常用于表示分数和价格 Decimal(max_digits=7,decimal_places=2)==>99999.99~0.00 |
FloatField | 浮点数 |
ImageField | 继承于FileField,对上传的内容进行校检,确保是有效的图片 |
5)约束选项
选项 | 说明 |
---|---|
null | 如果为True,表示允许为空,默认值是False。相当于python的None |
blank | 如果为True,则该字段允许为空白,默认值是False。相当于python的空字符串,”” |
db_column | 数据表中真实的字段的名称,如果未指定,则使用属性的名称 |
db_index | 若值为True,则在表中会为此字段创建索引,默认值是False。相当于SQL语句中的key |
default | 默认值,当不填写数据时,使用该选项的值作为数据的默认值 |
primary_key | 如果为True,则该字段会成为模型的主键,默认值是False,一般不用设置,系统默认设置 |
unique | 如果为True,咋该字段在表中必须有唯一值,默认值是False。相当于SQL语句中的unique |
verbose_name | 设置此字段在admin界面上的显示名称 |
注意:null是数据库范畴的概念,blank是表单验证范畴
修改过字段选项【添加或更改】均要执行makemigrations和migrate
Meta类
使用模型类内部Meta类来给模型赋予属性
1 |
|
ORM操作
ORM的基本操作的核心是模型类的管理器对象
管理器对象(增加数据)
每个继承自models.Model的模型类,都会有一个objects对象被同样继承下来。这个对象叫管理器对象
数据库的增删改查可以通过模型的管理器实现
1 |
|
Django Shell
在Django提供了一个交互式的操作项目叫Django Shell 它能够在交互模式用项目工程的代码执行相应的操作
利用Django Shell 可以替代编写view的代码来进行直接操作
注意:项目代码发生变化时,重新进入Django Shell
启动方式:
1 |
|
使用方法:
1 |
|
查询操作
all()方法
用法:MyModel.objects.all()
作用:查询MyModel实体中所有的数据
等同于select * from table
返回值:QuerySet容器对象(可以把它当作一个数组),内部存放MyModel实例对象
可以在模型类中定义下划线_str_下划线方法,自定义QuerySet中的输出格式例如
1
2def _str_(self):
return '%s_%s_%s_%s'%(self.title,self.price...)#自定义的输出格式values(‘列1’,’列2’)方法
用法:MyModel.objects.values(…)
作用:查询部分列的数据并返回
等同于select 列1,列2 from xxx
返回值: QuerySet,返回查询结果容器,容器内存字典,每个字典代表一条数据,格式为{‘列1’:值1,’列2’:值2}
values_list(‘列1’,’列2’…)
用法:MyModel.objects.values_list(….)
作用:返回元组形式的查询结果
等同于select 列1,列2 from xxx
返回值:QuerySet容器对象,内部存放元组
会将查询出来的数据封装到元组中,在封装到查询集合QuerySet中
order_by()
用法:MyModel.objects.order_by(‘列’,’’列)
作用: 与all()方法不同,它会用SQL语句的ORDER BY子句对查询结果进行根据某个字段选择性的进行排序
说明:默认是按照升序排序,降序排序则需要在列前增加’-‘表示
条件查询
filter(条件)
语法:MyModel.objects.filter(属性1=值1,属性2=值2)
作用:返回包含此条件的全部数据集
返回值:QuerySet容器对象,内部存放MyModel实例
说明:当多个属性在一起时为”与”关系
get(条件)
语法:MyModel.objects.get(条件)
作用:返回满足条件的唯一一条数据
说明:该方法只能返回一条数据,查询结果多于一条数据则抛出Model.MultipleObjectsReturned异常,查询结果如果没有数据则抛出Model.DoesNotExist异常
查询谓词
定义:做更灵活的条件查询时需要使用查询谓词
说明:每一个查询谓词是一个独立的查询功能
- (字段)__exact:等值匹配
示例:
1
2Author.objects.filter(id__exact=1)
#等同于select * from author where id = 1__contains:包含指定值
1
2Author.objects.filter(name__contains='w')
#等同于select * from author where name like '%w%'__startswith:以xxx开始
__endswith:以xxx结束
__gt:大于指定值
样例:
1
2Author.objects.filter(age__gt=50)
#等同于select * from author where age > 50__gte:大于等于指定值
__lt:小于
__lte:小于等于
__in:查找数据是否在指定范围内
1
Author.objects.filter(countyr__in=['中国','日本','韩国']) #等同于select * from author where country in ('中国','日本','韩国')
__range:查找数据是否在指定的区间范围内
1
2
3#查找年龄在某一区间内的所有作者
Author.objects.filter(age__range=(35,50))
#等同于 select ... where Author between 35 and 50;
更新操作
1)更新单个数据
查
-通过get()得到要修改的实体对象
改
-通过对象.属性的方式修改数据
保存
-通过对象.save()保存数据
2)批量更新数据
直接调用QuerySet的update(属性=值)实现批量修改
示例:
1 |
|
删除操作
1)单个数据删除
查找查询结果对应的一个数据对象
调用这个数据对象的delete()方法实现删除
示例:
1
2
3
4
5try:
auth=Author.objects.get(id=1)
auth.delete()
except:
print(删除失败)
2)批量删除
查找查询结果集中满足条件的全部QuerySet查询集合对象
调用查询集合对象的delete()方法实现删除
示例:
1
2
3#删除全部作者中,年龄大于65岁的全部信息
auths=Author.objects.filter(age__gt=65)
auths.delete()
3)伪删除
- 通常在业务中不会轻易地把数据真正删除,取而代之的是做伪删除,即在表中添加一个布尔类型字段(is_active),默认为True;执行删除时,将欲删除数据的is_active字段置为False
- 注意:用伪删除时,确保显示数据的地方,均加了is_active=True的过滤查询
F对象和Q对象
F对象
一个F对象代表数据库中某条记录的字段的信息
作用:
-通常是对数据库中的字段值在不获取的情况下进行操作(也可以为标记),在业务数据更新时,为防止资源竞争和并发事件,用F对象就等于加了把锁,可以有条不紊地进行更新。
-用于类属性(字段)之间的比较
语法:
1 |
|
示例代码: 更新book实例中所有的零售价涨10块
1 |
|
实例2:对数据库中两个字段的值进行比较,列出哪儿些书的零售价高于定价
1 |
|
Q对象
当在获取查询结果集使用复杂的逻辑或 |、逻辑非~等操作时可以借助于Q对象进行操作
如:想找出定价低于20元或清华大学出版社的全部书,可以写成
1 |
|
Q对象在数据包django.db.models中,需要先导入再使用
作用:在条件中用来实现除and(&)以外的or(|)或not(~)操作
语法:
1 |
|
聚合查询和原生数据库操作
聚合查询
聚合查询是指对一个数据表中的一个字段的数据进行部分或全部进行统计查询,查bookstore_book数据表中的全部书的平均价格,查询所有书的总个数等,都要使用聚合查询
聚合查询分为:整表查询和分组查询
1)整表查询
不带分组的聚合查询是指导将全部数据进行集中统计查询
聚合函数[需要导入]
**-导入方法: from django.db.models import ***
-聚合函数: Sum,Avg,Count,Max,Min
语法:MyModel.objects.aggregate(结果变量名=聚合函数(‘列’))
返回结果:结果变量名和值组成的字典
格式为:{“结果变量名”:值}
2)分组聚合
分组聚合是指通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
语法:
- QuerySet.annotate(结果变量名=聚合函数(‘列’)
返回值:
- QuerySet
实例:
1 |
|
原生数据库操作
django也可以支持直接用sql语句的方式通信数据库
查询:使用MyModel.objects.raw()进行数据库查询操作查询
语法:MyModel.objects.raw(sql语句,拼接参数)
返回值:RawQuerySet集合对象【只支持基础操作,比如循环】
实例:
1 |
|
SQL注入
使用原生语句时小心SQL注入
定义:用户通过数据上传,将恶意的sql语句提交给服务器,从而达到攻击效果
案例1:用户在搜索好友的表单框里输入’1 or 1=1’
1 |
|
攻击结果:可查询出所有用户数据,’1=1’是恒等式,会会得出全表数据
sql注入防范
把刚才的sql拼接语句改成
1 |
|
原生数据库操作-cursor
完全跨过模型类操作数据库-查询/更新/删除
导入cursor所在的包
from django.db import connection
用创建cursor类的构造函数创建cursor对象,再使用cursor对象,为保证在出现异常时能释放cursor资源,通常使用with语句进行创建操作
1
2
3from django.db import connection
with connection.cursor() as cur:
cur.execute('执行SQL语句','拼接参数')
关系映射
在关系型数据库中,通产不会把所有数据都放在同一张表中,不易于扩展,常见关系映射有:
- 一对一映射
- 一对多映射
- 多对多映射
一对一映射
一对一是表示现实事物间存在的一对一的对应关系
如:一个家庭只有一个户主,一个男人有一个妻子,一个人有一个唯一的指纹信息等
语法:OneToOneField(类名,on_delete=xxx)
1 |
|
on_delete - 级联删除
- models.CASCADE 级联删除。Django模拟SQL语句约束ON_DELETE CASCADE的行为,并删除包含ForeignKey的对象
- models.PROTECT 抛出ProtectedError 以阻止被引用对象的删除;[等同于mysql默认的RESTRICT]
- SET_NULL 设置ForeignKey null; 需要null=True
- SET_DEFAULT 将ForeignKey设置为其默认值;必须设置ForeignKey的默认值
创建模型类
实例:
1 |
|
创建数据
无外键的模型类[Author]:
1
author1=Author.objects.create(name='王老师')
有外键的模型类[Wife]:
1
2wife1=Wife.objects.create(name='王夫人',author=author1) #关联王老师对象
wife1=Wife.objects.create(name='王夫人',author_id=1) #关联王老师对应主键值
查询数据
正向查询:直接通过外键属性查询,则称为正向查询
1
2
3
4#通过wife找author
from .models import Wife
wife=Wife.objects.get(name='王夫人')
print(wife.name,'的老公是',wife.author.name)反向查询:没有外键属性的一方,可以调用反向属性查询到关联的另一方
反向关联属性为’实例对象.引用类名(小写)’,如作家的反向引用为’作家对象.wife’
-当反向引用不存在时,则会触发异常
author1=Author.objects.get(name=’王老师’)
author1.wife.name
一对多映射
一对多是表示现实事物间存在的一对多的对应关系
如:一个学校有多个班级,一个班级有多个学生,一本图书只能属于一个出版社,一个出版社允许出版多本图书
一对多需要明确出具体角色,在多表上设置外键
语法:当一个A类对象可以关联多个B类对象时
1 |
|
ForeignKey必须指定on_delete模式
创建模型类:
实例:
1 |
|
创建数据
先创建’一’再创建’多’
1 |
|
查询数据
正向查询[通过Book查询 Publisher]
1
2
3
4
5#通过publisher属性查询即可
#book.publisher
abook=Book.objects.get(id=1)
print(abook.title,'的出版社是:',abook.publisher.name)反向查询[通过Publisher查询对应的所有的Book]
需要用到反向属性(下面例子中的book_set),反向属性的命名规则为关联的多的类名小写+_set
django 默认每个主表的对象都有一个是外键的属性,可以通过它来查询到所有属于主表的子表的信息。这个属性的名称默认是以子表的名称小写加上_set()来表示(上面默认以b_set访问),默认返回的是一个querydict对象。
related_name 可以给这个外键定义好一个别的名称1
2
3
4
5
6pub1=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)
多对多映射
多对多表达对象之间多得多复杂关系,如:每一人都有不同的学校(小学,初中,高中…),每个学校都有不同的学生…
mysql中创建多对多需要依赖第三张表来实现
Django中无需手动创建第三张表,Django自动完成
语法:在关联的两个类中的任意一个类中,增加:属性=models.ManyToManyField(MyModel)
创建模型类
实例:一个作者可以出版多本图书,一本图书可以被多名作者同时编写
1 |
|
创建数据
方案1:先创建author再关联book
1 |
|
方案2:先创建book再关联author
1 |
|
查询数据
正向查询有多对多属性的对象查另一方
通过Book查询对应的所有的Author
此时多对多属性等价于objects
1
2book.authors.all() ->获取book对应的所有的author的信息
book.authors.filter(age__gt=80) ->获取book对应的作者中年龄大于80岁的作者的信息反向查询
通过Author查询对应的所有的Book
利用反向属性book_set
1
2author.book_set.all()
author.book_set.filter()
admin后台管理
django提供了比较完善的后台管理数据库的接口,可供开发过程中调用和测试使用
django会搜集所有已注册的模型类,为这些模型类提供数据管理界面,供开发者使用
注册自定义模型类
若要自己定义的模型类也能在/admin后台管理界中显示和管理,需要将自己的类注册到后台管理界面
注册步骤:
- 在应用app中的admin.py中导入注册要管理的模型models类,如:from .models import Book
- 调用admin.site.register 方法进行注册,如:admin.site.register(自定义模型类)
模型管理类
作用:为后台管理界面添加便于操作的新功能
说明:后台管理器类须继承自django.contrib.admin里的ModelAdmin类
使用方法:
在应用app的admin.py里定义模型管理器类
1
2class xxxxManager(admin.ModelAdmin):
......绑定注册模型管理器和模型类
1
2
3from django.contrib import admin
from .models import *
admin.site.register(YYYY,xxxxManager) #绑定YYYY模型类与管理器类 xxxxManager
模型管理器类的属性以及作用
- list_display 去控制哪些字段会显示在Admin的修改列表页面中
- list_display_links 可以控制list_display中的字段是否应该链接到对象的”更改”页面
- list_filter 设置激活Admin修改列表页面右侧栏中的过滤器
- search_fields 设置启用Admin更改列表页面上的搜索框
- list_editable 设置为模型上的字段名称列表,这将允许更改页面上进行编辑
1 |
|
内建用户系统
django带有一个用户认证系统。它处理用户账户、组、权限以及基于cookie的用户会话。
用户可以直接使用django自带的用户表
基本字段
模型类位置:
1 |
|
模型字段 | 用处 |
---|---|
username | 用户名 |
password | 密码 |
邮箱 | |
first_name | 名 |
last_name | 姓 |
is_superuser | 是否是管理员账号(/admin) |
is_staff | 是否可以访问admin管理界面 |
is_active | 是否是活跃用户,默认为True。一般不删除用户,而是将用户的is_avtive设为False |
last_login | 上一次的登录时间 |
date_joined | 用户创建的时间 |
基本操作
创建用户
创建普通用户create_user
1
2from django.contrib.auth.models import User
user=User.objects.create_user(username='用户名',password='密码',email='邮箱',....)创建超级用户create_superuser
1
2from django.contrib.auth.models import User
user=User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...)
删除用户
django建议我们还是用伪删除
示例:
1 |
|
校验密码
1 |
|
说明:
如果用户名密码校验成功则返回对应的user对象,否则返回None
修改密码
示例:
1 |
|
自带登录系统功能
登录状态保持
利用了session,但session保存时间不可控
1 |
|
登录状态校验
示例:
1 |
|
登录状态取消
1 |
|
内建用户表扩展字段
两种方案:
- 通过建立新表,跟内建表做1对1
- 继承内建的抽象user模型类
方案二:
步骤:
- 添加新的应用
- 定义模型类继承AbstractUser
- settings.py中知指明AUTH_USER_MODEL=’应用名.类名’
注意:此操作要在第一次Migrate之前进行
示例:- user/models.py -添加user应用
1 |
|
settings.py添加配置
1 |
|
添加用户
1 |
|
缓存
定义:缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存
意义:视图渲染有一定成本,数据库的频繁查询过高;所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数;用户拿到响应的时间成本会更低
缓存配置
设置数据库缓存
将缓存的数据存储在您的数据库中
说明:尽管存储介质没有更换,但是当把一次负责查询的结果直接存储在表里,比如多个条件的过滤查询结果,可避免重复进行复杂的查询,提升效率;
1
2
3
4
5
6
7
8
9
10
11
12# #数据库缓存设置,需要手动执行创建表的命令(my_cache_table)
CACHES={
'default':{
'BACKEND':'django.core.cache.backends.db.DatabaseCache',
'LOCATION':'my_cache_table',
'TIMEOUT':300, #缓存保存时间 单位秒,默认值为300
'OPTIONS':{
'MAX_ENTRIES':300, #缓存最大数据条数
'CYLL_FREQUENCY':2, #缓存条数达到最大值时,删除1/x的缓存数据
}
}
}创建数据库缓存表的操作:
1
python manage.py createcachetable
本地内存缓存:
数据缓存到服务器内存中
配置:
1
2
3
4
5
6CACHES={
'default':{
'BACKEND':'django.core.cache.backends.locmem.LocMemCache',
'LOCATION':'unique-snowflake',
}
}文件系统缓存
将缓存的数据存储到本地文件中
1
2
3
4
5
6
7CACHES={
'default':{
'BACKEND':'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION':'/var/tem/django_cache', #这个是文件夹的路径
#'LOCATION':'c:\test\cache',#windows下示例
}
}
缓存使用
整体缓存
视图函数中:
样例:
1
2
3
4
5from django.views.decorators.cache import cache_page
@cache_page(30) #当前缓存保存的时间 单位s
def my_view(request):
....在路由中:
示例:
1
2
3
4
5from django.views.decorators.cache import cache_page
urlpatterns=[
path('foo/',cache_page(60)(my_view))
]
局部缓存
缓存api的使用:
先引入cache对象:
使用caches[‘CACHE配置key’]导入具体对象
1
2
3from django.core.cache import caches
cache1=caches['myalias']
cache2=caches['myalias_2']from django.core.cache import cache 相当于直接引入CACHES配置项的’default’项
cache.set(key,value,timeout) - 存储缓存
- key:缓存的key,字符串类型
- value:python对象
- timeout:缓存存储时间(s),默认为CACHES中的TIMEOUT值
- 返回值:None
cache.get(key) - 获取缓存
- key:缓存的key
- 返回值:为key的具体值,如果没有数据,则返回None
cache.add(key,value) - 存储缓存
- 注意:只在key不存在时生效
- 返回值:True[存储成功]或者False[存储失败]
cache.get_or_set(key,value,timeout) - 如果未获取到数据,则执行set操作
- 返回值:value
cache.set_many(dict,timeout) - 批量存储缓存
- dict:key和value的字典
- timeout:存储时间(s)
- 返回值:插入不成功的key的数组
cache.get_many(key_list) - 批量存储缓存数据
- key_list:包含key的数组
- 返回值:取到的key和value的字典
cache.delete(key) - 删除key的缓存数据
- 返回值:None
cache.delete_many(key_list) - 批量删除
- 返回值:None
浏览器缓存
强缓存
不会向服务器发送请求,直接从缓存中读取资源
- 响应头 - Expires
- 定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点(绝对时间)
- 样例:Expires:Thu,02 Apr 2030 05:14:08 GMT
- 响应头 - Cache-Control
- 在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当’Cache-Control:max-age=120’ 代表请求创建时间后的120秒,缓存失效(相对时间)
- 说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用Cache-Control
协商缓存
强缓存中的数据一旦过期。还需要跟服务器进行通信,从而获取最新数据;如果强缓存的数据是一些静态文件,大图片等;
考虑到大图片这类比较费带宽且不易变化的数据,强缓存时间到期后,浏览器会去跟服务器协商,当前缓存是否可用,如果可用,服务器不必返回数据,浏览器继续使用原来缓存的数据,如果文件不可用,则返回最新数据
1.Last-Modified响应头和 If-Modified-Since请求头
- Last-Modified为文件的最近修改时间,浏览器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需要协商的缓存
- 当缓存到期后,浏览器将获取到的Last-Modified值做为请求头If-Modified-Since的值,与服务器发请求协商,服务端返回304响应码[响应体为空],代表缓存继续使用,200响应码代表缓存不可用[响应体为最新资源]
ETag响应头和If-None-Match响应头
- ETag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,ETag就会重新生成
- 缓存到期后,浏览器将Etag响应头的值作为If-None-Match请求头的值,给服务器发请求协商;服务器接到请求头后,比对文件标识,不一致则认为资源不可用,返回200响应码[响应体为最新资源];可用则返回304响应码
Last-Modified和ETag的相对区别
- 精度不一样 - Etag准确率更高
- 性能上 - Last-Modified高,Etag需要计算文件标识
- 优先级 - Etag高;两个头同时出现,浏览器优先取Etag
分页
定义:分页是指在web页面有大量数据需要显示,为了阅读方便在每个页面中只显示部分数据
优点:
- 方便阅读
- 减少数据提取量,减轻服务器压力
- Django提供了Paginator类可以方便的实现分页功能
- Paginator类位于’django.core.paginator’模块中
Paginator对象
负责分页数据整体的管理
对象的构造方法:
1 |
|
参数;
- object_list需要分页数据的对象列表
- per_page每页数据个数
返回值:
Paginator对象
属性:
- count:需要分页数据的对象总数
- num_pages:分页后的页面总数
- page_range:从1开始的range对象,用于记录当前面码数
- per_page:每页数据的个数
Paginator方法:
paginator对象.page(number)
- 参数number为页码信息(从1开始)
- 返回当前number页对应的页信息
- 如果提供的页码不存在,抛出InvalidPage异常
Paginator异常exception:
InvalidPage:总的异常基类,包含以下两个异常子类
- PageNotAnlnteger:当向page()传入一个不是整数的值时抛出
- EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出
Page对象
负责具体某一页的数据的管理
创建对象:
- Paginator对象的page()方法返回Page对象
- page=paginator.page(页码)
Page对象属性:
- object_list:当前页上所有数据对象的列表
- number:当前页的序号,从1开始
- paginator:当前page对象相关的Paginator对象
Page对象方法:
- has_next():如果有下一页返回True
- has_previous():如果有上一页返回True
- has_other_page():如果有上一页或下一页返回True
- next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
- previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvaildPage异常
实例:
视图层:
1 |
|
前端代码:
1 |
|
csv文件
定义:逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)
说明:可被常见制表工具,如excel等直接进行读取
python中生成csv文件
python提供了内建库 - csv;可直接通过该库操作csv文件
案例:
1 |
|
csv文件下载
在网站中,实现下载csv,注意如下:
- 响应Content-Type类型需修改为text/csv。这告诉浏览器该文档是CSV文件,而不是HTML文件
- 响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称。它将被浏览器用于开启”另存为…”对话框
案例代码:
1 |
|
文件上传
定义:用户可以通过浏览器将图片等文件传至网站
场景:
- 用户上传头像
- 上传流程性的文档[pdf,txt等]
上传规范 - 前端[html]
文件上传必须为POST提交方式
表单<form>
中文件上传时必须有带有enctype=”multipart/form-data”时才会包含文件内容数据。
表单中用<input type="file" name="xxx">
标签上传文件
示例:
1 |
|
上传规范 - 后端[django]
视图函数中,用request.FILES去文件框的内容
file=request.FILES[‘xxx’]
说明:
- FILES的key对应页面中file框的name值
- file绑定文件流对象
- file.name 文件名
- file.file 文件的字节流数据
配置文件的访问路径和存储路径
在setting.py中设置MEDIA相关配置;Django把用户上传的文件,统称为media资源
Django把用户上传的文件,统称为media资源
1 |
|
MEDIA_URL和MEDIA_ROOT需要手动绑定
步骤:主路由中添加路由
1 |
|
说明:等价于做了MEDIA_URL开头的路由,Django接到该特征请求后去MEDIA_ROOT路径查找资源
文件写入
方案1:传统的open方式
1
2
3
4
5
6
7
8
9
10
11
12@csrf_exempt
def upload_view(request):
if request.method=='GET':
return render(request,'text_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
得借助orm层的字段:FileField(upload=’子目录名’)
1
2
3
4
5
6
7
8
9@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=titile,myfile=a_file)
return HttpResponse('---upload is ok---')orm层示例:
1
2
3
4
5from django.db import models
class Content(models.Model):
title=models.CharField('文章名字',max_length=11)
picture=models.FileField(upload_to='picture')
发送邮件
业务场景:
- 业务告警
- 邮件验证
- 密码找回
邮件相关的三个协议
- SMTP
- SMTP的全称是”Simple Mail Transfer Protocol”,即简单邮件传输协议(25号端口)
- 它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转
- 属于”推送”协议
- IMAP
- IMAP全称是Internet Mail Access Protocol,即交互式邮件访问协议,是一个应用层协议(端口是143)
- 用来从本地邮件客户端(Outlook Express、Foxmail、Mozilla、Thunderbird等)访问远程服务器上的邮件
- 属于”拉取”协议
- POP3
- POP3事是Post Office Protocol 3 的简称,即邮局协议的第三个版本,是TCP/IP协议族中的一员(默认端口是110)
- 本协议主要用于支持使用客户端远程管理在服务器上的电子邮件
- 属于”拉取”协议
IMAP跟POP3的区别
两者均为”拉取”型协议,负责从邮件服务器中下载邮件
IMAP具备摘要浏览功能,可预览部分摘要,再下载整个邮件
IMAP为双向协议,客户端操作可反馈给服务器
POP3必须下载全部邮件,无摘要功能
POP3为单向协议,客户端操作无法同步服务器
邮件收发全过程
django就是充当邮件客户端的身份
Django发邮件
Django中配置邮件的功能,主要为SMTP协议,负责发邮件
原理
- 给Django授权一个邮箱
- Django用该邮箱给对应收件人发送邮件
- django.core.mail封装了电子邮件的自动发送SMTP协议
授权步骤
以QQ邮箱为例:
申请QQ号
用QQ号登录QQ邮箱并修改设置
- 用申请到QQ号和密码登录 到https://mail.qq.com
- 修改’QQ邮箱->设置->账户->”POP3/IMAP….服务”
- 拿到授权码
Django配置
1
2
3
4
5
6EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST='smtp.qq.com' #腾讯QQ邮箱SMTP服务器地址
EMAIL_PORT=25 #SMTP服务的端口号
EMAIL_HOST_USER='xxxx@qq.com' #发送邮件的QQ邮箱
EMAIL_HOST_PASSWORD='******' #在QQ邮箱->设置->账户->"POP3/IMAP...服务"里得到的在第三方登录QQ邮箱授权码
EMAIL_USE_TLS=False #与SMTP服务器通信时,是否启动TLS链接(安全链接)默认False
函数调用
1 |
|
项目部署
项目部署是指在软件开发完毕后,将开发机器上运行的软件实际安装到服务器上进行长期运行
在安装机器上安装和配置同版本的环境[py,数据库等]
django项目迁移
虚拟机linux系统下做的迁移,示例:linux命令如下
1
2sudo scp /home/tarena/django/mysite1 #要复制的项目
root@88.77.66.55:/home/root/xxx #要迁移到的外部目录,得输入用户名和用uWSGI替代python manage.py runserver方法启动服务器
配置nginx反向代理服务器
用nginx配置静态文件路径,解决静态路径问题
uWSGI
uWSGI网关接口配置
WSGI(Web Server Gateway Interface)Web服务器网关接口,是Python应用程序或框架和Web服务器之间的一种接口,被广泛使用
使用python manage.py runserver通常只在开发和测试环境中使用
当开发结束后,完善的项目代码需要在一个高效稳定的环境运行,这时可以使用WSGI
uWSGI是WSGI的一种,它实现了http协议 WSGI协议以及uwsgi协议
uWSGI功能完善,支持协议众多,在python web圈热度极高
uWSGI主要以学习配置为主
配置uWSGI
添加配置文件 项目同名文件夹/uwsgi.ini,例如:mysite1/mysite1/uwsgi.ini
文件以[uwsgi]开头,有如下配置项:
套接字方式的 IP地址:端口号 [此模式需要有nginx]
socket=127.0.0.1:8000
Http通信方式的 IP地址:端口号
http=127.0.0.1:8000
1 |
|
项目当前工作目录
示例:
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
特殊说明
Django的settings.py需要做如下配置
- 修改settings.py将DEBUG=True改为DEBUG=False(正式上线时是要更改该配置,要不然用户会看到我们的报错信息)
- 修改settings.py将ALLOWED_HOSTS=[]改为ALLOWED_HOSTS=[‘网站域名’]或者[‘服务监听的ip地址’]
uWSGI的运行管理
启动uWSGI
cd到uWSGI配置文件所在目录下
uwsgi –ini uwsgi.ini
**ps aux|grep ‘uwsgi’**检测是否启动uwsgi是否启动和停止
停止uWSGI
cd到uWSGI配置文件所在目录
uwsgi –stop uwsgi.pid
重启uWSGI
uwsgi –reload uwsgi.pid
uWSGI的运行说明
- 无论是启动后还是关闭,都需要执行ps aux|grep ‘uwsgi’确认是否符合预期
- 启动成功后,进程在后台执行,所有日志均输出在配置文件所在目录的uwsgi.log钟
- Django中代码有任何修改,需要重新启动uwsgi
uWSGI测试
在浏览器端输入http://127.0.0.1:8000/url进行测试
注意: 此时端口8000被uWSGI进程监听,并非runserver
如果当前有与其返回,则uWSGI启动成功
uWSGI常见问题汇总
启动失败:端口被占用
原因:有其他进程占用uWSGI启动的端口;
解决方案:可执行 sudo lsof -i:端口号 查询出具体进程;杀掉进程后,重新启动uWSGI即可
停止失败:stop无法关闭uWSGI
原因:重复启动uWSGI,导致pid文件中的进程号失准
解决方案:ps出uWSGI进程,手动kill掉
nginx
- Nginx是轻量级的高性能Web服务器,提供了诸如HTTP代理和反向代理、负载均衡等一系列重要特性
- C语言编写
- nginx作用:
- 负载均衡,多台服务器轮流处理请求
- 反向代理 - 原理:客户端请求nginx,再由nginx将请求转发uWSGI运行的django
反向代理:就是nginx可以接收来自五湖四海的网民的请求,并将这些请求分发给多台django机器中
*
负载均衡:均衡的把请求分发给django机器
nginx的运行
启动nginx
cd 到/usr/local/nginx/sbin目录中
输入命令**./nginx**来启动nginx
停止nginx
cd 到/usr/local/nginx/sbin目录中
输入命令./nginx -s stop或者./nginx -s quit
.nginx -s stop 是快速停止,可能会导致数据没有完全保存
.nginx -s quit 是有序停止
重启nginx
命令: ./nginx -s reopen 或者 ./nginx -s reload
查询nginx进程
用于检测nginx是否启动成功
命令: ps -ef | grep nginx
命令大全
killall nginx #杀死所有nginx进程
nginx -t #检测配置文件是否有语法错误,然后退出
nginx -v #显示版本信息并退出
nginx -V #显示版本和配置选项信息,然后退出
nginx -t #检测配置文件是否有语法错误,然后退出,因为有些文件权限没给,所以前面加个sudo
nginx -T #检测配置文件是否有语法错误,转储并退出
nginx -q #在检测配置文件期间屏蔽非错误信息
nginx -?,-h #打开帮助信息
nginx -p prefix #设置前缀路径(默认是:/usr/share/nginx/)
nginx -c filename #设置配置文件(默认是:/etc/nginx/nginx.conf)
nginx -g directives #设置配置文件外的全局指令
nginx和uwsgi排错
常见问题排查
排查问题宗旨 是 看日志!看日志! 看日志!! 看日志!!!
- nginx日志位置:
异常信息存放地:/usr/local/nginx/logs/error.log
正常访问信息存放地:/usr/local/nginx/logs/access.log
uwsgi日志位置:
项目同名目录下,uwsgi.log
访问127.0.0.1:80 地址,502响应
502响应代表nginx反向代理配置成功,但是对应的uwsgi未启动
访问127.0.0.1:80/url 404响应
- 路由的确不在django配置中(黄色页面报错) - nginx配置错误,未禁止掉 try_files
nginx静态文件配置问题
静态文件配置步骤
创建新路径-主要存放Django所有静态文件 如:/home/dreamcreator/djdemo_static/
在Django settings.py中添加新配置
1
2STATIC_ROOT = '/home/dreamcreator/djdemo_static/static'
#注意此配置路径为 存放所有正式环境中需要的静态文件进入项目,执行python3 manage.py collectstatic 执行该命令后,Django将项目中所有静态文件复制到STATIC_ROOT中,包括Django内建的静态文件
Nginx配置中添加新配置
1 |
|
404/500页面
定义和配置
在模板文件夹内添加404.html 模板,当视图触发Http404异常时将会被显示
404.html仅在发布版中(即settings.py中得DEBUG=False时)才起作用,当响应处理函数触发Http404异常时就会跳转到404界面
邮箱告警
定义和配置
当正式服务器上代码运行有报错时,可将错误追溯信息发至指定的邮箱
settings.py中 - 在基础邮箱授权后 添加如下配置:
1 |
|
过滤敏感信息
报错邮件中会显示一些错误的追踪,这些错误追踪会出现如password等敏感信息,Django已经将配置文件中的敏感信息过滤修改为多个星号,但是用户自定义的视图函数需要手动过滤敏感信息
可过滤如下信息:
局部变量
1
2
3
4
5
6
7
8from 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
...说明:
- 若报错邮件中牵扯到user,pw,cc等局部变量的值,则会将其替换成****,而name变量还显示其真实值
- 多个装饰器时,需要将其放在最顶部
- 若不传参数,则过滤所以局部变量的值
POST提交数据
1
2
3
4
5
6
7from django.views.decorators.debug import sensitive_post_parameters
@sensitive_post_parameters('password','username')
def index(requet):
s=request.POST['username']+request.POST['abcd']
#'abcd'并不存在,此时引发error
#POST中username及password的值会被替换成 ******
Celery框架
概念:Celery是一个简单、灵活且可靠的,处理大量信息的分布式系统,专注于实时处理的异步任务队列,同时也支持任务调度。celery是一个基于Python开发的模块,可以帮助我们对任务进行分发和处理
组成:
- 消息中间件(message broker)
- 任务执行单元(worker)
- 任务执行结果存储(task result store)
消息中间件
Celery本身不提供消息服务,但是可以方便的和第三方提供的消息中间件集成。包括,RabbitMQ,Redis等等
任务执行单元
Worker是Celery提供的任务执行的单元,worker并发的运行在分布式的系统节点中
任务结果存储
Task result store用来存储Worker执行的任务的结果,Celery支持以不同方式存储任务的结果,包括AMQP,redis等
另外,Celery还支持不同的并发和序列化手段
- 并发:Prefork,Eventlet,gevent,threads/single threaded
- 序列化:pickle,json,yaml,msgpack,zilb,bzip2 compression,Cryptographic message signing等等
redis
redis服务端启动
1 |
|
redis服务端关闭
1 |
|
redis服务端重启
1 |
|
redis客户端启动
如果把redis-cli文件复制到了/usr/local/bin/目录下的话
1 |
|
如果没有
1 |
|
redis客户端关闭
- Ctrl+c
- 客户端内输入 quit
- 客户端内输入 exit
redis内部库的切换
- 进入redis客户端
- 输入命令 select 你要切换库的序号
- 输入 keys * 可以看到该库中所有的键
django部署Celery
Celery的环境部署
- 在django项目外创建一个给celery的单独目录
- 手动创建config.py文件用于存储celery配置,再建一个main.py文件用于跟django连接的主配置
- 在celery单独创建要使用task的目录然后在该目录中创建tasks.py文件用于写任务