• 初识Django


    一 使用Django前的准备

    1. http协议

      Django是python的web框架之一,既然要学习web框架,首先就要明白什么是http协议。

      首先,超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。它规定了客户端与服务端消息传输的格式,是客户端和服务器端请求和应答的标准(TCP)。

      1.1 四大特性:

        1. 基于TCP/IP协议,作用于应用层的协议
        2. 基于请求响应(每一次点击都是一次请求)
        3. 无状态(同一客户端无论连接多少次,它都当你第一次来)
        4. 无连接(websocket,数据请求完成之后就断开)

      1.2 数据格式之请求:

        请求首行()
        请求头(一堆k,v键值对)
        (这里是空格,数据格式请求之一!!!)
        请求体(post请求携带的数据)

        

      1.3 数据格式之响应

        响应首行()
        响应头(一堆k,v键值对)
        (这里是空格,数据格式响应之一!!!)
        响应体(post请求携带的数据)

       

      1.4 响应状态码

        1xx 服务器已经成功接收到你的数据正在处理,你可以继续提交其他数据
        2xx 请求成功 服务器已经将你的请求的数据发送给你了
        3xx 重定向
        4xx 请求资源错误/不存在
        5xx 服务器错误

        状态码详细信息点我

      注意:我们通常使用谷歌浏览器,可以通过 检查->Network查看网页加载的信息,里面有数据格式的信息。

    2. 动静态页面

      静态页面:页面上的数据都是写死的,万年不变

      动态页面:

        页面上的数据是从后端动态获取的
        比如后端获取当前时间
        后端获取数据库数据然后传递给前端页面

    3. 什么是web框架

      Web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。其类型有基于请求的和基于组件的两种框架。

      目前python有三大主流框架:

    1. Django:大而全,自带了很多功能模块,类似航空母舰(缺点:有点笨重),目前用的比较多
    2. Flask:短小精悍,自带的功能模块很少,大部分都依赖于第三方模块(小而轻)
    3. Tornado:异步非阻塞,主要用来处理高IO 多路复用的情况,可以写游戏后端

      

    4. 模块渲染

      后端生成的数据直接传递给前端页面使用(并且前端页面可以灵活操作该数据)》》》模板语法
    模板渲染 模板语法需要依赖于第三方模块(这里使用jinja2模块):
        pip install jinja2
        from jinja2 import Template
      模板语法 jinja2支持前端直接使用类似于python的语法操作数据
        1. 后端打开HTML文件,用r模式读取数据,用data变量接收
        2. tem = Template(data) //提交渲染页面
        3. tem.render(user_dic = {'name': 'jason', 'password': 123})
      //这样过后,前端可以通过user_dic拿到该字典,固定语法如下{{}},里面的字典中写的语法类似于python
        <p>{{user_dic}}</p>
        <p>{{user_dic.name}}</p>
        <p>{{user_dic['password']}}</p>
        <p>{{user_dic.get('name')}}</p>
      //for模板循环语法
        {%for user in user_dict%}  //user_dict是数据库cursor.fetchall()回来的数据:[{},{},{}]
          循环体代码
        {%endfor%}
      //实现将改数据发给前端,前端用table美化数据展示
      该for循环放在table的tbody里面,每一次循环都生成一次tr
      //templates文件夹用来存放所有的HTML文件

     5. 仿Django框架推导

    1. 新建一个socket TCP服务端,为了实现浏览器连接服务端,
           让服务端消息符合HTTP协议(在开头加一个conn.send(b'HTTP/1.1 200 OK 
    
    '))
        2. 想要实现网页地址加不同的后缀能显示不同的内容(如:127.0.0.1:8080/index,网页显示index)
        3. data = conn.recv(1024), 打印该data,发现里面装的二进制存的是数据格式之请求
        4. 将data解码为字符串,字符串二次切分(1:'
    ', 2:' ')列表list索引1取到的就是地址后缀
        5. if list[1] == '/index': conn.send('index');这样就实现了
        6. 还不够方便,要实现并发就要导入模块,这使用wsgire模块(自动将请求与接收的数据封装成符合HTTP协议的格式)
          (使用同socketserver有点像,第二个参数传run(socketserver是传一个类名,这里run是函数名))
        7. run会接收到两个参数env, response,env就是之前data解码后的字符串,不过被自动切分并组成大字典的形式,
           env['PATH_INFO']就是后缀名,response用来设置HTTP响应的状态码和头信息
        8. 后缀名多了之后要写一堆elif判断,所以抽离出一个对应关系urls = [('后缀名', 函数名), ('后缀名', 函数名)]
        9. urls叫路由,函数叫做视图函数,我们将它们各放入其他模块里(urls.py, views.py),之后导入模块即可。
        10. 为了后端数据(比如字典)发给前端使用,引入jinja2模块。
            from jinja2 import Template
            def reg(env):
                user_dic = {'name': 'jason', 'password': 123}
                with open('html文件名', 'r', encoding='utf-8') as f:
                    data = f.read()
                tem = Template(data)  //提交渲染页面
                return tem.render(user_dic=user_dic)
    仿Django框架推导(随便看看就好,别当真)

       二话不说,直接上代码。

      5.1 第一步,我们通过发送头conn.send('HTTP/1.1 200 ok /r/n/r/n')消息能成功展示在网页上

    import socket
    
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8081))
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
        # 一定要先接收一下信息,因为网页作为客户端连接会发送GET请求
        data = conn.recv(1024)
        # 先发送一个头信息,让接下去发送的消息符合HTTP协议
        conn.send(b'HTTP/1.1 200 OK 
    
    ')
        conn.send(b'hello')
        conn.close()
    1. 简单实现网页连接服务端

        5.2 第二步,根据地址后缀的不同展示不同的内容

    import socket
    
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8081))
    server.listen(5)
    
    while True:
        conn, addr = server.accept()
        # 一定要先接收一下信息,因为网页作为客户端连接会发送GET请求
        data = conn.recv(1024)
        # 将data输出,然后复制出来看看,可以找到规律用split切分
        print(data)
        data = data.decode('utf-8').split('
    ')[0]
        current_path = data.split(' ')[1]
        # 先发送一个头信息,让接下去发送的消息符合HTTP协议
        conn.send(b'HTTP/1.1 200 OK 
    
    ')
        if current_path == '/index':
            conn.send(b'index')
        else:
            conn.send(b'hello')
        conn.close()
    2. 拿127.0.0.1:8081/index 举例

      5.3 第三步, 导入wsgiref模块实现并发(它会封装请求和接收的数据,让数据符合HTTP协议)

    from wsgiref.simple_server import make_server
    
    
    def run(env, response):
        # response是response用来设置HTTP响应的状态码和头信息,状态码固定'200 OK'
        # 状态码类似我们之前的conn.send(b'HTTP/1.1 200 OK 
    
    ')
        # 然后头信息固定是列表里面套元组的形式,元组里必须要有两个元素,元组的个数至少一个
        response('200 ok', [('name', 'egon'), ('password', '123')])
        current_path = env['PATH_INFO']
        if current_path == '/index':
            res = 'index'
        else:
            res = '404 not find'
        # return的也要求是列表里面一个二进制数据
        return [res.encode('utf-8')]
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8081, run)
        server.serve_forever()
    wsgiref实现根据地址后缀展示不同内容

      5.4 第四步,当后缀名越来越多,if判断分支变多,所以需要抽离代码来优化。

    from wsgiref.simple_server import make_server
    
    
    def index():
        return 'index'
    
    
    urls = [
        ('/index', index)
    ]
    
    
    def error():
        return '404 not found'
    
    
    def run(env, response):
        # response响应状态码和头信息
        response('200 ok', [('name', 'egon'), ('password', '123')])
        # 取出地址后缀
        current_path = env['PATH_INFO']
        func = None
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                # 只要找到了就终止for循环
                break
        if func:
            res = func()
        else:
            res = error()
        return [res.encode('utf-8')]
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, run)
        server.serve_forever()
    第一步优化

      5.5 第五步, 再次优化, 将抽离出来的urls和响应执行的函数分别作为一个模块。

    from wsgiref.simple_server import make_server
    from urls import *
    
    
    def run(env, response):
        response('200 ok', [('name', 'egon'), ('password', '123')])
        current_path = env['PATH_INFO']
        func = None
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                break
        if func:
            res = func()
        else:
            res = error()
        return [res.encode('utf-8')]
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, run)
        server.serve_forever()
    服务器servser代码
    import time
    from jinja2 import Template
    import pymysql
    
    
    def index():
        return 'index'
    
    
    def error():
        return '404 not found'
    
    
    def get_time():
        with open('templates/get_time.html', 'r', encoding='utf-8') as f:
            data = f.read()
        res = data.replace('time', time.strftime('%Y-%m-%d %X'))
        return res
    
    
    def get_user():
        user_dic = {'name': 'egon', 'password': '123'}
        with open('templates/get_user.html', 'r', encoding='utf-8') as f:
            data = f.read()
        tem = Template(data)
        return tem.render(user_dic=user_dic)
    
    
    def get_db():
        # 连接数据库
        conn = pymysql.connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            password='root',
            database='day54',
            charset='utf8',
            autocommit=True
        )
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        sql = 'select * from web_frame'
        cursor.execute(sql)
        # 得到数据库里web_frame表所有的数据:[{}, {}, {}]
        user_list = cursor.fetchall()
        with open('templates/get_db.html', 'r', encoding='utf-8') as f:
            data = f.read()
        tem = Template(data)
        return tem.render(user_list=user_list)
    抽离出的views.py代码
    from views import *
    
    urls = [
        ('/index', index),
        ('/get_time', get_time),
        ('/get_user', get_user),
        ('/get_db', get_db)
    ]
    抽离出的urls.py代码
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>get_time</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    @@time@@
    </body>
    </html>
    views.py get_time函数需要用的html文件
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>get_user</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
    <p>{{user_dic}}</p>
    <p>{{user_dic.name}}</p>
    <p>{{user_dic['name']}}</p>
    <p>{{user_dic.get('password')}}</p>
    </body>
    </html>
    views.py get_user函数需要用的html文件
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>get_db</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <style>
            th {
                text-align: center;
            }
        </style>
    </head>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-8">
                <table class="table table-bordered table-striped table-hover">
                    <thead>
                        <tr>
                            <th>id</th>
                            <th>框架名称</th>
                            <th>socket</th>
                            <th>路由器与视图函数</th>
                            <th>模块渲染</th>
                        </tr>
                    </thead>
                    <tbody class="text-center">
                        {%for user_dic in user_list%}
                            <tr>
                                <td>{{user_dic['id']}}</td>
                                <td>{{user_dic['框架名称']}}</td>
                                <td>{{user_dic['socket']}}</td>
                                <td>{{user_dic['路由器与视图函数']}}</td>
                                <td>{{user_dic['模块渲染']}}</td>
                            </tr>
                        {%endfor%}
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    </body>
    </html>
    views.py get_db函数需要用的html文件

      上述就是用Django之前的最终版本了,里面还加了一点东西:

    1. 用jinja2模块渲染网页,实现后端数据发给前端后,前端也能像后端一样处理数据。(语法看上面4.模块渲染)。
    2. 用pymysql模块连接数据库,并将数据库取出的内容发给前端(该HTML页面也先用jinja2渲染)。前端运动Bootstrap的table展示内容。

      总结:

      关于wsfiref.simple_server中的make_server的使用

    • wsfiref.simple_server中的make_server的使用与socketserver模块使用有点像。不过它的第二个参数是函数名,函数会接收到两个参数env和response(这两个只是变量名),第一个是大字典(客户端连接时发送的GET请求数据被切分后组成的字典,其中env['PATH_INFO']能拿到地址的后缀)。然后response是用来设置HTTP想要的状态码和信息头,固定写法--列表里面两个元素['200 OK',[('信息1', '信息2')]] ,第一个元素是状态码字符串,第二个元素是列表嵌套元组(元组必须有两个元素,但是元组个数不限)。
    • run函数必须return一个存放二进制数据的列表。 ['二进制字符串']
    • jinja2渲染页面基本写法:

    def reg():
        user_dic = {'name': 'jason', 'password': '123'}
        with open('html文件名', 'r', encoding='utf-8') as f:
            data = f.read()
        tem = Template(data)  #提交渲染页面
        # 将user_dic传给前端
        return tem.render(user_dic=user_dic)

    二. 关于Django的部分知识

    1. 要正常运行Django的注意事项

      1.1 注意事项(要正常跑Django,遵循以下原则):
      1. 计算机名称不能有中文
      2. 一个pycharm窗口就是一个项目,不要多项目放同一个窗口里面
      3. 项目名不能有中文

      1.2 强调:
      1. 用Django一定要保证只有一个在运行状态
      2. 记得清楚浏览器缓存(谷歌浏览器:检查->右上角三个点->settings->Network->勾选其中的Disable cache)

      

    2. 版本问题

      Django下载(LTS代表官方还在维护):

      推荐下载1.11.11版本
        1. 命令行直接下载:pip install django==1.11.11
        2. pycharm下载settings里面project->interpreter->+搜索django下载

      验证是否下载成功(用cmd窗口):
        django-admin

     3. Django中app的概念

      如果说Django是一个学校,app就是各个学院,每个app对应一个功能。所以Django项目新建的时候我们肯定要建app,空荡荡的学校多恐怖。

     4. 创建Django项目的两种方式:

      4.1 方式一(命令行创建)
        创建Django项目:
          django-admin startproject 项目名
          cd 项目名
        创建app应用
          python3 manage.py startapp app01
        启动Django服务
          python3 manage.py runserver

        关闭Django服务

          ctrl + c
        ps:用命令行创建Django项目时不会自动创建templates文件夹
          需要手动创建(随后需要去配置文件配置TEMPLATES,该该文件夹路径加入其中)

      4.2 方式二(pycharm创建)
        new project-> Django-> Application name写上(环境不要用虚拟的)->勾选左下角Enable Django admin(后台管理)

        

        创建app:
        1. pycharm命令行创建(tab键可以补全)
         python3 manage.py startapp app01

        2. tools -> run manage.py task

    5. Django各个文件的作用

      5.1 应用名
        migrations   数据库迁移记录相关数据
        admin.py     Django后台管理相关
        models.py   模型表相关
        views.py      视图函数相关(视图函数不一定就是函数,也可以是类),然后每个视图函数都必须返回一个HttpResponse(render和redirect内部也是return的HttpResponse)。
      5.2 项目名
        settings.py    配置文件

        urls.py          路由与视图函数的映射关系
        wsgi.py         wsgiref
      5.3 其他:  

        templates     项目用到的所有HTML文件
        manage.py   Django入口文件

      5.3 settings中需要注意的地方

      确认templates已加入环境变量

      DATABASES是用来连接数据库的,可以修改数据库名字来实现连接其他数据库。

     

      新建的app一定要加入到INSTALLED_APPS

      中间件MIDDLEWARE

    6. Django给前端传值的两种方式

      6.1 Django小白必会三板斧(每个视图函数都要有一个request参数,因为Django会自动传一个参数,我们就要拿一个参数接收)
        from django.shorcuts import render, HttpResponse, redirect
        HttpResponse 返回字符串
        redirect 重定向,既可以重定向至别人的网址,也可以是自己的网址
        render 返回一个HTML页面 render(request, 'html文件名')
      6.2 两种给前端传值的方式
        第一种:

    def reg(request):
        user_dic = {'name': 'jason', 'password': 123}
        #locals()会把这个函数内出现的所有变量名都传给html文件
        return render(request, 'html文件名', locals()) 

        第二种:

    def reg(request):
        user_dic = {'name': 'jason', 'password': 123}
        return render(request, 'html文件名', {'user_dic': user_dic})

     注意:

    • 第一种有时候效率会比较低,如果变量名很多的话,而且我们并不是所有变量名都要用到。
    • django识别到你的代码变化之后会自动重启,但是有时候速度比较慢,我们可以手动重启,也可以多刷新几次前端页面。
    • django的render使用与jinja2的render不一样,不要搞混了。比如说要把一个字典user_dic给前端,jinja2是render(user_dic = user_dic);django是render(request, 'html文件名', {'user_dic': user_dic})或者是render(request, 'html文件名', locals())
    • django使用render时如果用locals(),它会把所在的那个函数里所有变量名都传给前端,简单粗暴。
    • 当我们在网页中输入URL比如:127.0.0.1:8080/login,并回车时,会发现URL变成了127.0.0.1:8080/login/,这并不是浏览器自动帮我们加上的,而是login在Django路由中匹配不到,它会帮我们变成login/再去匹配一次,内部走的是重定向(响应状态码3xx),上图。

  • 相关阅读:
    nsq 启动流程讲解
    nsq 初识
    【资料】http包接口和结构体
    http包详解 2
    http包详解 1
    openstack多节点部署运维
    一款简单实用的串口通讯框架(SerialIo)
    ~MySQL Perfect~
    linux创建用户设置密码
    linux安装tomcat且配置环境变量
  • 原文地址:https://www.cnblogs.com/maoruqiang/p/10982112.html
Copyright © 2020-2023  润新知