WEB框架本质和第一个Django实例

2022-07-21 10:56:07 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

Web框架本质

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

代码语言:javascript复制
总的来说:Web框架的本质就是浏览器和服务器基于socket套接字实现请求和响应的过程
半成品自定义web框架
代码语言:javascript复制
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服务本质上客户端和服务端基于socket进行的请求和响应的过程。这段代码就是它们的祖宗。

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

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

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

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

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

代码语言:javascript复制
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()

输出:

代码语言:javascript复制
b'GET / HTTP/1.1rnHost: 127.0.0.1:8080rnConnection: keep-alivernUpgrade-Insecure-Requests: 1rnUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36rnAccept: text/html,application/xhtml xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8rnDNT: 1rnAccept-Encoding: gzip, deflate, brrnAccept-Language: zh-CN,zh;q=0.9rnCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zvrnrn'

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

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

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

HTTP协议介绍

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

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

HTTP GET请求的格式:

代码语言:javascript复制
GET /path HTTP/1.1
header1:v1rn
header2:v2rn

使用 rn分隔多个header

HTTP POST请求格式:

代码语言:javascript复制
POST /path HTTP/1.1
header1:v1rn
header2:v2rn
rnrn
请求体...

当遇到连续两个 rnrn时,表示Header部分结束了,后面的数据是Body。

HTTP响应的格式:

代码语言:javascript复制
200 OK
Header1:v1rn
Header2:v2rn
rnrn
响应体...
处女版自定义web框架

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

代码语言:javascript复制
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 OKrnrn")
    conn.send(b"OK")
    conn.close()

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

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

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

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

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

代码语言:javascript复制
"""
根据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")  # 把收到的字节类型的数据转换成字符串
    # 按rn分割
    data1 = data.split("rn")[0]
    url = data1.split()[1]  # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OKrnrn')  # 因为要遵循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判断? 当然不用,我们有更聪明的办法。

代码语言:javascript复制
"""
根据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")  # 把收到的字节类型的数据转换成字符串
    # 按rn分割
    data1 = data.split("rn")[0]
    url = data1.split()[1]  # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OKrnrn')  # 因为要遵循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判断,怎么办?我们还是有办法!(只要思想不滑坡,方法总比问题多!)

代码语言:javascript复制
"""
根据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")  # 把收到的字节类型的数据转换成字符串
    # 按rn分割
    data1 = data.split("rn")[0]
    url = data1.split()[1]  # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OKrnrn')  # 因为要遵循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文件,读取出它内部的二进制数据,然后再发送给浏览器。

代码语言:javascript复制
"""
根据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")  # 把收到的字节类型的数据转换成字符串
    # 按rn分割
    data1 = data.split("rn")[0]
    url = data1.split()[1]  # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OKrnrn')  # 因为要遵循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()
让网页动态起来

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

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

