Python-Web框架之【Django】

一、什么是web框架

    框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的舞台来做表演。

    对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。

 1 import socket
 2 
 3 def handle_request(client):
 4 
 5     buf = client.recv(1024)
 6     client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8"))
 7     client.send("<h1 style='color:red'>Hello, web</h1>".encode("utf8"))
 8 
 9 def main():
10 
11     sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
12     sock.bind(('localhost',8001))
13     sock.listen(5)
14 
15     while True:
16         connection, address = sock.accept()
17         handle_request(connection)
18         connection.close()
19 
20 if __name__ == '__main__':
21 
22     main()
web应用本质

    最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

        正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。即:web服务网关接口。  定义了Web服务器与Web应用(或Web框架)之间的标准接口

    Python中wsgiref就是WSGI接口的一个模块,功能相当于apache、nginx

    如何实现一个web框架:

 1 from wsgiref.simple_server import make_server
 2 
 3 
 4 #http请求处理函数
 5 def application(environ, start_response):
 6     print(environ)      #请求信息
 7     start_response('200 OK', [('Content-Type', 'text/html')])   #设置响应头
 8 
 9     return [b'<h1>Hello, web!</h1>']    #返回响应体,字符串迭代对象
10 
11 
12 if __name__ == '__main__':
13     httpd = make_server('127.0.0.1', 8000, application)
14 
15     print ("Serving HTTP on port 8000...")
16 
17     httpd.serve_forever()   #监听http请求
step1
整个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。
注意
 1 from wsgiref.simple_server import make_server
 2 
 3 
 4 #http处理函数
 5 def application(environ, start_response):
 6     # print(environ)    #请求信息
 7     start_response('200 OK', [('Content-Type', 'text/html')])   #设置响应头
 8 
 9     with open("index1.html","rb") as f:
10         data = f.read()
11     return [data]       #返回响应体
12 
13 
14 if __name__ == '__main__':
15     httpd = make_server('127.0.0.1', 8000, application)
16 
17     print ("Serving HTTP on port 8000...")
18 
19     httpd.serve_forever()       #监听http请求
step2
 1 from wsgiref.simple_server import make_server
 2 
 3 
 4 #http处理函数
 5 def application(environ, start_response):
 6     # print(environ)    #请求信息
 7     start_response('200 OK', [('Content-Type', 'text/html')])   #设置响应头
 8 
 9     if environ["PATH_INFO"] == "/heilong":
10         return [b"<h1>Hello, heilong!</h1>"]       #返回响应体
11     elif environ["PATH_INFO"] == "/xiaohei":
12         return [b"<h1>Hello, xiaohei!</h1>"]
13     else:
14         return [b"404 Not Found"]
15 
16 
17 if __name__ == '__main__':
18     httpd = make_server('127.0.0.1', 8000, application)
19 
20     print ("Serving HTTP on port 8000...")
21 
22     httpd.serve_forever()       #监听http请求
step3
 1 from wsgiref.simple_server import make_server
 2 
 3 
 4 def heilong():
 5     with open("heilong.html","rb") as f:
 6         data = f.read()
 7     return data
 8 
 9 def xiaohei():
10     with open("xiaohei.html","rb") as f:
11         data = f.read()
12     return data
13 
14 def route_ctrl():
15     url_dic = {
16         "/heilong":heilong,
17         "/xiaohei":xiaohei,
18     }
19     return url_dic
20 
21 #http处理函数
22 def application(environ, start_response):
23     # print(environ)    #请求信息
24     url_path=environ["PATH_INFO"]
25 
26     start_response('200 OK', [('Content-Type', 'text/html')])   #设置响应头
27 
28     url_patterns=route_ctrl()
29 
30 
31     for item in url_patterns:
32         if item == url_path:
33             func = url_patterns.get(item)
34             return [func()]
35     else:
36         return [b"404 Not Found"]
37 
38 
39 
40 if __name__ == '__main__':
41     httpd = make_server('127.0.0.1', 8000, application)
42 
43     print ("Serving HTTP on port 8000...")
44 
45     httpd.serve_forever()       #监听http请求
step4
 1 from wsgiref.simple_server import make_server
 2 import time
 3 
 4 def heilong(req):
 5     with open("heilong.html","rb") as f:
 6         data = f.read()
 7     return data
 8 
 9 def xiaohei(req):
