一、初识Django

Django 是一个web框架
web框架的本质
- web框架的本质就是一个socket服务端(帮你处理了socket,让你只需要专注于逻辑处理) 
- 符合wsig协议的web服务器 b/s架构:浏览器---服务器,本质也是c/s架构

 

二、HTTP协议

http请求协议:
    请求首行:
        请求的方法 空格 请求地址 空格 请求协议
            GET        /index     HTTP/1.1\r\n
    请求头:
        (key:value的形式展现)
        'Connection: keep-alive\r\n
        Pragma: no-cache\r\n
        Cache-Control: no-cache\r\n
        Upgrade-Insecure-Requests: 1\r\n
        User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 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\r\n\r\n'
    请求体:
        可以携带data数据(向服务器发送的数据)
        如:用户名/密码等信息
http响应协议:
    响应首行:
        HTTP/1.1 200 OK \r\n
    响应头:
        key:value \r\n
        xx:xx \r\n
        yy:yy \r\n
    响应体:
        响应数据...

 

 

三、Web框架简介

把一个请求拆分成几部分,每个部分干相同的事
Python中的web框架:
    Django:大而全的框架
    flask:轻量级 微框架
    Tornado:高性能的框架
MVC架构
    M:模型层,model
    V:视图,对应django中的T
    C:控制器,根据用户的请求地址,执行某些代码

MTV架构
    django是MTV架构的
        M:模型层,数据操作层,model
        T:模板层,template
        V:视图层,view
            路由控制+视图层 是mvc的c层

 

 

四、Django的安装和使用

- 安装Django的三种方式:
    1 在cmd窗口下:pip3 install django == 1.11.9
    2 在pycharm的Terminal窗口下: pip3 install django == 1.11.9
    3 在pycharm - setting中安装,选择版本号: 1.11.9
- 卸载Django
    1 pip3 unistall django
    2 在pycharm-setting中进行卸载
- 安装完成后dgango后会在python36\Scripts,会多一个django-admin.exe
- 创建Django项目
    命令行 django-admin startproject myfirstdjango(了解就行)

 

五、Django项目的目录结构

- manage.py 项目入口 运行项目需要用到
- templates文件夹:放模板文件,放html模板

- 项目名的文件夹(*****现阶段需要记住的)
    setting.py 项目配置文件(整个项目的配置信息)    *****
    urls.py 路由和函数的映射关系  ***** 
    wsgi.py django框架帮咱写的socket

- app名字的文件夹
    migrations 数据库迁移相关的,记录变化的文件
    admin.py 后台管理相关(知道就行)
    apps.py app的配置信息(app自己的配置信息)
    models.py 数据库相关,函数取数据用的 *****
    tests.py 测试相关的
    views.py 视图(业务逻辑)   *****

- app的概念(每个django项目至少有一个app!!!)
    大学 ---- django项目
    xx学院 ---- 一个个的app
    创建app的命令(新建app01)
        python3 manage.py startapp app01

 

六、运行Django项目

python3 manage.py runserver
重点*****
    安装django
    创建django项目
    创建app
    运行项目
- pycharm中创建项目 输入项目名称+自定义app名称+解释器选择
- pycharm中运行项目 绿色箭头运行

 

七、Django框架的简单使用

- django项目中重点需要关注的文件夹:
    - urls.py
    - views.py
    - models.py
1 在浏览器输入 http://127.0.0.1/index 并在页面显示hello django
    > 首先在views.py中键入新手三件套
        from django.shortcuts import render
        from django.shortcuts import HttpResponse
        from django.shortcuts import redirect
    > 其次在views.py中输入index页面的函数
        def index(request):
            return HttpResponse('hello django')     #可以向浏览器返回字符串
    > 接着在urls.py中新增路由和函数的映射关系
        from app01 import views
        urlpatterns = [
            url(r'^index/', views.index),
        ]
    > 接着点击绿色箭头运行该项目
    > 最后打开浏览器输入 http://127.0.0.1/index查看页面显示内容