代码语言:javascript复制
"""
根据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")  # 把收到的字节类型的数据转换成字符串
    # 按rn分割
    data1 = data.split("rn")[0]
    url = data1.split()[1]  # url是我们从浏览器发过来的消息中分离出的访问路径
    conn.send(b'HTTP/1.1 200 OKrnrn')  # 因为要遵循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()

浏览器和服务器实现请求和响应的顺序

代码语言:javascript复制
浏览器(socket客户端)
        2. www.cnblogs.com(42.121.252.58,80)
            sk.socket()
            sk.connect((42.121.252.58,80))
             
            sk.send('我想要xx')
        5. 接收
        6. 连接断开
         
         
         
    博客园(socket服务端)
        1. 监听ip和端口(42.121.252.58,80)
            while True:
                用户 = 等待用户连接
                3. 收到'我想要xx'
                4. 响应:“好”
                用户断开

在客户端和服务端进行完一次请求和响应后会自动断开,当再次请求和响应的时候会重新来过,所以HTTP协议是无状态的

WEB框架之MVC/MTV

MVC模式
代码语言:javascript复制
MVC 是一种使用 MVC(Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式

Model(模型)表示应用程序核心(比如数据库记录列表)
View(视图)显示数据(数据库记录)
Controller(控制器)处理输入(写入数据库记录)
MVC 模式同时提供了对 HTML、CSS 和 JavaScript 的完全控制。

Model(模型)是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据。
View(视图)是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的前端网页。
Controller(控制器)是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据映射,模式渲染等。
MTV模式
代码语言:javascript复制
MTV(Model Templates Views):

Model(模型) -- Templates(模版) --Views(视图)

Django是标准的MTV框架。

Django处理顺序

1、wsgi

代码语言:javascript复制
socket请求处理

2、控制器(django框架本身)

代码语言:javascript复制
控制用户输入,url匹配,通过映射列表将一个请求发送到一个合适的视图;

3、views –Views

代码语言:javascript复制
python程序,向模型和模板发送(或获取)数据;

4、模型绑定 –Model

代码语言:javascript复制
数据库存取数据

5、模板引擎 –Templates

代码语言:javascript复制
用于将内容与展现分离,描述了数据如何展现(如网页模板);

6、模式渲染 –Views

代码语言:javascript复制
将模板和数据整合,形成最终网页;

7、控制器(django框架本身)

代码语言:javascript复制
返回用户展示。
MVC和MTV的区别
代码语言:javascript复制
MVC即模型-视图-控制器模式,就是为那些需要为同样的数据提供多个视图的应用程序而设计的。它很好地实现了数据层与表示层的分离,特别适用于开发与用户图形界面有关的应用程序。
控制器用来处理用户命令以及程序事件;模型维护数据并提供数据访问方法;视图用于数据的显示。

MTV即模型-模版-视图模式,其标准名称是有争议的。在MVC的解释中,视图描述了展现给用户的数据,是指所看到的数据,而不是如何看见它。在python中视图是指对某一特定URL的回调函数,
因为回调函数描述了所要展现的数据。模版用于将内容与展现分离。在django中,视图描述了要展现的数据,而视图一般转交给模版。模版描述了数据如何展现。控制器则是指django框架本身,
通过URL配置,系统将一个请求发送到一个合适的视图。

转自:http://www.cnblogs.com/daliangtou/p/5258905.html

服务器程序和应用程序

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

代码语言:javascript复制
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。

应用程序则负责具体的逻辑处理。

  为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

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

wsgi
代码语言:javascript复制
什么是WSGI?
WSGI(Web Server Common Interface)是专门为Python语言制定的web服务器与应用程序之间的网关接口规范,通俗的来说,只要一个服务器拥有一个实现了WSGI标准规范的模块(
例如apache的mod_wsgi模块),那么任意的实现了WSGI规范的应用程序都能与它进行交互。因此,WSGI也主要分为两个程序部分:服务器部分和应用程序部分;常用的WSGI服务器有uwsgi、Gunicorn 

什么是wsgiref?
wsgiref则是官方给出的一个实现了WSGI标准用于演示用的简单Python内置库,它实现了一个简单的WSGI Server和WSGI Application(在simple_server模块中),主要分为五个模块:
simple_server, util, headers, handlers, validate。
代码语言:javascript复制
wsgiref由什么用?
代码语言:javascript复制
我们利用wsgiref模块来替换我们自己写的web框架的socket server部分


wsgiref源码地址:https://pypi.python.org/pypi/wsgiref 
wsgiref

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

代码语言:javascript复制
"""
根据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:

代码语言:javascript复制
pip install jinja2
代码语言:javascript复制
<!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文件:

代码语言:javascript复制
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连接数据库:

代码语言:javascript复制
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表:

代码语言:javascript复制
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是什么
代码语言:javascript复制
Django是一个开放源代码的Web应用框架,由Python写成。采用了MT‘V的框架模式,即模型M,模板T和视图V。它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,
即是CMS(内容管理系统)软件。并于2005年7月在BSD许可证下发布。这套框架是以比利时的吉普赛爵士吉他手Django Reinhardt来命名的。
Django可以干什么
Django工作机制
代码语言:javascript复制
  当访问url的时候,Django会根据ROOT_URLCONF的设置来装载URLConf。

  然后按顺序逐个匹配URLConf里的URLpatterns。如果找到则会调用相关联的视图函数,并把HttpRequest对象作为第一个参数(通常是request)

  最后该view函数负责返回一个HttpResponse对象。
Django的组成

Django作为一个完善的web框架,主要包含如下几个部分

代码语言:javascript复制
用于进行数据持久化的ORM模块
用于进行URL地址分配的路由模块
用于进行模板页面处理的模板系统
用于进行表单操作的表单模型
用于进行性能突破的缓存系统 

专业术语解释   持久化:数据永久的保存的过程称为数据的持久化   ORM:将程序中的对象[Object]和数据库中的表[Relation]建立关联关系[Mapping]的过程称为ORM   路由:模拟生活中的路由器,将请求URL地址和对应的函数进行关联的操作称为路由

Django官网下载页面