10     with open("xiaohei.html","rb") as f:
11         data = f.read()
12     return data
13 
14 def showtime(req):
15     with open("showtime.html","rb") as f:
16         data = f.read().decode("utf-8")
17         data = data.replace("{{time}}",str(time.ctime()))
18     return data.encode("utf-8")
19 
20 def route_ctrl():
21     url_dic = {
22         "/heilong":heilong,
23         "/xiaohei":xiaohei,
24         "/showtime":showtime,
25     }
26     return url_dic
27 
28 #http处理函数
29 def application(environ, start_response):
30     # print(environ)    #请求信息
31     url_path=environ["PATH_INFO"]
32 
33     start_response('200 OK', [('Content-Type', 'text/html')])   #设置响应头
34 
35     url_patterns=route_ctrl()
36 
37     for item in url_patterns:
38         if item == url_path:
39             func = url_patterns.get(item)
40             return [func(environ)]
41     else:
42         return [b"404 Not Found"]
43 
44 
45 if __name__ == '__main__':
46     httpd = make_server('127.0.0.1', 8000, application)
47 
48     print ("Serving HTTP on port 8000...")
49 
50     httpd.serve_forever()       #监听http请求
step5

    到此,我们已经实现了一个简单的web框架

二、MVC和MTV模式

    著名的MVC模式:所谓MVC就是把web应用分为模型(M),控制器(C),视图(V)三层;他们之间以一种插件似的,解耦合的方式连接在一起。

    模型负责业务对象与数据库的对象(ORM),视图负责与用户的交互(页面),控制器(C)接受用户的输入调用模型和视图完成用户的请求。

      

    

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

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

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

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

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

    

三、Django框架的流程与命令行工具

    Django实现流程:

django
    #安装: pip3 install django

          添加环境变量

    #1  创建project
       django-admin startproject mysite

       ---mysite

          ---settings.py
          ---url.py
          ---wsgi.py

       ---- manage.py(启动文件)  

    #2  创建APP       
       python manage.py startapp  app01

    pycharm实现创建项目和应用:File——》New Project——》Django
            Location:项目路径及名称
            Application name:应用名

    #3  settings配置
    
       TEMPLATES

       STATICFILES_DIRS=(
            os.path.join(BASE_DIR,"statics"),
        )

       STATIC_URL = '/static/' 
       #  我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找#4  根据需求设计代码
           url.py
           view.py

    #5  使用模版
       render(req,"index.html")   

    #6  启动项目
       python manage.py runserver  127.0.0.1:8090

    #7  连接数据库,操作数据
       model.py

    

    django的命令行工具

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

    <1> 创建一个django工程 : django-admin.py startproject mysite

            当前目录下会生成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

           这样我们的django就启动起来了!当我们访问:http://127.0.0.1:8080/时就可以看到:

           

    <4>生成同步数据库的脚本:python manage.py makemigrations  

                     同步数据库:  python manage.py migrate   

 

四、Django的配置文件settings.py

  1、STATIC_ROOT和STATIC_URL

