• django


    Django 

    django:Django是一个高级Python Web框架,鼓励快速开发和干净,实用的设计。由经验丰富的开发人员构建,它可以解决Web开发的大部分麻烦,因此您可以专注于编写应用程序而无需重新发明轮子。它是免费和开源的。

    采用了MTV的框架模式,即模型M,视图V和模版T。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的。

    • 用于创建模型的对象关系映射
    • 为最终用户设计的完美管理界面
    • 一流的 URL 设计
    • 设计者友好的模板语言
    • 缓存系统。
    层次
    职责
    模型(Model),即数据存取层
    处理与数据相关的所有事务: 如何存取、如何验证有效性、包含哪些行为以及数据之间的关系等。
    模板(Template),即表现层
    处理与表现相关的决定: 如何在页面或其他类型文档中进行显示。
    视图(View),即业务逻辑层
    存取模型及调取恰当模板的相关逻辑。模型与模板的桥梁。

    目前使用django版本为1.11 .11

    python 3.6

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

    半成品自定义web框架

    import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() while True: conn, addr = sk.accept() data = conn.recv(8096) conn.send(b"OK") conn.close()
     

    可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。

    用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?

    所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。

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

    HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?

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

    import socket sk = socket.socket() sk.bind(("127.0.0.1", 80)) sk.listen() while True: conn, addr = sk.accept() data = conn.recv(8096) print(data) # 将浏览器发来的消息打印出来 conn.send(b"OK") conn.close()
     

    输出:

    b'GET / HTTP/1.1 Host: 127.0.0.1:8080 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 DNT: 1 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv '
     

    然后我们再看一下我们访问博客园官网时浏览器收到的响应信息是什么。

    响应相关信息可以在浏览器调试窗口的network标签页中看到。

    点击view source之后显示如下图:

    我们发现收发的消息需要按照一定的格式来,这里就需要了解一下HTTP协议了。

    HTTP协议介绍

    HTTP协议对收发消息的格式要求

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

    HTTP GET请求的格式:

    HTTP响应的格式:

     处女版自定义web框架

    经过上面的补充学习,我们知道了要想让我们自己写的web server端正经起来,必须要让我们的Web server在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行,这样我们就实现了一个正经的Web框架了。

    import socket

    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('127.0.0.1', 8000))
    sock.listen()

    while True:
    conn, addr = sock.accept()
    data = conn.recv(8096)
    # 给回复的消息加上响应状态行
    conn.send(b"HTTP/1.1 200 OK ")
    conn.send(b"OK")
    conn.close()

     

    我们通过十几行代码简单地演示了web 框架的本质。

    接下来就让我们继续完善我们的自定义web框架吧!

    根据不同的路径返回不同的内容

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

    小事一桩,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...

    """
    根据URL中不同的路径返回不同的内容
    """

    import socket
    sk = socket.socket()
    sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
    sk.listen() # 监听


    while 1:
    # 等待连接
    conn, add = sk.accept()
    data = conn.recv(8096) # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
    # 按 分割
    data1 = data.split(" ")[0]
    url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同内容
    if url == "/index/":
    response = b"index"
    elif url == "/home/":
    response = b"home"
    else:
    response = b"404 not found!"

    conn.send(response)
    conn.close()

     

    根据不同的路径返回不同的内容--函数版

    上面的代码解决了不同URL路径返回不同内容的需求。

    但是问题又来了,如果有很多很多路径要判断怎么办?难道要挨个写if判断? 当然不用,我们有更聪明的办法。

    """
    根据URL中不同的路径返回不同的内容--函数版
    """

    import socket
    sk = socket.socket()
    sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
    sk.listen() # 监听


    # 将返回不同的内容部分封装成函数
    def index(url):
    s = "这是{}页面!".format(url)
    return bytes(s, encoding="utf8")


    def home(url):
    s = "这是{}页面!".format(url)
    return bytes(s, encoding="utf8")


    while 1:
    # 等待连接
    conn, add = sk.accept()
    data = conn.recv(8096) # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
    # 按 分割
    data1 = data.split(" ")[0]
    url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同内容,response是具体的响应体
    if url == "/index/":
    response = index(url)
    elif url == "/home/":
    response = home(url)
    else:
    response = b"404 not found!"

    conn.send(response)
    conn.close()

     

    根据不同的路径返回不同的内容--函数进阶版

    看起来上面的代码还是要挨个写if判断,怎么办?我们还是有办法!(只要思想不滑坡,方法总比问题多!)

    """
    根据URL中不同的路径返回不同的内容--函数进阶版
    """

    import socket
    sk = socket.socket()
    sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
    sk.listen() # 监听


    # 将返回不同的内容部分封装成函数
    def index(url):
    s = "这是{}页面!".format(url)
    return bytes(s, encoding="utf8")


    def home(url):
    s = "这是{}页面!".format(url)
    return bytes(s, encoding="utf8")


    # 定义一个url和实际要执行的函数的对应关系
    list1 = [
    ("/index/", index),
    ("/home/", home),
    ]

    while 1:
    # 等待连接
    conn, add = sk.accept()
    data = conn.recv(8096) # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
    # 按 分割
    data1 = data.split(" ")[0]
    url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同内容
    func = None # 定义一个保存将要执行的函数名的变量
    for i in list1:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b"404 not found!"

    # 返回具体的响应消息
    conn.send(response)
    conn.close()

     

    返回具体的HTML文件

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

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

    """
    根据URL中不同的路径返回不同的内容--函数进阶版
    返回独立的HTML页面
    """

    import socket
    sk = socket.socket()
    sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
    sk.listen() # 监听


    # 将返回不同的内容部分封装成函数
    def index(url):
    # 读取index.html页面的内容
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    # 返回字节数据
    return bytes(s, encoding="utf8")


    def home(url):
    with open("home.html", "r", encoding="utf8") as f:
    s = f.read()
    return bytes(s, encoding="utf8")


    # 定义一个url和实际要执行的函数的对应关系
    list1 = [
    ("/index/", index),
    ("/home/", home),
    ]

    while 1:
    # 等待连接
    conn, add = sk.accept()
    data = conn.recv(8096) # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
    # 按 分割
    data1 = data.split(" ")[0]
    url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同内容
    func = None # 定义一个保存将要执行的函数名的变量
    for i in list1:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b"404 not found!"

    # 返回具体的响应消息
    conn.send(response)
    conn.close()

     

    让网页动态起来

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

    没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)

    """
    根据URL中不同的路径返回不同的内容--函数进阶版
    返回HTML页面
    让网页动态起来
    """

    import socket
    import time

    sk = socket.socket()
    sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
    sk.listen() # 监听


    # 将返回不同的内容部分封装成函数
    def index(url):
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    now = str(time.time())
    s = s.replace("@@oo@@", now) # 在网页中定义好特殊符号,用动态的数据去替换提前定义好的特殊符号
    return bytes(s, encoding="utf8")


    def home(url):
    with open("home.html", "r", encoding="utf8") as f:
    s = f.read()
    return bytes(s, encoding="utf8")


    # 定义一个url和实际要执行的函数的对应关系
    list1 = [
    ("/index/", index),
    ("/home/", home),
    ]

    while 1:
    # 等待连接
    conn, add = sk.accept()
    data = conn.recv(8096) # 接收客户端发来的消息
    # 从data中取到路径
    data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
    # 按 分割
    data1 = data.split(" ")[0]
    url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OK ') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
    # 根据不同的路径返回不同内容
    func = None # 定义一个保存将要执行的函数名的变量
    for i in list1:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b"404 not found!"

    # 返回具体的响应消息
    conn.send(response)
    conn.close()

     

    服务器程序和应用程序

    对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。

    服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

    应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。

    这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

    这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

    常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

    从这继续...

    wsgiref

    我们利用wsgiref模块来替换我们自己写的web框架的socket server部分:

    """
    根据URL中不同的路径返回不同的内容--函数进阶版
    返回HTML页面
    让网页动态起来
    wsgiref模块版
    """

    import time
    from wsgiref.simple_server import make_server


    # 将返回不同的内容部分封装成函数
    def index(url):
    with open("index.html", "r", encoding="utf8") as f:
    s = f.read()
    now = str(time.time())
    s = s.replace("@@oo@@", now)
    return bytes(s, encoding="utf8")


    def home(url):
    with open("home.html", "r", encoding="utf8") as f:
    s = f.read()
    return bytes(s, encoding="utf8")


    # 定义一个url和实际要执行的函数的对应关系
    list1 = [
    ("/index/", index),
    ("/home/", home),
    ]


    def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO'] # 取到用户输入的url
    func = None
    for i in list1:
    if i[0] == url:
    func = i[1]
    break
    if func:
    response = func(url)
    else:
    response = b"404 not found!"
    return [response, ]


    if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8090, run_server)
    print("我在8090等你哦...")
    httpd.serve_forever()

     

    jinja2

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

    下载jinja2:

    pip install jinja2

    <!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">
    <title>Title</title>
    </head>
    <body>
    <h1>姓名:{{name}}</h1>
    <h1>爱好:</h1>
    <ul>
    {% for hobby in hobby_list %}
    <li>{{hobby}}</li>
    {% endfor %}
    </ul>
    </body>
    </html>

    index2.html文件

    使用jinja2渲染index2.html文件:

    from wsgiref.simple_server import make_server
    from jinja2 import Template


    def index():
    with open("index2.html", "r") as f:
    data = f.read()
    template = Template(data) # 生成模板文件
    ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]}) # 把数据填充到模板里面
    return [bytes(ret, encoding="utf8"), ]


    def home():
    with open("home.html", "rb") as f:
    data = f.read()
    return [data, ]


    # 定义一个url和函数的对应关系
    URL_LIST = [
    ("/index/", index),
    ("/home/", home),
    ]


    def run_server(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
    url = environ['PATH_INFO'] # 取到用户输入的url
    func = None # 将要执行的函数
    for i in URL_LIST:
    if i[0] == url:
    func = i[1] # 去之前定义好的url列表里找url应该执行的函数
    break
    if func: # 如果能找到要执行的函数
    return func() # 返回函数的执行结果
    else:
    return [bytes("404没有该页面", encoding="utf8"), ]


    if __name__ == '__main__':
    httpd = make_server('', 8000, run_server)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()

     

    现在的数据是我们自己手写的,那可不可以从数据库中查询数据,来填充页面呢?

    使用pymysql连接数据库:

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

     

    创建一个测试的user表:

    CREATE TABLE user( id int auto_increment PRIMARY KEY, name CHAR(10) NOT NULL, hobby CHAR(20) NOT NULL )engine=innodb DEFAULT charset=UTF8;
     

    模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。

    Django

    Django官网下载页面

    安装(安装最新LTS版):

    pip3 install django==1.11.9
     

    创建一个django项目:

    下面的命令创建了一个名为"mysite"的Django 项目:

    django-admin startproject mysite
     

    目录介绍:

    mysite/ ├── manage.py # 管理文件 └── mysite # 项目目录 ├── __init__.py ├── settings.py # 配置 ├── urls.py # 路由 --> URL和函数的对应关系 └── wsgi.py # runserver命令就使用wsgiref模块做简单的web server
     

    运行Django项目:

    python manage.py runserver 127.0.0.1:8000
     

    模板文件配置:

    TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, "template")], # template文件夹位置 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
     

    静态文件配置:

    STATIC_URL = '/static/' # HTML中使用的静态文件夹前缀 STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), # 静态文件存放位置 ]
     

    看不明白?有图有真相:

    刚开始学习时可在配置文件中暂时禁用csrf中间件,方便表单提交测试。

    MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]

     

    Django基础必备三件套:

    from django.shortcuts import HttpResponse, render, redirect
     

    HttpResponse

    内部传入一个字符串参数,返回给浏览器。

    三剑客 render redirect Httpresponse 都返回Httpresponse对象

    例如:

    def index(request): # 业务逻辑代码 return HttpResponse("OK")
     

    render

    除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。

    将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2)

    例如:

    def index(request): # 业务逻辑代码 return render(request, "index.html", {"name": "alex", "hobby": ["烫头", "泡吧"]})
     

    redirect

    接受一个URL参数,表示跳转到指定的URL。

    例如:

    def index(request): # 业务逻辑代码 return redirect("/home/")
     

    重定向是怎么回事?

    启动Django报错:

    Django 启动时报错 “UnicodeEncodeError ...”

    报这个错误通常是因为计算机名为中文,改成英文的计算机名重启下电脑就可以了。

     Django 启动报错“SyntaxError: Generator expression must be parenthesized”

    报这个错很大可能是因为使用了Python3.7.0,而目前(2018-06-12)Python3.7.0和Django还有点兼容性问题,换回Python3.6的环境即可。

     

  • 相关阅读:
    作用域 + this指向 的一道没面试题
    找出数组中最大的值
    统计数组中每个值出现的次数, 统计对象中每个字符出现的次数
    uniapp在h5 和 APP 端兼容性 bug 解决方案
    数组去重的常用方法,利用Promise实现函数按序执行
    momentjs实现距离当前时长并且回现中文效果
    SQL server 上机练习题
    JS 9
    JS 8
    JS 7
  • 原文地址:https://www.cnblogs.com/Sunbreaker/p/11536586.html
Copyright © 2020-2023  润新知