命令安装指定版本(安装最新LTS版):

cmd中输入一下命令即可:

代码语言:javascript复制
pip3 install django==1.11.9
Pycharm安装Django:
代码语言:javascript复制
点击>File | Settings | Project: mysite | Project Interpreter |点击最右边的加号 |在弹出页面的搜索框中输入Django |在右下方勾选Specify version选中安装版本|
点击左下方的 Install Package即可完成
Pycharm删除已安装版本:
代码语言:javascript复制
点击>File | Settings | Project: mysite | Project Interpreter | 右边方框中选中已经安装好的Django | 最右边选中红色减号即可完成删除
命令删除已经安装的版本:
代码语言:javascript复制
pip uninstall django
创建一个django项目:

首先必须切换到项目的指定位置(将要保存项目多的地方)

代码语言:javascript复制
cd  /d F:   (切换到F盘)

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

代码语言:javascript复制
django-admin startproject mysite
使用Pycharm创建
代码语言:javascript复制
File--》New Project-->Django-->(Location项目路径,Interpreter是所安装的Python的版本)-->右下角点击create即可
Django结构目录介绍:
代码语言:javascript复制
mysite/
├── manage.py  # 管理文件
└── mysite  # 项目目录
    ├── __init__.py
    ├── settings.py  # 配置
    ├── urls.py  # 路由 --> URL和函数的对应关系
    └── wsgi.py  # runserver命令就使用wsgiref模块做简单的web server     和服务器相关的
Django结构分析
代码语言:javascript复制
Django 程序
    Django - 对整个程序进行配置
    settings - 配置文件
    usls - url对应关系 对应逻辑函数 为每个逻辑函数 分配相应视图
    wsgi - 遵循WSIG规范, uwsgi   nginx
    manage.py - #管理Django 程序
          执行的相关命令:
          python  manage.py
          python  manage.py  startapp   名称     创建APP
          数据库迁移:
          python manage.py  makemigrations      
          python manage.py migrate
代码语言:javascript复制
APP
    migrations 数据库操作记录->相应表结构发生变化,比如说字段类型,并不是增删改成的变动
    admin Django 为我们提供的后台管理
    apps 配置当前APP
    models ORM,写指定的类通过命令可以创建数据库结构.
    tests 单元测试
    Views 业务逻辑代码   
运行Django项目:

执行manage.py文件

代码语言:javascript复制
python manage.py runserver 127.0.0.1:8000
模板文件配置:
代码语言:javascript复制
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',
            ],
        },
    },
]
配置静态文件

对于静态文件,也就是 我们Templates存放html文件视图所对应相关的JS,CSS,图片存放的对方 通常我们在 程序目录下建立一个文件夹->static(这里写死就可以了,不要换别的名字)

settings.py文件下添加如下配置,意思是在视图对应的静态文件会自动在static目录下检索

代码语言:javascript复制
STATICFILES_DIRS = (os.path.join(BASE_DIR,'static'),)

看不明白?有图有真相:

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

代码语言:javascript复制
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',
]

Djang中Project和APP的区别

代码语言:javascript复制
project 是 整个项目(网站)
APP是指项目里的某个功能模块,比如user 是一个APP

以下内容为转载部分

Django原理讲解

  • 业务流程原理
代码语言:javascript复制
url请求---->访问路由系统(负责分发请求到相应视图函数)------>视图函数(处理请求)------>DataBase(数据库操作数据生成对应页面返回给用户)
  • 底层实现原理
代码语言:javascript复制
本质是Django就是一个Socket服务端,用户的浏览器其实就是一个Socket客户端.用户访问网站的过程就是服务端与客户端Socket通信的过程
代码语言:javascript复制
代码实现
代码语言:javascript复制
import socket

def handle_request(client):
    buf = client.recv(1024)
    client.send("HTTP/1.1 200 OKrnrn".encode())

    f = open('index.html', 'rb')
    data = f.read()
    client.send(data)


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8000))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        handle_request(connection)
        connection.close()
if __name__ == '__main__':
    main()

Django中请求的生命周期

概述

首先我们知道HTTP请求及服务端响应中传输的所有数据都是字符串.

在Django中,当我们访问一个的url时,会通过路由匹配进入相应的html网页中.

请求生命周期概念

代码语言:javascript复制
是指当用户在浏览器上输入url到用户看到网页的这个时间段内,Django后台所发生的事情

而Django的生命周期内到底发生了什么呢??

