一、什么是web框架?
框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演。
web应用 访问请求流程
*浏览器发送一个HTTP请求;
*服务器收到请求,生成一个HTML文档(待补充;是否是全部类型的访问都需要生成文档);
*服务器把HTML文档作为HTTP响应的Body发送给浏览器;
*浏览器收到HTTP响应,从HTTP Body取出HTML文档并解析显示
对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。(即BS架构)
静态页面--最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。
这个接口就是WSGI:Web Server Gateway Interface。
WSGI 简单介绍
WSGI,全称 Web Server Gateway Interface,或者 Python Web Server Gateway Interface ,是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。自从 WSGI 被开发出来以后,许多其它语言中也出现了类似接口。
WSGI 是作为 Web 服务器与 Web 应用程序或应用框架之间的一种低级别的,以提升可移植 Web 应用开发的共同点。WSGI 是基于现存的 CGI 标准而设计的。
很多框架都自带了 WSGI server ,比如 Flask,webpy,Django、CherryPy等等。当然性能都不好,自带的 web server 更多的是测试用途,发布时则使用生产环境的 WSGI server或者是联合 nginx 做 uwsgi。
通俗来讲,WSGI就像是一座桥梁,一边连着web服务器,另一边连着用户的应用。但是呢,这个桥的功能很弱,有时候还需要别的桥来帮忙才能进行处理。
WSGI的作用
WSGI有两方:“服务器”或“网关”一方,以及“应用程序”或“应用框架”一方。服务方调用应用方,提供环境信息,以及一个回调函数(提供给应用程序用来将消息头传递给服务器方),并接收Web内容作为返回值。
所谓的 WSGI中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序,而从应用程序的角度来说,中间件扮演服务器。“中间件”组件可以执行以下功能:
- 重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
- 允许在一个进程中同时运行多个应用程序或应用框架。
- 负载均衡和远程处理,通过在网络上转发请求和响应消息。
- 进行内容后处理,例如应用XSLT样式表。
WSGI 的设计确实参考了 Java 的 servlet。http://www.python.org/dev/peps/pep-0333/ 有这么一段话:
By contrast, although Java has just as many web application frameworks available, Java’s “servlet” API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API.
另外,需要提及的一点是:其它基于python的web框架,如tornado、flask、webpy都是在这个范围内进行增删裁剪的。例如tornado用的是自己的异步非阻塞“wsgi”,flask则只提供了最精简和基本的框架。Django则是直接使用了WSGI,并实现了大部分功能。
WSGI实例一 # -*- coding: utf-8 -*- from wsgiref.simple_server import make_server def application(environ, start_response): start_response(‘200 OK‘,[(‘Content-Type‘,‘text/html‘)]) return[b‘<h1 >Hello, web!</h1>‘] #‘‘中间为空,表示的是本地地址 httpd = make_server(‘‘,8080, application) print(‘Serving HTTP on port 8000...‘) # 开始监听HTTP请求: httpd.serve_forever()</pre><br><br>‘‘‘<br>1.整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写, <br>我们只负责在更高层次上考虑如何响应请求就可以了。<br><br>2.application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。 <br><br>3.Python内置了一个WSGI服务器,这个模块叫wsgiref<br><br>4.application()函数就是符合WSGI标准的一个HTTP处理函数, 它接收两个参数:<br>//environ:一个包含所有HTTP请求信息的dict对象; //start_response:一个发送HTTP响应的函数。 <br><br>5.在application()函数中,调用:start_response(‘200 OK‘,[(‘Content-Type‘,‘text/html‘)])<br>就发送了HTTP响应的Header,注意Header只能发送一次, 也就是只能调用一次start_response()函数。<br>start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每个<br>Header用一个包含两个str的tuple表示。 <br><br>6.通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。<br>然后,函数的返回值b‘<h1>Hello, web!</h1>‘将作为HTTP响应的Body发送给浏览器。 <br><br>7.有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML,通过<br>start_response()发送Header,最后返回Body。
WSGI实例一
WSGI实例二 from wsgiref.simple_server import make_server def f1(): f1=open("jd_index1.html","rb") data1=f1.read() return[data1] def f2(): f2=open("tb_index.html","rb") data2=f2.read() return[data2] def application(environ, start_response): print(environ[‘PATH_INFO‘]) path=environ[‘PATH_INFO‘] start_response(‘200 OK‘,[(‘Content-Type‘,‘text/html‘)]) # 如果URL路径为京东,执行函数1,返回京东主页 if path=="/jingdong": return f1() # 如果URL路径为淘宝,执行函数2,返回淘宝主页 elif path=="/taobao": return f2() else: return["<h1>404</h1>".encode("utf8")] httpd = make_server(‘‘,8810, application) print(‘Serving HTTP on port 8810...‘) # 开始监听HTTP请求: httpd.serve_forever() 打开浏览器访问相应的路径 这里我们WSGI每次修改完数据后,都需要重新启动该服务。
WSGI实例二
WSGI实例3 打印当前时间 #!/usr/bin/env python # -*- coding: utf-8 -*- # @Date : 2016/11/24 # @Author : Jesson import time from wsgiref.simple_server import make_server def f1(req): print(req) print(req["QUERY_STRING"]) f1=open("jd_index1.html","rb") data1=f1.read() return[data1] def f2(req): f2=open("tb_index.html","rb") data2=f2.read() return[data2] def f3(req):#模版以及数据库 f3=open("current_time.html","rb") data3=f3.read() times=time.strftime("%Y-%m-%d %X", time.localtime()) # 在前端相应的页面 设置自定义模版语言‘! !‘ data3=str(data3,"utf8").replace("!time!",str(times)) return[data3.encode("utf8")] def routers(): urlpatterns =( (‘/jingdong‘,f1), (‘/taobao‘,f2), ("/cur_time",f3) ) return urlpatterns def application(environ, start_response): print(environ[‘PATH_INFO‘]) path=environ[‘PATH_INFO‘] start_response(‘200 OK‘,[(‘Content-Type‘,‘text/html‘)]) urlpatterns = routers() func =None for item in urlpatterns: if item[0]== path: func = item[1] break if func: return func(environ) else: return["<h1>404</h1>".encode("utf8")] httpd = make_server(‘‘,8828, application) print(‘Serving HTTP on port 8828...‘) # 开始监听HTTP请求: httpd.serve_forever()
WSGI实例3 打印当前时间
MVC和MTV设计模式
MVC百度百科:全名Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
通俗解释:一种文件的组织和管理形式!不要被缩写吓到了,这其实就是把不同类型的文件放到不同的目录下的一种方法,然后取了个高大上的名字。当然,它带来的好处有很多,比如前后端分离,松耦合等等,就不详细说明了。
模型(model):定义数据库相关的内容,一般放在models.py文件中。 视图(view):定义HTML等静态网页文件相关,也就是那些html、css、js等前端的东西。 控制器(controller):定义业务逻辑相关,就是你的主要代码。
MTV: 有些WEB框架觉得MVC的字面意思很别扭,就给它改了一下。view不再是HTML相关,而是主业务逻辑了,相当于控制器。html被放在Templates中,称作模板,于是MVC就变成了MTV。这其实就是一个文字游戏,和MVC本质上是一样的,换了个名字和叫法而已,换汤不换药。
Django的MTV模型组织
在web开发的项目文件中,相关目录分开存放,必须要有机制将他们在内里进行耦合。在Django中,urls、orm、static、settings等起着重要的作用。
二、Django
1、学Django学的是什么?
1. 目录结构规范
2. urls路由方式
3. settings配置
4. ORM操作
5. 模板渲染
6. 其它
2、Django工作流程
安装: pip3 install django
创建project工程:django-admin startproject mysite
创建APP应用:python mannage.py startapp app01
settings配置:TEMPLATES
STATICFILES_DIRS=(os.path.join(BASE_DIR,"statics"),) #一定要加逗号
# 我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找
根据需求设计代码:url.py view.py
模版引擎:templates
连接数据库,操作数据:model.py
3、Django命令行工具
django-admin.py 是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每一个Django Project里都会有一个mannage.py。
manage.py —– Django项目里面的工具,通过它可以调用django shell和数据库等。
settings.py —- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
urls.py —– 负责把URL模式映射到应用程序。
在mysite目录下创建app01应用: python manage.py startapp app01
启动django项目:python manage.py runserver 8080
生成同步数据库的脚本文件:python manage.py makemigrations
同步数据库: python manage.py migrate
注意:
在开发过程中,数据库同步误操作之后,难免会遇到后面不能同步成功的情况,解决这个问题的一个简单粗暴方法是把migrations目录下的脚本(除__init__.py之外)全部删掉,再把数据库删掉之后创建一个新的数据库,数据库同步操作再重新做一遍。
4、Django--admin
- 当我们访问http://127.0.0.1:8080/admin/时,会出现:
所以我们需要为进入这个项目的后台创建超级管理员:python manage.py createsuperuser 终端执行上述命令,设置好用户名和密码后便可登录啦!
设置用户名的时候,邮箱email可以不用输入,密码有复杂度校验,设置的复杂点。
另外,还会提示你,密码会明文显示。
Warning: Password input may be echoed.
OK~ 如此优雅的后台界面!
- 清空数据库:** python manage.py flush**
- 查询某个命令的详细信息: django-admin.py help startapp
admin 是Django 自带的一个后台数据库管理系统。 - 启动交互界面 :pythonmanage.pyshell
这个命令和 直接运行 python 进入 shell 的区别是:你可以在这个 shell 里面调用当前项目的 models.py 中的 API,对于操作数据,还有一些小测试非常方便。
如何更改正在运行的开发服务器端口
#进入django命令行,执行:
python manage.py runserver 加上新的端口号8080
5、Django URL (路由系统)
1.单一路由对应
url(r‘^index$‘, views.index),
2、基于正则的路由
url(r‘^index/(\d*)‘, views.index), url(r‘^manage/(?P<name>\w*)/(?P<id>\d*)‘, views.manage),
3、添加额外的参数
url(r‘^manage/(?P<name>\w*)‘, views.manage,{‘id‘:333}),
4、为路由映射设置名称
url(r‘^home‘, views.home, name=‘h1‘), url(r‘^index/(\d*)‘, views.index, name=‘h2‘),
设置名称之后,可以在不同的地方调用,如:
- 模板中使用生成URL {% url ‘h2‘ 2012 %}
- 函数中使用生成URL reverse(‘h2‘, args=(2012,)) 路径:django.urls.reverse
- Model中使用获取URL 自定义get_absolute_url() 方法 class NewType(models.Model):
caption = models.CharField(max_length=16) def get_absolute_url(self): """ 为每个对象生成一个URL 应用:在对象列表中生成查看详细的URL,使用此方法即可!!! :return: """ # return ‘/%s/%s‘ % (self._meta.db_table, self.id) # 或 from django.urls import reverse return reverse(‘NewType.Detail‘, kwargs={‘nid‘: self.id})
获取请求匹配成功的URL信息:request.resolver_match
5、路由分发
url(r‘^web/‘,include(‘web.urls‘)),
6、Ajax操作
$.ajax({ url:‘/csrf1.html‘, #指定路径 type:‘POST‘, #什么方式发送,get,post,put,delete headers:{‘X-CSRFToken‘:token}, #请求头也可以放东西 data:{‘user‘:user}, #请求体传的的东西 dataType:‘JSON‘, #接收来自服务端的json字符串自动解成字符串 traditional:true, #串列表或元组的时候用 success:function (arg) { #会调函数,参数是服务端返回的字符串 console.log(arg); } })
7、Bootstrap BootStrap响应式布局@media fontawesome
8、阻止默认事件:
js阻止默认事件的发生:onclick=‘return func();‘ function down(){return false;}
jQuery事件阻止默认事件发生:$(‘#addModal‘).click(function(){return false;})
9、Http请求生命周期:
请求头 -> 提取URL -> 路由关系匹配 -> 函数 (模板+数据渲染) -> 返回用户(响应头+响应体)
10、redirect:
重定向是服务端给客户端发送一个重定向命令,然后客户端在连接到新的地址,重定向是有服务端发起,浏览器完成
11、render:
模板渲染也是有服务端模板和要返回给客户的内容结合后的新的字符串发送给浏览器,浏览器识别并显示出来
12、母版
layout.html
{% block x %}{% endblock %}
13、子版
{% extends ‘layout‘ %}
{% block x %}.......{% endblock %}
14、Cookie
在浏览器上保持的键值对,参数(超时时间,域名)
服务器写Cookie:
#obj=HttpReponse(...)
#obj=render(...)
obj=redirect(...) #不一定是redirect三个中的任意一个都行
obj.set_cookie(k1,v1,max_age) #max_age超时时间,过了这个时间就消失了
obj.set_signed_cookie(k1,v1,max_age,salt=‘fff‘) #加盐版
获取Cookie:
request.COOKIES.get(‘k1‘)
request.get_signed_cookie(‘k1‘.salt=‘fff‘) #接收加盐版Cookie
Cookie作用登录和限制操作次数(投票)
15、ORM操作
需要修改的地方: 1.将MySQLdb修改为pymysql 2.把SQLlite改为mysql 步骤: 1.创建数据库 2.DATABASES = { ‘default‘: { ‘ENGINE‘: ‘django.db.backends.mysql‘, ‘NAME‘:‘db6‘, ‘USER‘: ‘root‘, ‘PASSWORD‘: ‘‘, ‘HOST‘: ‘localhost‘, ‘PORT‘: 3306, } } 3.import pymysql pymysql.install_as_MySQLdb() 4.在models中创建: class UserInfo(models.Model): nid=models.BigAutoField(primary_key=True) username=models.CharField(max_length=32) password=models.CharField(max_length=64) 5.注册app01 INSTALLED_APPS = [ ‘django.contrib.admin‘, ‘django.contrib.auth‘, ‘django.contrib.contenttypes‘, ‘django.contrib.sessions‘, ‘django.contrib.messages‘, ‘django.contrib.staticfiles‘, ‘app01‘ ] 6.创建数据表 Python manage.py makemigrations python manage.py migrate 7.class UserGroup(models.Model): ‘‘‘ 部门 ‘‘‘ title=models.CharField(max_length=32) class UserInfo(models.Model): ‘‘‘ 员工 ‘‘‘ nid=models.BigAutoField(primary_key=True) #AutoField 自动字段,这里是自增字段相当于自增int类型 user=models.CharField(max_length=32) password=models.CharField(max_length=64) # age=models.IntegerField(null=True) age=models.IntegerField(default=1) ug=models.ForeignKey("UserGroup",null=True)<br> 8.连表正反向操作
#两张表userinfo(含外键列ut_id)和usertype
#对象正向操作: ut.(usertype列名)
#对象反向操作:userinfo_set.(userinfo列名)
value正向操作:ut__(usertype列名)
value反向操作:userinfo__(userinfo列名)
9、更多操作
http://www.cnblogs.com/wupeiqi/articles/6216618.html
16、分页
17、xss攻击
19、CSRF
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/csrf1.html" method="POST"> //方式一*********************************************** {# {% csrf_token %}#} <input type="text" name="user" id="user"> <input type="submit" value="提交"> <button onclick="submitForm()">Ajax提交</button> </form> </body> <script src="/static/jquery-3.2.1.js"></script> <script src="/static/jquery.cookie.js"></script> <script> //方式二*********************************************** {# function submitForm() {#} {# var csrf = $(‘input[name="csrfmiddlewaretoken"]‘).val();#} {# var user = $(‘#user‘).val();#} {# $.ajax({#} {# url:‘/csrf1.html‘,#} {# type:‘POST‘,#} {# data:{‘user‘:user,‘csrfmiddlewaretoken‘:csrf},#} {# success:function (arg) {#} {# console.log(arg);#} {# }#} {# })#} {# }#} //方式三*********************************************** function submitForm() { var token=$.cookie(‘csrftoken‘); var user=$(‘#user‘).val(); $.ajax({ url:‘/csrf1.html‘, type:‘POST‘, headers:{‘X-CSRFToken‘:token}, data:{‘user‘:user}, dataType:‘JSON‘, traditional:true, success:function (arg) { console.log(arg); } }) } </script> </html><br><br><br><br><br>
1. CSRF
a. 基本应用
form表单中添加 {% csrf_token %}
b. 全站禁用
# ‘django.middleware.csrf.CsrfViewMiddleware‘,
c. 局部禁用
‘django.middleware.csrf.CsrfViewMiddleware‘, from django.views.decorators.csrf import csrf_exempt @csrf_exempt def csrf1(request): if request.method == ‘GET‘: return render(request,‘csrf1.html‘) else: return HttpResponse(‘ok‘)
d. 局部使用
# ‘django.middleware.csrf.CsrfViewMiddleware‘, from django.views.decorators.csrf import csrf_exempt,csrf_protect @csrf_protect def csrf1(request): if request.method == ‘GET‘: return render(request,‘csrf1.html‘) else: return HttpResponse(‘ok‘)
e. 特殊CBV
from django.views import View from django.utils.decorators import method_decorator @method_decorator(csrf_protect,name=‘dispatch‘) class Foo(View): def get(self,request): pass def post(self,request): pass
PS:CBV中添加装饰器
def wrapper(func): def inner(*args,**kwargs): return func(*args,**kwargs) return inner
# 1. 指定方法上添加装饰器
# class Foo(View): # # @method_decorator(wrapper) # def get(self,request): # pass # # def post(self,request): # pass
# 2. 在类上添加
# @method_decorator(wrapper,name=‘dispatch‘) # class Foo(View): # # def get(self,request): # pass # # def post(self,request): # pass
Ajax提交数据时候,携带CSRF:
a. 放置在data中携带
<form method="POST" action="/csrf1.html"> {% csrf_token %} <input id="user" type="text" name="user" /> <input type="submit" value="提交"/> <a onclick="submitForm();">Ajax提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function submitForm(){ var csrf = $(‘input[name="csrfmiddlewaretoken"]‘).val(); var user = $(‘#user‘).val(); $.ajax({ url: ‘/csrf1.html‘, type: ‘POST‘, data: { "user":user,‘csrfmiddlewaretoken‘: csrf}, success:function(arg){ console.log(arg); } }) } </script>
b. 放在请求头中
<form method="POST" action="/csrf1.html"> {% csrf_token %} <input id="user" type="text" name="user" /> <input type="submit" value="提交"/> <a onclick="submitForm();">Ajax提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script src="/static/jquery.cookie.js"></script> <script> function submitForm(){ var token = $.cookie(‘csrftoken‘); var user = $(‘#user‘).val(); $.ajax({ url: ‘/csrf1.html‘, type: ‘POST‘, headers:{‘X-CSRFToken‘: token}, data: { "user":user}, success:function(arg){ console.log(arg); } }) } </script>
20、include
导入小组件 pub.html <div> <h3>特别漂亮的组件</h3> <div class="title">标题:{{ name }}</div> <div class="content">内容:{{ name }}</div> </div> ********************************************************************* test.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> {% include ‘pub.html‘ %} {% include ‘pub.html‘ %} {% include ‘pub.html‘ %} </body> </html>
21、函数
函数-> 自动执行 模板自定义函数: - simple_filter - 最多两个参数,方式: {{第一个参数|函数名称:"第二个参数"}} - 可以做条件判断 - simple_tag - 无限制: {% 函数名 参数 参数%}
22、Session
定义:保存在服务端的数据(本质是键值对)
应用:依赖cookie
作用:保持会话(Web网站)
好处:敏感信息不会直接给客户端
23、自关联
class UInfo(models.Model): nickname=models.CharField(max_length=32) username=models.CharField(max_length=32) password=models.CharField(max_length=64) gender_choices=[ (1,‘男‘), (2,‘女‘), ] gender=models.IntegerField(choices=gender_choices) m=models.ManyToManyField(‘UInfo‘) ‘‘‘ Reverse accessor for ‘U2U.b‘ clashes with reverse accessor for ‘U2U.g‘. ‘UInfo.b‘的反向访问器与“UInfo.g”的反向访问器冲突, obj(对象男).U2U_set.all()与obj(对象女).U2U_set.all()一样产生冲突 related_qury_name:[a,b] obj(对象男).a_set.all() obj(对象女).b_set.all() related_name:[a,b] obj(对象男).a.all() obj(对象女).b.all() ‘‘‘ # class U2U(models.Model): # g=models.ForeignKey(‘UInfo‘,related_name=‘boys‘) # b=models.ForeignKey(‘UInfo‘,related_name=‘girls‘) class Comment(models.Model): new_id=models.IntegerField() #新闻ID content=models.CharField(max_length=32) #评论内容 user=models.CharField(max_length=32) #评论者 reply=models.ForeignKey(‘Comment‘,null=True,blank=True)
自关联models
def test2(request): #多对多两张表自关联 # #主流方式一,传id # models.U2U.objects.create(b_id=1,g_id=4) # models.U2U.objects.create(b_id=1,g_id=5) # models.U2U.objects.create(b_id=1,g_id=6) # models.U2U.objects.create(b_id=2,g_id=5) # models.U2U.objects.create(b_id=3,g_id=6) # #主流方式二,传对象 # boy=models.UInfo.objects.filter(gender=1,id=2).first() # girl=models.UInfo.objects.first(gender=2,id=6).first() # models.U2U.objects.create(b=boy,g=girl) # #虽然穿的是对象但是其内部本质也是取的id # xz=models.UInfo.objects.filter(id=1).first() # ret=xz.girls.all() #和xz对象(行)有关系的U2U表中的所有对象(行) # print(ret) # for u in ret: # print(u.g.nickname) #从U2U跨表到UInfo #多对多ManyToMany自关联,from属于正向操作,to属于反向操作 #from正向操作 # xz=models.UInfo.objects.filter(id=1).first() # objs=xz.m.all() # # print(objs) # for obj in objs: # print(obj.nickname) #to反向操作 # mr=models.UInfo.objects.filter(id=4).first() # print(mr.nickname) # v=mr.uinfo_set.all() # for row in v: # print(row.nickname) #FK自关联 # obj=models.Comment.objects.filter(id=4).first() #对象(第四行) # print(obj.reply) #对象(与第四行相关联的那行) return HttpResponse(‘...‘)
自关联views