Web框架及Django初始化

Web框架本质

Web应用本质是一个socket套接字服务端,用户浏览器相当于socket客户端。

 自定义服务端

import socket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",8080))
server.listen(5)

while True:
    conn,client_addr=server.accept()
    data=conn.recv(1024)
    conn.send(b"hello")
    conn.close()

利用浏览器访问自定义的服务端("127.0.0.1",8080)出现下面结果

响应无效分析

响应无效,即服务端回应的内容浏览器无法识别。浏览器访问网站需遵循HTTP协议,则服务端发送的信息需遵循HTTP协议的规则。

HTTP协议主要规定了客户端和服务端之间的通信规则。

HTTP协议GET方法的请求格式

HTTP协议的响应格式

自定义服务端修改

import socket
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8090))
server.listen(5)

while True:
    conn,client_addr=server.accept()
    data=conn.recv(1024)
    #按照HTTP协议响应格式,给回复信息加上响应状态行
    conn.send(b"HTTP/1.1 200 OK

")
    #响应头部信息为空
    conn.send(b"hello")
    conn.close()

#浏览器显示结果hello

自定义服务端功能完善

1、根据不同路径返回不同内容

分析客户端请求的URL,根据不同的URL给出不同的响应

利用if判断回应不同请求

"""
根据浏览器访问的路径的不同,返回不同的内容
"""
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8090))
server.listen()

while 1:
    conn, addr = server.accept()
    data = conn.recv(8096)  # 收消息
    # print(data)
    # 从浏览器发送消息中,拿到用户访问的路径
    data_str = str(data, encoding="utf8")
    # 收到的消息格式bytes类型,需要转换成字符串进行下面的分割操作,
    # 根据回车符换行符(
)将请求信息分割得到包含请求信息的列表,
    # 第一个元素为请求状态行,再按空格分割状态行,取出第二个元素,即URL
    url = data_str.split("
")[0].split(" ")[1]
    print(url)

    #根据不同的URL返回不同的信息
    if url == "/home/":
       msg = b'<h1>home page</h1>'
    elif url == "/index/":
        msg = b'<h1>index page</h1>'
    else:
        msg = b'<h1>404</h1>'

    conn.send(b'HTTP/1.1 200 OK

')
    conn.send(msg)
    conn.close()
View Code

包装成函数回应不同请求

"""
根据浏览器访问的路径的不同,返回不同的内容
将不同页面的处理代码封装到函数中
"""
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8090))
server.listen()

def home(url):
    return b'<h1>home page</h1>'

def index(url):
    return b'<h1>index page</h1>'

