欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Django 系列博客(一)

程序员文章站 2022-05-04 13:43:50
Django 系列博客(一) 前言 学习了 python 这么久,终于到了Django 框架。这可以说是 python 名气最大的web 框架了,那么从今天开始会开始从 Django框架的安装到使用一步步的学习,这系列博客不会像前端的那样水了(立个 flag),希望可以成为高质量的博客。那么本篇博客 ......

django 系列博客(一)

前言

学习了 python 这么久,终于到了django 框架。这可以说是 python 名气最大的web 框架了,那么从今天开始会开始从 django框架的安装到使用一步步的学习,这系列博客不会像前端的那样水了(立个 flag),希望可以成为高质量的博客。那么本篇博客介绍 django 的安装以及如何在电脑上运行第一个 django 应用。

django 的安装

django 的安装很简单,在 win 和 mac 上面都可以使用 pip 安装命令安装,也可以通过 pycharm 安装,或者下载文件在命令行使用安装工具安装。

接下来我在 ubuntu 上安装做示例。

Django 系列博客(一)

在这里还安装了一个依赖包 pytz。这是用来做时区转换的一个第三方库。

其他平台的 pip 安装方式一样,不过要选用 python3的对应 pip 来安装,因为现在的 django 版本已经不支持 python2了。

虚拟环境的安装

什么是虚拟环境

  • 对真实的 python 解释器的一个拷贝版本
  • 事实有效,可以独立存在并运行解释 python 代码
  • 可以在计算机上拷贝多个虚拟环境

为什么要使用虚拟环境

  • 保证真实环境的纯净性
  • 框架的多版本共存
  • 方便做框架的版本迭代
  • 降低多框架共存的维护成本

安装虚拟环境

  1. 通过 pip 安装虚拟环境库

Django 系列博客(一)

因为我之前已经下载好了,所以这里直接显示请求已经完成,并且后面是安装的绝对路径。

  1. 前往目标文件夹

这个文件夹是你用来保存虚拟环境的文件夹,该文件夹一旦确定就不要轻易更改。

Django 系列博客(一)

这个 py3-env1是创建的一个纯净虚拟环境。

  1. 创建纯净的虚拟环境
virtualenv 虚拟环境名 (py3-env2)
  1. 终端启动虚拟环境
cd py3-env1\scripts
activate
  1. 进入虚拟环境下的 python 开发环境
python3
  1. 关闭虚拟环境
deactivate
  1. pycharm的开发配置
添加:创建项目 -> project interpreter -> existing interpreter -> virtualenv environment | system interpreter -> 目标路径下的 python.exe
删除:setting -> project -> project interpreter -> show all

mac 电脑从第三步直接到最后一步就好

了解

# 创建非纯净环境:
#   -- virtualenv-clone 本地环境 虚拟环境名
# mac配置终端,在终端运行虚拟环境
# 在用户根目录下的.bash_profile(没有需手动创建)文件中设置
# alias 终端指令名(env-py3)='/library/virtualenv/虚拟环境名/bin/python3'
# alias 终端指令名(env-pip3)='/library/virtualenv/虚拟环境名/bin/pip3'

http 协议

因为 django 框架应用层是采用的 http 协议,所以有必要了解 http 协议。

什么是 http 协议

  • http(hypertext transport protocol) 是超文本传输协议,而 https 也归属于 http 协议,s 代表安全。
  • 基于 tcp/ip协议基础上的应用层协议,底层实现仍为 socket
  • 基于请求-响应模式:通信一定是从客户端开始,服务端接收到客户端一定会做出对应响应
  • 无状态:协议不对任何一次通信状态和任何数据做保存
  • 无连接:一次连接只完成一次请求-响应,请求-响应完毕后会立即断开连接。

Django 系列博客(一)

http 工作原理

一次 http 连接称之为一个事务,过程可以分为四步

  1. 客户端与服务端建立连接
  2. 客户端发生一个 http 协议指定格式的请求
  3. 服务端接收请求后,回应一个 http 协议指定格式的响应
  4. 客户端将服务端的响应展现给用户