STATIC主要指的是如css,js,images这样文件,在settings里面可以配置STATIC_ROOT和STATIC_URL,
    配置方式与MEDIA_ROOT是一样的,但是要注意

    #STATIC文件一般保存在以下位置:

    #1、STATIC_ROOT:在settings里面设置,一般用来放一些公共的js,css,images等。

    #2、app的static文件夹,在每个app所在文夹均可以建立一个static文件夹,然后当运行collectstatic时,
    #    Django会遍历INSTALL_APPS里面所有app的static文件夹,将里面所有的文件复制到STATIC_ROOT。因此,
    #   如果你要建立可复用的app,那么你要将该app所需要的静态文件放在static文件夹中。

    # 也就是说一个项目引用了很多app,那么这个项目所需要的css,images等静态文件是分散在各个app的static文件的,比
    #  较典型的是admin应用。当你要发布时,需要将这些分散的static文件收集到一个地方就是STATIC_ROOT。

    #3、STATIC文件还可以配置STATICFILES_DIRS,指定额外的静态文件存储位置。
    #  STATIC_URL的含义与MEDIA_URL类似。

    # ----------------------------------------------------------------------------
    #注意1:
        #为了后端的更改不会影响前端的引入,避免造成前端大量修改

        STATIC_URL = '/static/'               #引用名
        STATICFILES_DIRS = (
            os.path.join(BASE_DIR,"statics")  #实际名 ,即实际文件夹的名字
        )

        #django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找
        #<script src="/statics/jquery-3.1.1.js"></script>
        #------error-----不能直接用,必须用STATIC_URL = '/static/':
        #<script src="/static/jquery-3.1.1.js"></script>

    #注意2(statics文件夹写在不同的app下,静态文件的调用):

        STATIC_URL = '/static/'

        STATICFILES_DIRS=(
            (os.path.join(BASE_DIR,"app01","statics")) ,
        )

        #html中引用方式一
        #<script src="/static/jquery-3.1.1.js"></script>

    #注意3:
        STATIC_URL = '/static/'
       #html中引用方式二
        {% load staticfiles %}
       # <script src={% static "jquery-3.1.1.js" %}></script>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load staticfiles %}
</head>
<body>
<h1>当前时间:{{time}}</h1>

