// Fork me on GitHub

Django框架简介

 web框架本质

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

复制代码
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"OK")
    conn.close()
复制代码

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

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

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

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

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

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

输出结果:

复制代码
"""
请求首行
b'GET / HTTP/1.1\r\n
请求头
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Cache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
\r\n
请求体
......
"""
复制代码

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

复制代码
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'HTTP/1.1 200 OK\r\n\r\n')
    conn.send(b'hello world')
    print(data)
复制代码

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

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

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

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

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

复制代码
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'HTTP/1.1 200 OK\r\n\r\n')
    data = data.decode('utf-8')  # 把从浏览器那里收到的字节类型的数据转换成字符串
    # print(data)
    target_url = data.split('\r\n')[0].split(' ')[1]  #按\r\n分割
    # 根据不同的路径返回不同内容
    if target_url == '/index':
        # conn.send(b'index')
        with open(r'D:\demo.html','rb') as f:
            conn.send(f.read())
    elif target_url == '/login':
        conn.send(b'login')
    else:
        conn.send(b'404 error')
    conn.close()
复制代码

基于wsgiref模块:

根据功能的不同拆分成不同的文件,用户在浏览器窗口输入url之所以能够获取到相应的资源,是因为后端早已经开设了相应的资源接口 

基于wsgiref模块以及文件拆分的特点:

若要开设新的资源
1.先在urls文件中写url与函数的对应关系
2.再去views文件中写对应的函数

urls.py:路由与视图函数的对应关系
views.py:里面就是放的一堆视图函数(视图函数可以是函数也可以是类)
templates文件夹:里面放的就是一堆html文件(模板文件夹)

动静态网页

静态网页:数据是写死的,万年不变

动态网页:数据是动态获取的,如获取当前时间,从数据库中取数据

jinja2模块 

提供了一个可以在html页面上书写类似于python后端的代码 来操作数据(模板语法) ,flask框架模板语法使用的就是jinja2模块,所以你只要下了flask框架 就会自动下载jinja2 

模板语法(jinja2模板语法非常贴近python语法 但是并不是所有的框架使用的都是jinja模板语法)

{{ xxx }}
<p>{{xxx.username}}</p>
<p>{{xxx['password']}}</p>
<p>{{xxx.get('hobby')}}</p>
<p>{{xxx.get('hobby')[0]}}</p>
<p>{{xxx.get('hobby').1}}</p>
{%for user_dict in xxx %}
    <tr>
        <td>{{ user_dict.id }}</td>
        <td>{{ user_dict.name }}</td>
        <td>{{ user_dict.hobby }}</td>
    </tr>
{% endfor %}

 

基于wsgiref模块、jinjia2实现web框架

from views import *

urls = [
    ('/index',index),
    ('/login',login),
    ('/xxx',xxx),
    ('/get_time',get_time),
    ('/get_user',get_user),
    ('/get_info',get_info)
]

urls.py
from views import *

urls = [
    ('/index',index),
    ('/login',login),
    ('/xxx',xxx),
    ('/get_time',get_time),
    ('/get_user',get_user),
    ('/get_info',get_info)
]
urls.py
def index(env):
    return 'index'

def login(env):
    return 'login'

def error(env):
    return '404 error'

def xxx(env):
    return 'xxx'

import time
def get_time(env):
    # 该函数需要返回一个html页面
    current_time = time.strftime('%Y-%m-%d %X')
    # 文件操作 读取html文件
    with open(r'F:\python\templates\02 get_time.html','r',encoding='utf-8') as f:
        data = f.read()  # html文件内容  字符串
    data = data.replace('gfdgsffsda',current_time)  # 利用字符串的替换
    return data

from jinja2 import Template
def get_user(env):
    user_dict = {'username':'jason','password':123,'hobby':['read','study','run']}
    with open(r'F:\python\templates\04 get_info.html','r',encoding='utf-8') as f:
        data = f.read()
    temp = Template(data)
    res = temp.render(xxx=user_dict)  # 将user_dict传递给html页面 在页面上通过变量名xxx就能够获取到user_dict
    return res


