文章目录

        • 18 Template概念
        • 19 模版语法
          • 19.1 变量
          • 19.2 点语法
          • 19.3 标签
        • 20 常见的请求状态码
        • 21 view视图函数
          • 21.1 概念及基础语法
          • 21.2 内置函数
          • 21.3 页面中的反向解析
            • 21.3.1 反向解析的基本使用
            • 21.3.2 反向解析的位置参数
            • 21.3.3 反向解析的关键字参数
            • 21.3.4 优点
          • 21.4 视图函数中的反向解析
            • 21.4.1 基本使用
            • 21.4.2 位置参数
            • 21.4.3 关键字参数
        • 22 request对象
          • 22.1 概念
          • 22.2 path
          • 22.3 GET
          • 22.4 POST
          • 22.5 面试题
          • 22.6 method
          • 22.7 encoding
          • 22.8 META
          • 22.9 COOKIES
          • 22.10 session
          • 22.11 FILES
        • 23 HttpResponse对象
          • 23.1 概念
          • 23.2 响应分类
          • 23.3 基类HttpResponse
          • 23.4 render转发
          • 23.5 HttpResponseRedirect重定向
          • 23.6 永久重定向VS临时重定向
          • 23.7 JsonResponse
          • 23.8 HttpResponse子类
        • 24 会话技术
          • 24.1 cookie
          • 24.2 session
          • 24.3 token(自定义session/加密的唯一字符串)
            • 24.3.1 基本概念
            • 24.3.2 验证方法
            • 24.3.3 常用Token生成方法
            • 24.3.4 token的应用
          • 24.4 cookie与session的区别
          • 24.5 session与token的区别
        • 25 csrf豁免
          • 25.1 csrf概念
          • 25.2 csrf攻击原理以及过程
          • 25.3 csrf攻击实例
          • 25.4 使用token进行防御
          • 25.5 缺点
          • 25.6 解决csrf的问题/csrf豁免
        • 26 模型迁移
        • 27 模型反向迁移
        • 28 模型关系
          • 28.1 一对一
            • 28.1.1 添加
            • 28.1.2 删除
            • 28.1.3 查询
          • 28.2 一对多
            • 28.2.1 添加
            • 28.2.2 删除
            • 28.2.3 查询
          • 28.3 多对多
            • 28.3.1 添加
            • 28.3.2 删除
            • 28.3.3 查询
        • 29 模型继承
        • 30 静态资源
        • 31 文件上传
        • 32 缓存
          • 32.1 Django内置缓存实现(三种方案)
        • 33 中间件
        • 34 分页器
        • 35 富文本

18 Template概念

概念:模板在Django框架中,模板是可以帮助开发者快速生成,呈现给用户页面的工具。模板的设计方式实现了我们MVT中VT的解耦,VT有着N:M的关系,一个V可以调用任意T,一个T可以供任意V使用。模板处理分为两个过程加载渲染模板中的动态代码段除了做基本的静态填充,可以实现一些基本的运算,转换和逻辑。早期的web服务器,只能处理静态资源请求;模板能处理动态资源请求,依据计算能生成相应的页面。注意:Django的模板语言是Django模板,flask的模板语言是jinja2模板
模板组成:模板主要有2个部分(1) HTML静态代码(2) 动态插入的代码段(挖坑,填坑)

19 模版语法

