web框架Django
Posted 鵬程萬裏
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了web框架Django相关的知识,希望对你有一定的参考价值。
一、什么是web框架?
框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单的说,就是你用别人搭建好的舞台来做表演。
对于所有的web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
最简单的web应用就是先把html用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用python编写web业务。这个接口就是WSGI:Web Server Gateway Interface。
from wsgiref.simple_server import make_server def foo1(request): f=open("alex.html","rb") data = f.read() f.close() return [data] def foo2(request): f = open("amos.html", "rb") data = f.read() f.close() return [data] def login(request): f = open("login.html", "rb") data = f.read() f.close() return [data] def reg(request): f = open("reg.html", "rb") data = f.read() f.close() return [data] def auth(request): print("+++++",request) user_union,pwd_union=request.get("QUERY_STRING").split("&") _,user=user_union.split("=") _,pwd=pwd_union.split("=") if user ==\'amos\' and pwd == \'123\': return ["登陆成功".encode()] else: return [b\'user or pwd is wrong\'] def routers(): URLpattern = ( (\'/login\',login), (\'/auth\',auth), (\'/alex\',foo1), (\'/amos\',foo2), (\'/reg\',reg), ) return URLpattern def application(environ,start_response): # environ里打包了所有的请求信息 path=environ.get("PATH_INFO") print(path) start_response(\'200 OK\',[(\'Content-Type\',\'text/html;charset=utf8\')]) #返回头信息,可以写多个键值对 urlpattern=routers() func=None for item in urlpattern: if path==item[0]: func=item[1] break if func: return func(environ) else: return [b\'404\'] # if path=="/amos": # f=open("amos.html","rb") # data=f.read() # f.close() # return [data] # elif path==\'/alex\': # f = open("alex.html", "rb") # data = f.read() # f.close() # return [data] # else: # return [b\'<h1>404</h1>\'] t=make_server("",8080,application) #实例化对象 t.serve_forever()
整个application()函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写, 我们只负责在更高层次上考虑如何响应请求就可以了。 application()函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。 Python内置了一个WSGI服务器,这个模块叫wsgiref application()函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数: //environ:一个包含所有HTTP请求信息的dict对象; //start_response:一个发送HTTP响应的函数。 在application()函数中,调用: start_response(\'200 OK\', [(\'Content-Type\', \'text/html\')]) 就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数。 start_response()函数接收两个参数,一个是HTTP响应码,一个是一组list表示的HTTP Header,每 个Header用一个包含两个str的tuple表示。 通常情况下,都应该把Content-Type头发送给浏览器。其他很多常用的HTTP Header也应该发送。 然后,函数的返回值b\'<h1>Hello, web!</h1>\'将作为HTTP响应的Body发送给浏览器。 有了WSGI,我们关心的就是如何从environ这个dict对象拿到HTTP请求信息,然后构造HTML, 通过start_response()发送Header,最后返回Body。 注意
二、MVC和MTV模式
Django的MTV模式本质是各组件之间为了保持松耦合关系,Django的MTV分别代表:
Model(模型):负责业务对象与数据库的对象(ORM)
Template(模板):负责如何把页面展示给用户
View(视图):负责业务逻辑,并在适当的时候调用Model和Template
此外,Django还有一个url分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
Django基本命令
1、创建一个Django project
django-admin startproject project_name
当前目录下会生成mysite的工程,目录结构如下:
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
2、在mysite目录下创建应用,比如blog:
python manage.py startapp blog
3、启动Django项目
python manage.py runserver 8080
二、路由配置系统(URLconf)
URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。
功能:客户端访问的url的路径(path)与视图函数一一映射关系
语法格式:
urlpatterns = [ url(正则表达式, views视图函数,参数,别名), ] 参数说明: 一个正则表达式字符串 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串 可选的要传递给视图函数的默认参数(字典形式) 别名:一个可选的name参数,可以让前端都用别名,这样代码维护起来就比较方便,比如 <form action="{% url \'别名\' %}" method="post"> 这样不管你的后端路径怎么改,都不会影响到前端代码
2.1:URLconf的正则字符串参数
2.1.1:简单配置
from django.conf.urls import url from . import views urlpatterns = [ url(r\'^articles/2003/$\', views.special_case_2003), url(r\'^articles/([0-9]{4})/$\', views.year_archive), url(r\'^articles/([0-9]{4})/([0-9]{2})/$\', views.month_archive), url(r\'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$\', views.article_detail),
\'\'\' NOTE: 一旦匹配成功则不再继续 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。 每个正则表达式前面的\'r\' 是可选的但是建议加上。 一些请求的例子: /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。 /articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。 /articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数 views.month_archive(request, \'2005\', \'03\')。 \'\'\'
2.1.2:有名分组(named group)
上面的实例使用简单的、没有命令的正则表达式组(通过括号)来捕获URL中的值并以位置参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL中的值并以关键字参数传递给视图。
在python正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,pattern是要匹配的模式。
下面是以上URLconf使用命名组打的重写:
from django.conf.urls import url from . import views urlpatterns = [ url(r\'^articles/2003/$\', views.special_case_2003), url(r\'^articles/(?P<year>[0-9]{4})/$\', views.year_archive), url(r\'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$\', views.month_archive), url(r\'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$\', views.article_detail), ]
这个实现与前面的实例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。例如:
/articles/2005/03/ 请求将调用views.month_archive(request, year=\'2005\', month=\'03\')函数 /articles/2003/03/03/ 请求将调用函数views.article_detail(request, year=\'2003\', month=\'03\', day=\'03\')。
在实际应用中,这意味你的URLconf会更加清晰且不容易产生参数顺序问题的错误——你可以在你的视图函数定义中重新安排参数的顺序。当然,这些好处是以简洁为代价;有些开发人员认为命名分组语法丑陋而繁琐。
2.1.3:URLconf在什么 上查找
URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。
例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/
。
在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/
。
URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POST
、GET
、HEAD
等等 —— 都将路由到相同的函数。
2.1.4:捕获的参数永远是字符串
每个捕获的参数都作为一个普通的Python字符串传递给视图,无论正则表达式使用的是什么匹配方式。例如,下面这行URLconf中:
url(r\'^articles/(?P<year>[0-9]{4})/$\', views.year_archive),
views.year_archive()
的year
参数将是一个字符串
2.1.5:指定视图参数的默认值
有一个方便地小技巧是指定视图参数的默认值。下面是一个URLconf和视图的示例:
# URLconf from django.conf.urls import url from . import views urlpatterns = [ url(r\'^blog/$\', views.page), url(r\'^blog/page(?P<num>[0-9]+)/$\', views.page), ] # View (in blog/views.py) def page(request, num="1"): ...
在上面的例子中,两个URL模式指向同一个视图views.page
—— 但是第一个模式不会从URL 中捕获任何值。如果第一个模式匹配,page()
函数将使用num
参数的默认值"1"。如果第二个模式匹配,page()
将使用正则表达式捕获的num
值。
2.2:name参数
\'\'\' urlpatterns = [ url(r\'^index\',views.index,name=\'INDEX\'), ] ################### def index(req): if req.method==\'POST\': username=req.POST.get(\'username\') password=req.POST.get(\'password\') if username==\'alex\' and password==\'123\': return HttpResponse("登陆成功") return render(req,\'index.html\') ##################### <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {# <form action="/index/" method="post">#} <form action="{% url \'INDEX\' %}" method="post"> 用户名:<input type="text" name="username"> 密码:<input type="password" name="password"> <input type="submit" value="submit"> </form> </body> </html> ####################### \'\'\'
三、编写视图
一个视图函数,或者剪短来说叫做视图,是一个简单的python函数,它接收web请求,并且返回web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片等等,是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它再你的python目录下面。除此之外没有更多的要求了——可以说“没有什么神奇的地方”。为了能够把代码放在某个地方,惯例是把视图放在叫做views.py的文件中,饭后把它放到你的项目或者应用目录里。
3.1:一个简单的视图
下面是一个返回当前日期和时间作为HTML文档的视图:
from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
让我们逐行阅读上面的代码:
- 首先,我们从 django.http模块导入了HttpResponse类,以及Python的datetime库。
- 接着,我们定义了current_datetime函数。它是一个视图函数。每个视图函数都应接收HttpRequest对象作为第一个参数,一般叫做request。
- 注意视图函数的名称并不重要;不需要用一个统一的命名方式来命名,以便让Django识别它。我们将其命名为current_datetime,是因为这个名称能够精确地反映出它的功能。
- 这个视图会返回一个HttpResponse对象,其中包含生成的响应。每个视图函数都要返回HttpResponse对象
http请求-响应过程中有两个核心对象:
http请求对象:HttpRequest
http响应响应:HttpResponse
3.2:快捷函数
3.2.1:render函数
---------------render(request, template_name[, context])
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
参数:
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。
status:响应的状态码。默认为200。
3.2.2:redirect(跳转)函数
-----------------------------------url.py url(r"login", views.login), url(r"amos_back", views.amos_back), -----------------------------------views.py def login(req): if req.method=="POST": if 1: # return redirect("/amos_back/") name="amos" return render(req,"my backend.html",locals()) return render(req,"login.html",locals()) def amos_back(req): name="Amos" return render(req,"my backend.html",locals()) -----------------------------------login.html <form action="/login/" method="post"> <p>姓名<input type="text" name="username"></p> <p>性别<input type="text" name="sex"></p> <p>邮箱<input type="text" name="email"></p> <p><input type="submit" value="submit"></p> </form> -----------------------------------my backend.html <h1>用户{{ name }}你好</h1> #总结: render和redirect的区别: #redirect走的是路径,render返回的是模板 # 1 if render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分 # 除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦. # 2 the most important: url没有跳转到/amos_back/,而是还在/login/,所以当刷新后 # 又得重新登录.
四、Template
4.1:模板支持的语法
模板语言:渲染变量{{变量名}} 渲染标签{% %} 目的:将变量嵌入到html中 注意: 1.只要带有模板语法的HTML都称为模板 2.render方法渲染时 把后端变量嵌入到模板中 locals():可以替换kye,value类型的上下文对象,他会把后端所有的变量嵌入到前端页面中,前 端直接可以用
4.2:深度查询(万能的句点号)
#最好是用几个例子来说明一下。 # 首先,句点可用于访问列表索引,例如: >>> from django.template import Template, Context >>> t = Template(\'Item 2 is {{ items.2 }}.\') >>> c = Context({\'items\': [\'apples\', \'bananas\', \'carrots\']}) >>> t.render(c) \'Item 2 is carrots.\' #假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点: >>> from django.template import Template, Context >>> person = {\'name\': \'Sally\', \'age\': \'43\'} >>> t = Template(\'{{ person.name }} is {{ person.age }} years old.\') >>> c = Context({\'person\': person}) >>> t.render(c) \'Sally is 43 years old.\' #同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有 #year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性: >>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year >>> d.month >>> d.day >>> t = Template(\'The month is {{ date.month }} and the year is {{ date.year }}.\') >>> c = Context({\'date\': d}) >>> t.render(c) \'The month is 5 and the year is 1993.\' # 这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适 # 用于任意的对象。 >>> from django.template import Template, Context >>> class Person(object): ... def __init__(self, first_name, last_name): ... self.first_name, self.last_name = first_name, last_name >>> t = Template(\'Hello, {{ person.first_name }} {{ person.last_name }}.\') >>> c = Context({\'person\': Person(\'John\', \'Smith\')}) >>> t.render(c) \'Hello, John Smith.\' # 点语法也可以用来引用对象的方法。 例如,每个 Python 字符串都有 upper() 和 isdigit() # 方法,你在模板中可以使用同样的句点语法来调用它们: >>> from django.template import Template, Context >>> t = Template(\'{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}\') >>> t.render(Context({\'var\': \'hello\'})) \'hello -- HELLO -- False\' >>> t.render(Context({\'var\': \'123\'})) \'123 -- 123 -- True\' # 注意这里调用方法时并* 没有* 使用圆括号 而且也无法给该方法传递参数;你只能调用不需参数的 # 方法。
4.3:变量的过滤器(filter)使用
语法格式: {{obj|filter:param}} truncatewords:3 按单词截断 truncatechars:20 按字符截断 如果从后端传一个标签,需要通过一个过滤器safe告诉前端这是一个安全的标签 add : 给变量加上相应的值 addslashes : 给变量中的引号前加上斜线 capfirst : 首字母大写 cut : 从字符串中移除指定的字符 date : 格式化日期字符串 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值
4.4:标签的使用(使用大括号百分比的组合来表示使用tag)
4.4.1:{% if %} 的使用
{% if num >= 100 and 8 %} {% if num > 200 %} <p>num大于200</p> {% else %} <p>num大于100小于200</p> {% endif %} {% elif num < 100%} <p>num小于100</p> {% else %} <p>num等于100</p> {% endif %} {% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量 {% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义,例如下面的标签是不合法的: {% if obj1 and obj2 or obj3 %}
4.4.2:{% for %} 的使用
<ul> {% for obj in list %} <li>{{ obj.name }}</li> {% endfor %} </ul> #在标签里添加reversed来反序循环列表: {% for obj in list reversed %} ... {% endfor %} #{% for %}标签可以嵌套: {% for country in countries %} <h1>{{ country.name }}</h1> <ul> {% for city in country.city_list %} <li>{{ city }}</li> {% endfor %} </ul> {% endfor %} #系统不支持中断循环,系统也不支持continue语句,{% for %}标签内置了一个forloop模板变量, #这个变量含有一些属性可以提供给你一些关于循环的信息 1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1: {% for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> {% endfor %} 2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0 3,forloop.revcounter 4,forloop.revcounter0 5,forloop.first当第一次循环时值为True,在特别情况下很有用: {% for object in objects %} {% if forloop.first %}<li class="first">{% else %}<li>{% endif %} {{ object }} </li> {% endfor %} # 富有魔力的forloop变量只能在循环中得到,当模板解析器到达{% endfor %}时forloop就消失了 # 如果你的模板context已经包含一个叫forloop的变量,Django会用{% for %}标签替代它 # Django会在for标签的块中覆盖你定义的forloop变量的值 # 在其他非循环的地方,你的forloop变量仍然可用 #{% empty %} 如果循环对象为空时显示里面内容 {{li }} {% for i in li %} <li>{{ forloop.counter0 }}----{{ i }}</li> {% empty %} <li>this is empty!</li> {% endfor %} # [11, 22, 33, 44, 55] # 0----11 # 1----22 # 2----33 # 3----44 # 4----55
4.4.3:{% url %}
#引用路由配置的地址 <form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
4.4.4:{% with %}
#用更简单的变量名替代复杂的变量名 {% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
4.4.5:{% verbatim %}
#禁止render {% verbatim %} {{ hello }} {% endverbatim %}
4.4.6:自定义filter和simple_tag
1.查看app是否在配置文件中 2.在app中创建templatetags模块(必须的) 3.创建任意 .py 文件,如:my_tags.py 4.在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py :{% load my_tags %}
定义方法
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改变 @register.filter def filter_multi(v1,v2): return v1 * v2 @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 @register.simple_tag def my_input(id,arg): result = "<input type=\'text\' id=\'%s\' class=\'%s\' />" %(id,arg,) return mark_safe(result)
使用方法
{% load xxx %} #首行 # num=12 {{ num|filter_multi:2 }} #24 自定义过滤器 {{ num|filter_multi:"[22,333,4444]" }} #自定义标签 {% simple_tag_multi 2 5 %} 参数不限,但不能放在if for语句中 {% simple_tag_multi num 5 %}
4.5:extend母版继承
作用:减少公用页面区域的重复代码
#在母版中想要替换的地方写这条内容 {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
子页面引用时
{% extends "base.html" %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
工作方式:
在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由{% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。
注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到
以上是关于web框架Django的主要内容,如果未能解决你的问题,请参考以下文章