Python全栈开发-web框架之django

一:web框架html

什么是web框架?java

Web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。
这种框架有助于减轻网页开发时共通性活动的工做负荷,例如许多框架提供数据库访问接口、标准样板以及会话管理等,
可提高代码的可再用性。

web应用的流程:python

//浏览器发送一个HTTP请求;

//服务器收到请求,生成一个HTML文档;

//服务器把HTML文档做为HTTP响应的Body发送给浏览器;

//浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。
View Code

其实相对于全部的web应用,我感受就是客户端与服务端之间的交互过程,浏览器就是一个socket客户端。git

客户端与服务端
因此,最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。Apache、Nginx、Lighttpd等这些常见的静态服务器就是
干这件事情的。
若是要动态生成HTML,就须要把上述步骤本身来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,若是咱们本身来写这些底层代码,还没开始写动态HTML呢,
就得花个把月去读HTTP规范。
正确的作法是底层代码由专门的服务器软件实现,咱们用Python专一于生成HTML文档。由于咱们不但愿接触到TCP链接、HTTP原始请求和响应格式,
因此,须要一个统一的接口,让咱们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。
WSGI接口定义很是简单,它只要求Web开发者实现一个函数,就能够响应HTTP请求。
咱们来看一个最简单的Web版本的“Hello, web!”:
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()
小程序
上面的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也应该发送。

而后,函数的返回值'<h1>Hello, web!</h1>'将做为HTTP响应的Body发送给浏览器。

有了WSGI,咱们关心的就是如何从environ这个dict对象拿到HTTP请求信息,而后构造HTML,经过start_response()发送Header,最后返回Body。

整个application()函数自己没有涉及到任何解析HTTP的部分,也就是说,底层代码不须要咱们本身编写,咱们只负责在更高层次上考虑如何响应请求就能够了。

不过,等等,这个application()函数怎么调用?若是咱们本身调用,两个参数environ和start_response咱们无法提供,返回的str也无法发给浏览器。

因此application()函数必须由WSGI服务器来调用。有不少符合WSGI规范的服务器,咱们能够挑选一个来用。可是如今,咱们只想尽快测试一下咱们编写的application()函数真的能够把HTML输出到浏览器,因此,要赶忙找一个最简单的WSGI服务器,把咱们的Web应用程序跑起来。
好消息是Python内置了一个WSGI服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指该实现彻底符合WSGI标准,可是不考虑任何运行效率,仅供开发和测试使用。
attention
运行WSGI服务

咱们先编写hello.py,实现Web应用程序的WSGI处理函数:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, web!</h1>'
而后,再编写一个server.py,负责启动WSGI服务器,加载application()函数:

# server.py
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入咱们本身编写的application函数:
from hello import application

# 建立一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print "Serving HTTP on port 8000..."
# 开始监听HTTP请求:
httpd.serve_forever()
确保以上两个文件在同一个目录下,而后在命令行输入python server.py来启动WSGI服务器:
注意:若是8000端口已被其余程序占用,启动将失败,请修改为其余端口。

启动成功后,打开浏览器,输入http://localhost:8000/,就能够看到结果了:
按Ctrl+C终止服务器。

若是你以为这个Web应用太简单了,能够稍微改造一下,从environ里读取PATH_INFO,这样能够显示更加动态的内容:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')
你能够在地址栏输入用户名做为URL的一部分,将返回Hello, xxx!:
不管多么复杂的Web应用程序,入口都是一个WSGI处理函数。HTTP请求的全部输入信息均可以经过environ得到,HTTP响应的输出均可以经过start_response()加上函数返回值做为Body。

复杂的Web应用程序,光靠一个WSGI函数来处理仍是太底层了,咱们须要在WSGI之上再抽象出Web框架,进一步简化Web开发
过程分析

二:MVC和MTV模式程序员

2.1 MVCweb

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑汇集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不须要从新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。