{#<script src="/static/jquery-3.1.1.js"></script>#}
<script src="{% static 'jquery-3.1.1.js' %}"></script>
<script>
    $("h1").css("color","red")
</script>
</body>
</html>
def showtime(request):

    # return HttpResponse("hello django")
    t = time.ctime()
    #{"time":t}将t变量值渲染为html文件中的{{time}},在后端渲染之后传给前端
    return render(request,"showtime.html",{"time":t})

 

五、Django的URL(路由系统) 

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

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

  

    参数说明:

  • 一个正则表达式字符串
  • 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
  • 可选的要传递给视图函数的默认参数(字典形式)
  • 一个可选的name参数
 1 from django.conf.urls import url,include
 2 from django.contrib import admin
 3 from showtime import views            #showtime是当前应用名
 4 
 5 urlpatterns = [
 6     url(r'^admin/', admin.site.urls),
 7     url(r"^showtime/$",views.showtime),
 8 
 9     #分组,一个()为一个组,做一个参数传递给views.tupletest(y,m)
10     url(r"^tupletest/(\d{4})/(\d{2})",views.tupletest),
11     #分组后,?<变量名>指定,做为views.tupletest(request,year,month)的参数名,必需与指定的一致
12     url(r"^tupletest/(?P<year>\d{4})/(?P<month>\d{2})$",views.tupletest),
13     
14     url(r"^register/",views.register),
15     #name指定别名
16     url(r"^register/",views.register,name="reg"),
17 ]
一个简单的URL配置

  上述URL配置中,name指定了别名后,相对应的视图函数所对应的html文件中:

 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     {% load staticfiles %}    //Template模板
 7 </head>
 8 <body>
 9 
10 <h1>注册信息</h1>
11 <form action="{% url 'reg' %}" method="post">     //按别名指定URL
12 
13     <p>用户名:<input type="text" name = "user"></p>
14     <p>年龄:<input type="text" name = "age"></p>
15     <p>爱好:<input type="checkbox" name = "hobby" value="游泳">游泳
16              <input type="checkbox" name = "hobby" value="听音乐">听音乐
17              <input type="checkbox" name = "hobby" value="跑步">跑步
18     </p>
19     <p><input type="submit"></p>
20 </form>
21 
22 </body>
23 </html>
View Code

  

  URL的分发:

 1 from django.conf.urls import url,include
 2 from django.contrib import admin
 3 from showtime import views
 4 
 5 urlpatterns = [
 6     url(r'^admin/', admin.site.urls),
 7     url(r"^showtime/$",views.showtime),        #分发后这里URL必需用$指定以什么结尾,否则分发后的URL都将访问views.showtime
 8 
 9     url(r"^showtime/",include("showtime.urls")),        #URL分发
10 ]
URL分发
 1 from django.conf.urls import url,include
 2 from showtime import views
 3 
 4 urlpatterns = [
 5     # url(r"^tupletest/(\d{4})/(\d{2})",views.tupletest),
 6     url(r"tupletest/(?P<year>\d{4})/(?P<month>\d{2})$",views.tupletest),
 7 
 8     # url(r"^register/",views.register),
 9     url(r"register/",views.register,name="reg"),
10 ]
11 
12 #注意:访问时,url路径前必需showtime,即相应的应用目录
分发到相应的应用中,而不在全局urls中

六、Django的views(视图函数)

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

      http请求:HttpRequest对象

      http响应:HttpResponse对象

    所在位置:django.http

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

  1、HttpRequest对象的属性和方法:

 1 # path:       请求页面的全路径,不包括域名
 2 #
 3 # method:     请求中使用的HTTP方法的字符串表示。全大写表示。例如
 4 #
 5 #                    if  req.method=="GET":
 6 #
 7 #                              do_something()
 8 #
 9 #                    elseif req.method=="POST":
10 #
11 #                              do_something_else()
12 #
13 # GET:         包含所有HTTP GET参数的类字典对象
14 #
15 # POST:       包含所有HTTP POST参数的类字典对象
16 #
17 #              服务器收到空的POST请求的情况也是可能发生的,也就是说,表单form通过
18 #              HTTP POST方法提交请求,但是表单中可能没有数据,因此不能使用
19 #              if req.POST来判断是否使用了HTTP POST 方法;应该使用  if req.method=="POST"
20 #
21 #
22 #
23 # COOKIES:     包含所有cookies的标准Python字典对象;keys和values都是字符串。
24 #
25 # FILES:      包含所有上传文件的类字典对象;FILES中的每一个Key都是<input type="file" name="" />标签中                     name属性的值,FILES中的每一个value同时也是一个标准的python字典对象,包含下面三个Keys:
26 #
27 #             filename:      上传文件名,用字符串表示
28 #             content_type:   上传文件的Content Type
29 #             content:       上传文件的原始内容
30 #
31 #
32 # user:       是一个django.contrib.auth.models.User对象,代表当前登陆的用户。如果访问用户当前
33 #              没有登陆,user将被初始化为django.contrib.auth.models.AnonymousUser的实例。你
34 #              可以通过user的is_authenticated()方法来辨别用户是否登陆:
35 #              if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
36 #              时该属性才可用
37 #
38 # session:    唯一可读写的属性,代表当前会话的字典对象;自己有激活Django中的session支持时该属性才可用。
39 
40 #方法
41 get_full_path(),   比如:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()得到的结果就是/index33/?name=123
42 req.path:/index33
View Code

  2 HttpResponse对象:

    对于HttpRequest对象来说,是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。

    HttpResponse类在django.http.HttpResponse

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

页面渲染:         render()(推荐)<br>
                       render_to_response(),    #与render()区别是不用写request参数

页面跳转:         redirect("路径")    #达到某条件时跳转到另一个页面,注:参数必须是“路径”,即URL,而非HTML模板
#与render("html模板")区别,redirect会改变url,而render不会改变url,重新刷新不会保留当前页面,

locals():    可以直接将函数中所有的变量传给模板  注:request的属性也都可以传递过去
View Code

七、Template基础

  模板系统介绍

    你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,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)来实现这种模式,这就是本章要具体讨论的问题。

 

  1、模板的组成

    组成:HTML代码+逻辑控制代码

  2、逻辑控制代码的组成

    变量:(使用双大括号来引用变量):

    语法:

{{var_name}}

    Template对象和Context对象

    Template:模板中如{{}}、{%%}

    Context:上下文,如locals()、{"name":user}

#cmd 中如下测试:
>>> 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}))

#推荐使用
return render(request,"index.html",locals())

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

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

 1 from django.shortcuts import render
 2 
 3 # Create your views here.
 4 
 5 class People():
 6     def __init__(self,name,sex):
 7         self.name = name
 8         self.sex = sex
 9 
10     def learn(self):
11         print("学习。。。")
12         return "正在学习!"
13 
14 
15 def templatevar(request):
16 
17     li = ["heilong","xiaolong","xiaohei"]
18 
19     dic = {"name":"heilong","age":20,"gender":""}
20 
21     p1 = People("heilong","")
22 
23     return render(request,"Template_var_deep.html",locals())
views.py
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 
 9 <h2>{{ li.0 }}</h2>         <!--heilong-->