代码语言:javascript复制
1. 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端
请求头和请求体中会包含浏览器的动作(action),这个动作通常为get或者post,体现在url之中.

2. url经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配,
一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
3. 视图函数根据客户端的请求查询相应的数据.返回给Django,然后Django把客户端想要的数据做为一个字符串返回给客户端.
4. 客户端浏览器接收到返回的数据,经过渲染后显示给用户.

第一步:浏览器输入网址。接下来你以为就到django的urls了?No,紧接着是要经过django里的settings.py里的MIDDLEWARE配置,也就是中间件。

第二步:中间件通过之后才会到urls,通过urls的配置,找到views里的函数或类

第三步:执行函数或类,返回一个字符串。

第四步:再通过一系列的中间件。

第五步:前端或模板语言获取到字符串,然后解析,在页面上展示出来。

视图函数根据客户端的请求查询相应的数据后.如果同时有多个客户端同时发送不同的url到服务端请求数据

服务端查询到数据后,怎么知道要把哪些数据返回给哪个客户端呢??

因此客户端发到服务端的url中还必须要包含所要请求的数据信息等内容.

例如,http://www.aaa.com/index/?nid=user这个url中,

代码语言:javascript复制
客户端通过get请求向服务端发送的nid=user的请求,服务端可以通过request.GET.get("nid")的方式取得nid数据

客户端还可以通过post的方式向服务端请求数据.

当客户端以post的方式向服务端请求数据的时候,请求的数据包含在请求体里,这时服务端就使用request.POST的方式取得客户端想要取得的数据

代码语言:javascript复制
需要注意的是
代码语言:javascript复制
request.POST是把请求体的数据转换一个字典,请求体中的数据默认是以字符串的形式存在的.

FBV模式和CBV模式

FBV
代码语言:javascript复制
一个url对应一个视图函数,这个模式叫做FBV(Function Base Views)
CBV
代码语言:javascript复制
CBV(Class Base views),即一个url对应一个类

例子:使用cbv模式来请求网页

路由信息:

代码语言:javascript复制
urlpatterns = [
    url(r'^fbv/',views.fbv),
    url(r'^cbv/',views.CBV.as_view()),
]

视图函数配置:

代码语言:javascript复制
from django.views import View

class CBV(View):
    def get(self,request):
        return render(request, "cbv.html")

    def post(self,request):
        return HttpResponse("cbv.get")

cbv.html网页的内容:

代码语言:javascript复制
<body>
<form method="post" action="/cbv/">
    {% csrf_token %}
    <input type="text">
    <input type="submit">
</form>
</body>

启动项目,在浏览器中输入http://127.0.0.1:8000/cbv/,回车,得到的网页如下:

在input框中输入”hello”,后回车,得到的网页如下:

使用fbv的模式,在url匹配成功之后,会直接执行对应的视图函数.

而如果使用cbv模式,在url匹配成功之后,会找到视图函数中对应的类,然后这个类回到请求头中找到对应的Request Method.

代码语言:javascript复制
如果是客户端以post的方式提交请求,就执行类中的post方法;
如果是客户端以get的方式提交请求,就执行类中的get方法

然后查找用户发过来的url,然后在类中执行对应的方法查询生成用户需要的数据.

fbv方式请求的过程

用户发送url请求,Django会依次遍历路由映射表中的所有记录,一旦路由映射表其中的一条匹配成功了, 就执行视图函数中对应的函数名,这是fbv的执行流程

cbv方式请求的过程

当服务端使用cbv模式的时候,用户发给服务端的请求包含url和method,这两个信息都是字符串类型

服务端通过路由映射表匹配成功后会自动去找dispatch方法,然后Django会通过dispatch反射的方式找到类中对应的方法并执行

类中的方法执行完毕之后,会把客户端想要的数据返回给dispatch方法,由dispatch方法把数据返回经客户端

例子,把上面的例子中的视图函数修改成如下:

代码语言:javascript复制
from django.views import View

class CBV(View):
    def dispatch(self, request, *args, **kwargs):
        print("dispatch......")
        res=super(CBV,self).dispatch(request,*args,**kwargs)
        return res

    def get(self,request):
        return render(request, "cbv.html")

    def post(self,request):
        return HttpResponse("cbv.get")

打印结果:

代码语言:javascript复制
<HttpResponse status_code=200, "text/html; charset=utf-8">
dispatch......
<HttpResponse status_code=200, "text/html; charset=utf-8">

需要注意的是:

