zoukankan      html  css  js  c++  java
  • Django学习第一天

    一、web的请求流程

    web应用的请求流程:

        就是浏览器和server端的一次通信过程,浏览器作为客户端发了一次请求到服务器,服务器接收到这个内容之后再返回一个响应,这就是一次web应用。而在这一次web应用中如果返回的是一个固定的html内容的话,这里就不会涉及任何的web框架。

        用户向服务器送一个请求,这个请求是浏览器把url打包成一个个键值对发过去的,所以发过去的是带着url的请求,不光有url,还有很多键值对,这些键值对里面包含了访问的主机信息、请求信息等等。发出去后,server端接收到了这次请求,如果要返回的是一个固定的html页面,用户不能做任何交互,那么这个过程就变得简单了:服务器只需要把用户发过来的请求按照http协议解析开,然后服务器只需要把用户想要html页面打包发过去就可以了。在这里会有一个web服务器的概念, 以后工作中会遇到叫nginx或apache的web服务器软件。

        nginx和apache是用来接收用户请求来做http的解析的web服务器软件,它们就是用来做http协议的解析与http协议的封装的。它们把客户端发过的数据打包成一个对象的形式交给服务器的web应用。 有了这些web服务器软件程序员就不必管用户发过来的请求是按什么规范创造的,然后去按什么规范去解了,节省了繁琐的过程,程序员只需要专注网页的开发。

        web服务器软件(nginx或apache)要做的事是:它先把用户发过来的数据打包成一个对象的形式后交给web应用,对于web应用而言,我怎么取方便我就怎么拿(通过键值对等)。web服务器类似于一个接口,接口就是为别人提供一个功能,别人不管这个功能是怎么实现的,他只要知道调用接口名并实现了某个功能即可。

        web应用才是逻辑指挥的,它相当于大脑。web服务器软件把生成的对象交给web应用后,web应用来决定返回哪个html页面。返回的html页面还是得交给web服务器软件来进行http协议的组装。最后web服务器把组装好的html页面发给浏览器。这就是一次完整的web请求。

        我们要学的 django框架目的是为了节省开发时间,提高开发效率,所以它的位置应该是web应用这里起作用。一次web应用里可能重复很多步,我们把这些重复的内容作为一个框架,每次再创建一个应用,再把这些重复的内容下载下来再添加一些其它的需求。如果没有一个web服务器,没有解析过程、没有发送过程,那么这套模式就不能够被调度起来。

        由于我们没有学nginx或apache这些web服务器软件,所以我们这里暂时用python的一个wsgiref模块来替代web服务器软件。

        python自己设计了一套服务器的接口,只要符合接口规范,那么我就可以认为你可以完成我的类似于服务器软件的功能,所以这个接口协议叫做WSGI,python里有一个叫wsgiref模块。这个wsgiref模块内部里封装了socket套接字的创建,有了这个模块之后,它就能代替web服务器软件了。

    二、wsgi协议

    自己先写一个web框架

    为什么自己来写个web框架呢?因为直用django的话,只能看到它的效果,它的流程是看不到的。我们用自己写的web框架再去与django框架去类比一下,然后就知道具体是怎么实现的了。

    import socket
    
    def handle_request(client):
    
        buf = client.recv(1024)
        client.send("HTTP/1.1 200 OK\r\n\r\n".encode("utf8"))  # 响应头信息
        client.send("<h1 style='color:red'>Hello,XiaoBai</h1>".encode("utf8"))  # \r\n\r\n后就是你要渲染到页面上的内容了
    
    def main():
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        sock.bind(('localhost',8001))
        sock.listen(5)
    
        while True:
            connection,address = sock.accept()
            handle_request(connection)
            connection.close()
    
    if __name__ == '__main__':
    
        main()
    

    (1)我们要引用wsgiref模块中的一个叫make_server的类,这个类是通过simple_server来调用的,所以我们要做的第一件事是先把它引用过来。

    from wsgiref.simple_server import make_server
    # 用到wsgiref接口模块下面的一个叫make_server的类 def application(environ,start_response):
    # wsgiref所有的请求信息封装成一个对象,这个对象就是environ,这里面所有的封装信息是通过解析http协议给封装好的。
    # 所以我们要写需求的时候,直接从这个environ对象里想要什么拿什么就是了,http规范这一步就被忽略隐藏了。 start_response('200 OK',[('Content-Type','text/html')])
    # Content-Type是告诉你的浏览器给你返回的是一个什么类型的文本。
    # 这一步必须有的原因是:http协议解析里面必须包含一个响应头和一个响应体,响应头start_response这个函数就是帮我设置这些内容的。
    # 第一个:状态码,第二部分内容放响应头信息。响应头是由许多键值对组成的。它会把每一个键值对放到一个元组里。后面还可以跟元组,
    # 每个括号里都存放两个内容,一个代表键,一个代表值。把所有响应头的信息和设置全写进去。 return [b'Hello world'] t = make_server("",8800,application)
    # 这是一个类,它只是实例化出来 一个对象。如果不调用这个对象下面的server_forever方法,它不会执行。
    # make_server帮我创建好了socket对象。当调用它时t.serve_forever(),一定会调用application函数的执行,当有请求信息过来时则执行以下两行代码 # start_response('200 OK',[('Content-Type','text/html')])
    # return [b'<h1>Hello,world!<\h1>']
    # 你给它一个响应200 ok,然后拿到这个hello world,这个时候没有处理来自客户端的请求信息。

    t.serve_forever() # 括号中的前两个参数放的是ip地址和端口号,由于是本机的ip地址,所以可以不写。实现一次web通信就必须要 有ip和端口,这样别人才能访问你。
    # 第三个参数放的是函数名,当执行下面的httpd.server_forever()的时候,它执行的是application这个函数。
    # 实例化出一个类对象,这个对象调serve_forever()的时候,它会在内部创建一个socket对象来进行绑定进行listen,完事后accept起来
    # 当有外部客户端来连接的时候,它会执行对应的application函数下的代码,最后完成了一次最简单的请求。

    httpd.serve_forever()
    # 开始监听http请求:

    三、自定义一个web框架(1)

    environ是一个请求信息,environ包含所有的信息。它是一个字典,封装了所有的请求信息

    打印一下environ,查找打印的信息中有一个叫    ‘PATH_INFO’:'/favicon.ico'    的键值对。

    environ {'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\William\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'LAPTOP-E61OFNCB', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'FPS_BROWSER_APP_PROFILE_STRING': 'Internet Explorer', 'FPS_BROWSER_USER_PROFILE_STRING': 'Default', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\William', 'LOCALAPPDATA': 'C:\\Users\\William\\AppData\\Local', 'LOGONSERVER': '\\\\LAPTOP-E61OFNCB', 'NUMBER_OF_PROCESSORS': '4', 'ONEDRIVE': 'C:\\Users\\William\\OneDrive', 'OS': 'Windows_NT', 'PATH': 'C:\\Python\\Python36\\Scripts\\;C:\\Python\\Python36\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Python\\Python27\\Scripts\\;C:\\Python\\Python27;D:\\MySQL\\mysql-5.7.19-winx64\\bin;C:\\Program Files (x86)\\PBB Reader\\x64\\;C:\\Users\\William\\AppData\\Local\\Microsoft\\WindowsApps;D:\\MySQL\\mysql-5.7.19-winx64\\bin', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW', 'PROCESSOR_ARCHITECTURE': 'x86', 'PROCESSOR_ARCHITEW6432': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 78 Stepping 3, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '4e03', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files (x86)', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '1', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'C:\\Users\\William\\PycharmProjects\\Item_Python1', 'PYTHONUNBUFFERED': '1', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows', 'TEMP': 'C:\\Users\\William\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\William\\AppData\\Local\\Temp', 'USERDOMAIN': 'LAPTOP-E61OFNCB', 'USERDOMAIN_ROAMINGPROFILE': 'LAPTOP-E61OFNCB', 'USERNAME': 'William', 'USERPROFILE': 'C:\\Users\\William', 'WINDIR': 'C:\\Windows', 'SERVER_NAME': 'LAPTOP-E61OFNCB', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8800', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8800', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3159.5 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'HTTP_DNT': '1', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.8', 'wsgi.input': <_io.BufferedReader name=788>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}
    View Code

    找到 ‘PATH_INFO’:'/favicon.ico' 这个键值对后,我们当时在浏览器中输入127.0.0.1:8800时并没有输入路径。什么是路径?

    例如:127.0.0.1:8800/xiaobai    后面这个/xiaobai就是路径。

    这时我们想让浏览器中输入127.0.0.1:8800/xiaobai显示一个内容,127.0.0.1:8800/xiaoxiaobai又显示另一个内容,它们的区别就是它们的路径不一样,而所有的请求信息都在这个environ里面,这些路径我们可以从environ里面拿,所以我们要根据判断用get方法把PATH取出来。

    path = environ.get("PATH_INFO")
    print("path",path)
    加上以上两条代码后,再点启动

    然后在浏览器中输入127.0.0.1:8800/xiaobai,回车,再查看pycharm中的代码变化

    发现打印出来了,

    path /xiaobai就是我们想要的。

    path /favicon.ico是它自己默认的一个变量

    有了以上的内容后,改成别的名字也可以拿到了。例:

     那么现在我们可以通过这个变量来做分支判断。在这里我们可以不用return等于一个固定的内容了。这样就可以根据不同的人来返回不同的页面了。前提必须是“xiaobai”和“xiaobaibai”必须各有一个页面。所以我们先各建一个html。

    from wsgiref.simple_server import make_server

    def application(environ,start_response):

    path = environ.get("PATH_INFO")
    print("path",path)
    start_response('200 OK',[('Content-Type','text/html')])

    if path == '/xiaobai': # 做分支判断

    f = open("xiaobai.html","rb")
    # 打开xiaobai.html,发的时候是按照rb模式来发的,所以应该按rb来读
    # 它这个模块必须是对应字节才能发送,所以我们应该通过rb去拿
    data = f.read()
    f.close()
    return [data]
    elif path == "/xiaoxiaobai":
    f = open("xiaoxiaobai.html","rb")
    data = f.read()
    f.close()
    return [data]
    else:
    return [b"<h1>404</h1>"]

    # return [b"<h1>hello world</h1>"]

    t = make_server("",8800,application)
    t.serve_forever()

     点击启动

    得到如下结果:

    输入127.0.0.1:8800/xiaobai时则显示xiaobai的html页面

    输入127.0.0.1:8800/xiaoxiaobai时则显示xiaobai的html页面

    在127.0.0.1:8800后随便输入字符,如/haha时,则显示404的页面

    以上就是路径的分派,根据封装好的environ对象。

    四、自定义一个web框架(2)

    一个网站存放的路由可能是成百上千个,用if...else去判断是一个非常繁琐的过程。所以可以用反射或字典的格式

    写一个路径的函数:

    routers

    自定义一个登陆页面:

    自定义一个xiaobai页面:

    自定义一个xiaoxiaobai页面:

     MyWeb.py的代码:

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Author : William
    # @Time : 2017/8/28 12:03
    # @File : MyWeb.py

    from wsgiref.simple_server import make_server

    def foo1(request):

    f = open("xiaobai.html", "rb")
    data = f.read()
    f.close()
    return [data]

    def foo2(request):
    f = open("xiaoxiaobai.html", "rb")
    data = f.read()
    f.close()
    return [data]

    def login(request):
    f = open("login.html","rb")
    data = f.read()
    f.close()
    return [data]
    def auth(request):
    return[b"OK"]
    def routers():
    URLpattern = (
    # 每个路径对应一个函数名来处理它
    ("/auth",auth),
    ("/login", login),
    ("/xiaobai", foo1),
    ("/xiaoxiaobai", foo2),
    )
    return URLpattern

    def application(environ,start_response):

    path = environ.get("PATH_INFO")
    print("path",path)
    start_response('200 OK',[('Content-Type','text/html')])

    urlpattern = routers()
    # 执行routers()后得到的是一个对应关系的元组。拿到这个元组后起个名urlpattern
    func = None # 拿一个变量func赋给它,默认为None
    for item in urlpattern:
    # 对这个urlpattern进行for循环遍历,这个item就是遍历对应的关系。
    # 我们的目的是当用户输入一个xiaobai时执行这个foo1函数。当输入xiaoxiaobai时执行foo2函数
    # 所以我们在这里应该取出来进行判断,让谁跟这个path比,path就是用户输入的内容。
    if path == item[0]:# 例如:当用户输入/xiaobai时
    func = item[1]
    # 例如:匹配成功时则执行item[1],就是执行foo1这个函数,把这个变量赋给func
    break
    # 加上break是跳出循环
    # 如果都没有,则返回的是None
    if func: # 如果func有值,它不是None值,相当于找到了。找到的这个func就是要执行的函数
    return func(environ) # 执行找到的值的函数,如/xiaobai下的foo1函数
    else:
    return[b"404"]





    # elif path == "/xiaoxiaobai":

    # else:
    # return [b"<h1>404</h1>"]

    # return [b"<h1>hello world</h1>"]

    t = make_server("",8800,application)
    t.serve_forever()

    点击运行后执行效果:

    点击提交按钮后:

    此时,输入的用户密码信息就在浏览器中显示了,因为之前用的是get方法,换成post就不会显示在浏览器输入栏里。

    打印一下request的值:

    C:\Python\Python36\python.exe C:/Users/William/PycharmProjects/Item_Python1/Study/MyWeb.py
    127.0.0.1 - - [28/Aug/2017 17:49:42] "GET /auth?user=xiaobai&pwd=123 HTTP/1.1" 200 2
    path /auth
    +++++++ {'ALLUSERSPROFILE': 'C:\\ProgramData', 'APPDATA': 'C:\\Users\\William\\AppData\\Roaming', 'COMMONPROGRAMFILES': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files', 'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files', 'COMPUTERNAME': 'LAPTOP-E61OFNCB', 'COMSPEC': 'C:\\Windows\\system32\\cmd.exe', 'FPS_BROWSER_APP_PROFILE_STRING': 'Internet Explorer', 'FPS_BROWSER_USER_PROFILE_STRING': 'Default', 'HOMEDRIVE': 'C:', 'HOMEPATH': '\\Users\\William', 'LOCALAPPDATA': 'C:\\Users\\William\\AppData\\Local', 'LOGONSERVER': '\\\\LAPTOP-E61OFNCB', 'NUMBER_OF_PROCESSORS': '4', 'ONEDRIVE': 'C:\\Users\\William\\OneDrive', 'OS': 'Windows_NT', 'PATH': 'C:\\Python\\Python36\\Scripts\\;C:\\Python\\Python36\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files (x86)\\NVIDIA Corporation\\PhysX\\Common;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Python\\Python27\\Scripts\\;C:\\Python\\Python27;D:\\MySQL\\mysql-5.7.19-winx64\\bin;C:\\Program Files (x86)\\PBB Reader\\x64\\;C:\\Users\\William\\AppData\\Local\\Microsoft\\WindowsApps;D:\\MySQL\\mysql-5.7.19-winx64\\bin', 'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW', 'PROCESSOR_ARCHITECTURE': 'x86', 'PROCESSOR_ARCHITEW6432': 'AMD64', 'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 78 Stepping 3, GenuineIntel', 'PROCESSOR_LEVEL': '6', 'PROCESSOR_REVISION': '4e03', 'PROGRAMDATA': 'C:\\ProgramData', 'PROGRAMFILES': 'C:\\Program Files (x86)', 'PROGRAMFILES(X86)': 'C:\\Program Files (x86)', 'PROGRAMW6432': 'C:\\Program Files', 'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules', 'PUBLIC': 'C:\\Users\\Public', 'PYCHARM_HOSTED': '1', 'PYTHONIOENCODING': 'UTF-8', 'PYTHONPATH': 'C:\\Users\\William\\PycharmProjects\\Item_Python1', 'PYTHONUNBUFFERED': '1', 'SESSIONNAME': 'Console', 'SYSTEMDRIVE': 'C:', 'SYSTEMROOT': 'C:\\Windows', 'TEMP': 'C:\\Users\\William\\AppData\\Local\\Temp', 'TMP': 'C:\\Users\\William\\AppData\\Local\\Temp', 'USERDOMAIN': 'LAPTOP-E61OFNCB', 'USERDOMAIN_ROAMINGPROFILE': 'LAPTOP-E61OFNCB', 'USERNAME': 'William', 'USERPROFILE': 'C:\\Users\\William', 'WINDIR': 'C:\\Windows', 'SERVER_NAME': 'LAPTOP-E61OFNCB', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8800', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/auth', 'QUERY_STRING': 'user=xiaobai&pwd=123', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'text/plain', 'HTTP_HOST': '127.0.0.1:8800', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3159.5 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'HTTP_DNT': '1', 'HTTP_REFERER': 'http://127.0.0.1:8800/login', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.8', 'wsgi.input': <_io.BufferedReader name=812>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}
    127.0.0.1 - - [28/Aug/2017 17:49:42] "GET /favicon.ico HTTP/1.1" 200 3
    path /favicon.ico
    View Code

    找到用户名和密码后先进行分割,再进行判断:

    运行,看效果。

    在输入xiaobai和123,账号密码正确的情况下显示:

    在输入其它用户名密码的情况下显示:

     五、自定义一个web框架(3)

    由于以上写的代码看起来很乱,为了使代码有可读性,我们将其进一步优化,就是把处理的函数都单独放到一个文件里,然后调用。

    回到MyWeb.py文件里添加一行导入views.py的代码:

    from views import *

    再作进一步优化,把这些对应关系拿走,传到一个叫urls.py的文件里。

     

    回到MyWeb.py文件里添加导入urls.py的代码:

    还有在MyWeb.py文件末尾加上

    if __name__ == '__main__':,把以下两句代码缩进来,表示这是一个执行文件。

    最后,我们把xiaobai.html、xiaoxiaobai.html、login.html这三个页面放到一个文件夹中便于统一管理。

    创建一个文件夹:templates,然后把这些html文件放到里面,最后要引入时,则在views文件里把所有的html文件的路径进行修改即可

    以上就是自定义了一个基本的web框架。

    代码如下:

    login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>login</title>
    </head>
    <body>
    
    <h2>登陆页面</h2>
    <form action="http://127.0.0.1:8800/auth ">
        <p>用户名<input type="text" name="user"></p>
        <p>密  码<input type="password" name="pwd"></p>
        <p>
            <input type="submit">
        </p>
    </form>
    </body>
    </html>
    

    xiaobai.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>xiaobai</title>
    </head>
    <body>
    
    <h1> Welcome xiaobai</h1>
    </body>
    </html>
    

    xiaoxiaobai.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>xiaobai</title>
    </head>
    <body>
    
    <h1> Welcome xiaobaibai</h1>
    </body>
    </html>
    

    MyWeb.py

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    # @Author  : William
    # @Time    : 2017/8/28 12:03
    # @File    : MyWeb.py
    
    from wsgiref.simple_server import make_server
    
    from views import *
    import urls
    
    def routers():
        urls.URLpattern
        return URLpattern
    
    def application(environ,start_response):
    
        path = environ.get("PATH_INFO")
        print("path",path)
        start_response('200 OK',[('Content-Type','text/html')])
    
        urlpattern = routers()
    # 执行routers()后得到的是一个对应关系的元组。拿到这个元组后起个名urlpattern
        func = None # 拿一个变量func赋给它,默认为None
        for item in urlpattern:
    # 对这个urlpattern进行for循环遍历,这个item就是遍历对应的关系。
    # 我们的目的是当用户输入一个xiaobai时执行这个foo1函数。当输入xiaoxiaobai时执行foo2函数
    # 所以我们在这里应该取出来进行判断,让谁跟这个path比,path就是用户输入的内容。
            if path == item[0]:# 例如:当用户输入/xiaobai时
                func = item[1]
                # 例如:匹配成功时则执行item[1],就是执行foo1这个函数,把这个变量赋给func
                break
                # 加上break是跳出循环
                # 如果都没有,则返回的是None
        if func: # 如果func有值,它不是None值,相当于找到了。找到的这个func就是要执行的函数
            return func(environ) # 执行找到的值的函数,如/xiaobai下的foo1函数
        else:
            return[b"404"]
    
    
    
    
    
    #     elif path == "/xiaoxiaobai":
    
    #     else:
    #         return [b"<h1>404</h1>"]
    
        # return [b"<h1>hello world</h1>"]
    if __name__ == '__main__':
        t = make_server("",8800,application)
        t.serve_forever()
    

    urls.py

    from views import *
    
    URLpattern = (
        # 每个路径对应一个函数名来处理它
        ("/auth", auth),
        ("/login", login),
        ("/xiaobai", foo1),
        ("/xiaoxiaobai", foo2),
    )
    

    views.py

    def foo1(request):
    
        f = open("templates/xiaobai.html", "rb")
        data = f.read()
        f.close()
        return [data]
    
    def foo2(request):
        f = open("templates/xiaoxiaobai.html", "rb")
        data = f.read()
        f.close()
        return [data]
    
    def login(request):
        f = open("templates/login.html","rb")
        data = f.read()
        f.close()
        return [data]
    def auth(request):
        print("+++++++",request)
        user_union,pwd_union = request.get("QUERY_STRING").split("&")
        _,user = user_union.split("=")
        _,pwd = pwd_union.split("=")
    
        if user == "xiaobai" and pwd =="123":
        # 这里应该接数据库,由于是演示,直接用简单的字符串代替
            return [b"login successfully"]
        else:
            return [b"user or password is wrong"]
    

    以上代码中涉及到框架的是

    urls.py中的 URLpattern = ()

    MyWeb.py中的所有代码都是

    以后要用直接把它下载下来,第一步写对应关系,第二步写视图函数就OK了,再把MyWeb.py改成bin.py作为启动文件即可。如果设置到页面,直接到tempaltes里创建页面就可以了。这就是一个最简单的框架。

     六、MTV模型

    MTV和MVC框架是web框架的中的出名的两个模型,不管是MVC还是MTV,它们的流程都是一样的。

    MVC   model view controller

    MTV   model templates view

    两个model都是数据模型,就是跟数据库打交道的模块,所以我们会把数据相关的内容放到一个模块里,一个web框架或web应用最重要的一点就是如何与数据库打交道。

    MVC的view和MTV的templates是一样的,都是存放html文件的。

    controller是控制器的意思,view是视图的意思。MTV中的view包含view+controller

    我们学的Django模型是MTV模型。

    所以MTV包含以下四块:

    model   view    templates   controller

    controller就是我们之前写的自定义web框架中的urls,还有在bin.py文件中的if判断语句,它来决定哪个url对应哪个函数。是用来作路由分发的。

    view就是我们之前写的自定义web框架中的views,视图就是对来做逻辑处理的。

    templates就是我们之前写的自定义web框架中的templates,它是用来存放html文件的。

    七、Django项目的创建

    Django是python的web框架。所以Django框架一定是依赖于python解释器的。所以,安装Django时,pip install django是把django放到python2解释器里,pip3 install django是把django放到python3解释器里。

    检测是django是放到python2解释器里还是python3解释器里,只需要查看python2或python3的安装目录下的Scripts中,谁有django-admin.exedjango-admin.py这两个文件就知道django用的哪个解释器了。

    由于我早已安装了django,所以才出来已安装的提示。

    若dos下安装不成功,则在pycharm上安装django

    打开pycharm——File——settings——找到你创建的项目名(我的项目名是Project:Item_Python1)——Project Interpreter——在右面的选项框里

     创建一个django项目:

    django-admin startproject 自定义项目名

     

    成功创建了一个叫Mysite的项目名。

    打开Mysite项目,里面有两个文件,一个是Mysite,一个是manage.py

    manage.py是一个起始文件,与django进行交互的命令脚本,启动项目就是通过manage.py。你无论想要django做什么事情都要通过manage.py

    Mysite文件的名字与创建的项目名字相同,它一全局的一个文件夹,打开Mysite,里面有4个文件:

    __init__.py:空的,说明这个Mysite是一个包,既然是个包就可以进行包与包之间、模块与模块之间的调用。与django进行交互的命令脚本

    settings.py:完成相互的配置的。

    urls.py:做路由分发的,url跟视图的一个分发关系。(url中的path与视图函数的一一映射关系)重点

    wsgi.py:封装了socket套接字,它是一个底层文件

    项目与应用的关系:

    一个项目有多个应用(常见)

    一个应用可以被多个项目拥有

    创建一个django应用:

    想要创建一个django应用就必须告诉manage.py这个脚本。

    python manage.py startapp 应用名

     所有需求都写在这个应用里了,必须创建应用名。一个项目里可以只有一个应用,但是不可以没有。所以在创建django项目后就必须创建一个应用。

    创建了一个叫blog的文件夹。在电脑安装了多版本python的情况下,有的是pyhon3 manage.py startapp blog来创建应用名。

     打开django项目里可以看到创建的blog应用名。

    打开blog文件夹,我们重点要掌握的就是models.pyviews.py这两个文件。

    models是与数据库打交道的文件。

    views.py是存放视图函数的文件。

    admin.py是与后台数据管理相关的文件。

    apps.py是与应用相关的文件。

    tests.py是用来测试的文件。

     

    八、Django项目登陆设计

    1、pycharm中创建Django项目

    打开pycharm——File——New Project

    django项目创建完成

    2、写一个登录项目。

    第一步:先在urls.py文件里添加logoin函数映射关系。

    第二步:写一个登录的函数。

    第三步:写一个返回给用户的login页面。

    表格里的action="http://127.0.0.1:8080/auth"可以直接简写成"/auth",因为只写一个/auth,它默认就会拿当前网站的IP地址和端口

    第四步:启动django项目:

    python manage.py runserver 8800    

    # runserver后面什么也不加默认为本机的8000端口,若想要改,则直接在后面加端口号,本例改为8800

    第五步:打开浏览器,输入本机IP地址127.0.0.1:8800/login

     

    第六步:写一个验证用户名和密码的文件

     (1)当我们输入用户名和密码后,点击提交后,希望对输入的用户名和密码进行验证,然后走http://127.0.0.1:8800/auth这个地址。然而直接访问这个地址则不能被访问,因为这是启动的是django项目。所以我们得加一个叫auth的路径了。

    (2)输入账号密码点提交后应该走post的提交方式。后面不写method="post"默认是以get的提交方式提交。

    (3)

    注意:django里面改一下代码可以不用重启,它自动给你重启了。

    (4)点击提交后的流程。

     

    点击提交后,它应该走action那个路径

    action里的auth其实就是urls.py里的auth对应的视图函数——auth

     打开这个视图函数,该函数又返回了一个login.html的页面。

     

    点击提交后的页面:

    注意(只是在post提交数据的时候,get不会有这种情况)

    出现以上页面这是因为django自带的一个防止攻击的安全机制,我们只需把这个机制关闭即可。

    具体方法:进入setting.py文件里——找到'django.middleware.csrf.CsrfViewMiddleware'——用#号注释即可

    (5)打开浏览器再次输入账号密码后提交取得用户输入的账号密码

     

    点击提交后,页面会刷新,此时回到后台,查看pycharm下的代码,我们得到了用户输入的一个字典形式的用户名和密码了。现在只需要通过键就能取出来了。

    第七步:回到views.py,之前我们输入的打印内容

    第八步:将拿到用户输入的账号密码进行判断

    打开浏览器,继续输入用户名和密码进行测试:

    (1)登陆失败时,页面刷新

         点击提交后登陆没成功,返回一个页面:

    (2)登陆成功时,则会提示“欢迎登陆”的字符串

    点击提交后页面跳转至auth,并显示“登陆成功”的字符串:

     扩展部分:

    需求:把login路径和auth路径合并成一个login视图。

    答:

    第1步:为了防止报错,先注释auth函数里的代码

    第2步:在login页面里把action="http//127.0.0.1:8800/auth/"改成action="http//127.0.0.1:8800/login/"

    第3步:接下来我们就要把用户输入传过来的数据取出来做判断了。

     之前login函数下的代码和auth函数下的代码它们唯一的区别就是请求方式不同

    login函数下的代码是走的是第一次请求,地址栏,默认走的是get请求

    auth函数下的代码走的是第二次请求,form表单提交走的是post请求

    这两次请求它们的区别就是,logoin是get方式请求,auth是post方式请求

    所以我们只需要作个if判断,判断你走的是get请求还是post请求即可。

    如果是get请求,我就给你一个登陆页面,如果是post请求,说明是第2次过程,提交数据了。我们再把判断那步放到就行了。

     最后进行测试:

    登陆失败情况下:

    点击提交,刷新后仍然是在login页面上

     登陆成功情况下:

    点击提交,刷新后显示“登陆成功”字符串,依然是在login页面上

     



  • 相关阅读:
    servlet生命周期和线程安全
    如何保证Redis与数据库的数据一致性
    消息队列高可用、幂等性、顺序性、可靠性传输、堆积问题解决
    如何保证消息队列消息的顺序性
    RabbitMQ 如何保证消息不丢失?
    深入理解MySql事务
    MySQL/mariadb知识点总结
    如何实现一个线程安全的单例,前提是不能加锁
    DUBBO原理、应用与面经总结
    SpringBoot中资源初始化加载的几种方式
  • 原文地址:https://www.cnblogs.com/xiaoxiaobai/p/7440114.html
Copyright © 2011-2022 走看看