import pymysql
def get_info(env):
    conn = pymysql.connect(
        host = '127.0.0.1',
        port = 3306,
        user = 'root',
        password = '123',
        database = 'day49',
        charset = 'utf8',
        autocommit = True
    )
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    sql = "select * from userinfo"
    cursor.execute(sql)
    data = cursor.fetchall()  # [{},{},{}]
    # 将列表套字典的结构数据 直接传递给html页面
    with open(r'D:get_info.html','r',encoding='utf-8') as f:
        res = f.read()
    # 利用jinja2模块
    tmp = Template(res)
    # 利用对象的render方法 将数据直接传递给html页面
    res = tmp.render(xxx=data)
    return res

views.py
from wsgiref.simple_server import make_server
from views import *
from urls import urls


def run(env,response):
    """
    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 浏览器能够接受的内容
    """
    response('200 OK',[])
    # print(env)  # env是一个大字典  里面的PATH_INFO参数就是用户输入的后缀
    target_url = env.get('PATH_INFO')
    # if target_url == '/index':
    #     # 一堆逻辑判断
    #     return [b'index']
    # elif target_url == '/login':
    #     return [b'login']
    # 先定义一个变量 用来存储可能匹配到的函数名
    func = None
    # 1 for循环获取一个个的url与函数对应关系的元组
    for url in urls:  # url = (),(),()
        # 2 判断当前用户访问的url与元组第一个元素是否一致
        if target_url == url[0]:
            # 3 如果相等 说明有对应的后端逻辑代码 将匹配到的函数名赋值给func
            func = url[1]
            # 4 一旦用户匹配上了响应的url 应该立刻结束当前for循环了 因为再循环就没有意义
            break
    # 针对func是否有值 还需要判断
    if func:
        # 匹配上了  加括号直接调用
        res = func(env)
    else:
        # 匹配404逻辑代码
        res = error(env)
    return [res.encode('utf-8')]  # 返回函数的返回值


if __name__ == '__main__':
    # 监听127.0.0.1:8080 一旦有客户端来访问 会立刻将make_server第三个参数加括号调用执行
    server = make_server('127.0.0.1',8080,run)
    server.serve_forever()  # 启动服务端

wsgiref

 

web框架简介

python三大主流web框架

Django  优势:大而全,自身携带的组件和功能特别多,就类似于航空母舰。不足之处:笨重

Flask  优势:小而精,自身携带的组件和功能特别少,就类似于游骑兵,虽然自身功能比较少,但是第三方支持该框架的模块很多如果你将flask第三方模块全部叠加起来甚至可以超过django  不足之处:受限于第三方模块 

Tornado  异步非阻塞,天然支持高并发 甚至可以用它来开发游戏服务器

Django框架

Django框架注意事项:

  1. 计算机名称不能有中文 
  2. 项目文件名也不要用中文 
  3. 一个pycharm窗口就是一个单独的完整的项目 

Django版本:

推荐使用1.X版本里面的1.11.09~1.11.13

安装

pip3 install django==1.11.11

#测试是否安装成功
命令行输入 django-admin

创建django项目的两种方式

1.使用命令行创建

创建django项目:
django-admin startproject mysite(项目名)

效果:创建了一个mysite的文件夹

目录介绍
复制代码
mysite/
├── manage.py  # 管理文件
└── mysite  # 项目目录
    ├── __init__.py
    ├── settings.py  # 配置
    ├── urls.py  # 路由 --> URL和函数的对应关系
    └── wsgi.py  # runserver命令就使用wsgiref模块做简单的web server
复制代码
启动Django项目:
python manage.py runserver  # django默认的端口号是8000

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

报这个错很大可能是因为使用了Python3.7.0,而目前(2018-06-12)Python3.7.0和Django还有点兼容性问题。解决方案如下

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

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

创建具有独立功能的app
复制代码
python manage.py startapp app01  #通常情况下应该做到见名知意 

  app01
    -- migrations 文件夹
    -- __init__.py
    -- admin.py
    -- apps.py
    -- models.py
    -- tests.py
    -- views.py

复制代码
app(application)的概念

django其实是一个专注于开发app的web框架,一个空的django项目就类似于是一所大学,app就类似于大学里面的各个学院。

每个app其实就类似于不同的功能模块