代码语言:javascript复制
以get方式请求数据时,请求头里有信息,请求体里没有数据 以post请求数据时,请求头和请求体里都有数据. 

Django登录实现

  1. 在自己建立的APP 里的Views中添加逻辑函数 login render 这个模块相当于一个 open读取html文件中的数据返回给浏览器
代码语言:javascript复制
from django.shortcuts import render,redirect
def login(request):
    # request 包含用户提交的所有信息
    error_msg = ""
    if request.method == 'POST':
    # 获取用户通过post 提交过来的数据
        user = request.POST.get('user',None)
        pwd = request.POST.get('pwd',None)
        if user == 'admin' and pwd == "admin":
            #跳转到响应页面 ("/"相当于前面的地址127.1.0.0:8000)
            return redirect('/home')
        else:
            #用户名密码不匹配
            error_msg = "错了 !!!!天啊!!"

   //error_msg 替换html文件 相同字段文字
    return render(request, 'login.html',{'error_msg':error_msg})

2.登录函数逻辑所对应的 templates文件中的login.html

代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>login</title>
    <style>

        label{

            width: 80px;
            text-align: right;
            display: inline-block;

        }
    </style>
</head>
<body>

    <form action="/login/" method="post" style="background-color: cadetblue">
        <p>
            <label for="username">用户名</label>
            <input id="username" name="user" type="text"/>
        </p>
        <p>
            <label for="password">密码</label>
            <input id="password" name="pwd" type="text"/>
            <input type="submit" value="提交">

      //error_msg 替换请求 相同字段文字
            <span style="color: red">{{ error_msg}}</span>
        </p>
    </form>
</body>
</html>
  • 示例

效果展示.gif

页面数据的操作功能 同样的在自己建立的APP 里的Views中添加逻辑函数 home

代码语言:javascript复制
USER_LIST = []
#
# for index in range(20):
#     tem = {'username':'雪芙' str(index),'sex':'女','age':20 index}
#     USER_LIST.append(tem)
def home(request):
    if request.method == 'POST':
        #获取用户提交的数据 POST 请求中
        u = request.POST.get('username')
        s = request.POST.get('sex')
        a = request.POST.get('age')
        tem = {'username': u, 'sex': s, 'age':a}
        USER_LIST.append(tem)

//当首次没有数据时候,列表为空
    return  render(request,'home.html',{'user_list':USER_LIST})
  1. home函数逻辑所对应的 templates文件中的home.html
代码语言:javascript复制
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>home</title>
</head>
<body style="margin: 0">
    <div style="height:48px;background-color: cadetblue"></div>

    <div>
        <form action="/home/" method="post">
             <input type="text" name="username" placeholder="用户名"/>
             <input type="text" name="sex" placeholder="性别"/>
             <input type="text" name="age" placeholder="年龄"/>
             <input type="submit" value="添加">
        </form>
    </div>

    <div>
        <table>
          //页面中的for循环写法
            {% for row in user_list %}
                <tr>
                <td>{{ row.username }}</td>
                <td>{{ row.sex }}</td>
                <td>{{ row.age }}</td>
            </tr>
            {% endfor %}
        </table>
    </div>
</body>
</html>
  • 示例

循环操作.gif

总结
代码语言:javascript复制
Django的主要目的是简便、快速的开发数据库驱动的网站,Django有许多功能强大的第三方,本质是上是一个服务端的Socket连接,但是功能及其强大,封装后的功能简单易操作,深受广大用户喜爱
.配合上模板页面,数据处理,就可以建立自己的网站或者移动应用程序.

Django基础必备三件套:

代码语言:javascript复制
from django.shortcuts import HttpResponse, render, redirect
HttpResponse

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

代码语言:javascript复制
def index(request):
    # 业务逻辑代码
    return HttpResponse("OK")
render

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

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

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

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

代码语言:javascript复制
def index(request):
    # 业务逻辑代码
    return redirect("/home/")

Django基础注意事项

代码语言:javascript复制
1:get和post的应用场景
    访问网站的时候用get向网页提交数据的时候用post
2: 由页面向后端提交数据的时候,使用FORM表单
3:使用form表单注意事项
    form表单中必须要写action属性和method属性,如果涉及到提交文件的话必须写enctype属性
4: input标签必须放在form表单中,input标签必须写name属性
5: 提交按钮类型是submit(input  type=' submit')

启动Django报错:

Django 启动时报错 UnicodeEncodeError …

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

Django运行常见错误

详情链接

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/124484.html原文链接:https://javaforall.cn

0 人点赞