http 状态码

  • 1开头:

Django 系列博客(一)

  • 2开头:

Django 系列博客(一)

  • 3开头:

Django 系列博客(一)

  • 4开头:

Django 系列博客(一)

  • 5开头:

Django 系列博客(一)

请求报文

# 请求行  请求头  请求体
'''
post / http/1.1\r\n
host: 127.0.0.1:8001\r\n
connection: keep-alive\r\n
upgrade-insecure-requests: 1\r\n
user-agent: mozilla/5.0 (macintosh; intel mac os x 10_14_1) applewebkit/537.36 (khtml, like gecko) chrome/70.0.3538.110 safari/537.36\r\n
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\n
accept-encoding: gzip, deflate, br\r\n
accept-language: zh-cn,zh;q=0.9,en;q=0.8\r\n
\r\n
usr=abc&pwd=123
'''

响应报文

# 响应行  响应头  响应体
'''
http/1.1 200 ok\r\n
content-type:text/html\r\n
\r\n
login success
'''

使用原生 socket 完成和浏览器的交互

目录结构

01_socket
    -- 01_client.html:前台通过form表单访问后台的页面
    -- 01_login.html:后台测试渲染给前台的登录页面
    -- 01_index.html:后台测试渲染给前台的主页
    -- 01_server.py:后台服务器文件

因为 b/s架构的客户端已经由浏览器写好,所以只需要关注服务器端就ok。

服务器端代码

from socket import socket

# 设置响应头(包含响应行)
resp_header = b'http/1.1 200 ok\r\ncontent-type:text/html;charset=utf-8\r\n\r\n' # 连续两个\r\n表示响应头结束

# 设置服务器 socket 相关信息
server = socket()
server.bind('', 8080) # 空字符串表示绑定本机
server.listen(5)
print(('服务:http://localhost:8080'))

while true:
    # 获取以 http 协议发来的请求
    client, addr = server.accept()
    data = client.recv(1024)
    # 数据报文 包含请求行 请求头 请求体
    print(data)
    client.send(resp_header)
    
    # /index => 响应主页
    # /login => 登录页面
    # 错误 => 404
    # 数据 data, 字节形式 => 字符串形式
    strdata = str(data, encodeing)
    
    # 解析请求的数据,分析得到路由
    my_route = strdata.split('\r\n')[0].split(' ')[1]
    
    # 后台没有设置的路由,统统由404来处理
    dt = b'404'
    
    # 设置的路由返回响应的页面文件
    if my_route == '/index':
        with open('index 页面路径', 'rb') as f:
            dt = f.read()
    if my_route == '/login':
        with open('login 页面路径', 'rb') as f:
            dt = f.read()
            
    # /favicon.ico该请求是往后台请求标签图标
    if my_route == '/favicon.ico':
        with open('favicon.ico', 'rb') as f:
            dt = f.read()
    # 服务器发送响应体
    client.send(dt)
    # 一次循环,代表一次响应,也就是一次事务的完成,要关闭 http 请求连接
    client.close()

修改返回数据,完善响应体

# 字符串
client.send(b'http/1.1 200 ok\r\n')
client.send(b'\r\n')
client.send(b'normal socket web')
# html代码,请求头要设置支持 html 代码
client.send(b'http/1.1 200 ok\r\n')
client.send(b'content-type:text/html\r\n')
client.send(b'\r\n')
client.send(b'<h1>normal socket web</h1>')
# html文件(同级目录建立一个index.html页面)
client.send(b'http/1.1 200 ok\r\n')
client.send(b'content-type:text/html\r\n')
client.send(b'\r\n')
# 利用文件方式读取页面
with open('01_index.html', 'rb') as f:
    dt = f.read()
client.send(dt)

拓展

修改接收数据,模拟后台路由

