Django详解

本文参考 http://www.cnblogs.com/yuanchenqi/articles/6083427.html,在原文基础上略有改动。

  1. 什么是框架?

    1. 框架定义:

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

      最简单的web应用就是先把html用文件保存好,用一个现成的http服务器软件,接收用户请求,从文件中读取html,然后返回。
      如果要动态生成html,就需要把上面的步骤自己实现。不过,接收http请求、解析http请求、发送http响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态html,就得花个把月去读http规范。
      正确的做法是底层代码由专门的服务器软件实现,我们用python专注于生成html文档。因为我们不希望接触到TCP连接、http原始请求和响应格式,所以,需要一个统一个接口,让我们能专心用python编写web业务。
      这个接口就是:WSGI:Web Server Gateway Interface。
    3. 实例(自己搭一个框架)

      1. 实例1(简易服务):
        from wsgiref.simple_server import make_server
        
        
        def application(environ, start_response):
            print('environ: ', environ) # 打印看environ是个什么东西
            print('path_info:', environ['PATH_INFO']) # 打印看environ['PATH_INFO']是个什么东西
        
            start_response('200 OK', [('Content-Type', 'text/html')])
            return [b'<h1>Hello, world!</h1>']
        
        
        httpd = make_server('', 8000, application)
        
        print('Serving HTTP on port 8000...')
        
        httpd.serve_forever()
        启动服务后,用浏览器地址栏输入:"127.0.0.1:8000"后按回车,则会显示hello,world!
        
        控制台输出:
        
        
        整个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。
      2. 实例2(不同的请求返回不同的结果):
         

        from wsgiref.simple_server import make_server
        
        
        def f1(request):
            return [b'<h1>Hello, web!</h1>']
        
        
        def f2(request):
            return [b'<h1>Hello, book!</h1>']
        
        
        def application(environ, start_response):
            print('environ: ', environ)
            print('path_info:', environ['PATH_INFO'])
        
            start_response('200 OK', [('Content-Type', 'text/html')])
            # 获取路径信息
            path = environ['PATH_INFO']
            # 如果路径为'/book'就执行f2,如果为'/web'就执行f1,否则返回404
            if path == '/book':
                return f2(environ)
            elif path == '/web':
                return f1(environ)
            else:
                return [b'<h1>404!</h1>']
        
        
        httpd = make_server('', 8000, application)
        
        print('Serving HTTP on port 8000...')
        
        httpd.serve_forever()

        启动服务后,在浏览器地址栏中输入:http://127.0.0.1:8000/,返回404

        地址栏输入:http://127.0.0.1:8000/book

      3. 实例3(路由):
         

        from wsgiref.simple_server import make_server
        import time
        
        def f1(request):
            return [b'<h1>Hello, web!</h1>']
        
        
        def f2(request):
            return [b'<h1>Hello, book!</h1>']
        
        
        def f3(request):
            return [b'<h1>this is index page!</h1>']
        
        
        def login(request):
        
            return [b'<h1>login!</h1>']
        
        
        # 加入url路由, 以后有新的请求,在这里添加一个url元组即可。
        def routers():
            urlpatterns = (
                ('/web', f1),
                ('/book', f2),
                ('/index', f3),
                ('/login', login),
            )
            return urlpatterns
        
        
        def application(environ, start_response):
            print('environ: ', environ)
            print('path_info:', environ['PATH_INFO'])
        
            start_response('200 OK', [('Content-Type', 'text/html')])
            path = environ['PATH_INFO']
            
            # 遍历路由,匹配的话,则执行相应函数
            func = None
            for item in routers():
                if item[0] == path:
                    func = item[1]
                    break
            # 如果没有找到func,则说明没有匹配的url,返回404。
            if func:
                return func(environ)
            else:
                return [b'<h1>404!</h1>']
        
            # if path == '/book':
            #     return f2(environ)
            # elif path == '/web':
            #     return f1(environ)
            # else:
            #     return [b'<h1>404!</h1>']
        
        
        httpd = make_server('', 8000, application)
        
        print('Serving HTTP on port 8000...')
        
        httpd.serve_forever()

        在浏览器地址栏中输入:http://127.0.0.1:8000/login

        成功返回。

      4. 实例4(服务器返回动态结果)

        在这里实例中,浏览器中输入127.0.0.1:8000/time,服务端将从模板html文件中读取数据,然后将数据中的$current_time$替换为当前时间,这样就实现了向客户端返回动态数据。
        1. 模板 current_time.html
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>当前时间</title>
          </head>
          <body>
              <h2>
                  当前时间: $current_time$
              </h2>
          </body>
          </html>

           

        2. 服务端
          from wsgiref.simple_server import make_server
          import time
          
          def f1(request):
              return [b'<h1>Hello, web!</h1>']
          
          
          def f2(request):
              return [b'<h1>Hello, book!</h1>']
          
          
          def f3(request):
              return [b'<h1>this is index page!</h1>']
          
          
          def login(request):
          
              return [b'<h1>login!</h1>']
          
          
          def print_time(request):
              """
              读取模板文件,并将当前时间替换模板文件中的$current_time$
              """
              f = open('current_time.html', 'rb')
              data = f.read()
              data = str(data, 'utf8').replace('$current_time$', time.ctime())
              data = bytes(data, 'utf8')
              f.close()
              return [data]
          
          
          def routers():
              urlpatterns = (
                  ('/web', f1),
                  ('/book', f2),
                  ('/index', f3),
                  ('/login', login),
                  ('/time', print_time),
              )
              return urlpatterns
          
          
          def application(environ, start_response):
              print('environ: ', environ)
              print('path_info:', environ['PATH_INFO'])
          
              start_response('200 OK', [('Content-Type', 'text/html')])
              path = environ['PATH_INFO']
          
              func = None
              for item in routers():
                  if item[0] == path:
                      func = item[1]
                      break
              if func:
                  return func(environ)
              else:
                  return [b'<h1>404!</h1>']
          
          
          httpd = make_server('', 8000, application)
          
          print('Serving HTTP on port 8000...')
          
          httpd.serve_forever()
          
          在浏览器中输入:127.0.0.1:8000/time,可以看到服务器将向客户浏览器返回当前时间。

          不知不觉中,我们已经写了一个简单的web框架。
  2. 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。

  3. Django的流程和命令行工具

    1. Django实现流程
      1. 安装
        pip install django

         

      2. 创建项目
        django-admin startproject mysite
        
        目录结构如下:
        
               ---mysite
        
                  ---settings.py
                  ---url.py
                  ---wsgi.py
        
               ---- manage.py(启动文件) 

         

      3. 创建app
        python mannage.py startapp  app01

         

      4. 配置项目settings.py
        TEMPLATES
        
               STATICFILES_DIRS=(
                    os.path.join(BASE_DIR,"statics"),
                )
        
               STATIC_URL = '/static/' 
               #  我们只能用 STATIC_URL,但STATIC_URL会按着你的STATICFILES_DIRS去找#4  根据需求设计代码
                   url.py
                   view.py

         

      5. 使用模板 views
        render(req,"index.html") 

         

      6. 编写model

         
      7. 迁移数据库
        makemigrations [app]
        
        migrate [app]

         

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

         

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

         
        1. manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
        2. settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
        3. urls.py ----- 负责把URL模式映射到应用程序。
      2. 创建app应用
        python manage.py startapp blog
      3. 启动django项目
        python manage.py runserver 127.0.0.1 8080

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

               

      4. 迁移数据库
        1. 生成同步数据库脚本
          python manage.py makemigrations
        2. 同步数据库
          python manage.py migrate

          注意:在开发过程中,数据库同步误操作之后,难免会遇到后面不能同步成功的情况,解决这个问题的一个简单粗暴方法是把migrations目录下的脚本(除__init__.py之外)全部删掉,再把数据库删掉之后创建一个新的数据库,数据库同步操作再重新做一遍。    

      5. 创建超级管理员
        python manage.py createsuperuser
      6. 清空数据库
        python manage.py  flush
      7. 查询某个命令的详细信息
        django-admin.py  help  startapp
      8. 启动交互界面
        python manage.py  shell
      9. 查看详细列表
        python manage.py
    3. 实例
      1. 实例1(提交数据并展示)
         
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
        <h1>创建个人信息</h1>
        
        <form action="/userInfor/" 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>
        
        <h1>信息展示</h1>
        
        <table border="1">
        
            <tr>
                <td>姓名</td>
                <td>性别</td>
                <td>邮箱</td>
            </tr>
            {% for i in info_list %}
        
                <tr>
                    <td>{{ i.username }}</td>
                    <td>{{ i.sex }}</td>
                    <td>{{ i.email }}</td>
                </tr>
        
            {% endfor %}
        
        </table>
        
        </body>
        </html>
        
        
        -----------------------url.py---------------------------------------
        url(r'^userInfor/', views.userInfor)
        
        -----------------------views.py--------------------------------------
        
        info_list=[]
        
        def userInfor(req):
        
            if req.method=="POST":
                username=req.POST.get("username",None)
                sex=req.POST.get("sex",None)
                email=req.POST.get("email",None)
        
                info={"username":username,"sex":sex,"email":email}
                info_list.append(info)
        
            return render(req,"userInfor.html",{"info_list":info_list})

         

      2. 实例2(数据库版)
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>Title</title>
        </head>
        <body>
        <h1>创建个人信息</h1>
        
        <form action="/userInfor/" 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>
        
        <h1>信息展示</h1>
        
        <table border="1">
        
            <tr>
                <td>姓名</td>
                <td>性别</td>
                <td>邮箱</td>
            </tr>
            {% for i in info_list %}
        
                <tr>
                    <td>{{ i.username }}</td>
                    <td>{{ i.sex }}</td>
                    <td>{{ i.email }}</td>
                </tr>
        
            {% endfor %}
        
        </table>
        
        </body>
        </html>
        
        
        ----------------------------------------------models.py
        from django.db import models
        
        # Create your models here.
        
        
        class UserInfor(models.Model):
        
            username=models.CharField(max_length=64)
            sex=models.CharField(max_length=64)
            email=models.CharField(max_length=64)
        
        ----------------------------------------------views.py
        
        from django.shortcuts import render
        
        from app01 import models
        # Create your views here.
        
        
        def userInfor(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.UserInfor.objects.create(
                    username=u,
                    sex=s,
                    email=e
                )
        
                info_list=models.UserInfor.objects.all()
        
                return render(req,"userInfor.html",{"info_list":info_list})
        
            return render(req,"userInfor.html")
  4. Django的配置文件(settings)

    1. 概述
      静态文件交由web服务器处理,Django本身不处理静态文件。简单的处理逻辑如下(nginx):
      URI请求 ---》 按照web服务器里面的配置规则先处理,以nginx为例,主要配置在nginx.conf里的location
      ---》如果是静态文件,则交由nginx直接处理
      ---》如果不是,则交由django处理,django根据urls.py里的规则进行匹配

      为了方便开发,Django提供了在开发环境中对静态文件的处理机制,方法如下:
      1. 在INSTALLED_APPS里面加入django.contrib.staticfiles'

      2. 在urls.py里加入
         

        if settings.DEBUG:  
                   urlpatterns += patterns('', url(r'^media/(?P<path>.*)$', 
                   'django.views.static.serve', {'document_root': settings.MEDIA_ROOT }),   
                    url(r'^static/(?P<path>.*)$',
                  'django.views.static.serve',{'document_root':settings.STATIC_ROOT}), )  

         

    2. MEDIA_ROOT和MEDIA_URL
      静态文件的处理又包括STATIC和MEDIA两类,Django中是这样定义的:
      MEDIA:指用户上传的文件,比如在Model里面的FileFIeld,ImageField上传的文件。如果你定义MEDIA_ROOT=c:\temp\media,那么File=models.FileField(upload_to="abc/")
      上传的文件就会被保存到c:\temp\media\abc
      
      
      class blog(models.Model):  
          Title=models.charField(max_length=64)  
          Photo=models.ImageField(upload_to="photo") 
      
      # 上传的图片就上传到c:\temp\media\photo,而在模板中要显示该文件
      # 在settings里面设置的MEDIA_ROOT必须是本地路径的绝对路径,一般是这样写:
          BASE_DIR= os.path.abspath(os.path.dirname(__file__))  
          MEDIA_ROOT=os.path.join(BASE_DIR,'media/').replace('\\','/') 
      MEDIA_URL是指从浏览器访问时的地址前缀,举个例子:
      MEDIA_ROOT=c:\temp\media\photo
      MEDIA_URL="/data/"
      在开发阶段,media的处理由django处理:
      访问http://localhost/data/abc/a.png 就是访问
      c:\temp\media\photo\abc\a.png
      在模板里这样写:<img src='{{MEDIA_URL}}abc/a.png'>
      在部署阶段最大的不同在于你必须让服务器来处理media文件,因为你必须在web服务器中配置,才能让web服务器访问media文件
      以nginx为例,可以在nginx.conf里面这样写:
      location ~/media/{
          root/temp/
          break;
      }
      
    3. 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. STATICFILES_DIRS
        STATIC文件还可以配置STATICFILES_DIRS,指定额外的静态文件存储位置。STATIC_URL的含义与MEDIA_URL类似
      4. 注意:
        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=(
              ('hello',os.path.join(BASE_DIR,"app01","statics")) ,
          )
          
          <script src="/static/hello/jquery-1.8.2.min.js"></script>

           

        3. load staticfiles
          STATIC_URL = '/static/'
          {% load staticfiles %}
          # <script src={% static "jquery-1.8.2.min.js" %}></script>

           

    4. 其它重要参数
      APPEND_SLASH
             Default: True
             When set to True, if the request URL does not match any of the patterns in the URLconf and it 
             doesn’t end in a slash, an HTTP redirect is issued to the same URL with a slash appended. Note 
             that the redirect may cause any data submitted in a POST request to be lost.
      APPEND_SLASH
      默认:True
      当设置为true时,如果请求URL与urlconf及其中的任何模式都不匹配不以斜杠结尾,向同一个URL发出HTTP重定向,并附加斜杠。注意:重定向可能导致在POST请求中提交的任何数据丢失。
      
  5. Django URL(路由系统)

    URL配置(URLconf)就像Django所支持网站的目录。它本质是URL模式以及要为该URL模式以及要为该URL模式调用的视图函数之间的映射表;
    urlpatterns = [
        url(正则表达式, views视图函数,参数,别名),
    ]
    
    1. URLconf例子
      from django.conf.urls import url
      from django.contrib import admin
      
      from app01 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})/$', 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]+)/$', views.article_detail),
      
      ]
      
      #1   There’s no need to add a leading slash, because every URL has that. For
      #    example, it’s ^articles, not ^/articles.
      #1   不需要添加前导/,因为每个url都有前导/。举例:^articles是正确的,^/articles是不正确的。
      
      #2   A request to /articles/2005/03/ would match the third entry in the list.
      #    Django would call the function views.month_archive(request, '2005', '03').
      #2   /articles/2005/03/将匹配列表中的第4项,Django将调用views.month_archive(request, '2005', '03')。
      
      #3   /articles/2005/3/ would not match any URL patterns
      #3   /articles/2005/3/ 不能匹配列表中的任何url。
      
      #4   /articles/2003/ would match the first pattern in the list, not the second one
      #4   /articles/2003/ 将匹配列表中的第一项,而不是第二项。
      
      #5   /articles/2003/03/03/ would match the final pattern. Django would call the
      #    functionviews.article_detail(request, '2003', '03', '03').
      #5   /articles/2003/03/03/ 将匹配最后一项,Django将调用views.article_detail(request, '2003', '03', '03')。

       

    2. 命名组(Named groups)

      The above example used simple, non-named regular-expression groups (via parenthesis) to capture bits of the URL and pass them as positional arguments to a view. In more advanced usage, it’s possible to use named regular-expression groups to capture URL bits and pass them as keyword arguments to a view.
      上面的示例使用简单的非命名正则表达式组(通过括号)捕获URL的位,并将其作为位置参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组捕获URL位,并将其作为关键字参数传递给视图。

             In Python regular expressions, the syntax for named regular-expression groups is (?P<name>pattern), where name is the name of the group and pattern is some pattern to match.
      在Python正则表达式中,命名正则表达式组的语法是(?p<name>pattern),其中name是组的名称,而pattern是一些要匹配的模式。

      Here’s the above example URLconf, rewritten to use named groups:
      下面的例子展示了使用命名组来重写上面的例子:
       

      # 准备
      import re
      
      ret = re.search('(?P<id>\d{3})/(?P<name>\w{3})','weeew34ttt123/ooo')
      
      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),

       This accomplishes exactly the same thing as the previous example, with one subtle difference: The captured values are passed to view functions as keyword arguments rather than positional arguments.
      这和前面的例子的实现完全相同,但有一个细微的区别:捕获的值将作为关键字参数,而不是位置参数传递给视图函数。

    3. 传递额外选项给视图函数(Passing extra options to view functions)
      URLconfs have a hook that lets you pass extra arguments to your view functions, as a Python dictionary.
      URL配置有一个钩子,它使你能传递额外的参数作为Python字典传递到你的视图函数。
      The django.conf.urls.url() function can take an optional third argument which should be a dictionary of extra keyword arguments to pass to the view function.
      django.conf.urls.url()函数可以接收可选的第三个参数,该参数是传递给视图函数的额外关键字参数的字典。
      例子:
      from django.conf.urls import url
      from . import views
        
      urlpatterns = [
          url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
      ]
      

      In this example, for a request to /blog/2005/, Django will call views.year_archive(request, year='2005',foo='bar').
      This technique is used in the syndication framework to pass metadata and options to views.
      Dealing with conflicts

             It’s possible to have a URL pattern which captures named keyword arguments, and also passes arguments with the same names in its dictionary of extra arguments. When this happens, the arguments in the dictionary will be used instead of the arguments captured in the URL.


      在这个例子中,一个请求 /blog/2005/,Django将调用views.year_archive(request, year='2005',foo='bar').
      这个技术在联合框架中被用于将元数据和选项传递给视图。
      冲突处理:
      有可能有一个URL模式,它捕获已命名的关键字参数,并在其额外参数字典中传递具有相同名称的参数。发生这种情况时,将使用字典中的参数,而不是URL中捕获的参数。
      字典中的参数优先于捕获的参数。

    4. name param
      urlpatterns = [
          url(r'^index',views.index,name='bieming'),
          url(r'^admin/', admin.site.urls),
          # 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),
      
      ]
      ###################
      
      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 'bieming' %}" method="post">
               用户名:<input type="text" name="username">
               密码:<input type="password" name="password">
               <input type="submit" value="submit">
           </form>
      </body>
      </html>
      
      
      #######################

       

    5. 包含URL配置(Including other URLconfs)
      from django.conf.urls import include, url
      
      urlpatterns = [
         url(r'^admin/', admin.site.urls),
         url(r'^blog/', include('blog.urls')),
      ]

       
  6. Django Views(视图函数)


    http请求中两个核心对象:
    http请求:HttpRequest对象
    http响应:HttpResponse对象
    所在位置:django.http
    之前我们用到的参数request就是HttpRequest, 检测方法:isinstance(request, HttpRequest)
     
    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
      注意一个常用方法:request.POST.getlist('')

       

    2. HttpResponse对象

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

        HttpResponse类在django.http.HttpResponse

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

      页面渲染:         render()(推荐)
                       render_to_response(),
      页面跳转:         redirect("路径")
      locals():    可以直接将函数中所有的变量传给模板
      -----------------------------------url.py
      
       url(r"login",   views.login),
       url(r"yuan_back",   views.yuan_back),
      
      -----------------------------------views.py
      def login(req):
          if req.method=="POST":
              if 1:
                  # return redirect("/yuan_back/")
                  name="yuanhao"
      
                  return render(req,"my backend.html",locals())
      
          return render(req,"login.html",locals())
      
      
      def yuan_back(req):
      
          name="苑昊"
      
          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的区别:
      #   1 if render的页面需要模板语言渲染,需要的将数据库的数据加载到html,那么所有的这一部分
      #     除了写在yuan_back的视图函数中,必须还要写在login中,代码重复,没有解耦.
      
      #   2 the most important: url没有跳转到/yuan_back/,而是还在/login/,所以当刷新后
      #     又得重新登录.

       

  7. Template

    1. 模板系统介绍
      def current_datetime(request):
          now = datetime.datetime.now()
          html = "<html><body>It is now %s.</body></html>" % now
          return HttpResponse(html)

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

      1. 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。

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

      3. 程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
        基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。

    2. 模板的组成
      组成:HTML代码+逻辑控制代码
    3. 逻辑控制代码的组成
      1. 变量
        1. 语法格式:
          {{ var_name }}
        2. Template和Context对象
          >>> 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}))
          Django模板解析非常快捷。大部分的解析工作都是在后台通过对简短正则表达式一次性调用来完成。xml的模板引擎形成鲜明对比,那些引擎承担了xml解析器的开销,且往往Django模板渲染引擎要慢上几个数量级。
        3. 深度变量查找(.)
          #最好是用几个例子来说明一下。
          # 首先,句点可用于访问列表索引,例如:
          
          >>> 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. 变量的过滤器(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 %}
            {{ 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'
           

           

      2. 标签
        {% tag %}
        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 %}

           

        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

           

        3. csrf_token

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

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

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

           

        5. with
          用更简单的变量名替代复杂的变量名
          {% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
        6. verbatim
          禁止render
          {% verbatim %}
                   {{ hello }}
          {% endverbatim %}

           

        7. load
          加载便签库
           
      3. 自定义filter和simple_tag
        1. 在app中创建templatetags模块(必须的)
        2. 创建任意.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)

           

        3. 在使用自定义simple_tag和filter的html文件中导入之前创建的my_tags.py
          {% load my_tags%}
        4. 使用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 %}

           

        5. 在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag
          注意:filter可以用在if等语句后,simple_tag不可以
          {% if num|filter_multi:30 > 100 %}
              {{ num|filter_multi:30 }}
          {% endif %}

           

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

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

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

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

  8. Models

    1. 数据库配置
    2. ORM对象关系映射
  9. admin的配置
    以前学习时候的笔记,没有写完,忘记发了,现在发一下吧。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值