while 1:
    conn, addr = server.accept()
    data = conn.recv(1024)  # 收消息
    # print(data)
    # 从浏览器发送消息中,拿到用户访问的路径
    data_str = str(data, encoding="utf8")
    # print(data_str)
    url = data_str.split("
")[0].split(" ")[1]
    print(url)

    #路径不同执行不同的函数,即返回不同的信息
    if url == "/home/":
       msg = home(url)
    elif url == "/index/":
        msg = index(url)
    else:
        msg = b'<h1>404</h1>'

    conn.send(b'HTTP/1.1 200 OK

')
    conn.send(msg)
    conn.close()
函数+if判断
""
根据浏览器访问的路径的不同,返回不同的内容
将不同页面的处理代码封装到函数中
优化频繁的if判断

"""
import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8090))
server.listen()

def home(url):
    s = "this is {} page!".format(url)
    return bytes(s, encoding="utf8")

def index(url):
    return b'<h1>index page</h1>'

def user(url):
    return b'hehe'

while 1:
    conn, addr = server.accept()
    data = conn.recv(8096)  # 收消息
    # print(data)
    # 从浏览器发送消息中,拿到用户访问的路径
    data_str = str(data, encoding="utf8")
    # print(data_str)
    url = data_str.split("
")[0].split(" ")[1]
    print(url)

    url2func = [
        ("/index/", index),
        ("/home/", home),
        ("/user/", user),
    ]
    func = None
    for i in url2func:
        if url == i[0]:
            func = i[1]  # 拿到将要执行的函数
            break
    else:
        func = None
    if func:
        msg = func(url)  # 执行对应的函数
    else:
        msg = b'<h1>404</h1>'  # 找不到要执行的函数就返回404

    conn.send(b'HTTP/1.1 200 OK

')
    conn.send(msg)
    conn.close()
简化if判断

2、返回具体的HTML文件

将HTML文件打开,以bytes类型发给客户端

import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8090))
server.listen()

def home(url):
    s = "this is {} page!".format(url)
    return bytes(s, encoding="utf8")

def index(url):
    return b'<h1>index page</h1>'

def user(url):
    return b'hehe'

# 请求URL为login时,返回具体的HTML文件
# 打开相应的HTML文件,发给客户端浏览器
# 浏览器根据HTML文件渲染响应信息
def login(url):
    with open("login.html", "rb") as f:
        return f.read()

# url和将要执行的函数的对应关系
url2func = [
    ("/index/", index),
    ("/home/", home),
    ("/user/", user),
    ("/login/", login),
]
while 1:
    conn, addr = server.accept()
    data = conn.recv(8096)  # 收消息
    # print(data)
    # 从浏览器发送消息中,拿到用户访问的路径
    data_str = str(data, encoding="utf8")
    # print(data_str)
    url = data_str.split("
")[0].split(" ")[1]
    print(url)

    func = None
    for i in url2func:
        if url == i[0]:
            func = i[1]  # 拿到将要执行的函数
            break
    else:
        func = None

    if func:
        msg = func(url)  # 执行对应的函数
    else:
        msg = b'<h1>404</h1>'  # 找不到要执行的函数就返回404

    # 按照HTTP协议的格式要求 回复消息
    conn.send(b'HTTP/1.1 200 OK

')  # 发送状态行
    conn.send(msg)  # 发送响应体
    conn.close()
添加HTML文件回应

 3、返回动态的HTML文件

在网页中定义特殊符号,用动态的数据去替换其,从而使不同访问者得到不同的数据

import socket
import time

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8090))
server.listen()

def home(url):
    s = "this is {} page!".format(url)
    return bytes(s, encoding="utf8")

def index(url):
    return b'<h1>index page</h1>'

def user(url):
    # 不同的用户得到的页面上显示不同的时间
    # 用time模块得到当前的时间戳
    c_time = str(time.time())
    with open("user.html", "r") as f:
        data_s = f.read()
        data_s = data_s.replace("@@xx@@", c_time)  # 替换后的字符串数据
        return bytes(data_s, encoding="utf8")

def login(url):
    with open("login.html", "rb") as f:
        return f.read()

# url和将要执行的函数的对应关系
url2func = [
    ("/index/", index),
    ("/home/", home),
    ("/user/", user),
    ("/login/", login),
]

while 1:
    conn, addr = server.accept()
    data = conn.recv(8096)  # 收消息
    # print(data)
    # 从浏览器发送消息中,拿到用户访问的路径
    data_str = str(data, encoding="utf8")
    # print(data_str)
    url = data_str.split("
")[0].split(" ")[1]
    print(url)


    func = None
    for i in url2func:
        if url == i[0]:
            func = i[1]  # 拿到将要执行的函数
            break
    else:
        func = None

    if func:
        msg = func(url)  # 执行对应的函数
    else:
        msg = b'<h1>404</h1>'  # 找不到要执行的函数就返回404

    # 按照HTTP协议的格式要求 回复消息
    conn.send(b'HTTP/1.1 200 OK

')  # 发送状态行
    conn.send(msg)  # 发送响应体
    conn.close()
View Code

服务器程序

真实开发中的Python web程序图解

web程序分为:服务器程序和web应用程序

服务器程序主要处理浏览器与服务端之间的通信。

服务器程序负责对socket服务器进行封装,以及对请求的各种数据进行整理。

web应用程序主要负责具体的业务逻辑:1、处理不同的访问,返回不同的页面;2、在HTML页面上展现从数据库拿到的数据。

WSGI服务器

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

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

wsgiref代替socket实现客户端与服务端的通信

import time
from wsgiref.simple_server import make_server

def home(url):
    s = "this is {} page!".format(url)
    return bytes(s, encoding="utf8")

def index(url):
    return b'<h1>index page</h1>'

def user(url):
    # 不同的用户得到的页面上显示不同的时间
    c_time = str(time.time())
    with open("user.html", "r") as f:
        data_s = f.read()
        data_s = data_s.replace("@@xx@@", c_time)  # 替换后的字符串数据
        return bytes(data_s, encoding="utf8")

def login(url):
    with open("login.html", "rb") as f:
        return f.read()

# url和将要执行的函数的对应关系
url2func = [
    ("/index/", index),
    ("/home/", home),
    ("/user/", user),
    ("/login/", login),
]

# 按照wsgiref的要求定义一个run_server函数
def run_server(environ, start_response):
    """
    :param environ: 跟请求相关的参数
    :param start_response:
    :return:
    """
    # 设置HTTP响应的状态码和头信息
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
    # text/html说明返回的是一个HTML的文本文件
    url = environ['PATH_INFO']  # 取到用户输入的url

    func = None
    for i in url2func:
        if url == i[0]:
            func = i[1]  # 拿到将要执行的函数
            break
    else:
        func = None

    if func:
        msg = func(url)  # 执行对应的函数
    else:
        msg = b'<h1>404</h1>'  # 找不到要执行的函数就返回404

    return [msg, ]

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

模板渲染工具jinja2

下载

pip3 install jinja2
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<h1>hello</h1>
<table border="1">
    <thead>
    <tr>
        <th>id</th>
        <th>姓名</th>
        <th>爱好</th>
    </tr>
    </thead>
    <tbody>
    {% for user in user_list %}
    <tr>
        <td>{{user.id}}</td>
        <td>{{user.name}}</td>
        <td>{{user.hobby}}</td>
    </tr>
    {% endfor %}
    </tbody>
</table>
</body>
</html>
user.html

使用jinja2渲染user.html文件

from wsgiref.simple_server import make_server
from jinja2 import Template

    with open("user.html", "r", encoding="utf8") as f:
        data_s = f.read()

        template = Template(data_s)  # 生成一个模板文件实例

        msg = template.render({"id": 1,"name":"chris","hobby":"book"})  # 把数据填充到模板里面
        return bytes(msg, encoding="utf8")

# url和将要执行的函数的对应关系
url2func = [
    ("/user/", user),
]

# 按照wsgiref的要求定义一个run_server函数
def run_server(environ, start_response):
    """
    :param environ: 跟请求相关的参数
    :param start_response:
    :return:
    """
    # 设置HTTP响应的状态码和头信息
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
    url = environ['PATH_INFO']  # 取到用户输入的url

    func = None
    for i in url2func:
        if url == i[0]:
            func = i[1]  # 拿到将要执行的函数
            break
    else:
        func = None
    if func:
        msg = func(url)  # 执行对应的函数
    else:
        msg = b'<h1>404</h1>'  # 找不到要执行的函数就返回404
    return [msg, ]

if __name__ == '__main__':
    httpd = make_server('127.0.0.1', 8090, run_server)
    print("我在8090等你哦...")
    httpd.server_forever()
手动写入数据

jinja2渲染HTML文件的本质是字符串替换,即将HTML中的特殊字符替换成要展示的数据。

利用数据库里查询的数据来填充user.html文件

1、使用pymysql模块连接数据库

conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='xx',
        database='xx',
        charset='utf8'
    )
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute('select * from user')
    ret = cursor.fetchall()
    cursor.close()
    conn.close()

#conn建立的链接,cursor数据库邮标
#host数据库服务器IP,port数据库端口, user数据库用户名,数据库密码password,要使用的数据库库名database,数据库字符编码charset
链接数据库

2、创建user表,以及插入信息

3、利用数据库查出的信息填充user.html文档,并利用jinja2渲染user.thml文件。

from wsgiref.simple_server import make_server
from jinja2 import Template
import pymysql

def user(url):
    # 从数据库里面去到所有的用户信息,
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='xx',
        database='xx',
        charset='utf8'
    )
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    cursor.execute('select * from user')
    ret = cursor.fetchall()
    print(ret)
    # 在页面上显示出来

    with open("user.html", "r", encoding="utf8") as f:
        data_s = f.read()

        template = Template(data_s)  # 生成一个模板文件实例

        msg = template.render({"user_list": ret})  # 把数据填充到模板里面

        return bytes(msg, encoding="utf8")

# url和将要执行的函数的对应关系
url2func = [
    ("/user/", user),  
]

# 按照wsgiref的要求定义一个run_server函数
def run_server(environ, start_response):
    """
    :param environ: 跟请求相关的参数
    :param start_response:
    :return:
    """
    # 设置HTTP响应的状态码和头信息
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
    url = environ['PATH_INFO']  # 取到用户输入的url

    func = None
    for i in url2func:
        if url == i[0]:
            func = i[1]  # 拿到将要执行的函数
            break
    else:
        func = None
    if func:
        msg = func(url)  # 执行对应的函数
    else:
        msg = b'<h1>404</h1>'  # 找不到要执行的函数就返回404
    return [msg, ]

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

Django

 官网地址https://www.djangoproject.com/download/

安装

pip3  install django==1.11.11
#设置下载版本

创建django项目

#命令行输入命令,创建一个名为"mydjg"的Django项目
django-admin startproject mydjg

目录介绍

#创建一个Django项目,项目中会默认生成以下文件
mysite/
├── manage.py  # 管理文件
└── mysite  # 项目目录
    ├── __init__.py
    ├── settings.py  # 配置
    ├── urls.py  # 路由 --> URL和函数的对应关系
    └── wsgi.py  # runserver命令就使用wsgiref模块做简单的web server

运行项目命令

#命令行输入命令
python manage.py runserver 127.0.0.1:8090

 pycharm运行方式

TEMPLATES文件配置

TEMPLATES文件夹存放项目相关HTML文件
#在setting文件中查找templates文件夹路径的配置是否完成
#templates文件夹中存放项目相关的HTML文件,Django会去该文件夹的路径下查找相关HTML文件

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],  #template文件夹位置,告诉Django取该目录下找HTML文件
        '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',
            ],
        },
    },
]

静态文件配置

# setting文件中的静态文件配置信息
# 静态文件的引用路径(HTML文件中通过什么来找静态文件)
STATIC_URL = '/static/'  # 别名

# 告诉Django去这个目录下找我的静态文件(CSS和JS文件 图片等)
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

 

Django基础三件套

1、导入模块

from django.shortcuts import HttpResponse,render,redirect

2、使用该模块回应请求

HttpResponse

#使用该模块给浏览器回应具体消息
#传入字符串参数,返回给浏览器
def home(request):
    #此处可添加业务逻辑代码
    return HttpResponse("hello")

render

#返回给浏览器一个HTML文件,浏览器会根据HTML标签显示器页面

def login(request):
    #此处可添加业务逻辑代码
    return render(request, "login.html")

除request参数外还接受一个待渲染的模板文件和一个保存具体数据的字典参数。
将数据填充进模板文件,最后把结果返回给浏览器。(类似于我们上面用到的jinja2) 如下:
def index(request): # 业务逻辑代码 return render(request, "index.html", {"name": "chris", "hobby": ["read", "music"]})

redirect

#传一个URL参数,让浏览器跳转到该地址页面
def login(request):
   #业务逻辑代码
    return redirect("https://www.baidu.com/")
#URL为绝对地址

def login(request):
   #业务逻辑代码
    return redirect("/home/")
#跳转的地址是自己的网页,URL为相对路径

页面跳转重定向

浏览器向服务端发请求时,服务端回应的是一个跳转信息(redirect),则浏览器会拿着跳转信息的网址,重新给该网址发送请求,跳转到新网址的页面。

图片说明如下:

 对应关系

#在urls文件中存放URL和函数的对应关系
例如:
urlpatterns = [
    url(r'^admin/', admin.site.urls),  #默认生成
    url(r'^home/',home),
    url(r'^login/',login),
]

启动Django报错

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

报错原因:通常是因为计算机名为中文,改成英文的计算机名重启电脑。

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

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

原文地址:https://www.cnblogs.com/mingkong-z/p/9166998.html