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\r\n
  3 Header1:v1\r\n
  4 Header2:v2\r\n
  5 \r\n\r\n ----------> 与内容之间隔着两个换行符
  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\r\n\r\n")
  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("\r\n\r\n")  # 将请求的数据按照\r\n\r\n分割
 10     header_list = header[0].split("\r\n")  # 按\r\n分割header
 11     temp = header_list[0].split("")  # 按空格分割第一行
 12     url = temp[1]  # 取到请求的URL
 13     conn.send(b"HTTP/1.1 200 OK\r\n\r\n")  # 发送响应头
 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("\r\n\r\n")  # 将请求的数据按照\r\n\r\n分割
 18     header_list = header[0].split("\r\n")  # 按\r\n分割header
 19     temp = header_list[0].split("")  # 按空格分割第一行
 20     url = temp[1]  # 取到请求的URL
 21     conn.send(b"HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n")  # 发送响应头
 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("\r\n\r\n")  # 将请求的数据按照\r\n\r\n分割
 22     header_list = header[0].split("\r\n")  # 按\r\n分割header
 23     temp = header_list[0].split("")  # 按空格分割第一行
 24     url = temp[1]  # 取到请求的URL
 25     conn.send(b"HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n")  # 发送响应头
 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("\r\n\r\n")  # 将请求的数据按照\r\n\r\n分割
 26     header_list = header[0].split("\r\n")  # 按\r\n分割header
 27     temp = header_list[0].split("")  # 按空格分割第一行
 28     url = temp[1]  # 取到请求的URL
 29     conn.send(b"HTTP/1.1 200 OK\r\ncontent-type:text/html; charset=utf-8\r\n\r\n")  # 发送响应头
 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:\temp\media,那么File=models.FileField(upload_to="abc/")#,上传的文件就会被保存到c:\temp\media\abc

        eg:
            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/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/,所以当刷新后又得重新登录。
posted @ 2017-11-26 21:10  总有问题刁难朕  阅读(1332)  评论(0编辑  收藏  举报