10 <h2>{{ li.1 }}</h2>         <!--xiaolong-->
11 <h2>{{ li.2 }}</h2>         <!--xiaohei-->
12 <hr>
13 <h2>{{ dic.name }}</h2>     <!--heilong-->
14 <h2>{{ dic.age }}</h2>      <!--20-->
15 <h2>{{ dic.gender }}</h2>   <!---->
16 <hr>
17 <h2>{{ p1.name }}</h2>     <!--heilong-->
18 <h2>{{ p1.sex }}</h2>   <!---->
19 <!-- 如果调用方法,则返回函数的返回值。注:函数不能有参数,没什么意义-->
20 <h2>{{ p1.learn }}</h2>   <!--正在学习!-->
21 
22 </body>
23 </html>
Template_var_deep.html

  变量的过滤器(filter)的使用

    {{obj|filter:param}}

# 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 %}     #关闭安全机制  与safe功能类似
  {{ 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 heilong'
View Code

  标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)

    {% if %} 的使用 

    {% if %}标签计算一个变量值,如果是“true”,即它存在、不为空并且不是false的boolean值,系统则会显示{% 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 %}

    {% for %}的使用

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

    {%csrf_token%}:csrf_token标签

    用于生成csrf_token的标签,用于防治跨站攻击验证。注意如果你在view的index里用的是render_to_response方法,不会生效

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

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

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

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

{% with total=fhjsaldfhjsdfhlasdfhljsdal %}
{{ total }}    #注:只能在with与endwith之间用
{% endwith %}

    {% verbatim %}: 禁止render

{% verbatim %}

         {{ hello }}    #这里不会被Template渲染,只返回{{ hello }}内容

{% endverbatim %}

    {% load %}: 加载标签库

    自定义filter和simple_tag

      a、在app中创建templatetags模块(必须的)

      b、创建任意 .py 文件,如:my_tags.py

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)

      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 %}

      e、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.

      注:filter与simple_tag区别

        filter可以用在if等语句后,simple_tag不可以

        filter只能传一个参数,而simple_tag不限

        在simple_tag中也可以引用变量,只是不需要加{{}}

   extend模板继承

    为了解决网站中,公共页面的代码重复与冗余

    extend(继承)模板标签

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

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

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

      step1:定义基础模板

         父模板中添加模板标签

{% block 名称 %}
{% endblock %}

      step2:修改子模板

{% extends "基础模板" %}        #必须写在首行


{% block 名称 %}        #名称必须与父模板中一致,多个的话不能重复

    要修改的内容

{% endblock %}

      注意:

        子模板中{% extends %}  必须写在第一行,否则模板继承不起作用

        一般来说,{% block %}  越多越好,这样比较灵活

        不允许在同一个模板中定义多个同名的{% block %}

     具体来看一个例子:

#urls.py

from django.conf.urls import url
from django.contrib import admin
from manager_system import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r"^index/",views.index),
    url(r"^student/",views.student),
]

 

#views.py

from django.shortcuts import render

# Create your views here.

def index(request):

    return render(request,"index.html")

def student(request):

    li = ["heilong","xiaolong","xiaohei"]

    return render(request,"student.html",locals())
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    {% block styles %}

    {% endblock %}

    <style>
        *{
            margin: 0;
            padding: 0;
        }
        .title{
            width: 100%;
            height: 40px;
            background-color: #7259ff;
            line-height: 40px;
            text-align: center;
            color: white;
            font-size: 20px;
        }
        .left{
            width: 20%;
            min-height: 600px;
            background-color: darkgray;
        }
        .manager{
            text-align: center;
            padding: 20px 0;
            font-size: 25px;
        }
        .content{
            width: 80%;
            min-height: 600px;
            text-align: center;
        }
        .left,.content{
            float: left;
        }
    </style>

</head>
<body>

<div class="outer">

    <div class="title">标题</div>
    <div class="left">
        <div class="student manager"><a href="/student">学生管理</a></div>
        <div class="teacher manager"><a href="">老师管理</a></div>
        <div class="course manager"><a href="">课程管理</a></div>
        <div class="classes manager"><a href="">班级管理</a></div>
    </div>
    <div class="content">

        {% block content %}
            <h1>欢迎登录管理系统</h1>
        {% endblock %}

        {% include "thanks.html" %}
    </div>