# 分析接收到的数据
data = client.recv(1024)
# 保证接收到的数据作为字符串进行以下处理
data = str(data, encoding='utf-8')
# 拆分出地址位
route = data.split('\r\n')[0].split(' ')[1]
# 匹配地址,做出不同的响应
if route == '/index':
    with open('01_index.html', 'rb') as f:
        dt = f.read()
elif route == '/login':
    with open('01_login.html', 'rb') as f:
        dt = f.read()
else:
    dt = b'404'
client.send(dt)

框架演变

目录结构

02_frame
    -- favicon.ico
    -- index.html
    -- manage.py

manage.py

import socket
import pymysql
# 响应头
resp_header = b'http/1.1 200 ok\r\ncontent-type:text/html\r\n\r\n'

# 请求处理
def index():
    # 以字节方式读取文件
    with open('index.html', 'rb') as f:
        dt = f.read()
    return dt
def ico():
    with open('favicon.ico', 'rb') as f:
        dt = f.read()
    return dt
def user():
    # 数据库操作
    conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
    cur = conn.cursor(pymysql.cursors.dictcursor)
    cur.execute('select * from user')
    users = cur.fetchall()
    print(users)
    users = '''%d:%s
    %d:%s''' % (users[0]['id'], users[0]['name'], users[1]['id'], users[1]['name'])
    return users.encode('utf-8')

# 设置路由
urls = {
    # 请求路径与请求处理函数一一对应
    '/index': index,
    '/favicon.ico': ico,
    '/user': user
}

# 设置socket
def serve(host, port):
    server = socket.socket()
    server.bind((host, port))
    print('start:http://' + host + ':' + str(port))
    server.listen(5)
    while true:
        sock, addr = server.accept()
        data = sock.recv(1024)
        data = str(data, encoding='utf-8')
        print(data)
        route = data.split('\r\n')[0].split(' ')[1]

        resp = b'404'
        if route in urls:
            resp = urls[route]()

        sock.send(resp_header)
        sock.send(resp)
        sock.close()

# 启服务
if __name__ == '__main__':
    serve('127.0.0.1', 8002)

项目演变

目录结构

03_proj
    -- template
        -- index.html
        -- user.html
    favicon.ico
    start.py
    urls.py
    views.py

index.html

<h1>{{ name }}</h1>

user.html

<table border="1">
    <tr>
        <th>id</th>
        <th>name</th>
        <th>password</th>
    </tr>
    {% for user in users%}
    <tr>
        <td>{{user.id}}</td>
        <td>{{user.name}}</td>
        <td>{{user.password}}</td>
    </tr>
    {% endfor %}
</table>

start.py

from wsgiref.simple_server import make_server
from urls import urls


def app(env, response):
    print(env)
    # 设置响应头
    response("200 ok", [('content-type', 'text/html')])
    route = env['path_info']
    print(route)
    data = urls['error']()
    if route in urls:
        data = urls[route]()
    # 返回二进制响应体
    return [data]


if __name__ == '__main__':
    server = make_server('127.0.0.1', 8003, app)
    print('start:http://127.0.0.1:8003')
    server.serve_forever()

urls.py

from views import *
urls = {
    '/index': index,
    '/favicon.ico': ico,
    '/user': user,
    'error': error
}

views.py

import pymysql
# 利用jinja2来渲染模板,将后台数据传给前台
from jinja2 import template

def index():
    with open('templates/index.html', 'r') as f:
        dt = f.read()
    tem = template(dt)
    resp = tem.render(name='主页')
    return resp.encode('utf-8')

def ico():
    with open('favicon.ico', 'rb') as f:
        dt = f.read()
    return dt

def user():
    # 数据库操作
    conn = pymysql.connect(host='127.0.0.1', port=3306, db='django', user='root', password='root')
    cur = conn.cursor(pymysql.cursors.dictcursor)
    cur.execute('select * from user')
    users = cur.fetchall()
    print(users)

    with open('templates/user.html', 'r') as f:
        dt = f.read()
    tem = template(dt)
    resp = tem.render(users=users)

    return resp.encode('utf-8')

def error():
    return b'404'