MVC开始是存在于桌面程序中的,M是指业务模型,V是指用户界面,C则是控制器,使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可使用不一样的表现形式。好比一批统计数据能够分别用柱状图、饼图来表示。C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新。[

Model(模型)表示应用程序核心(好比数据库记录列表)。
View(视图)显示数据(数据库记录)。
Controller(控制器)处理输入(写入数据库记录)。
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的彻底控制。
Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。
  一般模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。
  一般视图是依据模型数据建立的。
Controller(控制器)是应用程序中处理用户交互的部分。
  一般控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
MVC的简介

2.2 MTV正则表达式

Django的MTV模式本质上与MVC模式没有什么差异,也是各组件之间为了保持松耦合关系,只是定义上有些许不一样,Django的MTV分别表明:

       Model(模型):负责业务对象与数据库的对象(ORM)

       Template(模版):负责如何把页面展现给用户

       View(视图):负责业务逻辑,并在适当的时候调用Model和Template

       此外,Django还有一个url分发器,它的做用是将一个个URL的页面请求分发给不一样的view处理,view再调用相应的Model和Template

Django是MTV框架。
MTV简介

三:django的简介(流程之命令行工具)shell

3.1  django的安装数据库

 

django  其实在这里有2种安装方式,一种是直接在命令提示符中安装还有是在pycharm中直接去安装, 
   命令提示符安装:    pip3 install django ,这里须要注意的一点是Python中好多东西都是经过pip3来进行实现的,因此要注意pip3的路径是否是已经添加到环境变量中去,这个命令执行之后也要把django的路径添加到环境变量中去,

1 建立project   :  在任意一个盘下打开命令提示符,  
django-admin startproject mysite,mysite是本身起的名字,可是由于这是国际惯例,因此就用mysite,下面是创建的mysite的结构:
---mysite
          ---settings.py
          ---url.py
          ---wsgi.py
---- manage.py    启动文件
2.建立APP,python manage.py startapp app1
 
能够在pycharm中直接去搜索django而后安装就能够,

3.2  django的命令行工具django

3.21   首先来建立一个django工程  django-admin.py startproject mysite,

django-admin.py 是Django的一个用于管理任务的命令行工具,manage.py是对django-admin.py的简单包装,每个Django Project里都会有一个mannage.py。  生成的mysite工程目录以下

  • manage.py ----- Django项目里面的工具,经过它能够调用django shell和数据库等。
  • settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其余一些工做的变量。
  • urls.py ----- 负责把URL模式映射到应用程序。  

 3.22  在mysite下建立blog应用,能够在命令提示符建立也能够直接在pycharm中建立

3.23  启动django项目   ,python manage.py runserver 8000,8000是本机的端口号,默认的,也能够本身设置,而后咱们只须要访问网址127.0.0.1:8080/  就能够看到咱们所设计的内容

3.24  生成同步数据库的脚本   python manage.py makemigrations

由于有时咱们所操做的数据咱们但愿保存下来,其实全部的数据都在数据库中存在,

同步数据库:  python manage.py migrate

当咱们再去访问一个页面的时候咱们所操做的数据就会保存到数据库中,

3.25 建立超级管理用户  python manage.py createsuperuser  

3.26  清空数据库   python manage.py flush

3.27  查询某个命令的详细信息  django-admin help startapp

这里的admin是django自带的一个后台数据管理系统

3.28 启动交互页面  python manage.py shell

3.29 在pycharm终端上输入python manage.py  就能够查看详细的列表

先来个简单的显示当前时间的例子来看一下具体的流程是什么?

cur_time.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>当前时间{{ abc }}
</h1>
</body>
</html>

url.py:
 url(r"^cur_time",views.cur_time),

views.py:
def cur_time(request):
    times=datetime.datetime.now()
    #return HttpResponse("<h1>ok<h1>")

    return render(request,"cur_time.html",{"abc":times})
显示时间

 在这里要注意的是好比你在url.py写路径的收要跳转到视图函数中去,那么久须要在此把模块导入进去,可是有一点比较好的是,在咱们得这个settings里面,django已经把环境变量给咱们配置好了,咱们就能够直接的去引用模块,

下面是个例子。来让咱们看看究竟是怎样运行的,以提交数据为例

index.html文件显示:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/userInfo/" 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>
{#<hr> 标签在 HTML 页面中建立一条水平线。#}
<hr>
<h1>数据展现</h1>
<table border="1px">
    <tr>
        <td>姓名</td>
        <td>性别</td>
        <td>邮箱</td>
    </tr>

    {% for i in user_list %}
    <tr>
        <td>{{ i.username }}</td>
        <td>{{ i.sex }}</td>
        <td>{{ i.email }}</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

{#<tr> 标签订义 HTML 表格中的行。一个tr标签就是表明一行#}
{#tr 元素包含一个或多个 th 或 td 元素。这里的td表示三列#}


url.py:
url(r"^userInfo",views.userInfo),

views.py:

#正常的提交数据并显示
# user_list=[]
# def userInfo(req):
#     # req.POST:{"username":alex,}
#     if req.method=="POST":
#         username=req.POST.get("username",None)
#         sex=req.POST.get("sex",None)
#         email=req.POST.get("email",None)
#         user={"username":username,"sex":sex,"email":email}
#         user_list.append(user)
#     return render(req,"index.html",{"user_list":user_list})
提交数据显示
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/userInfo/" 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>
{#<hr> 标签在 HTML 页面中建立一条水平线。#}
<hr>
<h1>数据展现</h1>
<table border="1px">
    <tr>
        <td>姓名</td>
        <td>性别</td>
        <td>邮箱</td>
    </tr>

    {% for i in user_list %}
    <tr>
        <td>{{ i.username }}</td>
        <td>{{ i.sex }}</td>
        <td>{{ i.email }}</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

{#<tr> 标签订义 HTML 表格中的行。一个tr标签就是表明一行#}
{#tr 元素包含一个或多个 th 或 td 元素。这里的td表示三列#}

url.py:
url(r"^userInfo",views.userInfo),

views.py:
#提交数据并在数据库中显示
def userInfo(req):
    if req.method=="POST":
        u=req.POST.get("username",None)
        s=req.POST.get("sex",None)
        e=req.POST.get("email",None)

        # ---------表中插入数据方式一
        # info={"username":u,"sex":e,"email":e}
        # models.UserInfor.objects.create(**info)

        # ---------表中插入数据方式二

        models.UserInfo.objects.create(
            username=u,
            sex=s,
            email=e,
        )

    user_list=models.UserInfo.objects.all()
    return render(req,"index.html",{"user_list":user_list})
提交数据(数据库)

 四:django  URL(路由系统)

URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL模式以及要为该URL模式调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

urlpatterns = [
    url(正则表达式, views视图函数,参数,别名),   
]
参数说明:

一个正则表达式字符串
一个可调用对象,一般为一个视图函数或一个指定视图函数路径的字符串
可选的要传递给视图函数的默认参数(字典形式)
一个可选的name参数

来看一个url中的样式代码  nonamed group

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from blog import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r"^cur_time",views.cur_time),
    url(r"^userInfo",views.userInfo),

    #url(r"^articles/2003/$",views.special_case_2003),
    url(r'^articles/[0-9]{4}/$', views.year_archive),

    #url(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$",views.year_archive),
    #参数三
    # url(r"^index/(?P<name>[0-9]){4}/",views.index,{"name":"alex"})
    #参数四
    url(r"^pay/index",views.index,name="alex"),
   # url(r"^blog",include("blog.urls")),
    url(r"login",views.login),
    url(r"home",views.home),
    url(r"add",views.add),

]
example
from django.conf.urls import url
from django.contrib import admin

from app01 import views

urlpatterns = [
   这里面的^以及$和正则表达式里面是同样的就是以^后面的开头以$前面的结尾
url(r'^articles/2003/$', views.special_case_2003),
    这里是0到9的任意一个数字重复4次,就是至关于上面的2003,不过这里有多样性没有定死了。可是好比说有2个url,若是在第一个以及显示了那么第二个就会被覆盖就是它是从上往下开始匹配的,匹配到就结束。
#url(r'^articles/[0-9]{4}/$', views.year_archive),
这里表示就是年月,可是看下面多加了一个括号,再去运行就会报错显示参数的个数不对,因此咱们就须要在视图函数里面得括号中再加一个参数就行
    url(r'^articles/([0-9]{4})/$', views.year_archive),  #no_named group

    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),

    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', 
这里的加号就是重复数字1到无穷次,不管有多少都能匹配上
views.article_detail),

]
这些事url里面的无名参数
代码加注释

named group

上面介绍的是一个无名分组,就会彻底按位子去匹配,这些参数都没有名字,下面介绍一下有名分组即named group

那么我想确定在说到正则这一块确定好多人已经把这一部分的知识忘记的差很少了,因此呢,在讲有名参数以前,咱们先来看一个小例子来回顾下当初的有些知识点

import re

ret=re.search('(?P<id>\d{3})/(?P<name>\w{3})','weeew34ttt123/ooo')
这里的?P是固定的格式,表示这种分组是有名字的,匹配出一个id为三个数字,一个name为三个字母
print(ret.group())
print(ret.group('id'))
print(ret.group('name'))

 

这和上面的差很少,就是把无名改成有名参数
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),
]
有名参数例子
#参数三
# url(r"^index/(?P<name>[0-9]){4}/",views.index,{"name":"alex"})
#参数四
url(r"^pay/index",views.index,name="alex"), 这里的name= 是固定格式
下面是一个登陆并提交数据的例子来看一下
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#action   form 表单提交的路径#}
{#<form action="/index/" method="post">#}
{#<form action={% url "alex" %} method="post">#}
{#    <input type="text" name="username">#}
{#    <input type="password" name="pwd">#}
{#    <input type="submit" value="submit">#}
{#</form>#}


<form action="/login/" method="post">
    <input type="text" name="username">
    <input type="password" name="pwd">
    <input type="submit" value="submit">
</form>



</body>
</html>

url.py:
url(r"^pay/index",views.index,name="alex"),

views.index

def index(req):
    if req.method == "POST":
        username = req.POST.get("username")
        pwd=req.POST.get("pwd")
        if username=="alex"and pwd=="123":
            return HttpResponse("登陆成功")
    return render(req,"login.html")
登陆函数

可是呢,咱们在url中写的一些url中去匹配的路径,有可能在上面匹配到之后下面的就覆盖了,这是一个弊端,

因此呢,咱们能够单建一个url,咱们不可能只有这一个blog APP,还会有其余的,咱们能够把每个应用的映射关系创建在本身的url.py中,这样就不会乱了,也不会出现覆盖的现象。

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin
from blog import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', include('blog.urls'))

]
建立本身的url

因此呢之后关于每个APP的,咱们均可以写到它本身下的url中去

五:django views (视图函数)

http请求中产生两个核心对象:

http请求:HttpRequest对象

http响应:HttpResponse对象

所在位置:django.http

以前咱们用到的参数request就是HttpRequest    检测方法:isinstance(request,HttpRequest)

5.1  HttpRequest 对象的属性和方法

# path:       请求页面的全路径,不包括域名
#
# method:     请求中使用的HTTP方法的字符串表示。全大写表示。例如
#
#                    if  req.method=="GET":
#
#                              do_something()
#
#                    elseif req.method=="POST":
#
#                              do_something_else()
#
# GET:         包含全部HTTP GET参数的类字典对象
#
# POST:       包含全部HTTP POST参数的类字典对象
#
#              服务器收到空的POST请求的状况也是可能发生的,也就是说,表单form经过
#              HTTP POST方法提交请求,可是表单中可能没有数据,所以不能使用
#              if req.POST来判断是否使用了HTTP POST 方法;应该使用  if req.method=="POST"
#
#
#
# COOKIES:     包含全部cookies的标准Python字典对象;keys和values都是字符串。
#
# FILES:      包含全部上传文件的类字典对象;FILES中的每个Key都是<input type="file" name="" />标签中                     name属性的值,FILES中的每个value同时也是一个标准的python字典对象,包含下面三个Keys:
#
#             filename:      上传文件名,用字符串表示
#             content_type:   上传文件的Content Type
#             content:       上传文件的原始内容
#
#
# user:       是一个django.contrib.auth.models.User对象,表明当前登录的用户。若是访问用户当前
#              没有登录,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你
#              能够经过user的is_authenticated()方法来辨别用户是否登录:
#              if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
#              时该属性才可用
#
# session:    惟一可读写的属性,表明当前会话的字典对象;本身有激活Django中的session支持时该属性才可用。

#方法
get_full_path(),   好比:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()获得的结果就是/index33/?name=123
req.path:/index33
View Code

5.2 HttpResponse  对象

对于HttpRequest对象来讲,是由django自动建立的,可是,HttpResponse对象就必须咱们本身建立。每一个view请求处理方法必须返回一个HttpResponse对象。

  HttpResponse类在django.http.HttpResponse

  在HttpResponse对象上扩展的经常使用方法:

页面渲染:         render()(推荐)<br>                 render_to_response(),  这种后面你的括号里面不用加参数
页面跳转:         redirect("路径")
locals():    能够直接将函数中全部的变量传给模板
补充:

来看个例子

这是按顺序分的,一个一个对应的
url.py:
    url(r"login",views.login),
    url(r"home",views.home),
    url(r"add",views.add),

views.py:
def login(req):
    if req.method=="POST":
        if 1:
           return redirect("/home/")
            # return render(req,"home.html",locals())
    return render(req,"login.html")
    # return redirect('login')
def home(req):
    name="小胡"
    return render(req,"home.html",{"name":name})

def add(req):
    # if req.method=="POST":
    #     return HttpResponse("OK")

    num=12
    return render(req,"add.html",locals())


login.html
home.html
add.html
View Code

六:Template 基础

模板(Template)指C++程序设计语言中的函数模板与类别模板,大致对应于java和C#中的泛型。

你可能已经注意到咱们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python代码之中。

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

尽管这种技术便于解释视图是如何工做的,但直接将HTML硬编码到你的视图里却并非一个好主意。 让咱们来看一下为何:

  • 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改每每比底层 Python 代码的修改要频繁得多,所以若是能够在不进行 Python 代码修改的状况下变动设计,那将会方便得多。

  • Python 代码编写和 HTML 设计是两项不一样的工做,大多数专业的网站开发环境都将他们分配给不一样的人员(甚至不一样部门)来完成。 设计者和HTML/CSS的编码人员不该该被要求去编辑Python的代码来完成他们的工做。

  • 程序员编写 Python代码和设计人员制做模板两项工做同时进行的效率是最高的,远胜于让一我的等待另外一我的完成对某个既包含 Python又包含 HTML 的文件的编辑工做。

基于这些缘由,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 咱们可使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。

6.1 模板的组成

 HTML代码+逻辑控制代码

6.2  逻辑控制代码的组成 

6.21  变量    使用大括号来引用变量    它的格式为:{{ name}}  ,就是在2个大括号里面写上变量名

    Template(模板)和Context(上下文) 对象     

return render(req,"home.html"他就是模板,{"name":name})这个就是上下文,来看一段代码
3.22 render 方法的使用

>>> python manange.py shell  (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'
View Code

可是咱们要进行这些操做不能直接在命令行中去操做,咱们首先要尽到django的环境中去才能执行

>>> python manange.py shell  (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'


# 同一模板,多个上下文,一旦有了模板对象,你就能够经过它渲染多个context,不管什么时候咱们均可以
# 像这样使用同一模板源渲染多个context,只进行 一次模板建立而后屡次调用render()方法渲染会
# 更为高效:
# Low
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))
    这种方法在实现的时候模板建立了三遍,不太好下面的就是只建立了一次
# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))
View Code

Django 模板解 析很是快捷。 大部分的解析工做都是在后台经过对简短正则表达式一次性调用来完成。 这和基于 XML 的模板引擎造成鲜明对比,那些引擎承担了 XML 解析器的开销,且每每比 Django 模板渲染引擎要慢上几个数量级。

from django.shortcuts import render,HttpResponse
from django.template.loader import get_template #记得导入
# Create your views here.


import datetime
from django.template import Template,Context

# def current_time(req):
    #原始的视图函数
    # now=datetime.datetime.now()
    # html="<html><body>如今时刻:<h1>%s.</h1></body></html>" %now
    # return HttpResponse(html)



# def current_time(req):

      #django模板修改的视图函数
#     now=datetime.datetime.now()
#     t=Template('<html><body>如今时刻是:<h1 style="color:red">{{current_date}}</h1></body></html>')
      #t=get_template('current_datetime.html')
#     c=Context({'current_date':now})
#     html=t.render(c)
#     return HttpResponse(html)

#另外一种写法(推荐)

def current_time(req):

    now=datetime.datetime.now()

    return render(req, 'current_datetime.html', {'current_date':now})

推荐方式
当前时间的例子

 6.23   深度变量的查找(万能的句点号)         

在到目前为止的例子中,咱们经过 context  传递的简单参数值主要是字符串,然而,模板系统可以很是简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。

在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。

 

#最好是用几个例子来讲明一下。
# 首先,句点可用于访问列表索引,例如:

>>> 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'

# 注意这里调用方法时并* 没有* 使用圆括号 并且也没法给该方法传递参数;你只能调用不需参数的
# 方法。
万能的句点号例子

 6.24  标签(tag)的使用,结构为:使用大括号和百分号的组合来使用tag{% %},里面放的就是标签,语句

6.241  {% if %}的使用

       {% if %}标签计算一个变量值,若是是“true”,即它存在、不为空而且不是false的boolean值,系统则会显示{% if %}和{% endif %}间的全部内容

       须要注意的一点是只要有一个if 开头就必须有一个endif 来结束

{% 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 %}
if例子代码

 

6.242          {% for %}循环的使用

其实我感受和上面的if差很少,都是用来判断条件的,须要注意的一点也是在最后面也须要加上结束的语句{%endfor%}

{% for %}标签容许你按顺序遍历一个序列中的各个元素,每次循环模板系统都会渲染{% for %}和{% endfor %}之间的全部内容

<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
for 循环例子

if for  均可以对列表,字典进行操做,和咱们得python中是同样的
6.243   {%csrf_token%}:csrf_token标签

用于生成csrf_token的标签,用于防治跨站攻击验证。注意若是你在view的index里用的是render_to_response方法,不会生效,若是想要实现须要这样写

from django.template import RequestContext,Template
return render_to_response("index.html",locals(),context_RequestContext(req))

 

     其实,这里是会生成一个input标签,和其余表单标签一块儿提交给后台的。

6.244         {% url %}:  引用路由配置的地址

<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>
View Code

6.245     {% with %}:用更简单的变量名替代复杂的变量名

{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
View Code

6.246       {% verbatim %}: 禁止render

{% verbatim %}
         {{ hello }}
{% endverbatim %}
View Code

6.247  {% load %}:加载标签库 

 

6.3      自定义filter 和simple_tag

a.在app中建立template模块(必须的,就得是这个名字)

b. 建立任意.py文件。如my_tags.py

 

from django import template
from django.utils.safestring import mark_safe
register = template.Library()  #register的是固定变量名,不能改变
#不能用于if语句,可多个参数
@register.simple_tag
def my_add100(v1):
    return v1 + 100

可是此时主要注意的是须要在HTML文件中开头部分写上{% load add100 %}这句话,就是把这个函数引用进来
#最多2个参数
# @register.filter
# def my_add100(v1,v2,v3):
#     return v1+100+v2+v3

@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)
View Code

 

------c、在使用自定义simple_tag和filter的html文件中导入以前建立的 my_tags.py :{% load my_tags %}

------d、使用simple_tag和filter(如何调用)

-------------------------------.html
{% 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 %}
View Code

e、在settings中的INSTALLED_APPS配置当前app,否则django没法找到自定义的simple_tag.

注意:

filter能够用在if等语句后,simple_tag不能够

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

 

 

6.31   经常使用过滤器   语法格式{{obj|filter:param}}

 在操做一个对象的时候咱们也直接这样:{{obj|upper}}所有大写     {{obj|lower}}所有小写   {{obj|first upper}}  第一个大写可是只取第一个      {{obj|capfirst}}  第一个大写后面还会有

# 1  add          :   给变量加上相应的值
   #
   # 2  addslashes   :    给变量中的引号前加上斜线
   #
   # 3  capfirst     :    首字母大写
   #
   # 4  cut          :   从字符串中移除指定的字符
   #
   # 5  date         :   格式化日期字符串
   #
   # 6  default      :   若是值是False,就替换成设置的默认值,不然就是用原本的值
   #
   # 7  default_if_none:  若是值是None,就替换成设置的默认值,不然就使用原本的值


#实例:

#value1="aBcDe"
{{ value1|upper }}<br>

#value2=5
{{ value2|add:3 }}<br>

#value3='he  llo wo r ld'
{{ value3|cut:' ' }}<br>

#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>

#value5=[]
{{ value5|default:'空的' }}<br>

#value6='<a href="#">跳转</a>'

{{ value6 }}

{% autoescape off %}
  {{ value6 }}
{% endautoescape %}

{{ value6|safe }}<br>

{{ value6|striptags }}

#value7='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>

#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}<br>
    value9='hello I am yuan'
filter 的方法

 6.4                  extend  模板继承

在讲解了模板加载机制以后,咱们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签容许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,能够是一个变量,也能够是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减小重复。

------extend(继承)模板标签

到目前为止,咱们的模板范例都只是些零星的 HTML 片断,但在实际应用中,你将用 Django 模板系统来建立整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减小共用页面区域(好比站点导航)所引发的重复和冗余代码?

解决该问题的传统作法是使用 服务器端的 includes ,你能够在 HTML 页面中使用该指令将一个网页嵌入到另外一个中。 事实上, Django 经过刚才讲述的 {% include %} 支持了这种方法。 可是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。

本质上来讲,模板继承就是先构造一个基础框架模板,然后在其子模板中对它所包含站点公用部分和定义块进行重载。

那么来看一个例子,好比说订单和购物车,其中好多代码都是重复的,咱们把共同的代码放在一块儿,各自的放在一块儿,进行调节使用

 

 from django.conf.urls import url
from django.contrib import admin
from blog import views
url(r"^orderd/",views.orderd),
    url(r"^shopping_car/",views.shopping_car),
URL
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .page-header{
            height: 50px;
            background-color: rebeccapurple;
        }

        .page-body .menu{
            height: 500px;
            background-color: antiquewhite;
            float: left;
            width: 20%;
        }

        .page-body .content{
            height: 500px;
            background-color: cornflowerblue;
            float: left;
            width: 80%;
        }
        .page-footer{
            height: 50px;
            background-color: darkcyan;
            clear: both;
        }
    </style>
</head>
<body>
<div>
    <div class="page-header"></div>
    <div class="page-body">
        <div class="menu">
            <a href="/orderd/">订单</a><br>
            <a href="/shopping_car/">购物车</a>
        </div>




        <div class="content">

            {% block content %}
                <div class="xxx" style="color: red">pppppp</div>
            {% endblock %}

        </div>
    </div>
    <div class="page-footer"></div>


</div>
</body>
</html>
base.HTML公共部分
{% extends "base.html" %}


{% block content %}

       {{ block.super }}
       <div>订单</div>

{% endblock %}
order.py
{% extends "base.html" %}


{% block content %}
        <div class="content">
            购物车2
        </div>
{% endblock %}
shopping_car

这个叫作 base.html 的模板定义了一个简单的 HTML 框架文档,咱们将在本站点的全部页面中使用。 子模板的做用就是重载、添加或保留那些块的内容。 (若是你一直按顺序学习到这里,保存这个文件到你的template目录下,命名为 base.html .)

咱们使用模板标签: {% block %} 。 全部的 {% block %} 标签告诉模板引擎,子模板能够重载这些部分。 每一个{% block %}标签所要作的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。

如下是其工做方式。 在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎当即装载其父模板,即本例中的 base.html 。

此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。所以,引擎将会使用咱们在 block title %} 中定义的标题,对 {% block content %} 也是如此。 因此,网页标题一块将由{% block title %}替换,一样地,网页的内容一块将由 {% block content %}替换。

注意因为子模板并无定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容老是被看成一条退路。

继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板均可以访问到你传到模板中的每个模板变量。

你能够根据须要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:

  1. 建立 base.html 模板,在其中定义站点的主要外观感觉。 这些都是不常修改甚至从不修改的部分。

  1. 为网站的每一个区域建立 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对base.html 进行拓展,并包含区域特定的风格与设计。

  1. 为每种类型的页面建立独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。

这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工做。

如下是使用模板继承的一些诀窍:

  • 若是在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 不然,模板继承将不起做用。

  • 通常来讲,基础模板中的 {% block %} 标签越多越好。 记住,子模板没必要定义父模板中全部的代码块,所以你能够用合理的缺省值对一些代码块进行填充,而后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。

  • 若是发觉本身在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。

  • 若是你须要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 若是只想在上级代码块基础上添加内容,而不是所有重载,该变量就显得很是有用了。

  • 不容许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是由于block 标签的工做方式是双向的。 也就是说,block 标签不只挖了一个要填的坑,也定义了在模板中这个坑所填充的内容。若是模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪一个块的内容。

相关文章
相关标签/搜索