</div>

</body>
</html>
index.html
{% extends "index.html" %}

{%  block styles %}
    <style>
        .stu_mes{
            color: red;
        }
    </style>
{% endblock %}


{% block content %}
    {{ block.super }}
    {% for item in li %}
        <h1 class="stu_mes">{{ forloop.counter }} : {{ item }}</h1>
    {% endfor %}
{% endblock %}
student.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    <h2>感谢使用</h2>

</body>
</html>
thanks.html

    上述例子中还用到一个{% include "thanks.html" %}

    即将一个现有的模板添加到另一个模板中

八、Models(数据库模型)

  1、数据库的配置

    django默认支持sqlite,mysql, oracle,postgresql数据库。

         <1> sqlite

              django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3

         <2> mysql

              引擎名称:django.db.backends.mysql

    mysql驱动程序

      MySQLdb(mysql python)

      mysqlclient

      MySQL

      PyMySQL(纯python的mysql驱动程序)

    在django的项目中会默认使用sqlite数据库,在settings里有如下设置

# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',               #数据库引擎
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),       #数据库名
    }
}

    如果我们想要更改数据库,需要修改如下

# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',         #数据库引擎
        'NAME': 'heilong',                                       #你的数据库名称
        'USER': 'root',                                            #你的数据库用户名
        'PASSWORD': '123456',                              #你的数据库密码
        'HOST': '192.168.189.130',                         #你的数据库主机,留空默认为localhost
        'PORT': '3306',                                           #你的数据库端口
    }
}
#注意
NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建

USER和PASSWORD分别是数据库的用户名和密码。

设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。

然后,启动项目,会报错:no module named MySQLdb

这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL

所以,我们只需要找到项目名文件下的__init__,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()

  2、ORM对象关系映射

    用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。

    ORM: 一个类对应一个表,类属性对应字段;一个实例对象对应一条记录

 

    表与表之间的关系(两张表):

      一对一:利用外键约束并且创建唯一约束(unique)

      一对多:利用外键约束实现

      多对多:另外创建一个表用来存放两个表之间的关系(还是利用外键约束),本质就是两个一对多关系

 

    优点: 1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发。

        2 可以避免一些新手程序猿写sql语句带来的性能问题。

    缺点:1  性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。

       2  对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。

                    3  通过QuerySet的query属性查询对应操作的sql语句

author_obj=models.Author.objects.filter(id=2)
print(author_obj.query)

    Django操作数据库步骤:

      1、数据库配置:修改DATABASES={...}

      2、修改项目文件下的__init__.py

import pymysql
pymysql.install_as_MySQLdb()

      3、models.py中创建类(表)

#

from django.db import models

# Create your models here.


class book(models.Model):
    name=models.CharField(max_length=20)
    price=models.IntegerField()
    pub_date=models.DateField()

      4、根据类生成迁移文件,命令如下:

#Terminal终端中
python manager.py makemigrations

      5、将迁移文件写入到数据库,转换为对应的表,命令如下:

#Terminal终端中
python manager.py migrate

    

    

    分析代码:

         <1>  每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。

         <2>  每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。

         <3>  模型之间的三种关系:一对一,一对多,多对多。

               一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;

               一对多:就是主外键关系;(foreign key)

               多对多:(ManyToManyField) 自动创建第三张表(当然我们也可以自己创建第三张表:两个foreign key)

    <4>  模型常用的字段类型参数

<1> CharField
        #字符串字段, 用于较短的字符串.
        #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.

<2> IntegerField
       #用于保存一个整数.

<3> FloatField
        # 一个浮点数. 必须 提供两个参数:
        #
        # 参数    描述
        # max_digits    总位数(不包括小数点和符号)
        # decimal_places    小数位数
                # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
                #
                # models.FloatField(..., max_digits=5, decimal_places=2)
                # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
                #
                # models.FloatField(..., max_digits=19, decimal_places=10)
                # admin 用一个文本框(<input type="text">)表示该字段保存的数据.

