• web框架之Django


    web开发介绍

    互联网刚兴起的那段时间大家都热衷于CS架构,也就是Client/Server模式。每个用户的电脑上安装一个Client,就像QQ这种终端软件。
    随着互联网的发展,开发客户端显得越来越‘重’,BS架构(Browser/Server模式)越来越流行,也就是用户只需要一个浏览器就足够了。

    前端性能优化的方式
    1. 减少DOM操作
    2. 部署前,图片压缩,代码压缩
    3. 优化JS代码结构,减少冗余代码
    4. 减少HTTP请求,合理设置 HTTP缓存
    5. 使用内容分发CDN加速
    6. 静态资源缓存
    7. 图片延迟加载

    一个页面从输入URL到页面加载显示完成,这个过程中都发生了什么?

    输入地址之后:

    1. 浏览器查找域名的IP地址
    2. 这一步包括DNS具体的查找过程,包括:浏览器缓存->系统缓存->路由器缓存…
    3. 浏览器向Web服务器发送一个HTTP请求
    4. 服务器的永久重定向响应(从 http://example.comhttp://www.example.com)
    5. 浏览器跟踪重定向地址
    6. 服务器处理请求
    7. 服务器返回一个HTTP响应
    8. 浏览器显示HTML
    9. 浏览器发送请求获取嵌入在HTML 中的资源(如图片、音频、视频、CSS、JS等等)
    10. 浏览器发送异步请求

    Web框架

    我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。
    这样我们就可以自己实现Web框架了。

      1 import socket
      2 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      3 sock.bind(('127.0.0.1', 8000))
      4 sock.listen(5)
      5 while True:
      6     conn, addr = sock.accept()
      7     data = conn.recv(8096)
      8     conn.send(b"OK")
      9     conn.close()

    可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。
    用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定?
    你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?
    所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。

    这个规则就是HTTP协议,以后你发送请求信息也好,回复响应信息也罢,都要按照这个规则来。

    那HTTP协议是怎么规定消息格式的呢?

    让我们首先看下我们在服务端接收到的消息是什么。

    然后再看下我们浏览器收到的响应信息是什么。
    响应头在浏览器的network窗口可以看到,我们看到的HTML页面内容就是响应体。本质上还是字符串,因为浏览器认识HTML,所以才会渲染出页面。

    HTTP协议介绍

    每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。
    HTTP响应的Header中有一个Content-Type表明响应的内容格式。如text/html表示HTML网页。

      1 // 报头信息
      2 HTTP/1.1 200 OK
    
      3 Header1:v1
    
      4 Header2:v2
    
      5 
    
     ----------> 与内容之间隔着两个换行符
      6 // --------------------------------- 内容
      7 Body...

    让我们的Web框架在给客户端回复响应的时候按照HTTP协议的规则加上响应头,这样我们就实现了一个正经的Web框架了。

      1 import socket
      2 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
      3 sock.bind(('127.0.0.1', 8000))
      4 sock.listen(5)
      5 while True:
      6     conn, addr = sock.accept()
      7     data = conn.recv(8096)
      8     conn.send(b"HTTP/1.1 200 OK
    
    ")
      9     conn.send(b"OK")
     10     conn.close()

    这样就结束了吗?
    如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?

    小事一桩,我们可以从请求头里面拿到请求的URL,然后做一个判断。

      1 import socket
      2 sk = socket.socket()
      3 sk.bind(("127.0.0.1", 8080))
      4 sk.listen(5)
      5 while True:
      6     conn, addr = sk.accept()
      7     data = conn.recv(8096)
      8     data_str = str(data, encoding="utf-8")  # 把收到的字节数据转换成字符串类型
      9     header = data_str.split("
    
    ")  # 将请求的数据按照
    
    分割
     10     header_list = header[0].split("
    ")  # 按
    分割header
     11     temp = header_list[0].split("")  # 按空格分割第一行
     12     url = temp[1]  # 取到请求的URL
     13     conn.send(b"HTTP/1.1 200 OK
    
    ")  # 发送响应头
     14     if url == "/index/":
     15         conn.send(b"this is index page.")
     16     else:
     17         conn.send(b"404 not found.")
     18     conn.close()

    解决了不同URL返回不同内容的需求。
    但是问题又来了,如果有很多很多页面怎么办?难道要挨个判断?
    当然不用,我们有更聪明的办法。

      1 import socket
      2 def index():
      3     return b"this is index page."
      4 def login():
      5     return b"this is login page."
      6 url_func_map = [
      7     ("/index/", index),
      8     ("/login/", login)
      9 ]
     10 sk = socket.socket()
     11 sk.bind(("127.0.0.1", 8080))
     12 sk.listen(5)
     13 while True:
     14     conn, addr = sk.accept()
     15     data = conn.recv(8096)
     16     data_str = str(data, encoding="utf-8")  # 把收到的字节数据转换成字符串类型
     17     header = data_str.split("
    
    ")  # 将请求的数据按照
    
    分割
     18     header_list = header[0].split("
    ")  # 按
    分割header
     19     temp = header_list[0].split("")  # 按空格分割第一行
     20     url = temp[1]  # 取到请求的URL
     21     conn.send(b"HTTP/1.1 200 OK
    content-type:text/html; charset=utf-8
    
    ")  # 发送响应头
     22     func_name = None  # 将要执行的函数
     23     for i in url_func_map:
     24         if i[0] == url:
     25             func_name = i[1]
     26             break  # 跳出循环
     27     if func_name:  # 如果找到了要执行的函数
     28         response = func_name()
     29     else:
     30         response = b"404 not found."
     31     conn.send(response)
     32     conn.close()

    完美解决了不同URL返回不同内容的问题。
    但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?

    没问题,不管是什么内容,最后都是转换成字节数据发送出去的。
    我可以打开HTML文件,读取出它内部的二进制数据,然后发送给浏览器。

      1 import socket
      2 def index():
      3     with open("index.html", "rb") as f:
      4         html_data = f.read()
      5     return html_data
      6 def login():
      7     with open("login.html", "rb") as f:
      8         html_data = f.read()
      9     return html_data
     10 url_func_map = [
     11     ("/index/", index),
     12     ("/login/", login)
     13 ]
     14 sk = socket.socket()
     15 sk.bind(("127.0.0.1", 8080))
     16 sk.listen(5)
     17 while True:
     18     conn, addr = sk.accept()
     19     data = conn.recv(8096)
     20     data_str = str(data, encoding="utf-8")  # 把收到的字节数据转换成字符串类型
     21     header = data_str.split("
    
    ")  # 将请求的数据按照
    
    分割
     22     header_list = header[0].split("
    ")  # 按
    分割header
     23     temp = header_list[0].split("")  # 按空格分割第一行
     24     url = temp[1]  # 取到请求的URL
     25     conn.send(b"HTTP/1.1 200 OK
    content-type:text/html; charset=utf-8
    
    ")  # 发送响应头
     26     func_name = None  # 将要执行的函数
     27     for i in url_func_map:
     28         if i[0] == url:
     29             func_name = i[1]
     30             break  # 跳出循环
     31     if func_name:  # 如果找到了要执行的函数
     32         response = func_name()
     33     else:
     34         response = b"404 not found."
     35     conn.send(response)
     36     conn.close()

    这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。

    没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。

      1 import socket
      2 def index():
      3     with open("index.html", "rb") as f:
      4         html_data = f.read()
      5     return html_data
      6 def login():
      7     with open("login.html", "rb") as f:
      8         html_data = f.read()
      9     import time
     10     time_str = str(time.time())
     11     html_str = str(html_data, encoding="utf-8")
     12     new_html = html_str.replace("@@a@@", time_str)
     13     return bytes(new_html, encoding="utf-8")
     14 url_func_map = [
     15     ("/index/", index),
     16     ("/login/", login)
     17 ]
     18 sk = socket.socket()
     19 sk.bind(("127.0.0.1", 8080))
     20 sk.listen(5)
     21 while True:
     22     conn, addr = sk.accept()
     23     data = conn.recv(8096)
     24     data_str = str(data, encoding="utf-8")  # 把收到的字节数据转换成字符串类型
     25     header = data_str.split("
    
    ")  # 将请求的数据按照
    
    分割
     26     header_list = header[0].split("
    ")  # 按
    分割header
     27     temp = header_list[0].split("")  # 按空格分割第一行
     28     url = temp[1]  # 取到请求的URL
     29     conn.send(b"HTTP/1.1 200 OK
    content-type:text/html; charset=utf-8
    
    ")  # 发送响应头
     30     func_name = None  # 将要执行的函数
     31     for i in url_func_map:
     32         if i[0] == url:
     33             func_name = i[1]
     34             break  # 跳出循环
     35     if func_name:  # 如果找到了要执行的函数
     36         response = func_name()
     37     else:
     38         response = b"404 not found."
     39     conn.send(response)
     40     conn.close()

    这是一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。
    这个过程就相当于HTML模板渲染数据。
    本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。
    我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具:jinja2

      1 from jinja2 import Template
      2 template = Template(data)  # 生成模板实例
      3 template.render(data=data)  # 渲染数据

    pymysql连接数据库:

      1 conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
      2 cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
      3 cursor.execute("select name, age, department_id from userinfo")
      4 user_list = cursor.fetchall()
      5 cursor.close()
      6 conn.close()

    创建一个测试的user表:

      1 CREATE TABLE user(
      2   id int auto_increment PRIMARY KEY,
      3   name CHAR(10) NOT NULL,
      4   habit CHAR(20) NOT NULL
      5 )engine=innodb DEFAULT charset=UTF8;
      6 

    上述代码模拟了一个web服务的本质.
    而对于真实开发中的Python Web程序来说,一般分为两部分:服务器程序和应用程序。
    服务器程序负责对Socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
    应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py等。
    不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
    这样,服务器程序就需要为不同的框架提供不同的支持。
    这样混乱的局面无论对于服务器还是框架,都是不好的。
    对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。
    这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么它们就可以配合使用。
    一旦标准确定,双方根据约定的标准各自进行开发即可。

    WSGI(Web Server Gateway Interface)就是这样一种规范,它定义了使用Python编写的Web APP与Web Server之间接口格式,实现Web APP与Web Server间的解耦。

    Python标准库提供的独立WSGI服务器称为wsgiref

      1 from wsgiref.simple_server import make_server
      2 def run_server(environ, start_response):
      3     start_response('200 OK', [('Content-Type', 'text/html')])
      4     return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
      5 if __name__ == '__main__':
      6     httpd = make_server('', 8000, run_server)
      7     print("Serving HTTP on port 8000...")
      8     httpd.serve_forever()
      9 

    Django开发模式就是使用的wsgiref模块作为Web Server。

    Django

    安装与基本配置

      1 pip3 install django
    
      1 // 命令行创建项目
      2 django-admin startproject mysite
      3 
      4 // 也可以用pycharm创建
      5 

    目录介绍:

      1 mysite/
      2 ├── manage.py  # 管理文件
      3 └── mysite  # 项目目录
      4     ├── __init__.py
      5     ├── settings.py  # 配置
      6     ├── urls.py  # 路由 --> URL和函数的对应关系
      7     └── wsgi.py  # webserver  --> wsgiref模块
      8 

    运行

      1 // 命令行启动方式	
      2 python manage.py runserver 127.0.0.1:8000
    

    模板文件配置:

      1 # setting配置文件
      2 TEMPLATES = [
      3     {
      4         'BACKEND': 'django.template.backends.django.DjangoTemplates',
      5         'DIRS': [os.path.join(BASE_DIR, "template")],  # template文件夹位置
      6         'APP_DIRS': True,
      7         'OPTIONS': {
      8             'context_processors': [
      9                 'django.template.context_processors.debug',
     10                 'django.template.context_processors.request',
     11                 'django.contrib.auth.context_processors.auth',
     12                 'django.contrib.messages.context_processors.messages',
     13             ],
     14         },
     15     },
     16 ]
     17 
     18 # 也就是说以后存放的模板文件(html)存放位置, 可以改名字,但是相应的名字也要修改

    静态文件配置:

      1 # 静态文件存放位置
      2 STATIC_URL = '/static/'  # HTML中使用的静态文件夹前缀
      3 STATICFILES_DIRS = (
      4     os.path.join(BASE_DIR, "static"),  # 静态文件存放位置
      5 )
      6 # 也就是以后 css,js 存放文件的位置
    STATIC_URL = '/static/'      # 别名
    STATICFILES_DIRS = (
                os.path.join(BASE_DIR,"static"),  #实际名 ,即实际文件夹的名字
            )
    
    '''
    
    注意点1:
     django对引用名和实际名进行映射,引用时,只能按照引用名来,不能按实际名去找
            <script src="/statics/jquery-3.1.1.js"></script>
            ------error-----不能直接用,必须用STATIC_URL = '/static/':
            <script src="/static/jquery-3.1.1.js"></script>
    
    注意点2:
     STATICFILES_DIRS = (
        ("app01",os.path.join(BASE_DIR, "app01/statics")),
            )
    
     <script src="/static/app01/jquery.js"></script>
    
    '''

    media配置:

    # in settings:
    
    MEDIA_URL="/media/"
    MEDIA_ROOT=os.path.join(BASE_DIR,"app01","media","upload")
    
    # in urls:
    from django.views.static import serve
    url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    http://127.0.0.1:8000/media/1.png
       '''
            静态文件的处理又包括STATIC和MEDIA两类,这往往容易混淆,在Django里面是这样定义的:
            
            MEDIA:指用户上传的文件,比如在Model里面的FileFIeld,ImageField上传的文件。如果你定义
            
            MEDIA_ROOT=c:	empmedia,那么File=models.FileField(upload_to="abc/")#,上传的文件就会被保存到c:	empmediaabc
    
            eg:
                class blog(models.Model):
                       Title=models.charField(max_length=64)
                       Photo=models.ImageField(upload_to="photo")
              上传的图片就上传到c:	empmediaphoto,而在模板中要显示该文件,则在这样写
              在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:	empmediaphoto
                MEDIA_URL="/data/"
            在开发阶段,media的处理由django处理:
    
               访问http://localhost/data/abc/a.png就是访问c:	empmediaphotoabca.png
    
               在模板里面这样写<img src="/media/abc/a.png">
    
               在部署阶段最大的不同在于你必须让web服务器来处理media文件,因此你必须在web服务器中配置,
               以便能让web服务器能访问media文件
               以nginx为例,可以在nginx.conf里面这样:
    
                     location ~/media/{
                           root/temp/
                           break;
                        }
    
               具体可以参考如何在nginx部署django的资料。
               
               
               '''
    View Code

    urls文件

    urls文件:

    from django.conf.urls import url
    from . import view
    urlpatterns = [

      url(r'^admin/', admin.site.urls),
    ]

    url参数 

    regex: 正则表达式,与之匹配的 URL 会执行对应的第二个参数 view。
    view: 用于执行与正则表达式匹配的 URL 请求。 --- 相当于通过url路由执行对应的函数
    kwargs: 视图使用的字典类型的参数。 可以传入视图中的参数。
    name: 用来反向获取 URL。

    regex:

       NOTE:
        1 一旦匹配成功则不再继续
        2 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
        3 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
        4 每个正则表达式前面的'r' 是可选的但是建议加上。
    
    一些请求的例子:
    
        /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
        /articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。
        /articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数
                           views.month_archive(request, '2005', '03')。
    
    
    #设置项是否开启URL访问地址后面不为/跳转至带有/的路径
    APPEND_SLASH=True

    无参分组

    url(r'^articles/([0-9]{4})/$', views.year_archive),
    def del_class(request, d):
        ...

    有参分组

    上面的示例使用简单的、没有命名的正则表达式组(通过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。

    在Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name 是组的名称,pattern 是要匹配的模式。

    下面是以上URLconf 使用命名组的重写:

    from django.conf.urls import url
    
    from . import views
    
    urlpatterns = [
        url(r'^articles/2003/$', views.special_case_2003),
        url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
        url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
        url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
    ]

    这个实现与前面的示例完全相同,只有一个细微的差别:捕获的值作为关键字参数而不是位置参数传递给视图函数。例如:

        /articles/2005/03/    
        请求将调用views.month_archive(request, year='2005', month='03')函数
        /articles/2003/03/03/ 
        请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。

    在实际应用中,这意味你的URLconf 会更加明晰且不容易产生参数顺序问题的错误 —— 你可以在你的视图函数定义中重新安排参数的顺序。当然,这些好处是以简洁为代价;有些开发人员认为命名组语法丑陋而繁琐。

    url(r'^delClass/(?P<del_id>d+)', views.del_class, name='del'),
    def del_class(request, del_id):
        ...

    URLconf 在请求的URL 上查找,将它当做一个普通的Python 字符串。不包括GET和POST参数以及域名。

    例如,http://www.example.com/myapp/ 请求中,URLconf 将查找myapp/

    在http://www.example.com/myapp/?page=3 请求中,URLconf 仍将查找myapp/

    URLconf 不检查请求的方法。换句话讲,所有的请求方法 —— 同一个URL的POSTGETHEAD等等 —— 都将路由到相同的函数。

    指定视图函数的默认值

    # URLconf
    from django.conf.urls import url
    
    from . import views
    
    urlpatterns = [
        url(r'^blog/$', views.page),
        url(r'^blog/page(?P<num>[0-9]+)/$', views.page),
    ]
    
    # View (in blog/views.py)
    def page(request, num="1"):

    在上面的例子中,两个URL模式指向同一个视图views.page —— 但是第一个模式不会从URL 中捕获任何值。如果第一个模式匹配,page() 函数将使用num参数的默认值"1"。如果第二个模式匹配,page() 将使用正则表达式捕获的num 值。

    view之Including other URLconfs(url管理)

    #At any point, your urlpatterns can “include” other URLconf modules. This
    #essentially “roots” a set of URLs below other ones.
    
    #For example, here’s an excerpt of the URLconf for the Django website itself.
    #It includes a number of other URLconfs:
    
    
    from django.conf.urls import include, url
    
    urlpatterns = [
       url(r'^admin/', admin.site.urls),
       url(r'^blog/', include('blog.urls')),
    ]

    如果一个项目下有多个app应用, 但是url都写在一个urls.py文件里, 就会显的非常杂乱, 而且耦合性很强, 如果一个app的url出了问题,就会影响到其他的url使用, 所以为了解决这个可以使用 include来解决:

    使用方法,可以在app里的urls文件里,创建对应关系, 然后在项目的urls的文件里指定文件路径。

    kwargs 参数

    // urls文件 
    from django.conf.urls import url
    from django.contrib import admin
    from django.shortcuts import HttpResponse, render, redirect
    
    
    def demo(request, msg):
        print('-------------------')
        print(msg)
        return HttpResponse('ok')
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^demo/', demo, {'msg': 'hello world'})
    ]

    name参数 用来当前url的路由地址

    // html文件
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
      <title>Title</title>
    </head>
    <body>
    <span>当前网页的路由地址是 {% url 'flag' %}</span>
    </body>
    </html>

    结果:

     新手三件套

    from django.shortcuts import HttpResponse, render, redirect

    HttpResponse:  带有字符串作为内容的HTTP响应类。用于回复浏览器的一种封装工具, 可以是接受字符串,也可以接收字符串类型的html标签

    render: 把HttpResponse 封装在函数里,实现了更好的功能,可以返回一个html文件内容。

    redirect:可以实现跳转网页的功能。

    HttpResponse

    from django.conf.urls import url
    from django.contrib import admin
    from django.shortcuts import HttpResponse, render, redirect
    
    
    def demo(request, msg):
        return HttpResponse('<h1>Hello World!</h1>')
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^demo/', demo, {'msg': 'hello world'}, name='flag')
    ]

    属性
    属性:
      django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。
       除了特殊说明的之外,其他均为只读的。
    
    ''' 
    
    1.HttpRequest.body
    
      一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。
    
      但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST 。
    
      另外,我们还可以用 python 的类文件方法去操作它,详情参考 HttpRequest.read() 。
    
     
    
    2.HttpRequest.path
    
      一个字符串,表示请求的路径组件(不含域名)。
    
      例如:"/music/bands/the_beatles/"
    
    
    
    3.HttpRequest.method
    
      一个字符串,表示请求使用的HTTP 方法。必须使用大写。
    
      例如:"GET"、"POST"
    
     
    
    4.HttpRequest.encoding
    
      一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 'utf-8')。
       这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。
       接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。
       如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。
    
     
    
    5.HttpRequest.GET 
    
      一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。
    
     
    
    6.HttpRequest.POST
    
      一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。
    
      POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。
       因此,不应该使用 if request.POST  来检查使用的是否是POST 方法;应该使用 if request.method == "POST" 
    
      另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。
    
     
    
    7.HttpRequest.REQUEST
    
      一个类似于字典的对象,它首先搜索POST,然后搜索GET,主要是为了方便。灵感来自于PHP 的 $_REQUEST。
    
      例如,如果 GET = {"name": "john"}  而 POST = {"age": '34'} , REQUEST["name"]  将等于"john", REQUEST["age"]  将等于"34"。
    
      强烈建议使用 GET 和 POST 而不要用REQUEST,因为它们更加明确。
    
     
    
    8.HttpRequest.COOKIES
    
      一个标准的Python 字典,包含所有的cookie。键和值都为字符串。
    
     
    
    9.HttpRequest.FILES
    
      一个类似于字典的对象,包含所有的上传文件信息。
       FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。
    
      注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会
       包含数据。否则,FILES 将为一个空的类似于字典的对象。
    
     
    
    10.HttpRequest.META
    
       一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例:
    
        CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
        CONTENT_TYPE —— 请求的正文的MIME 类型。
        HTTP_ACCEPT —— 响应可接收的Content-Type。
        HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
        HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
        HTTP_HOST —— 客服端发送的HTTP Host 头部。
        HTTP_REFERER —— Referring 页面。
        HTTP_USER_AGENT —— 客户端的user-agent 字符串。
        QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
        REMOTE_ADDR —— 客户端的IP 地址。
        REMOTE_HOST —— 客户端的主机名。
        REMOTE_USER —— 服务器认证后的用户。
        REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
        SERVER_NAME —— 服务器的主机名。
        SERVER_PORT —— 服务器的端口(是一个字符串)。
       从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时,
        都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_  前缀。
        所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。
    
     
    11.HttpRequest.user
    
      一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。
    
      如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。
    
        例如:
    
        if request.user.is_authenticated():
            # Do something for logged-in users.
        else:
            # Do something for anonymous users.
         
    
           user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。
    
         -------------------------------------------------------------------------------------
    
        匿名用户
        class models.AnonymousUser
    
        django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点:
    
        id 永远为None。
        username 永远为空字符串。
        get_username() 永远返回空字符串。
        is_staff 和 is_superuser 永远为False。
        is_active 永远为 False。
        groups 和 user_permissions 永远为空。
        is_anonymous() 返回True 而不是False。
        is_authenticated() 返回False 而不是True。
        set_password()、check_password()、save() 和delete() 引发 NotImplementedError。
        New in Django 1.8:
        新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。
    
     
    
    12.HttpRequest.session
    
       一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。
        完整的细节参见会话的文档。
    
     
    13.HttpRequest.resolver_match
    
      一个 ResolverMatch 的实例,表示解析后的URL。这个属性只有在 URL 解析方法之后才设置,这意味着它在所有的视图中可以访问,
       但是在 URL 解析发生之前执行的中间件方法中不可以访问(比如process_request,但你可以使用 process_view 代替)。
    '''
    方法
    '''
    1.HttpRequest.get_host()
    
      根据从HTTP_X_FORWARDED_HOST(如果打开 USE_X_FORWARDED_HOST,默认为False)和 HTTP_HOST 头部信息返回请求的原始主机。
       如果这两个头部没有提供相应的值,则使用SERVER_NAME 和SERVER_PORT,在PEP 3333 中有详细描述。
    
      USE_X_FORWARDED_HOST:一个布尔值,用于指定是否优先使用 X-Forwarded-Host 首部,仅在代理设置了该首部的情况下,才可以被使用。
    
      例如:"127.0.0.1:8000"
    
      注意:当主机位于多个代理后面时,get_host() 方法将会失败。除非使用中间件重写代理的首部。
    
     
    
    2.HttpRequest.get_full_path()
    
      返回 path,如果可以将加上查询字符串。
    
      例如:"/music/bands/the_beatles/?print=true"
    
     
    
    3.HttpRequest.build_absolute_uri(location)
    
      返回location 的绝对URI。如果location 没有提供,则使用request.get_full_path()的返回值。
    
      如果URI 已经是一个绝对的URI,将不会修改。否则,使用请求中的服务器相关的变量构建绝对URI。
    
      例如:"http://example.com/music/bands/the_beatles/?print=true"
    
     
    
    4.HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
    
      返回签名过的Cookie 对应的值,如果签名不再合法则返回django.core.signing.BadSignature。
    
      如果提供 default 参数,将不会引发异常并返回 default 的值。
    
      可选参数salt 可以用来对安全密钥强力攻击提供额外的保护。max_age 参数用于检查Cookie 对应的时间戳以确保Cookie 的时间不会超过max_age 秒。
    
            复制代码
            >>> request.get_signed_cookie('name')
            'Tony'
            >>> request.get_signed_cookie('name', salt='name-salt')
            'Tony' # 假设在设置cookie的时候使用的是相同的salt
            >>> request.get_signed_cookie('non-existing-cookie')
            ...
            KeyError: 'non-existing-cookie'    # 没有相应的键时触发异常
            >>> request.get_signed_cookie('non-existing-cookie', False)
            False
            >>> request.get_signed_cookie('cookie-that-was-tampered-with')
            ...
            BadSignature: ...    
            >>> request.get_signed_cookie('name', max_age=60)
            ...
            SignatureExpired: Signature age 1677.3839159 > 60 seconds
            >>> request.get_signed_cookie('name', False, max_age=60)
            False
            复制代码
             
    
     
    
    5.HttpRequest.is_secure()
    
      如果请求时是安全的,则返回True;即请求通是过 HTTPS 发起的。
    
     
    
    6.HttpRequest.is_ajax()
    
      如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串'XMLHttpRequest'。
    
      大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。
    
      如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware, 
       你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应能够正确地缓存。
    
    
    
    7.HttpRequest.read(size=None)
    
      像文件一样读取请求报文的内容主体,同样的,还有以下方法可用。
    
      HttpRequest.readline()
    
      HttpRequest.readlines()
    
      HttpRequest.xreadlines()
    
      其行为和文件操作中的一样。
    
      HttpRequest.__iter__():说明可以使用 for 的方式迭代文件的每一行。'''

    注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用:

    request.POST.getlist("hobby")

     render

    render(request, template_name[, context])

    结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。

    参数:
         request: 用于生成响应的请求对象。
    
         template_name:要使用的模板的完整名称,可选的参数
    
         context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
    
         content_type:生成的文档要使用的MIME类型。默认为DEFAULT_CONTENT_TYPE 设置的值。
    
         status:响应的状态码。默认为200。
    // html文件
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
      <title>Title</title>
    </head>
    <body>
    <span>这是render渲染的页面</span>>
    </body>
    </html>
    from django.conf.urls import url
    from django.contrib import admin
    from django.shortcuts import HttpResponse, render, redirect
    
    
    def demo(request, msg):
        return render(request, 'demo.html')
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^demo/', demo, {'msg': 'hello world'}, name='flag')
    ]

     

    render还可以接收字典类型的参数,与模板语言实现交互:

    from django.conf.urls import url
    from django.contrib import admin
    from django.shortcuts import HttpResponse, render, redirect
    
    lst = [1, 2, 3, 4, 5]
    
    
    def demo(request, msg):
        return render(request, 'demo1.html', {'lst': lst})
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^demo/', demo, {'msg': 'hello world'}, name='flag')
    ]
    urls文件
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
      <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
      <title>Title</title>
    </head>
    <body>
    <ul>
    {% for i in lst %}
        <li>{{ i }}</li>
    {% endfor %}
    </ul>
    
    </body>
    </html>
    html文件

    redirect

    参数可以是:

    • 一个模型:将调用模型的get_absolute_url() 函数
    • 一个视图,可以带有参数:将使用urlresolvers.reverse 来反向解析名称
    • 一个绝对的或相对的URL,将原封不动的作为重定向的位置。

    默认返回一个临时的重定向;传递permanent=True 可以返回一个永久的重定向。

    示例:

    你可以用多种方式使用redirect() 函数。

    from django.conf.urls import url
    from django.contrib import admin
    from django.shortcuts import HttpResponse, render, redirect
    
    lst = [1, 2, 3, 4, 5]
    
    
    def demo(request, msg):
        return redirect('http://www.baidu.com')
    
    urlpatterns = [
        # url(r'^admin/', admin.site.urls),
        url(r'^demo/', demo, {'msg': 'hello world'}, name='flag')
    ]
    View Code
    传递对象

    将调用get_absolute_url() 方法来获取重定向的URL:

    from django.shortcuts import redirect
     
    def my_view(request):
        ...
        object = MyModel.objects.get(...)
        return redirect(object)
    传递视图名称

    可以带有位置参数和关键字参数;将使用reverse() 方法反向解析URL:

    def my_view(request):
        ...
        return redirect('some-view-name', foo='bar')
    传递url
    def my_view(request):
        ...
        return redirect('/some/url/')
    def my_view(request):
        ...
        return redirect('http://example.com/')

    默认情况下,redirect() 返回一个临时重定向。以上所有的形式都接收一个permanent 参数;如果设置为True,将返回一个永久的重定向:

    def my_view(request):
        ...
        object = MyModel.objects.get(...)
        return redirect(object, permanent=True)  
    重定向
    redirect关键点:两次请求过程,掌握流程。
    
    注意:render和redirect的区别:
    
    1、 if 页面需要模板语言渲染,需要的将数据库的数据加载到html,那么render方法则不会显示这一部分。
    
    2、 the most important: url没有跳转到/yuan_back/,而是还在/login/,所以当刷新后又得重新登录。
  • 相关阅读:
    浅析人脸检测之Haar分类器方法
    python调用网络摄像机
    jvm常用优化方案和方法
    jvm优化-垃圾收集器的类型
    jvm调优-垃圾回收算法
    JVM调优-java虚拟机内存模型及参数设置
    java NIO-Buffer
    java NIO-Channel
    java权限设置文件-java.policy
    java安全-安全管理器
  • 原文地址:https://www.cnblogs.com/wangyuanming/p/7900459.html
Copyright © 2020-2023  润新知