zoukankan      html  css  js  c++  java
  • Django

    理解部分

    软件开发架构

    • C/S:客户端/服务端
    • B/S:浏览器/服务器
    • ps:BS本质也是CS

    HTTP协议(超文本传输协议)

    • 定义:浏览器客户端与万维网服务器之间通信的相关规则。

    • 四大特性

      • 基于TCP/IP之上作用于应用层
      • 基于请求响应
      • 无状态(cookies session token)
      • 无连接(one night 情)
        • 长连接(websocket——软件加聊天功能,HTTP协议的大补丁)
    • 数据格式

      • 请求格式:请求首行(请求方式、协议版本) 请求头(一大堆键值对) 请求体(真正的数据,只有发post请求时才有,get请求不会有)
      • 响应格式:响应首行 响应头 响应体
    • 响应状态码:用特定的数字表示一些意思

      • 1XX:服务端已经成功接收到数据,正在处理,可继续提交数据
      • 2XX:服务端成功响应(200请求成功)
      • 3XX:重定向(eg:未登录跳转到登录页面)
      • 4XX:请求错误(404:请求资源不存在;403:拒绝访问,请求不符合访问标准)
      • 5XX:服务器内部错误(500)
    • 服务端

      • 24小时提供服务
      • 固定的IP和端口
      • 承受高并发(多个客户端连接)
    • 请求方式

      • get请求:朝别人要数据
      • post请求:向别人提交数据(eg:用户登录)
    • 端口号:标识计算机上某一个应用程序

      • 0-1024:操作系统默认
      • 1024-8000:常用软件默认端口
        • mysql:3306
        • Redis:6379
        • MongoDB:27017
        • flask:5000
        • Django:8000
      • 8000以后:避免端口冲突
    • url:统一资源定位符,相当于图书馆里面的书分类

    • 纯手撸web框架

      • 手动书写socket
      • 手动处理HTTP格式数据
    • 基于wsgiref模块

      • 该模块实现了上面两个手动的过程
      • 根据功能的不同拆分成不同的py文件
        • urls.py——路由(后缀名)与视图函数(将HTML页面放到浏览器渲染)对应关系。
        • views.py——存放视图函数(用于处理业务逻辑--函数和类)
        • templates——模板文件夹(一堆HTML文件)
    • 动静态网页

      • 静态网页:数据是写死的,万年不变
      • 动态网页:数据是实时获取的
        • 后端获取当前时间展示到前端
        • 后端获取数据库中的数据展示到前端
    • jinja2模块

      • 模板渲染:后端获取的数据类型传递给HTML页面

      • 模板语法(极其贴近python后端语法)

        • {{ 变量名 }}

        • {% for %}

          {{ 变量名 }}

          {% endfor %}

    • 简易版本请求流程图

      img

    • Python三大主流web框架及框架组成

      • 框架组成

        • A:socket部分
        • B:路由与视图函数对应关系
        • C:模板语法
      • Django

        • 优点:大而全,自带功能特别多
        • 缺点:过于笨重
        • A别人wsgiref,B、C自己
      • Flask

        • 优点:小而精,自带功能特别少,三方模块特别多
        • 缺点:依赖第三方模块
        • A别人werkeug(基于wfgiref)、B自己、C别人(jinja2)
      • Tornado

        • 异步(任务提交方式)非阻塞(软件运行状态),支持高并发,可开发游戏服务器
        • 三者自己
    • Django注意事项

      • 计算机名称不能用中文
      • 一个pycharm窗口就是一个项目
      • 项目名尽量不要用中文
    • Django版本问题

      • 1.11.9~1.11.13(LTS可维护版本)
      • 1.X;2.X
      • pycharm配置中安装
    • 如何验证Django是否安装成功?

      • 命令行输入django-admin
    • 如何使用Django?

      • 一个Django项目类似于一所大学,而app就类似于大学里面的学院,Django其实就是用来开发一个个应用的,一个app就相当于一块独立的功能,并支持任意多个app

      • 命令行使用

        • 创建Django项目
          • django-admin startproject 项目名
        • 启动Django项目
          • python manage.py runserver
        • 创建应用app
          • python manage.py startapp app01
        • 注意
          • 不会自动创建templates文件夹
          • settings文件夹,需要手动配置templates路径os.path.join(BASE_DIR, 'templates')
          • django server配置端口号,保证一个项目一个端口号
          • 新创建的app需要settings配置文件中注册app01.apps.App01Config,简写app01,而pycharm只会注册第一个创建项目时的应用。
          • 创建其他app:tools->run manage.py task 命令行输入关键字startapp app名,并且一定要注册
      • pycharm使用

    • 项目名

      • 跟项目名同名的文件夹
        • settings.py配置文件
        • urls.py路由与视图函数对应关系
      • 应用名
        • migrations文件夹
          • 存放数据库迁移记录,类似于日志
        • admin.py django后台管理
        • apps.py注册相关
        • models.py模型类
        • tests.py测试文件
        • views.py存放视图函数
      • templates文件夹
        • 存放HTML文件
      • manage.py django入口文件

    实践部分

    需求1:如何实现将服务端的数据发送到浏览器客户端?

    思路:

    • 如何将浏览器客户端与服务端建立链接
    • 根据HTTP协议
    import socket
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        data = conn.recv(1024)
        conn.send(b'hello world')
        conn.close()
    

    img

    此时浏览器客户端访问服务端IP和端口报错,这是因为服务端确实发消息了,但是浏览器客户端不认识,即发送的响应无效,那应该怎么解决呢?想到的是发送的消息应该遵循HTTP协议。

    import socket
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        data = conn.recv(1024)
        print(data)
        conn.send(b'HTTP:1.1 200 OK 
    
    <h1>hello world</h1>')
        conn.close()
    

    img

    效果是酱紫的,并且可以将服务器上的内容渲染到浏览器上,那我们再来看看服务端返回什么结果呢?HTTP协议数据

    b'GET / HTTP/1.1
     # 请求首行(请求方式和协议版本)
    Host: 127.0.0.1:8080
    
    Connection: keep-alive
    
    Cache-Control: max-age=0
    
    Upgrade-Insecure-Requests: 1
    
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36
    
    Sec-Fetch-Mode: navigate
    Sec-Fetch-User: ?1
    
    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
    
    Sec-Fetch-Site: none
    
    Accept-Encoding: gzip, deflate, br
    
    Accept-Language: zh-CN,zh;q=0.9
     #请求头:一大堆键值对,用来标识一些信息
    
    ' #请求体:一定要注意这里是空行并且没有数据,因为这是get请求,如果发post请求就有数据啦
    

    需求2:如何根据用户不同的输入地址,返回不同的内容?

    思路:

    • 如何拿到用户输入的地址(需求1就能拿到)--请求首行

    • 如何拿到后缀--但是此时是bytes格式,必须转成字符串格式才能切割,这里介绍一种万能方法,从此再也不用担心encode与decode绕来绕去了:

      byte = b'hello world'
      s = str(byte, encoding='utf-8')
      print(s, type(s))  # hello world <class 'str'>
      b = bytes(s, encoding='utf-8')
      print(b, type(b))  # b'hello world' <class 'bytes'>
      

    img

    import socket
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        conn.send(b'HTTP:1.1 200 OK 
    
    ')
        data = conn.recv(1024)
        current_path = str(data, encoding='utf-8').split('
    ')[0].split(' ')[1]
        if current_path == '/login':
            # 业务逻辑
            conn.send(b'login')
        elif current_path == '/index':
            conn.send(b'index')
        else:
            conn.send(b'404')
        conn.close()
    

    img

    此时又出现新问题了,那就是作为一名Python开发攻城狮,始终不能忘了龟叔对Python的定义,那就是简洁优雅,不然如何提升逼格呢?随着后缀名的增多,你看socket套接字重复倒无所谓,但是这么多elif谁受得了呢,所有我们得想一种办法。那就是看看有什么模块能不能帮我们做这些事情,我们直接导包不就行了,还真有一种那就是wsgiref,自己手撸太累了,让别人帮我们撸socket部分和elif部分。

    需求3:如何基于wsgiref实现根据用户输入的不同URL后缀,返回的相同内容呢?

    from wsgiref.simple_server import make_server
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        print(env)
        return [b'hello world']
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    

    img

    需求4:如何基于wsgiref模块实现根据用户不同的输入地址,返回不同的内容?

    from wsgiref.simple_server import make_server
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        current_path = env.get('PATH_INFO')
        if current_path == '/index':
            return [b'index']
        elif current_path == '/login':
            return [b'login']
        else:
            return [b'404 error']
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    

    需求5:如何定义一种数据类型,存放后缀与函数名之间的对应关系,使得代码既不显得冗余,又可以实现更多的功能?

    from wsgiref.simple_server import make_server
    
    
    def index():
        return 'index'
    
    
    def login():
        return 'login'
    
    
    def error():
        return '404 error'
    
    
    urls = [
        ('/index', index),  # 定义成函数,方便处理业务逻辑
        ('/login', login),
    ]
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        current_path = env.get('PATH_INFO')
        # 先定义一个变量名,用于存储后续得到的变量名
        func = None
        # 利用for循环取后缀
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                break  # 主动结束匹配
        if func:
            res = func()
        else:
            res = error()
        return [bytes(res, encoding='utf-8')]  # 所有函数全部返回字符串,再转换成bytes格式
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    

    需求5:如何基于wsgiref实现分层,避免杂乱无章?

    # urls.py
    from views import *
    
    urls = [
        ('/index', index),
        ('/login', login),
    ]
    # views.py
    def index():
        return 'index'
    
    
    def login():
        return 'login'
    
    
    def error():
        return '404 error'
    # wsgiref.py
    from wsgiref.simple_server import make_server
    from urls import urls
    from views import *
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        current_path = env.get('PATH_INFO')
        # 先定义一个变量名,用于存储后续得到的变量名
        func = None
        # 利用for循环取后缀
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                break  # 主动结束匹配
        if func:
            res = func()
        else:
            res = error()
        return [bytes(res, encoding='utf-8')]  # 所有函数全部返回字符串,再转换成bytes格式
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    

    需求6:如何将后端获取的数据传递给HTML页面,页面上展示当前时间?

    • 文件的后缀名是给人看的。
    # urls.py
    from views import *
    
    urls = [
        ('/index', index),
        ('/login', login),
        ('/get_time', get_time),
    ]
    # wsgiref.py
    from wsgiref.simple_server import make_server
    from urls import urls
    from views import *
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        current_path = env.get('PATH_INFO')
        # 先定义一个变量名,用于存储后续得到的变量名
        func = None
        # 利用for循环取后缀
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                break  # 主动结束匹配
        if func:
            res = func()
        else:
            res = error()
        return [bytes(res, encoding='utf-8')]  # 所有函数全部返回字符串,再转换成bytes格式
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    # get_time.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>获取当前时间</title>
    </head>
    <body>
    <h1>ahkhafgl</h1>
    </body>
    </html>
    # views.py
    def index():
        return 'index'
    
    
    def login():
        return 'login'
    
    
    def error():
        return '404 error'
    
    
    def get_time():
        import datetime
        print(datetime.datetime.now())  # 2019-10-19 15:46:53.518173,转化成人类友好的字符串类型
        current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
        with open(r'D:	est	emplatesget_time.html', 'r', encoding='utf-8') as f:
            data = f.read()
        data = data.replace('ahkhafgl', current_time)
        return data
    

    需求7:如何实现把一个字典传给HTML页面,并且页面能把它当做字典来使用(点属性或方法得到其中的值)?

    # urls.py
    from views import *
    
    urls = [
        ('/get_user', get_user),
    ]
    # wsgiref.py
    from wsgiref.simple_server import make_server
    from urls import urls
    from views import *
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        current_path = env.get('PATH_INFO')
        # 先定义一个变量名,用于存储后续得到的变量名
        func = None
        # 利用for循环取后缀
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                break  # 主动结束匹配
        if func:
            res = func()
        else:
            res = error()
        return [bytes(res, encoding='utf-8')]  # 所有函数全部返回字符串,再转换成bytes格式
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    # get_user.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>获取字典</title>
    </head>
    <body>
    <h1>{{user}}</h1>
    <h2>{{user.name}}</h2>
    <h3>{{user['age']}}</h3>
    <h4>{{user.get('hobby')}}</h4>
    </body>
    </html>
    # views.py
    def get_user(env):  # env参数是为了方便后续可能使用
        from jinja2 import Template
        dic = {'name': 'allen', 'age': '18', 'hobby': ['read', 'music']}
        with open(r'D:	est	emplatesget_user.html', 'r', encoding='utf-8') as f:
            data = f.read()
        temp = Template(data)
        res = temp.render(user=dic)
        return res
    

    需求8:如何实现从数据库读取数据展示到前端?

    # urls.py
    from views import *
    
    urls = [
        ('/get_db', get_db),
    ]
    # wsgiref.py
    from wsgiref.simple_server import make_server
    from urls import urls
    from views import *
    
    
    def app(env, response):
        """
        :param env: 请求相关的所有数据,以字典形式返回-environ环境变量
        :param response: 响应相关的所有数据
        :return:
        """
        response('200 ok', [])  # 固定写法
        current_path = env.get('PATH_INFO')
        # 先定义一个变量名,用于存储后续得到的变量名
        func = None
        # 利用for循环取后缀
        for url in urls:
            if current_path == url[0]:
                func = url[1]
                break  # 主动结束匹配
        if func:
            res = func(env)
        else:
            res = error(env)
        return [bytes(res, encoding='utf-8')]  # 所有函数全部返回字符串,再转换成bytes格式
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8080, app)
        # 实时监听URL,只要有客户端是以('127.0.0.1',8080)来连接,后缀名统一交给app函数去处理
        server.serve_forever()  # 启动服务端
    # get_user.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <div class="container">
        <div class="row">
            <div class="col-md-8 col-md-offset-2">
                <h1 class="text-center">用户列表</h1>
                <table class="table table-bordered table-striped table-hover">
                    <thead>
                        <tr>
                            <th>id</th>
                            <th>name</th>
                            <th>pwd</th>
                        </tr>
                    </thead>
                    <tbody>
                        {% for user_dict in user_list %}
                            <tr>
                                <td>{{ user_dict.id }}</td>
                                <td>{{ user_dict.name }}</td>
                                <td>{{ user_dict.pwd }}</td>
                            </tr>
                        {% endfor %}
                    </tbody>
                </table>
            </div>
        </div>
    
    </div>
    </body>
    </html>
    # views.py
    from pymysql.cursors import DictCursor
        from jinja2 import Template
        conn = pymysql.connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            passwd='',
            db='db02',
            charset='utf8',
            autocommit=True
        )
        cursor = conn.cursor(DictCursor)  # 这样返回列表套字典,不然是元组
        sql = 'select * from user_info'
        cursor.execute(sql)  # 影响的行数
        res = cursor.fetchall() #返回列表套字典
        print(res)
        with open(r'D:	est	emplatesget_db.html', 'r', encoding='utf-8') as f:
            data = f.read()
        temp = Template(data)
        return temp.render(user_list=res)
    

    img

  • 相关阅读:
    TP框架基础
    PHP文件上传
    layui-简单的登录注册界面
    layui-注册界面
    JavaScript原生Ajax请求纯文本数据
    ajax异步请求数据
    PHP数据库连接
    PHP页面跳转以及伪登录实例
    OVER 分析函数
    Ubuntu 16 vi输入方向键会变成ABCD的解决方法
  • 原文地址:https://www.cnblogs.com/daizongqi/p/11743690.html
Copyright © 2011-2022 走看看