<4> AutoField
        # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 
        # 自定义一个主键:my_id=models.AutoField(primary_key=True)
        # 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.

<5> BooleanField
        # A true/false field. admin 用 checkbox 来表示此类字段.

<6> TextField
        # 一个容量很大的文本字段.
        # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).

<7> EmailField
        # 一个带有检查Email合法性的 CharField,不接受 maxlength 参数.

<8> DateField
        # 一个日期字段. 共有下列额外的可选参数:
        # Argument    描述
        # auto_now    当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳.
        # auto_now_add    当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.
        #(仅仅在admin中有意义...)

<9> DateTimeField
        #  一个日期时间字段. 类似 DateField 支持同样的附加选项.

<10> ImageField
        # 类似 FileField, 不过要校验上传对象是否是一个合法图片.#它有两个可选参数:height_field和width_field,
        # 如果提供这两个参数,则图片将按提供的高度和宽度规格保存.     
<11> FileField
     # 一个文件上传字段.
     #要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, 
     #该格式将被上载文件的 date/time 
     #替换(so that uploaded files don't fill up the given directory).
     # admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) .

     #注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
            #(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. 
            # (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 
            #  WEB服务器用户帐号是可写的.
            #(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
            # 使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 
            # 出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField 
            # 叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径.

<12> URLField
      # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且
      # 没有返回404响应).
      # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)

<13> NullBooleanField
       # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
       # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.

<14> SlugField
       # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs
       # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50.  #在
       # 以前的 Django 版本,没有任何办法改变50 这个长度.
       # 这暗示了 db_index=True.
       # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate 
       # the slug, via JavaScript,in the object's admin form: models.SlugField
       # (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.

<15> XMLField
        #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径.

<16> FilePathField
        # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
        # 参数    描述
        # path    必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. 
        # Example: "/home/images".
        # match    可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名.  
        # 注意这个正则表达式只会应用到 base filename 而不是
        # 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
        # recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.
        # 这三个参数可以同时使用.
        # match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
        # FilePathField(path="/home/images", match="foo.*", recursive=True)
        # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<17> IPAddressField
        # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
<18># CommaSeparatedIntegerField
        # 用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.
字段类型

    <5>  Field重要参数

<1> null : 数据库中字段是否可以为空

    <2> blank: django的 Admin 中添加数据时是否可允许空值

    <3> default:设定缺省值

    <4> editable:如果为假,admin模式下将不能改写。缺省为真

    <5> primary_key:设置主键,如果没有设置django创建表时会自动加上:
        id = meta.AutoField('ID', primary_key=True)
        primary_key=True implies blank=False, null=False and unique=True. Only one
        primary key is allowed on an object.

    <6> unique:数据唯一

    <7> verbose_name  Admin中字段的显示名称

    <8> validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误


    <9> db_column,db_index 如果为真将为此字段创建索引

    <10>choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。
                如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),)
                gender = models.CharField(max_length=2,choices = SEX_CHOICES)
约束

  

  3、表的操作(增删改查)

    表记录的添加:

def add(request):
    #方式一:
    b = book(name = "python",price=99,pub_date="1919-12-1")
    b.save()
    b = book(name="linux", price=88, pub_date="2000-12-1")
    b.save()
    b = book(name="django", price=22, pub_date="2222-1-1")
    b.save()

    # 方式二:
    book.objects.create(name="php", price=44, pub_date="1111-1-1")
    return HttpResponse("添加成功")

    表记录的修改:

def modify(request):

    # 方式一:推荐使用
    book.objects.filter(name="php").update(price=444)

    # 方式二:
    b = book.objects.get(name="python")
    #book.objects.get(name="python")是一个model对象,只能取出一条记录,多条则报错
    b.price = 1212
    b.save()

    # 方式三:
    b = book.objects.filter(name="python")[0]
    #book.objects.filter(name="python")是一个QuerySet类型,是一个集合
    b.price = 1213
    b.save()

    return HttpResponse("修改成功")

    表记录的删除:

def delete(request):

    book.objects.filter(name="php").delete()

    return HttpResponse("删除成功")

 

 

    

 

 

posted @ 2017-02-27 21:36 黑龙orz 阅读(...) 评论(...)  编辑 收藏