19.1 变量
变量:视图传递给模板的数据获取视图函数传递的数据使用{{ var }}接收遵守标识符规则:拒绝关键字、保留字、数字……如果变量不存在,则插入空字符串来源:视图中传递过来的标签中,逻辑创建出来的
19.2 点语法
views.pydef testPoint(request):a = Animal.objects.first()hobby = {'eat': '肉','play': '篮球'}animal_list = Animal.objects.all()context = {'a': a,'hobby': hobby,'animal_list': animal_list}return render(request, 'testPoint.html', context=context)testPoint.html<body>{{ a.name }}  {# 属性 #}<br>{{ a.getName }}  {# 方法 #}<br>{{ hobby.eat }}  {# 字典中key对应的值 #}<br>{{ animal_list.0.name }}  {# 索引 #}<br>{{ animal_list.2.name }}</body>
弊端:模板中的小弊端,调用对象的方法,不能传递参数。为什么不能传递参数,因为连括号都没有。
19.3 标签
特征:标签分为单标签和双标签双标签必须闭合
功能标签·for循环<ul>{% for a in a_list %}<li>{{ a.name }}</li>{% endfor %}</ul><hr><ul>{% for a in a_list123 %}<li>{{ a.name }}</li>{% empty %}判断之前的代码有没有数据 如果没有,显示empty下面的代码{% endfor %}</ul>·forloop{% for a in a_list %}{{ forloop.counter }}  {# 表示当前是第几次循环,从1开始 #}
{#            {{ forloop.counter0 }}  {# 表示当前是第几次循环,从0开始 #}
{#            {{ forloop.revcounter }}  {# 表示当前是第几次循环,倒着数数,到1停 #}
{#            {{ forloop.revcounter0 }}  {# 表示当前第几次循环,倒着数,到0停 #}
{#            {{ forloop.first }}  {# 是否是第一个  布尔值 #}
{#            {{ forloop.last }}  {# 是否是最后一个 布尔值 #}{% endfor %}·if<ul>{% for a in a_list %}{% if forloop.first %}<li style="color: green">{{ a.name }}</li>{% elif forloop.last %}<li style="color: red">{{ a.name }}</li>{% else %}<li>{{ a.name }}</li>{% endif %}{% endfor %}
</ul>
注释
<!-- 这是一个注释 -->		这种注释不推荐,用户检查的时候可以看见单行注释{# 单行注释 #}
多行注释{% comment %}这是多行注释{% endcomment %}
乘
widthratio{% widthratio 数 分母 分子 %}{{ num }}  {# 5 #}
{% widthratio num 1 5 %}  {# 25 #}
{% widthratio num 5 1 %}  {# 1 #}
整除
奇偶行变色<ul>{% for a in a_list %}{% if forloop.counter|divisibleby:2 %}<li style="color: red">{{ a.name }}</li>{% else %}<li style="color: blue">{{ a.name }}</li>{% endif %}{% endfor %}</ul>
ifequal
不会忽略类型,code=10 和 code='10' 不一样。{# 判断code的值是否为10 #}{% ifequal code 10 %}yes{% else %}no{% endifequal %}
过滤器views.pydef testFilter(request):# js脚本,修改页面中的内容code1 = '''<script type="text/javascript">var li_list = document.getElementsByTagName('li');for(var i = 0; i < li_list.length; i++){li_list[i].innerHTML = '好好学习';}</script>'''a_list = Animal.objects.all()context = {'num': 10,'code': 'FGhdsA','a_list': a_list,'code1': code1}return render(request, 'testFilter.html', context=context)testFilter.html<body>
过滤器<br>
{{ num }}<br>
{{ num|add:10 }}<br>  {# 加 #}
{{ num|add:-10 }}<br>  {# 减 #}
{{ code|lower }}<br>  {# 全部变小写 #}
{{ code|upper }}<br>  {# 全部变大写 #}<ul>{% for a in a_list %}<li>{{ a.name }}</li>{% endfor %}
</ul>
{#{{ code1|safe }}#}
{% autoescape off %}  {# 和safe的作用一样,off表示生效,on表示失效 #}{{ code1 }}
{% endautoescape %}
</body>
结构标签
block块/坑,用来规划页面布局,填充页面,默认会覆盖{% block content %}tom{% endblock %}不被覆盖{% block header %}{{ block.super }}jack{% endblock %}extends继承,面向对象的体现,提高模板的复用率{% extends 'base_a.html' %}include包含,将其它模板作为一部分,包裹到我们的页面中{% block footer %}{{ block.super }}{% include 'base_c.html' %}{% endblock %}
加载静态资源
1. 手动创建文件夹static,在static下创建css等文件夹。2. 在settings.py的最后面添加
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')
]3. 在html中引入css
方式一
硬编码,不推荐。
<link rel="stylesheet" href="/static/css/testCss.css">方式二
在html的第一行添加
{% load static %}然后<link rel="stylesheet" href="{% static 'css/testCss.css' %}">
坑点:仅在DEBUG模式下可以使用,如果settings中的DEBUG=False,那么是不可以使用方式二。

20 常见的请求状态码

请求状态码2xx成功3xx302 重定向  301 永久重定向4xx客户端错误5xx服务端错误200 成功   
301 永久重定向 302 重定向  
403 防止跨站攻击 404 路径错误 405 请求方式 
500 业务逻辑错误

21 view视图函数

21.1 概念及基础语法
概念:视图函数是MTV中的View,相当于MVC中的Controller作用,控制器,接收用户请求,协调模板模型,对数据进行处理,负责模型和模板的数据交互。视图函数返回值类型:(1)以Json数据形式返回前后端分离JsonResponse(2)以网页的返回    HttpResponse   render   redirect
路由匹配规则
url匹配正则注意事项:  正则匹配时从上到下进行遍历,匹配到就不会继续向后查找了匹配的正则前方不需要加反斜线正则前需要加(r)表示字符串不转义
url匹配规则:按照书写顺序,从上到下匹配,没有最优匹配的概念,匹配到就停止了。在最后面加 / 表示精确匹配。			eg:    # 127.0.0.1:8000/app/hehe/ ===>hehe# 127.0.0.1:8000/app/hehehe/ ===>heheurl(r'^hehe',views.hehe),url(r'^hehehe', views.hehehe),
路由参数如果路由路径是精确匹配,那么匹配到了就不管后面的是否准确。路由参数使用正则接收,必须在视图函数中接收,接收的数据类型都是字符串类型。学习路由参数的目的是为了在请求路径和重定向中使用。
(1)基本使用
url(r'^testRoute/(\d+)/', views.testRoute),def testRoute(request, id):print(id)  # 8print(type(id))  # strreturn HttpResponse('testRoute')http://114.215.129.166:8000/day05/testRoute/8/2)位置参数使用圆括号包含规则,一个圆括号代表一个参数,代表视图函数上的一个参数,参数个数和视图函数上的参数一一对应(除默认request)
url(r'testLocation/(\d{4})/(\d+)/(\d+)/', views.testLocation),def testLocation(request, year, month, day):print(year + '-' + month + '-' + day)  # 2020-4-27return HttpResponse('testLocation')http://114.215.129.166:8000/day05/testLocation/2020/4/27/3)关键字参数可以在圆括号指定参数名字(?P<name>reg),视图函数中存在和圆括号中name对应的参数,参数不区分顺序,个数也需要保持一致,一一对应。
url(r'^testKey/(?P<year>\d{4})/(?P<month>\d+)/(?P<day>\d+)/', views.testKey),def testKey(request, month, day, year):print(year + '-' + month + '-' + day)  # 2020-4-27return HttpResponse('testKey')http://114.215.129.166:8000/day05/testKey/2020/4/27/
21.2 内置函数
内置函数locals()将局部变量,使用字典的形式打包,key是变量的名字,value是变量的值。def testLocal(request):name = 'lucy'age = 18return render(request, 'testLocal.html', context=locals())testLocal.html<body>{{ name }}{{ age }}</body>
21.3 页面中的反向解析
21.3.1 反向解析的基本使用
1. 在根路由中设置namespace属性,一般和app名一致url(r'day05/', include('Day05App.urls', namespace='day05')),2. 在子路由中设置name属性,获取谁的路由,name属性就写在那个url中,一般和app名一致url(r'^index/', views.index, name='index'),3. 在视图函数中使用 reverse('namespace:name')在模版中使用 {% url 'namespace:name' %}
作用:获取请求路径
在视图函数中使用
def testReverse(request):a = reverse('day05:index')print(a)  # /day05/index/return HttpResponse('测试反向解析')在模版中使用
{# 点击a标签,然后跳转到index请求 #}
<a href="{% url 'day05:index' %}">测试页面中的反向解析</a>
21.3.2 反向解析的位置参数
{% url 'namespace:name' value1 value2 ... %}
views.pydef testUrl(request):return render(request, 'testUrl.html')def testReverseLocation(request, year, month, day):print(year+'-'+month+'-'+day)return HttpResponse('测试位置参数')
urls.pyurl(r'^testUrl/', views.testUrl),url(r'^testReverseLocation/(\d{4})/(\d+)/(\d+)/', views.testReverseLocation, name='testReverseLocation'),
testUrl.html<a href="{% url 'day05:testReverseLocation' 2020 4 28 %}">反向解析之位置参数</a>
访问http://114.215.129.166:8000/day05/testUrl/
21.3.3 反向解析的关键字参数
{% url 'namespace:name' key1=value1 key2=vaue2... %}
views.pydef testReverseKey(request, day, month, year):print(year + '-' + month + '-' + day)return HttpResponse('测试位置参数')
urls.pyurl(r'^testReverseKey/(?P<year>\d{4})/(?P<month>\d+)/(?P<day>\d+)/', views.testReverseKey, name='testReverseKey'),
testUrl.html<a href="{% url 'day05:testReverseKey' year=2020 month=4 day=28 %}">反向解析之关键字参数</a>
访问http://114.215.129.166:8000/day05/testUrl/
21.3.4 优点
	如果在视图,模板中使用硬编码连接,在url配置发生改变时,需要变更的代码会非常多,这样导致我们的代码结构不是很容易维护,使用反向解析可以提高我们代码的扩展性和可维护性。
21.4 视图函数中的反向解析
一般视图函数中的反向解析都会结合重定向一起使用
21.4.1 基本使用
reverse('namespace:name')
21.4.2 位置参数
reverse('namespace:name', args=(value1, value2 ...))
reverse('namespace:name', args=[value1, value2 ...])
urls.pyurl(r'^testLocation/', views.testLocation),
url(r'^testReverseLocation/(\d{4})/(\d+)/(\d+)/', views.testReverseLocation, name='testReverseLocation'),
views.pydef testLocation(request):return redirect(reverse('res:testReverseLocation', args=[2020, 5, 1]))def testReverseLocation(request, year, month, day):return HttpResponse(year + '-' + month + '-' + day)
执行http://114.215.129.166:8000/res/testLocation/
21.4.3 关键字参数
reverse('namespace:name', kwargs={key1:value2, key2:value2 ...})
urls.pyurl(r'^testKey/', views.testKey),
url(r'^testReverseKey/(?P<year>\d{4})/(?P<month>\d+)/(?P<day>\d+)/', views.testReverseKey, name='testReverseKey'),
views.pydef testKey(request):return redirect(reverse('res:testReverseKey', kwargs={'year': 2020, 'month': 5, 'day': 1}))def testReverseKey(request, year, month, day):return HttpResponse(year + '-' + month + '-' + day)
执行http://114.215.129.166:8000/res/testKey/

22 request对象

22.1 概念
django框架根据Http请求报文自动生成的一个对象,包含了请求的各种信息。
22.2 path
请求的完整路径
def testReq(request):print(request.path)  # /req/testReq/return HttpResponse('testReq')
22.3 GET
获取get请求方式的参数
def testReq(request):print(request.GET)  # <QueryDict: {'name': ['zs', 'li'], 'age': ['18']}>name = request.GET.get('name')age = request.GET.get('age')print(name, age)  # li 18name_list = request.GET.getlist('name')print(name_list)  # ['zs', 'li']return HttpResponse('testReq')
执行http://114.215.129.166:8000/req/testReq/?name=zs&age=18&name=li
1. QueryDict类字典结构key-value
2. 一个key可以对应多个值
3. get 获取最后一个
4. getlist 获取多个
5. 类字典的参数,包含了get请求方式的所有参数
22.4 POST
获取post请求方式的参数
def testReq(request):print(request.POST)  # <QueryDict: {'name': ['zs', 'li'], 'age': ['18']}>name = request.POST.get('name')age = request.POST.get('age')print(name, age)  # li 18name_list = request.POST.getlist('name')print(name_list)  # ['zs', 'li']return HttpResponse('testReq')
22.5 面试题
QueryDict和Dict的区别?
dict是字典对象,没有urlencode()方法;
QueryDict对象,有urlencode()方法,作用是将QueryDict对象转换为url字符串;
一般QueryDict通过params = copy.deepcopy(request.GET)得到的,这时params是QueryDict对象,也能params[key]=value进行添加数据
22.6 method
request.method
哪些请求方式需要验证csrf中间件(会报错403)
答:get不需要,post、put、patch、delete都需要
请求的方法:GET和POST
GET:1.11版本最大参数的数据量2K,参数会显示在浏览器的地址栏上,不安全。
POST:参数存在请求体中,文件上传等。相对安全。
应用场景:前后端分离的底层,判断请求方式,执行对应的业务逻辑
22.7 encoding
	当你请求的时候,服务器响应之后有乱码问题,那么是因为你请求的时候没有指定编码格式,一般这种情况在python中很少见,除非操作系统区别太大。解决方案:请求的时候可以设置请求的编码格式。
def testReq(request):# 设置请求头的编码格式request.encoding = 'utf-8'return HttpResponse('testReq')
22.8 META
应用:反爬虫
def testReq(request):# 获取主机地址print(request.META.get('REMOTE_ADDR'))  # 117.136.87.111# 获取客户端的所有信息for key in request.META:print(key, request.META.get(key))return HttpResponse('testReq')
22.9 COOKIES
后续
22.10 session
后续
22.11 FILES
后续

23 HttpResponse对象

23.1 概念
当浏览器访问服务器的时候,服务器响应的数据类型
23.2 响应分类
django视图函数的返回值分类
(1) HTML响应HttpResponserenderredirect
(2) jsonJsonResponse(前后端分离)
23.3 基类HttpResponse
不使用模板,直接HttpResponse()
def testResponse(request):response = HttpResponse()response.content = '五一快乐'response.status_code = 404response.write('五一快乐')response.flush()  # 冲刷缓存区return response
write和flush经常结合在一起使用。
content和write没有区别。
23.4 render转发
def testRender(request):response = render(request, 'testRender.html')# render方法的返回值类型是HttpResponseprint(type(response))return response
23.5 HttpResponseRedirect重定向
def testRedirect(request):# 重定向就是执行其他的视图函数# HttpResponseRedirect方法的返回值类型是HttpResponseRedirect# HttpResponseRedirect继承了HttpResponseRedirectBase# HttpResponseRedirectBase继承了HttpResponse# 状态码302response = HttpResponseRedirect(reverse('res:index'))print(type(response))return response
def testSimpleRedirect(request):# redirect方法是通过一个参数来确定是永久重定向还是普通的重定向# 如果是永久重定向,那么类就是HttpResponsePermanentRedirect,状态码301# 如果是普通重定向,那么类就是HttpResponseRedirect,状态码302# HttpResponsePermanentRedirect和HttpResponseRedirect# 都继承了HttpResponseRedirectBase# HttpResponseRedirectBase继承了HttpResponseresponse = redirect(reverse('res:index'))print(type(response))return response
工作中,使用简写方式,redirect方法。
23.6 永久重定向VS临时重定向
重定向就是将网页自动转向重定向,即:1. 301永久性重定向:新网址完全继承旧网址,旧网址的排名等完全清零。301重定向是网页更改地址后对搜索引擎友好的最好方法,只要不是暂时搬移的情况,都建议使用301来做转址。
2. 302临时性重定向:对旧网址没有影响,但新网址不会有排名。
例如:我们之前网站的域名是a.com,现在替换成了b.com。但是用户并不知道域名改了,所以还是在浏览器里输入a.com,Web服务器(apache 或者 ngnix)在收到请求后,在响应中包含:状态码 301 及 b.com。用户的浏览器在收到响应后,自动将输入栏网址改变为 b.com。或者状态码 302 及 b.com。用户的浏览器在收到响应后,输入栏仍是显示旧网址,但是显示的是 b.com的内容。
企业中用的是临时重定向
不健康网站是永久重定向
23.7 JsonResponse
# django的视图函数,不可以直接返回data的# JsonResponse的缺点:1.中文乱码 2.序列化
def testJson(request):data = {'name': 'lucy','age': 28,# 'sex': '男'  "sex": "\u7537"}return JsonResponse(data=data)
解决中文乱码问题的方案:忽略编码
def testJson(request):data = {'name': 'lucy','age': 28,'sex': '男'}# 忽略编码return JsonResponse(data=data, json_dumps_params={'ensure_ascii': False})
序列化问题
def testJson(request):# django默认的视图函数是不支持序列化的# 需要使用django-rest-frameworka = Animal.objects.first()data = {'a': a}return JsonResponse(data=data)解决方案:在前后端分离,需要使用django-rest-framework,后续
JsonResponse也是HttpResponse的子类,它主要和父类的区别在于:1.它的默认Content-Type被设置为:application/json2.第一个参数,data应该是一个字典类型,当 safe 这个参数被设置为:False ,那data可以填入任何能被转换为JSON格式的对象,比如list, tuple, set。 默认的 safe 参数是 True,如果你传入的data数据类型不是字典类型,那么它就会抛出 TypeError 的异常。
23.8 HttpResponse子类
HttpResponse子类HttpResponseRedirect-临时重定向-302HttpResponsePermanentRedirect-永久重定向 -301HttpResponseBadRequest	-400HttpResponseNotFound-路径错误- 404HttpResponseForbidden- 403   csrf 防跨站攻击 HttpResponseNotAllowed-请求方式不允许- 405   HttpResponseServerError-代码逻辑不正确- 500Http404- Exception- raise 主动抛异常出来

24 会话技术

为什么会有会话技术?服务器如何识别客户端Http在Web开发中基本都是短连接请求生命周期从Request开始,到Response就结束
会话技术:cookie session  token(自定义的session)
当服务器收到浏览器发送过来的请求时,会创建session对象,该对象有一个属性叫做session_key,其值是唯一的。这个值会回传给浏览器,保存在浏览器中,在浏览器中它叫做session_id。
24.1 cookie
	客户端会话技术,数据都存储在客户端,以key-value进行存储,支持过期时间max_age,默认请求会携带本网站的所有cookie,cookie不能跨域名,不能跨浏览器,cookie默认不支持中文,base64cookie是服务器端创建,保存在浏览器端设置cookie应该在服务器 response获取cookie应该在浏览器 request删除cookie应该在服务器 response
需求执行一个请求,跳转到登陆页面;在页面中输入一个名字,提交;进入到welcome的页面,页面显示:欢迎尊敬的VipXXX;在welcome页面中有一个退出的按钮,点击退出,显示:欢迎尊敬的Vip游客;注意如果没有登陆,直接进入到了welcome会显示:欢迎尊敬的Vip游客。
实现urls.pyurl(r'^toCookieLogin/', views.toCookieLogin),url(r'^cookieLogin/', views.cookieLogin, name='cookieLogin'),url(r'^cookieWelcome/', views.cookieWelcome, name='cookieWelcome'),url(r'^cookieLogout/', views.cookieLogout, name='cookieLogout'),views.pydef toCookieLogin(request):return render(request, 'cookieLogin.html')def cookieLogin(request):name = request.POST.get('name')response = redirect(reverse('cook:cookieWelcome'))response.set_cookie('name', name)return responsedef cookieWelcome(request):name = request.COOKIES.get('name', '游客')print(name)return render(request, 'cookieWelcome.html', context=locals())def cookieLogout(request):response = redirect(reverse('cook:cookieWelcome'))response.delete_cookie('name')return responsecookieLogin.html<body><h1>登录页面</h1><form action="{% url 'cook:cookieLogin' %}" method="post"><input type="text" name="name"><button>登录</button></form></body>cookieWelcome.html<body>欢迎尊敬的VIP{{ name }}<br><a href="{% url 'cook:cookieLogout' %}">退出</a></body>
cookie加盐加盐的意思就是加密加密  response.set_signed_cookie('name', name, salt="xxxx")解密  获取的是加盐之后的数据uname = request.COOKIES.get('content)获取的是解密之后数据uname = request.get_signed_cookie("content", salt="xxxx")cookie加盐实现urls.pyurl(r'^setCookieSalt/', views.setCookieSalt),url(r'^getCookieSalt/', views.getCookieSalt, name='getCookieSalt'),views.pydef setCookieSalt(request):response = redirect(reverse('cook:getCookieSalt'))response.set_signed_cookie('name', 'zs', salt='xxx')return responsedef getCookieSalt(request):name1 = request.COOKIES.get('name')# zs:1jV7H2:rw3NeaTAdILHLp6-zMFhebh0lfcprint(name1)name = request.get_signed_cookie('name', salt='xxx')# zsprint(name)return HttpResponse('getCookieSalt')
cookie过期时间
通过Response将cookie写到浏览器上,下一次访问,浏览器会根据不同的规则携带cookie过来max_age:整数,指定cookie过期时间  单位秒expries:整数,指定过期时间,支持是一个datetime或timedelta,可以指定一个具体日期时间max_age和expries两个选一个指定过期时间的几个关键时间max_age 设置为 0 浏览器关闭失效设置为None	永不过期expires=timedelta(days=10) 10天后过期eg:response.set_cookie('name', name, max_age=30)
24.2 session
	服务端会话技术,数据都存储在服务端,默认存在内存 RAM,在django被持久化到了数据库中,该表叫做django_session,这个表中有三个字段,分别seesion_key,session_data,expris_date.session支持中文Django中session的默认过期时间是14天,支持过期,主键是字符串,默认做了数据安全,使用了base64加密,加密其实就是添加摘要。- 使用的base64之后,那么这个字符串会在最后面添加一个==,也就是session_data值的			后有==- 在前部添加了一个混淆串依赖于cookies
session使用:设置sessionrequest.session["username"] = username获取sessionusername = request.session.get("username")使用session退出del request.session['username']cookie是脏数据response.delete_cookie('sessionid')session是脏数据request.session.flush()冲刷session常用操作get(key,default=None) 根据键获取会话的值clear() 清楚所有会话,一般不用flush() 删除当前的会话数据并删除会话的cookiedelete request['session_id'] 删除会话session.session_key	获取session的keyrequest.session[‘user’] = username数据存储到数据库中会进行编码使用的是base64
session_id和session_key是一个东西,值一样,前者保存在浏览器,后者保存在服务器。
session是怎么生成的。当服务器接收到请求之后,就会创建session对象。
实现urls.pyurl(r'^toSessionLogin/', views.toSessionLogin),url(r'^sessionLogin/', views.sessionLogin, name='sessionLogin'),url(r'^sessionWelcome/', views.sessionWelcome, name='sessionWelcome'),url(r'^sessionLogout/', views.sessionLogout, name='sessionLogout'),views.pydef toSessionLogin(request):return render(request, 'toSessionLogin.html')def sessionLogin(request):name = request.POST.get('name')request.session['name'] = namereturn redirect(reverse('sess:sessionWelcome'))def sessionWelcome(request):name = request.session.get('name', '游客')return render(request, 'sessionWelcome.html', context=locals())def sessionLogout(request):# 删除的是session(django-session表中的数据)# 但是cookie仍然存在# 通过cookie找不到session的数据了# 所以cookie此时已经是脏数据了# del request.session['name']# return redirect(reverse('sess:sessionWelcome'))# 删除的是cookie# 那么session是脏数据# response = redirect(reverse('sess:sessionWelcome'))# response.delete_cookie('sessionid')# return response# 彻底删除request.session.flush()return redirect(reverse('sess:sessionWelcome'))toSessionLogin.html<body><h1>session登录</h1><form action="{% url 'sess:sessionLogin' %}" method="post"><input type="text" name="name"><input type="submit" value="登录"></form></body>sessionWelcome.html<body>欢迎尊敬的VIP{{ name }}<br><a href="{% url 'sess:sessionLogout' %}">退出</a></body>
24.3 token(自定义session/加密的唯一字符串)
24.3.1 基本概念
Token 的中文意思是“令牌”。主要用来身份验证。 Facebook,Twitter,Google+,Github 等大型网站都在使用。比起传统的身份验证方法(session),Token 有扩展性强,安全性高的特点,非常适合用在Web应用或者移动应用上,如果使用在移动端或客户端开发中,通常以Json形式传输,服务端会话技术,自定义的session,给他一个不能重复的字符串,数据存储在服务器中。
24.3.2 验证方法
使用基于Token的身份验证方法,在服务端不需要存储用户登录记录(用户名和密码)。大概流程是这样:
1.客户端使用用户名跟密码请求登录
2.服务端收到请求,去验证用户名与密码
3.验证成功后,服务端会签发一个 Token,再把这个Token发送给客户端
4.客户端收到Token以后可以把它存储起来,比如放在Cookie里或者Local Storage里
5.客户端每次向服务端请求资源的时候需要带着服务端签发的Token
6.服务端收到请求后,去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据
24.3.3 常用Token生成方法
(1) binascii.b2a_base64(os.urandom(24))[:-1]
使用举例:
import binascii
import os# os.urandom(n)随机产生n个字节的字符串,可以作为随机加密key使用
a = binascii.b2a_base64(os.urandom(24))[:-1]
print(a)  # b'dZz+O3Sl3+d29HvwpQRJZMZjJx421Xyl'总结:
这种算法的优点是性能快,缺点是有特殊字符,需要加replace来做处理。(2) sha1(os.urandom(24)).hexdigest()
使用举例:
import hashlib
import osa = hashlib.sha1(os.urandom(24)).hexdigest()
print(a)  # 184310a0856d653e65062fa9d1473786ff3d0322总结:
这种算法的优点是安全,不需要做特殊处理。缺点是覆盖范围差一些。(3) uuid4().hex
使用举例:
import uuida = uuid.uuid4().hex
print(a)  # 179965ce4a244dc29d9152c6c22dec4f总结:
Uuid使用起来比较方便,缺点为安全性略差一些。(4) base64.b32encode(os.urandom(20))/base64.b64encode(os.urandom(24))
使用举例:
import base64
import osa = base64.b32encode(os.urandom(20))
print(a)  # b'O2LABACHSV4KX6GE7FOOBPH4CLC2RZT5'b = base64.b64encode(os.urandom(24))
print(b)  # b'rtA3Lqcon3XMq1eXhHRxR3O74zKuLcat'总结:
可以用base64的地方,选择binascii.b2a_base64是不错的选择。
根据W3的sessionID的字串中对identifier的定义,sessionID中使用的是base64,但在Cookie的值内使用需要注意“=”这个特殊字符的存在。
如果要安全字符(字母数字),sha1也是一个不错的选择,性能也不错。
24.3.4 token的应用
import hashlib
import time
import base64
import hmac# 待加密内容
strdata = "xiaojingjiaaseafe16516506ng"h1 = hashlib.md5()
h1.update(strdata.encode(encoding='utf-8'))strdata_tomd5 = h1.hexdigest()# 原始内容: xiaojingjiaaseafe16516506ng ,加密后: d792ab10ffe7c89442838ee6a942e2c4
print("原始内容:", strdata, ",加密后:", strdata_tomd5)# 生产token
def generate_token(key, expire=3600):""":param key: str (用户给定的key,需要用户保存以便之后验证token,每次产生token时的key 都可以是同一个key):param expire: int(最大有效时间,单位为s):return: str"""ts_str = str(time.time() + expire)ts_byte = ts_str.encode("utf-8")sha1_tshexstr = hmac.new(key.encode("utf-8"), ts_byte, 'sha1').hexdigest()token = ts_str + ':' + sha1_tshexstrb64_token = base64.urlsafe_b64encode(token.encode("utf-8"))return b64_token.decode("utf-8")# 验证token
def certify_token(key, token):""":param key: str:param token: str:return:boolean"""token_str = base64.urlsafe_b64decode(token).decode('utf-8')token_list = token_str.split(':')if len(token_list) != 2:return Falsets_str = token_list[0]if float(ts_str) < time.time():# token expiredreturn Falseknown_sha1_tsstr = token_list[1]sha1 = hmac.new(key.encode("utf-8"), ts_str.encode('utf-8'), 'sha1')calc_sha1_tsstr = sha1.hexdigest()if calc_sha1_tsstr != known_sha1_tsstr:# token certification failedreturn False# token certification successreturn Truekey = "xiaojingjing"
print("key:", key)
user_token = generate_token(key=key)print("加密后:", user_token)
user_de = certify_token(key=key, token=user_token)
print("验证结果:", user_de)key = "xiaoqingqing"
user_de = certify_token(key=key, token=user_token)
print("验证结果:", user_de)
token加密算法,随便说,不固定。
获取了用户名,获取了他的ip,获取了他的当前系统时间,获取完后,使用了base64加密,加密之后,又加了'@'字符,加完@以后,又使用sha1加密,在使用md5又一次加密。
24.4 cookie与session的区别
1. cookie数据存放在客户端上,session数据放在服务器上。
2. cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗。 考虑到安全应当使用session。
3. session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能。 考虑到减轻服务器性能方面,应当使用COOKIE。建议:将登陆信息等重要信息存放为SESSION其他信息如果需要保留,可以放在COOKIE中
24.5 session与token的区别
1. 作为身份认证,token安全性比session好,因为每个请求都有签名还能防止监听以及重放攻击。
2. Session是一种HTTP存储机制,目的是为无状态的HTTP提供的持久机制。Session认证只是简单的把User信息存储到Session里,因为SessionID的不可预测性,暂且认为是安全的。这是一种认证手段。但是如果有了某个User的SessionID,就相当于拥有该User的全部权利。SessionID不应该共享给其他网站或第三方。
3. Token,如果指的是OAuth Token或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对App。其目的是让某App有权利访问 某用户 的信息。这里的Token是唯一的。不可以转移到其它App上,也不可以转到其它 用户 上。session使用的是base64加密,token可以自定义加密算法。

25 csrf豁免

25.1 csrf概念
CSRF跨站点请求伪造(Cross—Site Request Forgery)。攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。
25.2 csrf攻击原理以及过程
1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
25.3 csrf攻击实例
受害者 Bob 在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的 session,并且该 session 的用户 Bob 已经成功登陆。黑客 Lucy 在该银行也有账户,他知道上文中的 URL 能把钱进行转帐操作。Lucy 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Lucy 而非 Bob,他不能通过安全认证,因此该请求不会起作用。这时,Lucy 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码:src="http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory",并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Lucy 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Lucy 则可以拿到钱后逍遥法外。
25.4 使用token进行防御
目前防御 CSRF 攻击主要有三种策略:验证 HTTP Referer 字段;在请求地址中添加 token 并验证;在 HTTP 头中自定义属性并验证。CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于 session 之中,然后在每次请求时把 token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来说,要在 form 的最后加上 <input type="hidden" name="csrftoken" value="tokenvalue"/>,这样就把 token 以参数的形式加入请求了。但是,在一个网站中,可以接受请求的地方非常多,要对于每一个请求都加上 token 是很麻烦的,并且很容易漏掉,通常使用的方法就是在每次页面加载时,使用 javascript 遍历整个 dom 树,对于 dom 中所有的 a 和 form 标签后加入 token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的 html 代码,这种方法就没有作用,还需要程序员在编码时手动添加 token。
25.5 缺点
该方法还有一个缺点是难以保证 token 本身的安全。特别是在一些论坛之类支持用户自己发表内容的网站,黑客可以在上面发布自己个人网站的地址。由于系统也会在这个地址后面加上 token,黑客可以在自己的网站上得到这个 token,并马上就可以发动 CSRF 攻击。为了避免这一点,系统可以在添加 token 的时候增加一个判断,如果这个链接是链到自己本站的,就在后面添加 token,如果是通向外网则不加。不过,即使这个 csrftoken 不以参数的形式附加在请求之中,黑客的网站也同样可以通过 Referer 来得到这个 token 值以发动 CSRF 攻击。这也是一些用户喜欢手动关闭浏览器 Referer 功能的原因。
25.6 解决csrf的问题/csrf豁免
1. 注释中间件2. 在表单中添加 {%csrf_token%}<form action="/sess/testCsrf/" method="post">{% csrf_token %}<button>提交</button></form>3. 在视图函数上添加 @csrf_exempt@csrf_exemptdef testCsrf(request):if request.method == 'GET':return render(request,'testCsrf.html')elif request.method == 'POST':return HttpResponse('testCsrf')

26 模型迁移

model---->db
1. 迁移步骤生成迁移文件  python manage.py makemigrations执行迁移文件  python manage.py migrate2. 迁移文件的生成根据models文件生成对应的迁移文件根据models和已有迁移文件差别 生成新的迁移文件注意:django所有被迁移的字段,都是not null的约束,不允许为null,添加新的字段时,必须指定默认值3. 可以指定迁移的app  生成迁移文件	python manage.py makemigrations app	(注意app的名字后面不允许加/)执行迁移文件	python manage.py migrate    
4. 重新迁移删除迁移文件migrations所涉及的迁移文件删除迁移文件产生的表删除迁移记录django-migrations表中涉及的记录

27 模型反向迁移

db---->model
反向生成到指定的app下python manager.py inspectdb > App/models.py元信息中会包含一个属性  managed=False   不支持迁移注意:默认13张表的模型也会生成,删除即可。
另外,如果自己的模型不想被迁移,也可以使用 managed=False 进行声明。

28 模型关系

28.1 一对一
应用场景用于复杂表的拆分扩展新功能OneToOneField确认主从关系,谁声明关系谁就是从表底层实现,使用外键实现的,对外键添加了唯一约束
class Student(models.Model):name = models.CharField(max_length=32)class Meta:db_table = 'student'class IdCard(models.Model):cardnum = models.IntegerField()i_student = models.OneToOneField(Student, null=True, blank=True)class Meta:db_table = 'idcard'
28.1.1 添加
添加主表数据
def addStudent(request):student = Student()student.name = '老毕'student.save()return HttpResponse('添加成功')
添加从表数据
def addIdcard(request):idcard = IdCard()idcard.cardnum = '110'# 默认情况下  django所有的字段都是not null的  非空的# 如果想设置字段为null  那么可以添加索引# 即添加  null=True, blank=True# null一般和blank连用idcard.save()return HttpResponse('添加成功')
绑定
def bind(request):student = Student.objects.last()idcard = IdCard.objects.last()# 针对于模型idcard.i_student = student# 针对于表# idcard.i_student_id = student.id# 错误写法,因为左边是对象,右边是整型# idcard.i_student = student.ididcard.save()return HttpResponse('绑定成功')
28.1.2 删除
删除从表数据
def deleteIdcard(request):idcard = IdCard.objects.last()idcard.delete()return HttpResponse('删除从表成功')
删除主表数据(三种情况)
1. 删除主表数据,从表一起删除(默认情况,级联删除)默认情况下,OneToOneField中有一个属性叫做on_delete,如果没有,那么默认就是CASCADE  这个代表的是级联,删除主表数据,从表对应的数据将删除。i_student = models.OneToOneField(Student,on_delete=models.CASCADE)def deleteDefault(request):s = Student.objects.last()s.delete()return HttpResponse('删除主表数据,默认级联删除')2. 删除主表数据,从表的外键设置为null(解除依赖)在设置on_delete=models.SET_NULL的时候,必须在之前指定该字段必须为nulli_student = models.OneToOneField(Student,null=True,blank=True,on_delete=models.SET_NULL)添加null和blank要重新迁移,添加on_delete不需要重新迁移。def deleteNull(request):s = Student.objects.last()s.delete()return HttpResponse('删除主表数据,从表设置为null')3. 删除主表数据,从表的外键设置为默认值i_student = models.OneToOneField(Student, null=True, blank=True, on_delete=models.SET(4))def deleteSet(request):s = Student.objects.first()s.delete()return HttpResponse('删除主表数据,从表数据设置默认值')4. 如果主表下有对应的从表数据,那么就不允许删除,没有对应的数据,才可以删除,会报错i_student = models.OneToOneField(Student, null=True, blank=True, on_delete=models.PROTECT)def deleteProtect(request):s = Student.objects.last()s.delete()return HttpResponse('有关联数据,则不允许删除')开发中为了防止误操作,我们通常会设置为此模式。
django默认是级联删除。删除主表的时候,从表数据都会删除。
执行顺序:从表的数据删除之后,主表的数据跟着删除。def delete_student(request):student = Student.objects.get(pk=1)student.delete()return HttpResponse('删除成功')
28.1.3 查询
根据从表的对象,来查询主表的数据
eg:根据idcard对象来查询student的namedef getStudent(request):idcard = IdCard.objects.last()# i_student是idcard的显性属性   就是可以直接看到的属性print(idcard.i_student.name)return HttpResponse('根据从表对象来查询主表对象')
根据主表的数据,来查询从表的数据
eg:根据学生来查询cardnumdef getIdcard(request):student = Student.objects.last()# 当主表对象查询从表数据的时候可以使用隐性属性#    隐性属性:看不到 但是可以使用#    1v1:  student.idcard#    1vm:  student.idcard_setprint(student.idcard.cardnum)return HttpResponse('根据主表对象来查询从表数据')
28.2 一对多
class Dept(models.Model):name = models.CharField(max_length=32)class Meta:db_table = 'dept'class Emp(models.Model):name = models.CharField(max_length=32)e_dept = models.ForeignKey(Dept, null=True, blank=True)class Meta:db_table = 'emp'
28.2.1 添加
添加主表数据
def addDept(request):dept = Dept()dept.name = 'python开发部'dept.save()return HttpResponse('添加主表成功')
添加从表数据
def addEmp(request):emp = Emp()emp.name = '张三'# 一对多的外键也不能为null,设置null和blankemp.save()return HttpResponse('添加从表成功')
绑定
def bind(request):dept = Dept.objects.last()emp = Emp.objects.last()emp.e_dept = deptemp.save()return HttpResponse('绑定成功')
28.2.2 删除
删除从表数据
def deleteEmp(request):emp = Emp.objects.last()emp.delete()return HttpResponse('删除从表成功')
删除主表数据(三种情况)
1. 删除主表数据,从表一起删除(默认情况,级联删除)默认情况下,ForeignKey中有一个属性叫做on_delete,如果没有,那么默认就是CASCADE,这个代表的是级联,删除主表数据,从表对应的数据将删除。e_dept = models.ForeignKey(Dept,null=True,blank=True)def deleteDefault(request):dept = Dept.objects.last()dept.delete()return HttpResponse('默认删除主表')2. 删除主表数据,从表的外键设置为null(解除依赖)在设置on_delete=models.SET_NULL的时候,必须在之前指定该字段必须为nulle_dept = models.ForeignKey(Dept, null=True, blank=True, on_delete=models.SET_NULL)def deleteNull(request):dept = Dept.objects.last()dept.delete()return HttpResponse('删除主表,从表数据设置为null')3. 如果主表下有对应的从表数据,那么就不允许删除,没有对应的数据,才可以删除,会报错e_dept = models.ForeignKey(Dept, null=True, blank=True, on_delete=models.PROTECT)def deleteProtect(request):dept = Dept.objects.last()dept.delete()return HttpResponse('删除主表,有从表的对应数据,则报错')
28.2.3 查询
从获取主显性属性
主获取从隐性属性默认是 模型小写_set1. 该属性得返回值类型是relatedManager类型2. 注意relatedManager是一个不可以迭代得对象,所以需要调用Manager方法(all、first、last、filter、exclude、切片等)3. relatedManager也是Manager的一个子类
根据从表对象查询主表数据
def getDept(request):emp = Emp.objects.last()print(emp.e_dept.name)return HttpResponse('根据从表的数据查询主表的数据')
根据主表对象查询从表数据
def getEmps(request):dept = Dept.objects.last()# emps = dept.emp_set直接这样是不可以遍历的emps = dept.emp_set.all()for e in emps:print(e.id, e.name)return HttpResponse('根据主表对象查询从表的数据')
28.3 多对多
联合唯一
class Custom(models.Model):name = models.CharField(max_length=32)class Meta:db_table = 'custom'class Goods(models.Model):name = models.CharField(max_length=32)g_custom = models.ManyToManyField(Custom)class Meta:db_table = 'goods'
28.3.1 添加
添加主表数据
def addCustom(request):custom = Custom()custom.name = '张三'custom.save()return HttpResponse('添加主表数据')
添加从表数据
def addGoods(request):goods = Goods()goods.name = '小浣熊'goods.save()return HttpResponse('添加从表数据')
关系表添加数据
关系表没有模型(goods_g_custom)
def addRelation(request):custom = Custom.objects.last()goods = Goods.objects.last()# 从表的显性属性	来添加关系表的数据# goods.g_custom.add(custom)# 主表的隐性属性	来添加关系表的数据# custom.goods_set.add(goods)return HttpResponse('添加关系表数据')
28.3.2 删除
删除主表
默认是级联删除
def deleteCustom(request):custom = Custom.objects.last()custom.delete()return HttpResponse('删除custom是级联删除')
删除从表
默认也是级联删除
def deleteGoods(request):goods = Goods.objects.last()goods.delete()return HttpResponse('删除goods是级联删除')
删除关系表
def deleteRelation(request):custom = Custom.objects.last()goods = Goods.objects.last()# 根据从表的显性属性 删除# goods.g_custom.remove(custom)# 根据主表的隐性属性 删除# custom.goods_set.remove(goods)return HttpResponse('删除关系表数据')
28.3.3 查询
注意
多对多的查询和一对一、一对多不一样,必须调用first(),all()等。
根据从表数据查询主表数据
用从表的显性属性
def getCustom(request):goods = Goods.objects.last()# custom = goods.g_custom.first()# print(custom.id, custom.name)customs = goods.g_custom.all()for custom in customs:print(custom.id, custom.name)return HttpResponse('根据从表的数据查询主表的数据')
根据主表数据查询从表数据
用主表的隐性属性
def getGoods(request):custom = Custom.objects.last()goods_list = custom.goods_set.all()for goods in goods_list:print(goods.id, goods.name)return HttpResponse('根据主表的数据查询从表的数据')

29 模型继承

抽象模型:在父类的Model的元信息中添加 abstract=True抽象模型不会在数据库中产生表
子模型拥有父模型中的所有字段
class Animal(models.Model):name = models.CharField(max_length=32)class Meta:abstract = Trueclass Dog(Animal):type = models.CharField(max_length=16)class Meta:db_table = 'dog'class Cat(Animal):color = models.CharField(max_length=16)class Meta:db_table = 'cat'

30 静态资源

静态资源
1. 静态资源和模板的区别(1) 模板的路径不可以直接访问,必须通过请求来访问;static资源可以直接访问。http://114.215.129.166:8000/template/testTem.html	报错404http://114.215.129.166:8000/static/testStatic.html	可以执行(2) 模板语法不可以在静态资源中书写。2. 注意:(1) 使用的时候注意配置资源位置,在settings的最后配置。STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]使用  {% load static %}{% static '相对路径' %}(2) 全栈工程师要求会template开发工程师要求会前后端分离static

31 文件上传

要求:客户端必须使用POST指定enctype='multiplepart/form-data'
原生代码实现:适用django也适用于flask从request.FILES中获取到上传上来的文件,打开一个文件,从上传上来的文件进行读取,向打开的文件中进行写入,必须以二进制的格式来书写,每次写入记得flush。def testUpload(request):if request.method == 'GET':return render(request, 'testUpload.html')elif request.method == 'POST':icon = request.FILES.get('icon')# print(icon)  # time.jpg# print(type(icon))  # <class 'django.core.files.uploadedfile.InMemoryUploadedFile'># 因为使用的是windows连接的远程服务器# 那么这个时候就不可以使用绝对路径  绝对路径windows和centos的路径不一致# 上传或者下载的路径就都不对# 所以如果使用的是远程服务器 那么就要写相对路径with open(r'static/upload/time.jpg', 'wb') as fp:for part in icon.chunks():fp.write(part)fp.flush()return HttpResponse('上传成功')testUpload.html
<body><h1>上传图片</h1><form action="{% url 'com:testUpload' %}" method="post" enctype="multipart/form-data">{% csrf_token %}<input type="file" name="icon"><button>上传</button></form>
</body>实现步骤:(1)表单的请求方式是post(2)添加表单得属性 enctype='multipart/form-data'  二进制传输(3inputtype属性值为file4)获取表单中得file值  request.FILES.get获取的是文件的名字(5with open打开一个路径,然后以wb的模式使用for i in xxx.chunks()fp.write()fp.flush()
Django内置实现:
1 执行一个请求,跳转到一个页面2 页面中的请求方式是post,然后设置enctype<body>django文件上传<form action="{% url 'com:testDjangoUpload' %}" method="post" 		enctype="multipart/form-data">{% csrf_token %}<input type="file" name="icon"><button>上传</button></form></body>3 在settings中设置MEDIA_ROOTMEDIA_ROOT = os.path.join(BASE_DIR, 'static/自定义文件夹名字')会自动创建文件夹4 创建一个模型,模型中有一个属性的类型是imageFiled,该属性的约束是upload_to注意imageFiled依赖于pillow,必须安装,如果你的django版本自动添加了pillow,就不需要安装注意文件图片保存的位置是MEDIA_ROOT和upload_to的拼接class User(models.Model):u_name = models.CharField(max_length=32)u_icon = models.ImageField(upload_to='icons')class Meta:db_table = 'user'5 实例化对象,保存即可def testDjangoUpload(request):if request.method == 'GET':return render(request, 'testDjangoUpload.html')elif request.method == 'POST':icon = request.FILES.get('icon')user = User()user.u_name = '图片'user.u_icon = iconuser.save()return HttpResponse('上传成功')注意:
1. 重复添加同一个图片,那么会直接添加进去,文件的名字是文件名原名_唯一串
2. 数据库icon的值是upload_to + 文件名字隐藏bug:
linux系统中的文件夹,最多存储65535个文件,注意不是文件夹。
解决方案:套娃class User(models.Model):u_name = models.CharField(max_length=32)u_icon = models.ImageField(upload_to='%Y/%m/%d/icons')class Meta:db_table = 'user'支持时间格式化
%Y
%m
%d
%H
%M
%S
头像显示代码实现views.pydef getImage(request):user = User.objects.last()context = {'icon': user.u_icon}return render(request, 'getimage.html', context=context)getimage.html<head><meta charset="UTF-8"><title>Title</title><style>img{border-radius: 50%;width: 80px;height: 80px;}</style>
</head>
<body><img src="/static/uploadDjango/{{ icon }}" alt="">
</body>

32 缓存

目的:缓解服务器的读写压力提升服务器的响应速度提升用户体验将执行过的操作数据存储下来,在一定时间内,再次获取数据的时候,直接从缓存中获取比较理想的方案,缓存使用内存级缓存(redis)Django内置缓存框架存储中间数据的一种介质
32.1 Django内置缓存实现(三种方案)
装饰器缓存
装饰器封装在视图函数上
@cache_page(30)	必须给参数,表示缓存的时间,单位秒,但是不需要写timeout@cache_page(20)
def testCache(request):time.sleep(5)return HttpResponse('mama')
基于数据库的缓存
在真实的数据库中创建缓存表1. 创建缓存表
python manage.py createcachetable [table_name]
创建好后,表中的字段有cache_key, value, expires。2. 在settings中配置缓存信息
CACHES = {'default': {'BACKEND': 'django.core.cache.backends.db.DatabaseCache',# 缓存的位置,也就是缓存表的表名'LOCATION': 'my_cache_table',# 过期时间,单位秒'TIMEOUT': 60,# 前缀'KEY_PREFIX': 'python2001',}
}3. views.py
def testCache1(request):value = cache.get('ip')if value:return HttpResponse(value)else:# ip地址ip = request.META.get('REMOTE_ADDR')cache.set('ip', ip)return HttpResponse('来了老弟')
基于redis的缓存1. 安装django-redis和django-redis-cache
pip3 install django-redis
pip3 install django-redis-cache2. 在settings中配置缓存信息
CACHES = {'default': {'BACKEND': 'django_redis.cache.RedisCache',# 缓存的位置# redis一共有16个数据库,分别是0-15'LOCATION': 'redis://127.0.0.1:6379/1','OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'},'KEY_PREFIX': 'python2001',}
}3. views.py
def testCache2(request):value = cache.get('ip')if value:return HttpResponse('在缓存中拿到的数据')else:ip = request.META.get('REMOTE_ADDR')cache.set('ip', ip)return HttpResponse('在redis数据库中拿到的数据')4. redis中查看
^C(django2001) [nice@iZm5e0rpsu8z9upjwrq5xgZ Djangoday04]$ redis-cli
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "python2001:1:ip"
127.0.0.1:6379[1]> get python2001:1:ip
"\x80\x04\x95\x11\x00\x00\x00\x00\x00\x00\x00\x8c\r117.136.86.56\x94."
多级缓存之装饰器缓存
CACHES = {# default和django2001是缓存的名字'default': {'BACKEND': 'django.core.cache.backends.db.DatabaseCache','LOCATION': 'my_cache_table','TIMEOUT': 60 * 5,'KEY_PREFIX': 'databaseCache2001',},'django2001': {"BACKEND": "django_redis.cache.RedisCache","LOCATION": "redis://127.0.0.1:6379/1","OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient",},'KEY_PREFIX': 'redis2001',}
}装饰器缓存
@cache_page(30, cache='cache_name')
可以使用装饰器指定想要的数据库eg:
缓存时间的优先级,就近原则
缓存的时间以视图函数为主,就近原则下面代码缓存时间是30秒,而不是5分钟。
@cache_page(30, cache='default')
def testCache3(request):time.sleep(5)return HttpResponse('513')
多级缓存之缓存的应用
cache = caches['cache_name']eg:
def testCache4(request):cache = caches['django2001']value = cache.get('ip')if value:return HttpResponse(value)else:ip = request.META.get('REMOTE_ADDR')cache.set('ip', ip)return HttpResponse('在redis数据库中拿到的数据')

33 中间件

中间件:是一个轻量级的,底层的插件,可以介入到Django的请求和响应过程(面向切面编程)
中间件的本质就是一个python类
面向切面编程(Aspect Oriented Programming)简称AOP。AOP的主要实现目的是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合的隔离效果。
django内置的一个底层插件
从属于面向切面编程AOP在不修改源代码的情况下,动态去添加一些业务逻辑处理
中间件的典型实现 装饰器中间件就是使用类装饰实现的
面向切面编程切点(1)process_requestprocess_request(self,request):在执行视图前被调用,每个请求上都会调用,不主动进行返回或返回HttpResponse对象(2)process_viewprocess_view(self,request,view_func,view_args,view_kwargs):调用视图之前执行,每个请求都会调用,不主动进行返回或返回HttpResponse对象(3)process_template_response	process_template_response(self,request,response):在视图刚好执行完后进行调用,每个请求都会调用,不主动进行返回或返回HttpResponse对象(4)process_responseprocess_response(self,request,response):所有响应返回浏览器之前调用,每个请求都会调用,不主动进行返回或返回HttpResponse对象(5)process_exceptionprocess_exception(self,request,exception):当视图抛出异常时调用,不主动进行返回或返回HttpResponse对象切面切点处切开可以获得的数据
实现步骤:
书写,自定义中间件1. 在工程目录下创建middleware目录2. 目录中创建一个python文件3. 在python文件中导入中间件的基类from django.utils.deprecation import MiddlewareMixin4. 在类中根据功能需求,创建切入需求类,重写切入点方法class LearnAOP(MiddlewareMixin):def process_request(self,request):print('request的路径',request.GET.path)5. 启用中间件,在settings中进行配置,MIDDLEWARE中添加middleware.文件名.类名
应用:白名单def get_phone(request):if random.randrange(100) > 95:return HttpResponse("恭喜你抢到了小米8")return HttpResponse("正在排队")if request.path == "/app/getphone/":if ip == "127.0.0.1":if random.randrange(100) > 20:return HttpResponse("恭喜您免费获取小米8 256G版")黑名单if request.path == "/app/getticket/":if ip.startswith("10.0.122.1"):return HttpResponse("已抢光")
当某一段业务逻辑发生了错误  那么就会执行process_exception方法
process_exception界面友好化 应用交互友好化def process_exception(self, request, exception):print(request, exception)return redirect(reverse('app:index'))
注意:中间件的执行顺序中间件注册的时候是一个列表如果我们没有在切点处直接进行返回,中间件会依次执行如果我们直接进行了返回,后续中间件就不再执行了

34 分页器

分页是为了提升用户体验,并且减小服务器的负担而开发的
分页:真分页   每一次点击下一页或者上一页 都会向数据库发送请求 并且返回数据    访问数据库次数过多假分页   一次性读取所有数据  然后再内存中进行分页企业级开发中常用 真分页   
原生实现偏移加限制offset  limitstudents = Student.objects.all()[per_page*(page-1): page * per_page]
封装实现Paginator(分页工具)对象创建:Paginator(数据集,每一页数据数)paginator = Paginator(students, per_page)属性count对象总数num_pages:页面总数page_range: 页码列表,从1开始  *方法:page(整数): 获得一个page对象该方法的返回值类型是Page常见错误:InvalidPage:page()传递无效页码PageNotAnInteger:page()传递的不是整数Empty:page()传递的值有效,但是没有数据Page(具体哪一页)对象获得通过Paginator的page()方法获得属性object_list:	当前页面上所有的数据对象number:	当前页的页码值paginator:	当前page关联的Paginator对象方法has_next()	:判断是否有下一页has_previous():判断是否有上一页has_other_pages():判断是否有上一页或下一页next_page_number():返回下一页的页码previous_page_number():返回上一页的页码len():返回当前页的数据的个数应用场景:paginator对象  适用于 页码的遍历  eg 上一页 xxx  下一页page对象       适用于 是否有上一页 下一页  上一页页码  下一页页码 

35 富文本

富文本富文本:Rich Text Format(RTF),是有微软开发的跨平台文档格式,大多数的文字处理软件都能读取和保存RTF文档,其实就是可以添加样式的文档,和HTML有很多相似的地方写论坛,博客时使用的一种带样式的文本插件插件使用方式(1)安装插件pip install django-tinymce(2)在instatlled_app中添加tinymce(3)初始化在settings中注册tinymce应用设置默认的配置文件TINYMCE_DEFAULT_CONFIG = {'theme':'advanced','width':800,'height':600,}(4)创建模型from tinymce.models import HTMLFieldclass Blog(models.Model):sBlog = HTMLField()(5)使用在自己的页面中使用对应的输入方式 文本域 <textarea></textarea>引入tinymce<script type="text/javascript" src="/static/tiny_mce/tiny_mce.js"></script>初始化绑定文本域<script type="text/javascript">tinyMCE.init({"mode": "textareas","theme": "advanced","width": 800,"height": 600})</script>