zoukankan      html  css  js  c++  java
  • 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
    
    请求头
    Host: 127.0.0.1:8080
    
    Connection: keep-alive
    
    Cache-Control: max-age=0
    
    Upgrade-Insecure-Requests: 1
    
    User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36
    
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
    
    Accept-Encoding: gzip, deflate, br
    
    Accept-Language: zh-CN,zh;q=0.9
    
    
    
    请求体
    ......
    """
    复制代码

    要想让我们自己写的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
    
    ')
        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
    
    ')
        data = data.decode('utf-8')  # 把从浏览器那里收到的字节类型的数据转换成字符串
        # print(data)
        target_url = data.split('
    ')[0].split(' ')[1]  #按
    分割
        # 根据不同的路径返回不同内容
        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	emplates2 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	emplates4 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('登录成功!')
  • 相关阅读:
    hdu 1045 Fire Net
    hdu 1044 Collect More Jewels
    hdu 1043 Eight
    hdu 1042 N!
    hdu 1041 Computer Transformation
    hdu 1040 As Easy As A+B
    CI在ngnix的配置
    angularjs表单验证checkbox
    chrome浏览器跨域设置
    angularjs向后台传参,后台收不到数据
  • 原文地址:https://www.cnblogs.com/wddxx/p/13764245.html
Copyright © 2011-2022 走看看