复制代码
  购物网站
    用户相关 user
      用户相关的app
    订单相关 order
      订单相关的app
    投诉相关 tousu
      投诉相关的app
复制代码

不同的功能模块推荐使用不同的app去开发,django支持多app

Django主要文件功能
复制代码
    mysite
        -mysite
            --__init__.py
            --settings.py  项目配置文件
            --urls.py      路由视图函数对应关系 项目的总路由
            --wsgi.py      
        -manage.py
app01 --migrations文件夹 数据库改动记录 --__init__.py --__init__.py --admin.py django后台管理 --apps.py 注册app相关 --models.py 模型类(ORM) --tests.py 测试文件 --views.py 视图函数(******)

     db.sqlite3 django自带的一个小型用于本地测试的数据库(对日期格式的数据不是很敏感)
复制代码

2.使用pycharm创建

注意:1.使用命令行创建的django项目是不会自动创建templates模板文件夹,只能自己手动创建

2.命令行创建的django项目不但没有templates文件夹,配置文件中也没有填写路径,而pycharm创建的会自动添加


 容易犯的错误:代码修改了始终没有效果

1.在同一个端口起了多个服务 一直跑的是最开始的那个服务
2.浏览器缓存问题


解决浏览器缓存如下图

 

 补充:简化命令的方式

项目配置文件夹注意事项:(注册)

注意:创建app之后一定要先去setting文件中注册,在没有使用到app的一些特殊操作时(比如数据库相关),可以不用注册,但是注册后,应用的所有功能都能使用(强烈建议注册)

 

pycharm创建的Django项目的配置文件

命令行创建的Django项目的配置文件

 

国际化配置(根据需要修改)

 

Django能够自动重启,但是它的重启机制只要检测到你的代码有变化,则在一定的时间间隔内就会自动重启。所以有时候可能会出现 你代码还没写完就已经自动重启了

Django基础必备三件套

HttpResponse:返回字符串

render:返回html页面 并可以给html页面传数据

    模板的渲染(将数据在后端按照模板语法放入html对应的位置)

redirect:重定向

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def index(request):
    return HttpResponse('字符串')

def login(request):
    return render(request,'templates文件夹下的html文件名',{'user_dict':{'username':'jason','password':123},'userxxx':'hello world'}))

def home(request):
    # return redirect('http://www.baidu.com')
    return redirect('/index')

 

补充内容一

#bootsrap官网 https://v3.bootcss.com/css/

http协议  超文本传输协议
请求  和   响应

请求格式
    GET / HTTP/1.1  ---  GET /clschao/articles/9230431.html?name=chao&age=18 HTTP/1.1
    User-Agent:....
    xx:xx

    请求数据  get请求方法没有请求数据  post请求数据方法的请求数据放在这里

响应格式
    HTTP/1.1 200 ok
    kl:v1
    k2:v2

    响应数据