2 在浏览器输入 http://127.0.0.1/index 向浏览器返回一个页面
    > 首先在views.py中键入新手三件套
        from django.shortcuts import render
        from django.shortcuts import HttpResponse
        from django.shortcuts import redirect
    > 其次在views.py中输入index页面的函数
        def index(request):
            #render是一个方法,第一个参数传request对象,第二个参数是模板文件的名字
            return render(request,'index.html')   
    > 接着在templates文件夹中新建html页面,命名问index.html,并在页面中随意写一些内容
        <body>
        <h1>django的index页面</h1>
        <a href="https://baidu.com">点我看美女</a>
        </body>
    > 在浏览器输入 http://127.0.0.1/index 查看结果
3 在浏览器输入 http://127.0.0.1/index 重定向到www.baidu.com
    > 首先在views.py中键入新手三件套
        from django.shortcuts import render
        from django.shortcuts import HttpResponse
        from django.shortcuts import redirect
    > 其次在views.py中输入index页面的函数
        def index(request):
            # 返回重定向
            # 1xx 接受的请求正在处理,信息性状态码
            # 2xx 成功状态码
            # 3xx 重定向状态码
            # 4xx 客户端错误状态码
            # 5xx 服务器错误状态码
            return redirect('https://www.baidu.com')
    > 在浏览器输入 http://127.0.0.1/index 查看结果

 

八、Django框架层面分析