URL:  https://www.cnblogs.com/clschao/articles/9230431.html
    传送协议。
    层级URL标记符号(为[//],固定不变)
   
    服务器。(通常为域名,有时为IP地址)
    端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
    路径。(以“/”字符区别路径中的每一个目录名称)  /clschao/articles/9230431.html
    查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
    https://www.cnblogs.com/clschao/articles/9230431.html?name=chao&age=18
    

请求方法
	get post
    GET提交的数据会放在URL之后,也就是请求行里面,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456.(请求头里面那个content-type做的这种参数形式,后面讲) POST方法是把提交的数据放在HTTP包的请求数据部分中.
    GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制.
    GET与POST请求在服务端获取请求数据方式不同,就是我们自己在服务端取请求数据的时候的方式不同了

	常用的get请求方式:浏览器输入网址  ,a标签 ,form标签 method='get'
	post请求方法,一般都用来提交数据.比如用户名密码登录	

	其他方法:HEAD PUT DELETE TRACE OPTIONS CONNECT PATCH

响应状态码 
    1xx消息——请求已被服务器接收,继续处理
    2xx成功——请求已成功被服务器接收、理解、并接受
    3xx重定向——需要后续操作才能完成这一请求
    4xx请求错误——请求含有词法错误或者无法被执行
    5xx服务器错误——服务器在处理某个正确请求时发生错误

http协议特点
	1.基于  请求-响应 的模式
	2.无状态保存
	3.无连接  

请求:request
响应:response

 

补充内容二

视图

请求相关的属性方法(request--HttpRequest对象)

def index(request): #http相关请求信息---封装--HttpRequest对象

    if request.method == 'GET':
        print(request.body)  #获取post请求提交过来的原始数据
        print(request.GET)   #获取GET请求提交的数据
        # print(request.META)  # 请求头相关信息,就是一个大字典
        print(request.path) #/index/ 路径
        print(request.path_info) #/index/ 路径
        print(request.get_full_path())  #/index/?username=dazhuang&password=123 除了域名和端口
                                        #查询参数不算做路径的一部分
        return render(request,'index.html')
    else:
        print(request.body)  # b'username=dazhuang'
        print(request.POST) #获取POST请求提交的数据
        return HttpResponse('男宾三位,拿好手牌!')

request.FILES

响应相关的方法

HttpResponse  --- 回复字符串的时候来使用
render --- 回复一个html页面的时候使用
redirect -- 重定向
	示例:
	def login(request):
        if request.method == 'GET':
            return render(request,'login.html')
        else:
            username = request.POST.get('username')
            password = request.POST.get('password')
            if username == 'taibai' and password == 'dsb':
                # return render(request,'home.html')
                return  redirect('/home/')  #重定向
            else:
                return HttpResponse('滚犊子,赶紧去充钱!!!')

    #首页
    def home(request):
        return render(request,'home.html')
#301永久重定向 301永久重定向  301表示旧地址A的资源已经被永久移除了

FBV和CBV

FBV -- function based view
def home(request):
    print('home!!!')
    return render(request,'home.html')
CBV  -- class based view

views.py
    from django.views import View
    class LoginView(View):
        # 通过请求方法找到自己写的视图类里面对应的方法
        def get(self,request):

            return render(request,'login2.html')
        def post(self,request):
            username = request.POST.get('uname')
            password = request.POST.get('pwd')
            print(username,password)

            return HttpResponse('登录成功!')
            
urls.py
	url(r'^login2/', views.LoginView.as_view()),    #LoginView.as_view表示返回一个view()函数的
                             # 后边是函数路径,然后执行此函数       #名称,等价于views.view,
                                    #views是app01下面的文件

CBV通过不同的请求方法找到对应的试图类中的方法

关键点,反射

    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)  #反射

CBV的dispatch方法

from django.views import View
class LoginView(View):
    # GET 
    def dispatch(self, request, *args, **kwargs):
        print('请求来啦')
        ret = super().dispatch(request, *args, **kwargs)
        print('到点了,走人了')
        return ret
    def get(self,request):
        print('get方法执行了')
        return render(request,'login2.html')
    def post(self,request):
        username = request.POST.get('uname')
        password = request.POST.get('pwd')
        print(username,password)
        return HttpResponse('登录成功!')

FBV加装饰器

def n1(f):
    def n2(*args,**kwargs):
        print('请求之前')
        ret = f(*args,**kwargs)
        print('请求之后')
        return ret
    return n2
@n1
def home(request):
    print('home!!!')
    return render(request,'home.html')

CBV加装饰器

from django.views import View
from django.utils.decorators import method_decorator

def n1(f):
    def n2(*args,**kwargs):
        print('请求之前')
        ret = f(*args,**kwargs)
        print('请求之后')
        return ret
    return n2

# @method_decorator(n1,name='get') #方式三
class LoginView(View):
    # GET
    # @method_decorator(n1)  #方式2 给所有方法加装饰器
    def dispatch(self, request, *args, **kwargs):
        # print('请求来啦')
        ret = super().dispatch(request, *args, **kwargs)
        # print('到点了,走人了')
        return ret

    # @method_decorator(n1)  #方式1
    def get(self,request):
        print('get方法执行了')
        return render(request,'login2.html')
    def post(self,request):
        username = request.POST.get('uname')
        password = request.POST.get('pwd')
        print(username,password)
        return HttpResponse('登录成功!')

 

 

posted @ 2020-10-03 14:54  繁星春水  阅读(246)  评论(0编辑  收藏  举报
1 //2 3
//4