- 路由层:
- 基本使用
    # url是一个函数,函数参数,第一个参数是一个正则表达式
    # 用户请求的路径只有匹配成功,才去执行后面的函数
    url(r'^index$,views.index)
- 无名分组
    url(r'^index/(\d+)/(\d+)', views.index)
    括号内的内容会被分组区分开,当作一个个参数,传入到都好后面的views.index视图函数中
    视图函数会接受这些参数
- 有名分组
    url(r'^index/(?P<cc>\d+)/(?P<ee>\d+)', views.index)
    括号内的内容会被分组分出来,当作参数,以key:value的形式传入后面的视图函数
    视图函数会接受这些参数
- 反向解析
    1 补充重定向
    2 反向解析,通过名字,反解出名字对应的url地址
        例1:应用在视图函数中
        urls.py  
            url(r'^test222/$', views.test,name='ttt')

        views.py
            from django.shortcuts import reverse
            def index(request):
                url = reverse('ttt')
                return redirect(url)

        例2:应用在模板中
            <a href="{% url 'ttt' %}">点我看新美女</a>
- 路由分发
    例如:
        使用命令创建app02  python3 manage.py startapp app02
        ### 注意,新建app一定要在setting.py中进行注册
            ### 注册app,两种方式都可以,官方推荐使用下面的第一种
            找到INSTALLED_APPS = [],并在其中添加以下命令,
            1 'app02.apps.App01Config',
            2 'app02',

        使用方式:
            1 在项目文件夹的urls.py中,控制不同app的urls.py
                urlpatterns = [
                url(r'app01/',include('app01.urls')),   #正则部分不能加结束符$
                url(r'app02/',include('app02.urls')),   #正则部分不能加结束符$
                ]
            2 在app01文件夹中的urls.py中,控制本app的路由
                urlpatterns = [
                    url(r'^app01test/', views.test),
                ]
            3 在app02文件夹中的urls.py中,控制本app的路由
                urlpatterns = [
                    url(r'^app02test/', views.test),    #给该条路由重命名为ttt
                ]
            4 在不同app的文件夹中的views.py文件中输入定义test函数

            5 在web页面中输入测试:http://127.0.0.1:8000/app01/app01test/
                                http://127.0.0.1:8000/app02/app02test/
- django2.0版本的path
    - django2.x的re_path就是1.x的url
    - django2.x的path是新增的功能
        path('准确路径',view.test,name='ttt')
-视图层
    - 以get形式传的参数,直接拼接在路径后面,例如:http://127.0.0.1:8000/index/?name=lich&age=18
        这种方式的传参,并没有把数据放在http协议的请求体中,而是直接放到请求头部了
        (post提交的数据,才放在请求体中)

    - 请求对象request
        # request是一个请求对象

        # 请求方法
        print(request.method)   # 再浏览器里发的请求都是GET

        # 请求地址
        print(request.path) #访问的地址是/index/

        # 请求全路径
        print(request.get_full_path())  # <QueryDict: {}>当作一个字典

        #请求get形式传参
        print(request.GET)  #没有传参<QueryDict: {}>,所以为空
        # http://127.0.0.1:8000/index/?name=lich&age=18 这是传参方式

        # 请求体的内容
        print(request.body)

        # 以post形式传的参数
        # print(request.POST)

        # 查看请求源IP地址   REMOTE_ADDR 客户端请求源IP的字段,可以从这个字典中取出
        {'PATH': '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/aria2/bin:/Applications/Wireshark.app/Contents/MacOS', 'PYTHONPATH': '/Applications/PyCharm.app/Contents/helpers/pycharm_matplotlib_backend:/Users/lich/PycharmProjects/MySecondDjangProj', 'SHELL': '/bin/bash', 'PYTHONIOENCODING': 'UTF-8', 'VERSIONER_PYTHON_PREFER_32_BIT': 'no', 'USER': 'lich', 'TMPDIR': '/var/folders/z6/sc42tfkd71bbq228v7nc_c1w0000gn/T/', 'SSH_AUTH_SOCK': '/private/tmp/com.apple.launchd.cDNseej7iN/Listeners', 'DJANGO_SETTINGS_MODULE': 'MySecondDjangProj.settings', 'XPC_FLAGS': '0x0', 'PYTHONUNBUFFERED': '1', 'VERSIONER_PYTHON_VERSION': '2.7', '__CF_USER_TEXT_ENCODING': '0x1F5:0x19:0x34', 'Apple_PubSub_Socket_Render': '/private/tmp/com.apple.launchd.xzQeRBq6rT/Render', 'LOGNAME': 'lich', 'LC_CTYPE': 'zh_CN.UTF-8', 'XPC_SERVICE_NAME': 'com.jetbrains.pycharm.120044', 'PWD': '/Users/lich/PycharmProjects/MySecondDjangProj', 'PYCHARM_HOSTED': '1', 'HOME': '/Users/lich', 'PYCHARM_MATPLOTLIB_PORT': '61214', '__PYVENV_LAUNCHER__': '/usr/local/Cellar/python/3.6.5/bin/python3.6', 'TZ': 'UTC', 'RUN_MAIN': 'true', 'SERVER_NAME': '1.0.0.127.in-addr.arpa', 'GATEWAY_INTERFACE': 'CGI/1.1', 'SERVER_PORT': '8000', 'REMOTE_HOST': '', 'CONTENT_LENGTH': '17', 'SCRIPT_NAME': '', 'SERVER_PROTOCOL': 'HTTP/1.1', 'SERVER_SOFTWARE': 'WSGIServer/0.2', 'REQUEST_METHOD': 'POST', 'PATH_INFO': '/login/', 'QUERY_STRING': '', 'REMOTE_ADDR': '127.0.0.1', 'CONTENT_TYPE': 'application/x-www-form-urlencoded', 'HTTP_HOST': '127.0.0.1:8000', 'HTTP_CONNECTION': 'keep-alive', 'HTTP_CACHE_CONTROL': 'max-age=0', 'HTTP_ORIGIN': 'http://127.0.0.1:8000', 'HTTP_UPGRADE_INSECURE_REQUESTS': '1', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36', 'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'HTTP_REFERER': 'http://127.0.0.1:8000/login/', 'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br', 'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9', 'wsgi.input': <_io.BufferedReader name=5>, 'wsgi.errors': <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>, 'wsgi.version': (1, 0), 'wsgi.run_once': False, 'wsgi.url_scheme': 'http', 'wsgi.multithread': True, 'wsgi.multiprocess': False, 'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>}



    ### 注意:遇到403Forbidden,优先跑到setting.py中
            #先注释掉下面这行,等讲到中间件的时候再展开讨论
            # 'django.middleware.csrf.CsrfViewMiddleware',
- 模版层
    - DTL:Django Template Language - django的模版语言
    - {{ }}
        1,通过 "."来做深度查询
        例如:
            views.py配置
                import time
                class Person():
                    def __init__(self,name):
                        self.name = name

                def index(request):
                    #例1:
                    # ctime = str(time.time())
                    #
                    # return render(request,'new_index.html',{'time':ctime})

                    #例2:
                    ctime = str(time.time())
                    dic = {
                        'name':'lich',
                        'age':18
                    }
                    li = [1,2,3,4,5]

                    def test():
                        return '我是个函数'

                    #生成对象
                    lich = Person('lich')

                    #locals() 会把该函数中的所有的变量,构造成字典并传送到模板中
                    return render(request,'new_index.html',locals())

            new_index.html配置
                <body>
                    {# 模版语言例1: #}
                    <h1>模版语言之变量</h1>
                    <p>{{ ctime }}</p>
                    <p>{{ dic }}</p>

                    {#模板语法和python语法有点区别,直接用.就可以取值#}
                    {#在模板中写的变量,相当于print该变量,变量都是字符串#}
                    <p>{{ dic.name }}</p>
                    <p>{{ dic.age }}</p>
                    <p>{{ li }}</p>

                    {#取第一个值li.0#}
                    <p>{{ li.0 }}</p>
                    <p>{{ li.0 }}|{{ li.1 }}|{{ li.2 }}</p>

                    <p><a href="{{ dic.name }}">{{ li.3 }}</a></p>

                    {#执行一个函数,不要加括号#}
                    <p>{{ test }}</p>

                    {#打印了一个对象的内存地址#}
                    <p>{{ lich }}</p>

                    {#对象取值#}
                    <p>{{ lich.name }}</p>
                </body>

            urls.py配置
                urlpatterns = [
                    # url(r'^index/$', views.index),   #正则部分不能加结束符$
                    # url(r'^login/$', views.login),   #正则部分不能加结束符$
                    url(r'^index/$', views.index),   #正则部分不能加结束符$

                ]

        2,过滤器
            语法: {{obj|filter_name:param}}   变量名|过滤器方法名称:参数
            ### 记住date 和 safe 就可以了(xss作为了解)



                views.py配置
                    import time
                    import datetime

                    class Person():
                        def __init__(self,name):
                            self.name = name

                    def index(request):
                        #例1:
                        # ctime = str(time.time())
                        #
                        # return render(request,'new_index.html',{'time':ctime})

                        #例2:
                        ctime = str(time.time())
                        dic = {
                            'name':'lich',
                            'age':18
                        }
                        li = [1,2,3,4,5]

                        def test():
                            return '我是个函数'

                        #生成对象
                        lich = Person('lich')

                        count = 10
                        i = 1024

                        b = False
                        a = True

                        dat = datetime.datetime.now()

                        ss = '<input type="text" name="name">'

                        xss = '<script>alert(123)</script>'

                        #locals() 会把该函数中的所有的变量,构造成字典并传送到模板中
                        return render(request,'new_index.html',locals())

                nex_index.html配置
                    {#过滤器,相当于将count所代表的数值+1#}   #记住safe date 其他的了解即可
                    {{ count|add:1 }}

                    <p>{{ b }}</p>
                    <p>{{ b|default:'bbbbb' }}</p>
                    <p>{{ a|default:'aaaaa' }}</p>

                    {#列表长度计算#}
                    <p>li的长度为{{ li|length }}</p>

                    {#通过filesizeformat进行单位计量转换#}
                    <p>{{ i|filesizeformat }}</p>

                    {#时间格式化过滤器#}
                    <p>{{ dat }}</p>
                    <p>{{ dat|date:'Y-m-d H:i:s' }}</p>

                    {#可以在views.py写一些 html的语法,如果要在web显示出来功能,则需要使用safe#}
                    <p>{{ ss }}</p>
                    <p>{{ ss|safe }}</p>

                    {#跨站脚本攻击演示,django已经帮我们处理了这个,如果我们认为是安全的脚本,则使用safe#}
                    <p>{{ xss|safe }}</p>
未完待续

 模板语言用法。。。

 

 

待补充...

 

九、Forms---数据校验

1 froms是什么?--- 校验数据
    - 数据校验
    - 渲染页面
    - 渲染错误信息
    - 局部和全局钩子函数
    - 使用css的样式

如下代码来认识下forms的相关功能

(***项目文件夹中新建static文件夹-将bootstrap文件粘贴过来***)

    # 如何使用forms组件
    # 第一步 写一个类集成forms
    from django import forms
    from django.shortcuts import HttpResponse
    from django.shortcuts import redirect
    from django.shortcuts import render

    from django.core.exceptions import ValidationError
    from django.forms import widgets


    class RegForms(forms.Form):
        # 第二步 写要校验的字段
        name = forms.CharField(min_length=3, max_length=8, label='用户名', error_messages={'min_length': '太短了',
                                                                                        'max_length': '太长了',
                                                                                        'required': '该字段必填'},
                               widget=widgets.TextInput(attrs={'class': 'form-control'}))  # 变量名必须和register.html中的name保持一致
        pwd = forms.CharField(min_length=3, max_length=8, error_messages={'min_length': '太短了',
                                                                          'max_length': '太长了',
                                                                          'required': '该字段必填'},
                              widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
        re_pwd = forms.CharField(min_length=3, max_length=8, label='确认密码', error_messages={'min_length': '太短了',
                                                                                           'max_length': '太长了',
                                                                                           'required': '该字段必填'},
                                 widget=widgets.PasswordInput(attrs={'class': 'form-control'}))
        email = forms.EmailField(label='邮箱', error_messages={'invalid': '不是邮箱格式',
                                                             'required': '该字段必填'},
                                 widget=widgets.EmailInput(attrs={'class': 'form-control'}))

        # 局部钩子函数(某个字段,自定义的规则,比如不能以sb开头,用户名已存在。。。等等)
        # 固定写法:方法名必须为clean_字段名
        # 要求:不能以sb开头
        def clean_name(self):
            # self是当前forms对象,cleaned_data是清洗后的数据,从字典中取出name
            name = self.cleaned_data.get('name')
            if name.startswith('sb'):
                # 以sb开头了,禁止掉
                raise ValidationError('不能以sb开头')
            else:
                return name

        # 全局钩子函数(如:校验两次密码是否一致)
        # 固定写法:
        def clean(self):
            pwd = self.cleaned_data.get('pwd')
            re_pwd = self.cleaned_data.get('re_pwd')
            if pwd == re_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('两次密码不一致')


    # 多传了要校验的字段,能正常通过校验,只不过多传的字段被清洗掉了
    # 少传了,不行


    def register(request):
        if request.method == 'GET':
            # 生成一个空的forms
            forms = RegForms()
            error = ''
            # return render(request, 'register.html',{'forms':forms})
        elif request.method == 'POST':
            # 第三步 生成一个Forms类的对象,传一个字典
            forms = RegForms(request.POST)
            # 第四步 调用forms的is_vaild方法,完成校验 is_valid返回true或者false
            if forms.is_valid():
                # cleaned_data表示清洗后的数据,通过校验的字段数据以key:value形式形成一个字典
                print(forms.cleaned_data)
                return HttpResponse('注册成功')

            else:
                print(forms.errors)
                print(type(forms.errors))
                print(forms.errors.as_data)  # 打印字典格式的错误信息

                # 取全局错误信息
                error = forms.errors.get('__all__')[0]
                print(error)

        return render(request, 'register.html', {'forms': forms, 'error': error})
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">

        {#    引入bootstrap#}
        <link rel="stylesheet" href="/static/bs-3.3.7/css/bootstrap.css">
        <title>我是注册页面</title>
        <style>
            .errors {
                color: red;
            }
        </style>


    </head>
    <body>

    {#<form action="" method="post">#}
    {##}
    {#     不写csrf_toke 则会导致403forbidden#}
    {#    {% csrf_token %}#}
    {##}
    {#    <p>用户名: <input type="text" name="name"></p>#}
    {#    <p>密码: <input type="password" name="pwd"></p>#}
    {#    <p>确认密码: <input type="password" name="re_pwd"></p>#}
    {#    <p>邮箱: <input type="email" name="email"></p>#}
    {#    <p>xx: <input type="text" name="xx"></p>#}
    {##}
    {##}
    {#    <input type="submit" value="提交">#}
    {##}
    {#</form>#}

    {#<hr>#}
    {#<h1>forms渲染页面的第一种方式</h1>#}
    {#<form action="" method="post">#}
    {##}
    {#     不写csrf_toke 则会导致403forbidden#}
    {#    {% csrf_token %}#}
    {##}
    {#    <p>用户名: {{ forms.name }}</p>#}
    {#    <p>密码: {{ forms.pwd }}</p>#}
    {#    <p>确认密码: {{ forms.re_pwd }}</p>#}
    {#    <p>邮箱: {{ forms.email }}</p>#}
    {##}
    {##}
    {#    <input type="submit" value="提交">#}
    {##}
    {#</form>#}


    <div class="container-fluid">
        <div class="row">
            <div class="col-md-offset-3 col-md-6">

                <hr>
                <h1>forms渲染页面的第二种方式(###推荐使用###)</h1>
                <form action="" method="post" novalidate>

                    {#     不写csrf_toke 则会导致403forbidden#}
                    {% csrf_token %}
                    {% for form in forms %}

                        {#        默认使用需要RegForms中的key值写入label#}
                        <p>{{ form.label }} : {{ form }} <span class="errors">{{ form.errors.0 }}</span></p>

                    {% endfor %}


                    <input type="submit" value="提交"> <span class="errors">{{ error }}</span>
                </form>

            </div>
        </div>
    </div>


    {#<hr>#}
    {#<h1>forms渲染页面的第二种方式(###推荐使用###)</h1>#}
    {#<form action="" method="post" novalidate>#}
    {##}
    {#     不写csrf_toke 则会导致403forbidden#}
    {#    {% csrf_token %}#}
    {#    {% for form in forms %}#}
    {##}
    {#        默认使用需要RegForms中的key值写入label#}
    {#        <p>{{ form.label }} : {{ form }} <span class="errors">{{ form.errors.0 }}</span></p>#}
    {##}
    {#    {% endfor %}#}
    {##}
    {##}
    {#    <input type="submit" value="提交"> <span class="errors">{{ error }}</span>#}
    {#</form>#}


    {#<hr>#}
    {#<h1>forms渲染页面的第三种方式(###不推荐###)</h1>#}
    {#<form action="" method="post">#}
    {##}
    {#     不写csrf_toke 则会导致403forbidden#}
    {#    {% csrf_token %}#}
    {##}
    {#    {{ forms.as_p }}#}
    {#    {{ forms.as_ul }}#}
    {#    {{ forms.as_table }}#}
    {##}
    {#</form>#}
    {#<input type="submit" value="提交">#}


    </body>
    </html>
    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^register/', views.register),
    ]
    settings.py
    INSTALLED_APPS = [
    'app01.apps.App01Config',
    ]

    STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static')
    ]

 

 

十、jdango框架内容总结

0 HTTP协议
    -请求协议
        -请求首行:请求的方式,请求的地址
        -请求头:key:value的形式
        -请求体: 请求携带的数据(post)
    -响应协议
        -响应首行:状态码:1,2,3,4,5,
        -响应头:key:value的形式
        -响应体:响应的内容
    
1 web框架的本质
    -socket服务端
2 python中的web框架
    django
        -MTV
            -M:model
            -T:模板
            -V:视图层
        -MVC
            -M:model
            -V:模板
            -C:控制器,路由+视图

3 路由
    1 url('正则表达式',函数内存地址,kwargs={默认参数},name=别名)  是一个函数
    2 分组(有名分组,无名分组)
    3 反向解析
        -在视图层生成:revers()
        -在模板层生成:{% url 'test' %}
    4 路由分发:include
    
    5 django2.0版的path
        re_path:1.x的url
        path:第一个参数是一个准确路径
    
4 视图层
    -reuquest对象
        request.method
        request.path:liuqingzheng/p/9506212.html
        request.get_full_path():liuqingzheng/p/9506212.html?name=lqz&age=18
        request.GET:以get形式传的参数
        request.POST:以post形式传的参数
    -HttpResponse对象
        -三件套
            -render
            -redirect
            -HttpResponse
        -JsonResponse
    -上传文件:
        模板:enctype="multipart/form-data"
        视图:request.FILES.get('myfile')
-模版层
    -模板语言:DTL:django 模板语言
    -{{ python的变量 }}    
    -深度查询  .
        -{{ 字典.name }}
        -{{ 列表.0 }}
        -{{ 对象.属性/方法(显示的是方法的返回值) }}
    -过滤器
        add
        date *****
        {{python的时间对象|date:'Y-m-d'}}
-模型
  https://www.cnblogs.com/liuqingzheng/articles/9805991.html

-django进阶
  组件:
    django与ajax
    分页器组件
    ****forms组件
    ****cookie与session组件
    中间件组件
    Auth模块:认证
    ContentType组件:表关联
    Go与python的区别:
      go没有异常处理:defer,panic,recover
      go没有class,通过结构体