zoukankan      html  css  js  c++  java
  • web框架。Django--

     

    model.UserInfo._meta.app_label                       
    #获取该类所在app的app名称
    
    model.UserInfo._meta.model_name
    #获取该类对应表名(字符串类型)
    
    model.UserInfo._meta.get_field('username')
    #获取该类内指定字段信息(对象)
    
    model.UserInfo._meta.fields
    #获取该类内所有字段对象
    
    model.UserInfo._meta.get_fields
    #获取该类内所有字段信息(对象),包含反向关联的字段
    
    model.UserInfo._meta.many_to_many
    #获取该类内多对多字段信息
            
    model.UserInfo._meta.get_field('username').verbose_name
    #获取该类内‘username’字段,verbose_name 的值
    
     
    
    --------
      Book:
      list_filter=["state","publish","authors"]
      
      每一个字段相关信息:
       
           字段字符串  : "state"
           字段对象    : Book._meta.get_field("state")
           字段关联数据: 
                  if---choice类型字段:
                        字段对象.choices
                        
                  if---ForeignKey,ManytoMany:
                        字段对象.rel.to.objects.all()
           
    
      字段信息封装成类:
      
         class FilterField(object):
               def __init__(self,filter_field_name,filter_field_obj):
                   self.filter_field_name=filter_field_name
                   self.filter_field_obj=filter_field_obj
    
                   
               def get_data(self):
                   if isinstance(self.filter_field_obj,ForeignKey) or isinstance(self.filter_field_obj,ManyToManyField):
                       return self.filter_field_obj.rel.to.objects.all()
                   elif self.filter_field_obj.choices:
                       return self.filter_field_obj.choices
                   else:
                       pass
           
           
           state=FilterField("state",state_obj)
    
     
    
    obj = models.UserInfo.objects.create(...)
    
    #源码位置
    #from django.db.models.options import Options
    #from django.db.models.fields.reverse_related import ManyToOneRel
    
    field = obj._meta.related_objects[0]
    #拿到当前记录对象所对应的反向关联字段的queryset
    
    print(field[0].limit_choices_to)
    #拿到对应的limit_choices_to的字典的数据
    
    print(field[0].related_name)
    #拿到related_name属性所对应的值
    
    print(field[0].field_name)
    #拿到反向关联字段里的关联本表的字段
    
    print(field[0].field.model._meta.model_name)
    #拿到反向关联字段所在类名称
    Django中_Meta

    一,DIY一个web框架

    1.1什么是web框架

    1.2用socket模拟B-S的服务端

    1.3,浏览器端的network查看

    1.4,request格式

    1.5,response格式

    1.6,初识wsgiref

    1.7,wsgiref进行路由控制

    1.8,wsgiref做的web框架结构

     二、最广泛使用的web框架--Django

    2.1,Django简介

    2.2,Django安装,命令

    2.3,Django简单demo

     2.4,css/js等静态资源配置

    2.5,路由控制之--正则表达式匹配(re_path)

    2.6、响应体数据格式(render,HttpResponse,jsonResponse,FileResponse)

    2.7,路由控制之--正则表达式匹配(re_path(..?P<param>))

    2.8,路由控制之--分发(re_path(r"app01/",include(("app01.urls"))))

    2.9,路由控制之--反向解析(在HTML里或在views里)

    2.10,反向解析时别名冲突怎么办---使用名称空间(re_path(r"app01/",include(("app01.urls", "app01"))))

    2.11,re_path升级版---django2.x版本的path

    2.12,视图函数之request对象

    2.13,渲染变量 {{  obj  }}

    2.14,过滤器    {{ obj | filter_name:param}} 参数个数<=2

    2.15,渲染标签:{% url / csrf_token / if / for / with %} 参数个数>2

    2.16,自定义标签,过滤器(if条件判断只能用过滤器)

    2.17,模板语法之继承

    2.18 (补)inclution_tag标签用法(计算变量值得到HTML代码)

     2.19 (补) request.POST取数组只取到最后一个? 

     2.20 (补) 前端和path中的相对路径和绝对路径

    2.21(补)Django有两种静态文件

    2.22(补)Django用户表原生字段

    2.23(补)CNBLOG上传头像到media目录

    2.231(补)  CRM通过视图函数下载文件

    2.24 (补) Django-admin(不是项目必须)

    2.25 (补) 时间不对怎么办(这样调整后数据库存的时间和显示的时间不一样)

    2.26(补) Django事务控制

    2.261(补) Django-数据库加锁(行锁)

    2.27(补) Django发送邮件

    2.28(补) Django批量发现权限URL正则(发现反向解析表达式+url正则)

    2.29(补) 同一个页面向后端同一个视图函数提交两种post请求,如何进行甄别?

    2.30(补)时间时区设置

    • 在Django的配置文件settings.py中,有两个配置参数是跟时间与时区有关的,分别是TIME_ZONEUSE_TZ
    • 如果USE_TZ设置为True时,Django会使用系统默认设置的时区,即America/Chicago,此时的TIME_ZONE不管有没有设置都不起作用。
    • 如果USE_TZ 设置为False,而TIME_ZONE设置为None,则Django还是会使用默认的America/Chicago时间。若TIME_ZONE设置为其它时区的话,则还要分情况,如果是Windows系统,则TIME_ZONE设置是没用的,Django会使用本机的时间。如果为其他系统,则使用该时区的时间,设置USE_TZ = FalseTIME_ZONE = 'Asia/Shanghai', 则使用上海的UTC时间。

    一,DIY一个web框架

    1.1什么是web框架

    web框架全名web应用框架
    web开发: html css js
    web应用:类似socket,客户端是浏览器,作用是接受socket请求 处理 响应数据字符串

    1.2用socket模拟B-S的服务端

    浏览器端发送的是报文,就是一堆字符串,包括URL,请求方式等
    响应的数据也有固定格式:响应首行+响应头+响应体  响应体和前面的用两个 隔开,前面的所有用一个 分隔

    import socket
    soc=socket.socket()
    soc.bind(127.0.0.1,  8800)
    soc.listen()

    while True:
      conn,addr=soc.accept()
      data=conn.recv(1024)
      conn.send(b"helloworld")
      conn.close()
    上面的方式发出去浏览器解析不了,提示“收到的响应无法解析”
    因为要按http协议的格式发,所以要HTTP/1.1 200 ok helloworld这样发

    1.3,浏览器端的network查看

    如果响应的是<h1>helloworld</h1>
    在浏览器端审查,network-request,preview,response
    response可以看到响应的字符串,preview看到的是浏览器渲染后的标签

    1.4,request格式

    请求首行+请求头+请求体
    请求首行必须要有,请求头可加可不加,反正浏览器已经封装了。请求体不是每次请求都有
    请求首行:   
        GET /  HTTP/1.1
               方法(get/post),URI(/form/entity), 协议版本HTTP/1.1
               get一般做查询操作,没有请求体,数据放在url后面以?分隔开,post一般做数据库更新操作,有请求体
    请求头:
       包括若干键值对,是这次请求的信息,比如host表示请求的主机名,connection:(keep-alive表示这次请求后过3000ms再断开连接,close表示请求后立马断开),content-type             表示请求的数据类型,content-length表示本次请求发送的字节数
    请求体:
               get请求没有请求体,POST请求体里放参数。数据是放在URL后面以?分隔开,最后在接收端收到的URI里面
               get请求与post请求不同点有三:数据放的地方;数据大小;服务器端获取数据的方式。
    服务器端怎样区分请求首行,请求头,请求体?
              服务器端接收的报文里,遇到第一个 的前面是请求首行,后面的每个 分割的是请求头的键值对,直到遇到 后面就是请求体

    1.5,response格式

    响应首行+响应头+响应体
    响应格式跟请求一样,也是/r/n  /r/n/r/n来分隔
    响应首行:HTTP/1.1 200 ok 协议版本 状态码 状态码说明。
             2xx表示成功,3xx表示重定向,4xx表示资源未找到,5xx表示服务器代码运行出错,1xx表示请求正在处理。
             重定向理解:比如某网站域名由A.com变成了A1.com,如果有浏览器访问域名A.com,服务器会给它响应一个重定向状态码3xx,让浏览器重新发请求到A1.com,这个过程就叫重           定向。重定向对浏览器来说相当于发了两次请求
    响应头:键值对格式,例如:date 时间 content-length:长度  content-type:格式
    响应体:发给浏览器需要渲染到页面的内容 

     1.6,初识wsgiref

    URL格式:   协议://IP:端口/路径?a=1
    根据路径不同返回不同的页面
    wsgiref模块,作用是解析请求数据,把请求按http协议解析成字典,或者按http协议把数据封装成响应
    wsgiref用法:
    from wsgiref.simple_server import make_server
    def application(environ, start_response):
       #请求路径。请求的参数封装在这里面
       path=environ.get("PATH_INFO")
       #给响应添加响应首行和响应头
       #响应头[("content-type","xml")]
       start_response('200  ok', [])
       #给响应添加响应体用return
       return  [b'<h1>hello web</h1>']
       
    # 封装server
    httpd=make_server("", "8080",application)
    # 开始监听请求
    httpd.serve_forever()

     1.7,wsgiref进行路由控制

    之前看到的发送第一个请求的时候,会附加发送一个/favicon.ico的get请求,这个是干什么的呢?而且两次返回的内容一样。那页面的内容是哪一次返回的内容呢,这样不会覆盖吗?
    答:这个是网站标题的icon图标。是第一次请求的内容,因为favicon.ico的请求是浏览器自动发的,为了获取到标题栏的图标,而且该请求的请求头中有个字典,accept:image/webp image/apng...指的是本次请求期望得到一个图片。服务器端要以rb方式打开图片发过去就行了 
    ----------------------------------------------------------------
    from wsgiref.simple_server import make_server
    def index(environ):
        with open('index.html', 'r', encoding="utf-8") as f:
            data = f.read()
        return [data.encode('utf8')]
    def login(environ):
        with open('login.html', 'r', encoding="utf-8") as f:
            data = f.read()
        return [data.encode('utf8')]
     
     
    def application(environ, start_response):
        # 请求路径
        path = environ.get('PATH_INFO')
        path_func = {'/': index, '/login.html': login}
        response_data = [b'404!']
        if path in path_func.keys():
            # 处理请求函数需要用到environ
            response_data = path_func[path](environ)
        # 状态码 响应头
        start_response('200 ok',[])
        return response_data
    httpd = make_server("127.0.0.1",8089,application)
    httpd.serve_forever()
    -----------------------------------------------

    1.8,wsgiref做的web框架结构

    优化代码,从耦合方面:
    main.py:入口函数
    urls.py:路径与视图函数的映射关系 --url控制器
    views.py:视图函数,固定有一个形参environ --视图函数
    templates文件夹:存放HTML文件 --模板部分
    models.py:与数据库相关
    结构:
    yuan
    --urls.py
    --main.py
    --views.py
    --templates
       --login.html
       --index.html
    现在我们就实现了一个简单的web框架:名叫"yuan"
    用户只需要修改urls views templates就可以了

    1.main.py

    from wsgiref.simple_server import make_server
    
    '''
    main.py: 程序入口
    '''
    def application(environ, start_response):
        # 请求路径
        path = environ.get('PATH_INFO')
        from urls import path_func
        response_data = [b'404!']
        if path in path_func.keys():
            # 处理请求函数需要用到environ
            response_data = path_func[path](environ)
        # 状态码 响应头
        start_response('200 ok',[])
        return response_data
    httpd = make_server("127.0.0.1",8089,application)
    httpd.serve_forever()

    2.urls.py

    from views import index,login,auth
    
    '''
    urls.py: 存放请求路径对应的方法
    '''
    path_func = {
        '/': index,
        '/login.html': login,
        '/auth': auth
    }

    3.views.py

    from urllib.parse import parse_qs
    from models import query_user
    
    '''
    views.py: 存放各路径的处理函数
    '''
    def index(environ):
        with open('templates/index.html', 'r', encoding="utf-8") as f:
            data = f.read()
        return [data.encode('utf8')]
    def login(environ):
        with open('templates/login.html', 'r', encoding="utf-8") as f:
            data = f.read()
        return [data.encode('utf8')]
    def auth(environ):
        # 从请求体中获取username&passwd
        try:
            request_body_size = int(environ.get('CONTENT_LENGTH',0))
        except (ValueError):
            request_body_size = 0
        request_body = environ['wsgi.input'].read(request_body_size)
        data = parse_qs(request_body)
        user = data.get(b'username')[0].decode('utf8')
        pwd = data.get(b'passwd')[0].decode('utf8')
    
        # 从db2数据库中获取username&passwd
        pwd_db2 = query_user(user)
        if pwd_db2 and pwd==pwd_db2:
            return ['登录成功!'.encode('gbk')]
        else:
            return [b'<h2>Authcation Failure..</h2>']

    4.models.py

    '''
    models.py: 存放数据库处理逻辑
    '''
    import ibm_db
    conn = ibm_db.pconnect("database=POBC; "
                           "hostname=localhost; "
                           "port=50000; "
                           "protocol=tcpip; "
                           "uid=administrator; "
                           "pwd=wyz", "", "")
    
    # 根据username在SYS_ORG_TB表中查密码
    def query_user(username):
        sql = "SELECT PASSWD FROM SYS_USER_TB " 
              "WHERE USERNAME='{0}'".format(username)
        stmt = ibm_db.exec_immediate(conn, sql)
        tuple = ibm_db.fetch_tuple(stmt)
        # 查到了就返回密码,没查到返回False
        if tuple:
            return tuple[0]
        else:
            return False

     二、最广泛使用的web框架--Django

    2.1,Django简介

    flask 和 Django,flask是轻量级,Django是重量级
    MVC模型与MTV模型

    M代表模型:负责业务对象和数据库的关系映射----models.py
    T代表模板 负责如何把页面展示给用户----templates
    V代表视图 负责业务逻辑,并在适当时候调用M和T----views.py
    除了以上三层,还需要一个URL分发器,作用是将一个个URL页面请求分发给不同的view处理
    view再调用相应model和template。

    当某人请求你网站的某一页面时——比如说, "/polls/34/" ,Django 将会载入 mysite.urls 模块,因为这在配置项 ROOT_URLCONF =mysite.urls中设置了。

     

     2.2,Django安装,命令

    第一步:Django安装(2.2是版本号):pip3 install Django==2.2

    安装成功会出现django-admin.exe和django-admin.py文件

    第二步:先创建一个Django项目,名称为“mysite”

    先进入想要创建文件的目录,再输入以下命令:

    python C:UsersAdministratorAppDataLocalProgramsPythonPython36-32Scriptsdjango-admin.py   startproject   mysite

    第三步:然后在“mysite”目录下创建应用“blog”

    进入“mysite”目录,输入命令:

    python manage.py startapp blog

    -------------目录层级结构-----------

    一个项目可以有多个应用。比如微信是一个项目,里面有支付应用,聊天应用
    与项目相关的东西放在项目的文件夹里

    先记住四个文件:
    models.py
    views.py
    settings.py
    urls.py
    再创建一个templates文件夹,用来放HTML文件

    第四步:启动Django,输入命令

    vim project/settings.py     把此处该为ALLOWED_HOSTS = ['*']    # 表示允许外部地址访问Django项目

    python manage.py runserver  0.0.0.0:8080    # 0.0.0.0表示外部地址也能访问

    第五步:访问http://127.0.0.1:8080/

    2.3,Django简单demo

    浏览器访问 localhost:8000/timer  显示当前时间,和数据库中所有书籍

    第一步:

    settings.py里:    'DIRS': [os.path.join(BASE_DIR,'templates')],  # 模板HTML

            'app01',      # 应用

    # MySQL数据库配置,修改NAME和PASSWORD
    DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME': 'orm',
    'USER': 'root',
    'PASSWORD': 'mysql8',
    'HOST': '127.0.0.1',
    'PORT': 3306
    }
    }

    第二步:

    项目目录下的__init__.py

    import pymysql

    pymysql.install_as_MySQLdb()

    第三步:

    urls.py里:    path('timer/', views.timer)

    第四步:

    views.py里:  

    from django.shortcuts import render

    def timer(request): #请求函数
    import time
    ctime = time.time()
    books = Book.objects.all()
    Book.objects.create(title="python编程思想")
    return render(request,"timer.html",{'date':ctime,'books':books})

    models.py里:

    from django.db import models

    class Book(models.Model): #数据库表
    id=models.AutoField(primary_key=True)
    title=models.CharField(max_length=32)

    第五步:templates下建模板timer.html:

        <h1>当前时间:{{ date }}</h1>
    <h2>书籍列表</h2>
    {% for book in books %}
    <p>{{ book.title }}</p>
    {% endfor %}

    第六步:

    python manage.py makemigrations
    python manage.py migrate

    访问:http://127.0.0.1:8000/timer/

    PS:错误处理--

    如果Python解释器是3.4以上且Django是2.0以上还要注释掉:
    ..Python37Libsite-packagesdjangodbackendsmysqlase.py里的
    if version < (1, 3, 13):
      raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)

    如果报错:AttributeError: 'str' object has no attribute 'decode'

    query = query.decode(errors=‘replace’)  将decode修改为encode即可

    --安装下面的包,不需要在__init__.py中导入pymysql,也不需要改源代码

    pip3 install mysqlclient

    2.4,css/js等静态资源配置

    templates里HTML发给客户端后想让上面的css,js生效,那么引入路径必须写服务器的路径,因为HTML是发送给客户端去执行的
    这些css,js属于静态资源

    在app下创建一个静态文件夹statics里面有app01放jquery,js,css。
    在settings.py里写上:
    STATIC_URL = '/static/'
    STATICFILES_DIRS = [os.path.join(BASE_DIR, "statics")]   只访问js,css等可以不用写这个,先从项目下去找static文件夹,再从应用下去找static文件夹
    表明通过地址栏的路径别名'/static/'对应去找statics文件夹
    STATIC_URL与STATICFILES_DIRS是相对应的。

    例如:django_project--statics--app01--jquery、css、js
    访问:127.0.0.1:8000/static/app01/jquery就得到jQuery了
    127.0.0.1:8000/static/app01/css就得到css了
    127.0.0.1:8000/static/app01/jquery就得到jQuery了
    咱们页面的的css跟js用别名来引入
    <link rel="stylesheet" type="text/css" href="/static/app01/css">
    <script type="text/javascript" src="/static/app01/jquery"></script>
    <script type="text/javascript" src="/static/app01/js"></script>

    如果是其他文件例如.xls等可以通过这种方式来下载

    如果保持默认配置:STATIC_URL = '/static/'

    在每个app下创建static目录:

    App01/static/app01/xxx.css  -->  127.0.0.1:8000/static/app01/xxx.css

    App02/static/app02/xxx.css  -->  127.0.0.1:8000/static/app02/xxx.css

    当然需要注册App01和App02

    templates也是同样的操作

    2.5,路由控制之--正则表达式匹配(分组)

    使用re_path好处:

    进行权限配置URL发现的时候可以将URL配置转变为权限的正则表达式,而用path的路径不能变成正则表达式

    使用re_path切记:

    必须进行精确匹配,加上开始和结束符号: “^” “$”

    (如果用正则去匹配urls路由跟方法可以多对一)

    urls.py里引入:

    from django.urls import re_path
    # 路由配置 路径--->视图函数
    # 这个是用正则表达式去匹配,匹配成功就执行后面的函数
    re_path(r'^articles/2003/$', views.special_case_2003)  # 默认会传入request:special_case_2003(request)
    re_path(r'^articles/([0-9]{4})/$', views.special_case)  #这是分组匹配,相当于special_case_2003(request, year)  views.py方法里可以叫year或别的名字

    re_path(r"^$", views.index)   #这样写就是直接访问ip:port返回首页
    #只要分组就把分组匹配结果作为参数传入
    上面两个,如果访问localhost:8000/articles/2003/ 会执行第一个special_case_2003
    因为从前向后匹配,匹配上了就跳出

    views.py里:
    from django.shortcuts import HttpResponse
    def special_case_2003(request):
    return HttpResponse("<h1>2003</h1>") # 这里面放响应体


    *若要从URL中捕获一个值,只需要在它周围放置一对圆括号
    *不需要添加一个前导的反斜杠,因为每个URL都有,例如应该是^articles而不是^/articles
    *每个正则表达式前面的'r'是可选的但是建议加上。它告诉python这个字符串是“原始的”--字符串中的任何字符都不应该转义

    2.6、响应体数据格式(render,HttpResponse,jsonResponse,FileResponse)

    1,render前面看过,是响应页面的静态或者页面上带固定位置,固定个数的参数的资源

    2,httpresponse响应一串字符串,请求体的内容

    return HttpResponse(json.dumps(sys_org,ensure_ascii=False), content-type="application/json,charset=utf-8")

    3,json返回的是json格式,前端不用反序列化,拿来就能直接用。

    from django.http import JsonResponse
    # Create your views here.
    
    def ajax(request):
        response = {'org':[{'type': None, 'name': '其他'}, {'type': None, 'name': '政府部门'}]}
        return JsonResponse(response)

    4,重定向请求。127.0.0.1:8000/books/

    from django.shortcuts import redirect
    
    # Create your views here.
    
    def add_book(request):
        return redirect("/books/")

    上面两种用法是等价的。

    2.7,路由控制之--正则表达式匹配(有名分组)

    re_path(r'^articles/(?P<y>[0-9]{4})/(?P<m>[0-9]{2})/$', views.special_case)
    用“?P<year>”给每个组取个名字(相当于位置形参),他匹配还是按数字匹配,这样写传参就是这样传:views.special_case(request, y=2012, m=12)

    在使用的时候,可以不用管y m的顺序,但是必须要用y m。
    special_case(request, y, m)或special_case(request, m, y)

    2.8,路由控制之--分发

    一个项目中应用可以有多个,app01,app02,app03...
    假如有10个应用,每个应用100个URL。把1000个URL写在项目的urls.py里看起来就太复杂了
    怎么把他们分开呢?

    在app01里新建一个urls.py文件表示当前应用的路由,把原来全局urls.py复制进去
    在app01--urls.py里写自己的路径,

    如果这样写:
    在全局的里面引入一下:re_path(r"app01/",include("app01.urls")) 或re_path(r"^app01/",include("app01.urls"))
    127.0.0.1:8000/app01/articles/2003/
    127.0.0.1:8000/app01/articles/2004/
    如果有app02
    127.0.0.1:8000/app02/a2/2003/
    127.0.0.1:8000/app02/a2/2004/

    如果这样写:
    re_path(r"^",include("app01.urls"))
    就这样访问:
    127.0.0.1:8000/articles/2003/
    127.0.0.1:8000/articles/2004/
    127.0.0.1:8000/a2/2003/
    127.0.0.1:8000/a2/2004/

    2.9,路由控制之--反向解析(在HTML里或在views里):

    引子--登陆验证:
    path('login/', views.login)

    def login();
      return render(request, "login.html")

    <form action="http://127.0.0.1/login" method="post">
    用户名:<input type="text" name="user">
    密码:<input type="password" name="pwd">
    <input type="submit" values="登录">
    </form>

    action代表一个路径,如果点击提交,就又返回这个页面,同一个路由地址,请求方式不同。
    所以在方法里要判断,如果是get请求就返回页面,如果是post请求就校验
    试一下:get和post确实都得到同一个页面(要在settings.py里MIDDLEWARE的...csrf...注释)
    可以在视图的login方法里看,通过request.method属性得到请求方式
    if request.method=="GET":
      #返回登录页面
    else:
      #得到GET或POST请求里的数据,用字典来存放
      request.GET
      request.POST
      #取数据
      request.POST.get("user")
      request.POST.get("pwd")
    POST请求把上次get请求的页面覆盖了。

    正式开始说反向解析:

    第一种,在HTML文件里反向解析:
    因为URL里的路径'login/'会经常变,变了无所谓,但是点提交就不行了
    因为写在页面里的action也要变才行。所以就有了反向解析:给路径取别名“Log”
    ------反向解析-----

    得到绝对路径。path('del_book/', views.delete_book, name="del_book"),

    {% url 'del_book' %} 得到 '/del_book/'。

    path('login/', views.login, name="Log")
    action="{% url 'Log' %}"   #不带参数

    action="{% url 'Log' 1 2 3 %}"   #带分组参数的要传参数,有几个分组就传几个参数

    是在render渲染的时候处理的,之前渲染变量,这里渲染URL
    模板语法有两个:
    {{ }} 和 {% %}

    第二种,在views里反向解析:
    re_path(r'^articles/2003/$', views.special_case_2003, name='s_c_2003')
    re_path(r'^articles/([0-9]{4})/$', views.special_case, name='s_c'),
    --
    在任何views里都可以进行反向解析:
    from dfango.urls import reverse
    url = reverse("s_c_2003") #没有分组的,反向解析直接得到路径
    url = reverse("s_c", args=(1111,))
    #有分组的,要加参数,随便加,只要符合分组正则表达式就可以,得到articles/1111

    应用:反向解析带原页面的请求参数:

    --渲染方法:{% memory_previous_param request 'rbac:menu_edit' menu.id %}
    @register.simple_tag
    def memory_previous_param(request, name, *args, **kwargs):
    '''
    渲染时在URL里保存上次请求的参数
    需求:“一级菜单列表 http://127.0.0.1:8000/rbac/menu/list/
    选择某个一级菜单后,http://127.0.0.1:8000/rbac/menu/list/?mid=1
    然后点新增/修改/删除,完成后,一级菜单未选中”的问题
    解决方法:需要在选中某个一级菜单时,将之前渲染的“新增按钮”URL:
    http://127.0.0.1:8000/rbac/menu/edit/3/
    变成:
    http://127.0.0.1:8000/rbac/menu/edit/3/?_next='mid=1' 其中'mid=1'需要转义
    :param request: 请求对象,用于获取当前页面的GET请求参数
    :param name: 反向解析的URL别名
    :param args: 反向解析参数1
    :param kwargs: 反向解析参数2
    :return:
    '''
    basic_url = reverse(name, args=args, kwargs=kwargs)
    if not request.GET:
    return basic_url
    q = QueryDict(mutable=True)
    q['_next'] = request.GET.urlencode()
    return "%s?_next=%s" % (basic_url, q.urlencode())

    ============

    2.10,反向解析时别名冲突怎么办---使用名称空间

    用在反向解析的过程中
    多个app里有相同的别名怎么办?

    127.0.0.1:8000/app01/index
    127.0.0.1:8000/app02/index

    如果不用名称空间,在app01和app02的view里反向解析index得到的都是/app02/index
    所以有个覆盖问题得到的都是app02

    所以在全局的里面分别规定名称空间:
    re_path(r"app01/",include(("app01.urls", "app01")))
    re_path(r"^app02/",include(("app02.urls", "app02")))

    或者这样写:

    总的urls.py里进行分发:

    分urls.py中写app_name:

     反向解析时:reverse("polls:index")

    反向解析的时候指定名称空间:
    reverse("app01:index")
    reverse("app02:index")

    2.11,re_path升级版---django2.x版本的path

    看看解决什么问题:
    re_path('articles/(?P<year>[0-9]{4})/',year_archive)
    re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/detail/', detail_view)
    re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/edit/', edit_view)
    re_path('articles/(?P<article_id>[a-zA-Z0-9]+)/delete/', delete_view)
    上面写法有两个问题
    1.匹配到的数字,实际传的参数是字符串,能不能让Django完成数据类型转换?
    2.同样的正则表达式写了三次
    Django提供的path解决这个问题:
    path('articles/<int:year>/',year_archive)  #year是分组名称
    除了int转换器,还有那些转换器呢?
        str--默认,匹配除了"/"之外的非空字符串
        slug---匹配字母、数字以及横杠、下划线组成的字符串
        uuid--匹配格式化的UUID,例如0932093-3232-4343-3434434
        path--匹配任意非空字符串,包括"/",除了"?"
    处了这些,还可以自定义转换器:
    新建一个转换器模块:url_convert.py
    里面建一个类
    class one_convert:
      regex="[0-9]{2}" #只能叫regex
      def to_python(self, value):#匹配
        return int(value)
      def to_url(self, value):#反向解析url
        return '%04d' % value

    怎么使用呢?在urls.py里:
    from django.urls import register_converter
    from app01.url_convert import one_convert
    register_converter(one_convert, "two_w")

    path('articles/<two_w:month>/',month_archive)

    这样,就解决了上面提出的两个问题。

    2.12,视图函数之request对象

    views.py:接收请求,返回响应,request存放请求来的所有信息

    请求方式get/post:request.method
    请求数据:request.GET 和 request.POST
    请求路径:request.path #不包括get数据部分
    请求头:request.META
    request的方法:
    request.get_full_path() #包括get的数据部分
    request.is_ajax() #判断是不是ajax请求

    响应:
    return HttpResponse('123') #响应字符串
    return render(request, "index.html", {"name":name, "age":age}) #如果要把变量放在页面就有第三个参数

    2.13,渲染变量 {{  obj  }}

    模板文件不是单纯的HTML文件
    模板文件里可以包括模板语法
    字典,列表,对象怎么放到模板里呢?
    模板语法只有两个:渲染变量{{ }}和渲染标签{% %}
    return render(request, "index.html",locals()) #这样就把局部变量都传入了

    模板语法之深度查询;用点"."
    例如:
    a=[1,2,3]
    b={'name':'alex','age':32}
    在传给模板之后{{ a }} {{ b }}
    要取里面的值就叫“深度查询”:
    {{a.1}} 取到的是2
    {{b.name}} 取到的是'alex'
    如果有对象也是按这个方式来取

    2.14,过滤器    {{ obj | filter_name:param}} 参数个数<=2

    语法:{{ obj|filter_name:param}}
    例如:
    import datetime
    now = datetime.datetime.now()
    {{ now|date:"Y-m-d H:i:s"}} date是自带的过滤器,使用后显示“2001-02-03”
    系统自带的过滤器:
    default: {{value|default:'null'}} #如果变量为false或为空,使用给定的值,否则使用变量的值
    length:{{value|length}} #显示字符串或列表的长度
    filesizeformat:{{value|filesizeformat}} #如果value是12343423,输出是117.7kb
    date:{{ now|date:"Y-m-d"}} # 输入是datetime.datetime.now() 格式化输出日期时间
    slice:{{value|slice:"2:-1"}} #字符串切片
    truncatechars:{{value|truncatechars:9}} #如果字符串长度大于9会被截断,截断的部分用"..."结尾
    safe:{{value|safe}} # value="<a href=''>click</a>"   safe过滤器可以在渲染的时候将数字 1 转换成字符串 '1'

    如果前端是用{{ variable }}这种方式渲染,用上面的safe没有问题,如果前端是用forms组件渲染上面的方式就不起作用了

    <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field|safe }} 这样写不起作用。  

    前端是用forms组件渲染,就要用到mark_safe:

    from django import forms
    from django.utils.safestring import mark_safe

    icon_list = [
    ('fa-user', mark_safe('<i class="fa fa-user"></i>')),
    ('fa-th-list', mark_safe('<i class="fa fa-th-list"></i>'))
    ]
    class MenuForm(forms.Form):
    title = forms.CharField(max_length=30, label="菜单标题", widget=forms.TextInput(attrs={"class": "form-control"}))
    icon = forms.ChoiceField(label="菜单图标", choices=icon_list,
    widget=forms.RadioSelect())


    Django的模板中会对HTML标签和js等语法标签类似"<a><script>"等进行自动转义,
    这样是为了安全。如果有脚本标签等,会有安全问题,例如写1000个<script>alert(123)</script>
    但是有时候我们可能不希望这些HTML元素被转义
    比如做一个内容管理系统,后台添加的文章中是经过修饰的,这些
    修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本
    如果自动转义的话,显示的就是保护HTML标签的源文件。为了在
    Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量
    可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义

    2.15,渲染标签:{% url / csrf_token / if / for / with %} 参数个数>2

    for循环生成标签:
    列表:如果列表是空的会显示列表为空,要放在循环体里面的后半部分
    l=[1,2,3,4]
    {% for i in l %}
    <p>{{ i }}</p>

    {% empty %}
    <p>列表为空</p>
    {% endfor %}

    字典:并加序号{{ forloop.counter }}或{{ forloop.counter0 }}
    info=['name':'alex','age':12]
    {% for key in info %}
    <p>{{ forloop.counter }}   {{ key }} {{ info.key }}</p>
    {% endfor %}

    if标签:
    {% if user%}
    <p>已登录</p>
    {% else %}
    <p>未登陆</p>
    {% endif %}

    with标签:给很长的名字取别名,后面可以用别名代替
    {% with person.1.name as n %}
    {{ n }}
    {{ n }}
    {% endwith %}


    {% csrf_token %}
    之前的登陆页面,第一次提交get请求,第二次提交post请求,由同一个视图函数处理
    如果给同一个地址既能提交get请求又能提交post请求,如果提交post请求,会被csrf拦截。
    因为服务器不知道这次post请求之前是否进行了一次get请求,所以要在form表单里加csrf_token:
    <form>
    {% csrf_token
    ...
    </form>
    这样第一次提交get请求的时候,就在表单里渲染一个token(类似身份证)一起提交给服务器
    第二次提交post请求,服务器发现有这个token,就知道浏览器已经提交了一次get请求,就把结果返回。

    2.16,自定义标签,过滤器(if条件判断只能用过滤器)

    例如自定义一个乘法过滤器和乘法标签:
    1,在settings里注册INSTALLD_APPS里面加上"app01",
    2. 在app01下新建文件夹templatetags
    3. 创建任意.py文件例如my_tag_filter.py并在里面写:

    from django import template

    register = template.Library()

    @register.filter
    def multi_filter(x,y):
    return x*y

    @register.simple_tag
    def multi_tag(x,y):
    return x*y
    使用:
    {% load my_tag_filter %}
    {{ i|multi_filter:20 }}
    {% multi_tag 7 8 %}
    区别?
    过滤器只能定义两个形参,标签可以接受很多形参。标签是不是比过滤器好使?
    如果有多个参数可以用标签。如果 要在if条件判断后面使用就只能用过滤器
    选择的时候要灵活使用
    {% if i|multi_filter:10>100 %}  传入两个参数,管道符前面的值传递给方法的第一个参数,冒号后面的值传递给方法的第二个参数
    100
    {% else %}
    {{ i }}
    {% endif %}
    标签和过滤器解决复用性的问题

    2.17,模板语法之继承

    模板中的include用法:
    新建advertise.html,把要复用的HTML代码块放进去
    要在插入的地方写:
    {% include 'advertise.html' %}


    继承:比include强大
    公用的代码放在base.html里:省略号表示公用的HTML代码
    ...
    {% block con %}
    # 这里是个“盒子”叫“con”放可变的内容,父HTML里盒子可以有多个,
    {% endblock %} # 盒子里如果有代码也继承,子HTML可以选择重写或者不重写
    ...
    继承自base.html的子HTML:
    {% extends 'base.html' %} #把父盒子的东西拿过来
    {% block con %}
    写自己的东西
    {% endblock %}

    如果想把父类盒子的东西拿过来也想加自己的东西怎么办?
    {% block con %}
    {{ block.super }}
    写自己的东西
    {% endblock %}

    可以增加后缀,提高可读性
    {% block con %}

    {% endblock con%}

     2.18 (补)inclution_tag标签用法(计算变量值得到HTML代码)

    inclution_tag模板语法:
    HTML继承,构建的变量要传给base.html
    之前是把变量计算到值后传给模板去渲染,渲染成HTML代码
    inclution_tag是把数据和样式结合成标签函数,只要调用就拿到HTML代码

    跟自定义标签类似:
    1,在settings里注册INSTALLD_APPS里面加上"app01",
    2. 在app01下新建文件夹templatetags
    3. 创建任意.py文件例如my_tag_filter.py并在里面写:
    ---------------my_tag_filter.py-------------------
    from django import template

    register = template.Library()

    @register.inclusion_tag("classification.html")
    def get_classification_style(username):
    # 查询该用户
    user = UserInfo.objects.filter(username=username).first()
    # 该用户所有文章
    article_list = Article.objects.filter(user=user)
    # 该用户所有文章按分类统计个数
    category_c = article_list.values("category__title").annotate(c=Count("category__title"))
    # 该用户所有文章按标签统计个数
    tags_c = article_list.values("tags__title").annotate(c=Count("tags__title"))
    # 该用户所有文章按日期"某年某月"统计文章个数
    date_c = article_list.extra(select={"create_date": "date_format(create_time,'%%Y-%%m')"})
    .values('create_date').annotate(c=Count("nid"))
    return {"username":username, "category_c":category_c, "tags_c":tags_c, "date_c":date_c}

    -----------------------------------------------------
    4.在渲染HTML中写:
    --------------------"classification.html"---------------

    <div class="panel panel-primary">
    <div class="panel-heading">分类</div>
    <div class="panel-body">
    {% for cate in category_c %}
    <div><a href="/{{ username }}/category/{{ cate.category__title }}">{{ cate.category__title }} ({{ cate.c }})</a></div>
    {% endfor %}
    </div>
    </div>
    <div class="panel panel-warning">
    <div class="panel-heading">标签</div>
    <div class="panel-body">
    {% for tag in tags_c %}
    <div><a href="/{{ username }}/tag/{{ tag.tags__title }}">{{ tag.tags__title }} ({{ tag.c }})</a></div>
    {% endfor %}
    </div>
    </div>
    <div class="panel panel-danger">
    <div class="panel-heading">日期</div>
    <div class="panel-body">
    {% for dt in date_c %}
    <div><a href="/{{ username }}/archive/{{ dt.create_date }}">{{ dt.create_date }} ({{ dt.c }})</a></div>
    {% endfor %}
    </div>
    </div>
    ---------------------------------------------------------
    5.使用。在需要被继承的base.html中写:
    (这个自定义标签get_classification_style,可以用在任何模板上,执行过程是:
    先将username传给继承了base.html的模板
    再执行my_tag_filter.py中自定义的get_classification_style函数
    执行结果交给装饰器中的classification.html渲染成HTML代码
    再讲渲染后的HTML代码返回给调用的地方)
    --------------------base.html-------------------
    <div>
    {% load my_tags %}
    {% get_classification_style username %}
    </div>
    ---------------------------------------------

     2.19 (补) request.POST取数组只取到最后一个?

    注意,request.POST的类型是QueryDict,和普通的Dict不同的是,

    如果使用request.POST.get方法,只能获得数组的最后一个元素,

    必须使用getlist才能获取整个数组例如:request.POST.getlist('bands'),

    以Python列表的形式返回所请求键的数据。 若键不存在则返回空列表。 它保证了一定会返回某种形式的list。

    2.20 (补) 前端和path中的相对路径和绝对路径

    一、在urls.py中:

    如果写: path('timer/',views.timer)  则访问 http://127.0.0.1:8000/timer/  请求交给views.timer

    如果写: path('/timer/',views.timer)  则访问 http://127.0.0.1:8000//timer/  请求交给views.timer

    总结:在urls.py中路径写的什么就按什么去匹配。

    二、在前端模板中:

    如果写:<a href="/timer/"></a> 则跳转到http://127.0.0.1:8000/timer/

    如果写:<a href="timer/"></a> 则跳转到http://127.0.0.1:8000/timer/timer/

    总结:在前端模板中,绝对路径是相对IP:PORT; 相对路径是相对当前的路径

    三、在url中例如“timer/”后面的斜线必须要写

    这样输入http://127.0.0.1:8000/timer或者http://127.0.0.1:8000/timer/  都会跳转到 http://127.0.0.1:8000/timer/

    如果不写,输入http://127.0.0.1:8000/timer会跳转成功, 输入http://127.0.0.1:8000/timer/就会出现404

    2.21(补)Django有两种静态文件

      /static/  : js ,css, img等网站要用到的文件

      /media/: 用户上传的文件,例如拉勾网的简历等

    ------------------------------------

      用户上传头像时,

      ORM类中:

        avatar = models.FileField(upload_to='avatars/',default='/avatars/default.png')

      views.py中:

        avatar_obj = request.FILES.get("avatar")

        user_obj = UserInfo.objects.create_user(....., avatar=avatar_obj)

      如果这样写,Django会将用户上传的文件放在项目根目录的avatars文件夹中

    ----------------------------------------

      Django建议把用户上传的文件放在media目录下。

      media如何配置?

      1、在应用的目录下新建media文件夹

      2、在settings.py里:

        MEDIA_ROOT=os.path.join(BASE_DIR,"media")

      配置好后,用户上传的文件会被放在MEDIA_ROOT路径下的avatars文件夹中(如果没有,Django会自动创建) 

      3、如果想让用户像访问static那样访问media可以:

        settings.py里: MEDIA_URL= '/media/'

        urls.py里:

        from django.urls import re_path

        from django.views.static import serve

        from cnblog import settings

        re_path(r"media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT})

    也可以选择给media中的图片按日期创建目录

    models.py中:

      course_img = models.ImageField(upload_to="course/%Y-%m", verbose_name="课程的图片")

    项目settings.py中:

      # Media配置

      MEDIA_URL="/media/"

      MEDIA_ROOT = os.path.join(BASE_DIR, "media")

    urls.py中:

      from django.views.static import serve

      from django.urls import path,include, re_path

      urlpatterns = [

        #media路径配置

        #path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})

        re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})

      ]

    配置完成后,用Django的admin录入course_img字段xx.jpg,将在app目录下创建media/course/2019-01/xx.jpg

    访问xx.jpg资源:127.0.0.1:8008/media/course/2019-01/xx.jpg

    2.22(补)Django用户表原生字段

    username    varchar(150)   --用户名
    password    varchar(128)   --密码
    last_login    datetime(6)    --最后一次登录时间
    is_superuser    tinyint(1) --是否为超级用户
    first_name    varchar(30)    --
    last_name    varchar(150)   --
    email    varchar(254)       --邮箱
    is_staff    tinyint(1)     --是否员工
    is_active    tinyint(1)     --是否激活
    date_joined    datetime(6)    --加入时间

    2.23(补)CNBLOG上传头像到media目录

    第一步:models.py里

    class UserInfo(AbstractUser):
    '''用户信息扩展字段,每个用户对应一个博客'''
    nid = models.AutoField(primary_key=True)
    telephone = models.CharField(max_length=11,null=True,unique=True)
    # models.ImageField也是这样用,upload_to是上传的文件存放路径,default为默认头像路径
    avatar = models.FileField(upload_to='avatars/',default='avatars/default.png')
    create_time = models.DateTimeField(verbose_name='创建时间',auto_now_add=True) # 默认是当前时间

    第二步:前端ajax提交

    //提交注册
    $("#reg-btn").click(function () {
    var formdata = new FormData();

    {#方式一:#}
    {#formdata.append("user",$("#id_user").val());#}
    {#formdata.append("pwd",$("#id_pwd").val());#}
    {#formdata.append("re_pwd",$("#id_re_pwd").val());#}
    {#formdata.append("email",$("#id_email").val()); #}
    {#formdata.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val());#}
    {#formdata.append("avatar",$("#avatar")[0].files[0]);#}

    //方式二
    var request_data = $("#form").serializeArray();
    $.each(request_data,function (index, data) {
    formdata.append(data.name,data.value)
    });
    formdata.append("avatar",$("#avatar")[0].files[0]);

    $.ajax({
    url:'',
    type:'post',
    contentType:false,
    processData:false,
    data:formdata,
    success:function (data) {
    if(data.user){
    //校验成功
    location.href="/login/";
    }else{
    //校验失败
    //先清空
    $("span.error").text("");
    $("span.error").parent().removeClass("has-error");
    //再加上错误信息
    $.each(data.msg,function (name, error_list) {
    if(name=="__all__"){
    $("#id_re_pwd").next().text(error_list[0]);
    $("#id_re_pwd").parent().addClass("has-error");
    }
    $("#id_"+name).next().text(error_list[0]);
    $("#id_"+name).parent().addClass("has-error");
    });
    }
    }
    });
    });

    第三步settings.py配置好存储路径:

    第四步views.py里后台处理:

    avatar_obj = request.FILES.get("avatar")
    if avatar_obj:
    UserInfo.objects.create_user(username=user,password=pwd,email=email,avatar=avatar_obj)
    else:
    UserInfo.objects.create_user(username=user, password=pwd, email=email)

    2.231(补)  CRM通过视图函数下载文件

    为什么需要编写下载视图方法?

    当你碰到如下2种情况时,你需要编写自己的视图下载方法。

    • 你希望用户以附件形式获得文件,而不是浏览器直接打开。

    • 你希望允许用户下载一些保密文件,而不希望在html模板中暴露它们。

    可以使用HTTPResponse,SteamingHttpResonse,FileResonse。

    推荐使用FileResonse:

    from django.http import FileResponse
    import mimetypes
    def tpl(request):
    # 这一句很重要,限定用户只能下载指定文件夹下的文件
    tpl_path = os.path.join(settings.BASE_DIR, 'web', 'files', '顾客上传模板.xls')
    '''
    获取资源的媒体类型:mimetype
    在浏览器中显示的内容有 HTML、有 XML、有 GIF、还有 Flash ……
    那么,浏览器是如何区分它们,决定什么内容用什么形式来显示呢?答案是 MIME Type
    媒体类型通常是通过 HTTP 协议,由 Web 服务器告知浏览器的,更准确地说,是通过 Content-Type 来表示的
    '''
    content_type = mimetypes.guess_type(tpl_path)[0]
    response = FileResponse(open(tpl_path, mode='rb'), content_type=content_type)
    # Content-Disposition:这是响应头的固定参数,用户下载的文件被命名为customer_excel_tpl.xls
    response['Content-Disposition'] = "attachment;filename=%s" % 'customer_excel_tpl.xls'
    return response

    2.24 (补) Django-admin(不是项目必须)

      Django的内部的一个组件:后台数据管理组件(web页面)

      1、先创建超级管理员:python manage.py createsuperuser 

            针对用户认证组件对应的用户表才起作用

       2、登录http://127.0.0.1:8000/admin/

      3、注册,在应用的目录下admin.py里注册表orm类:

      from django.contrib import admin
      # Register your models here.
      from blog import models
      admin.site.register(models.UserInfo)
      admin.site.register(models.Blog)
      admin.site.register(models.Category)
      admin.site.register(models.Tag)
      admin.site.register(models.Article)
      admin.site.register(models.ArticleUpDown)
      admin.site.register(models.Article2Tag)

       4、刷新http://127.0.0.1:8000/admin/

    2.25 (补) 时间不对怎么办(这样调整后数据库存的时间和显示的时间不一样)

    settings.py里

    TIME_ZONE = 'Asia/Shanghai'  # 数据库时间和前端显示时间不一样

    ...

    USE_TZ = False # 如果单表查询的"datetime__month"不好使改为False

    2.26(补) Django事务控制

    下面的comment_obj和Article..两步是原子性的可以放在事务里:

    from django.db import transaction
    with transaction.atomic():
    comment_obj = Comment.objects.create(content=content,article_id=article_id,user_id=user_id,parent_comment_id=pid)
    Article.objects.filter(pk=article_id).update(comment_count=F("comment_count")+1)

    2.261(补) Django-数据库加锁(行锁)

    def action_multi_transfer(self, request, *args, **kwargs):
        '''
        把公共客户批量变为私人客户,把表中选中数据的consultant_id变为current_user_id
        '''
        # 当前登录的用户ID
        current_user_id = 7
        # 选中的数据的主键
        pk_list = request.POST.getlist('pk')
    
        # 限制:私人客户数量
        private_customer_count = models.Customer.objects.filter(status=2, consultant_id=current_user_id).count()
        max_priv = models.Customer.MAX_PRIVATE_CUSTOMER_COUNT
        if private_customer_count + len(pk_list) < max_priv:
            return HttpResponse('私人客户数量超限制:最多只能申请%s个.' % (max_priv - private_customer_count))
        # 符合id,顾问为空,状态为未报名的才能变成私人客户,(为了安全,要加锁)
        flag = False
        with transaction.atomic():
            # 在数据库中加行锁
            origin_queryset = models.Customer.objects.filter(id__in=pk_list, consultant__isnull=True,
                                                             status=2).select_for_update()
            # 如果选中了3个客户,却只查到2个客户
            if len(origin_queryset) == len(pk_list):
                models.Customer.objects.filter(id__in=pk_list, consultant__isnull=True,
                                               status=2).update(consultant_id=current_user_id)
                flag = True
        if not flag:
            return HttpResponse("手速太慢了,部分被选中的客户已被抢走。")

    2.27(补) Django发送邮件

    1、settings.py里:

    # 发送邮件
    EMAIL_HOST = 'smtp.exmail.qq.com' # 如果是163改为smtp.163.com
    EMAIL_PORT = 465
    EMAIL_HOST_USER = ''
    EMAIL_HOST_PASSWORD = ''
    # DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # 如果写这个send_mail方法就不用写发送方邮箱
    EMAIL_USE_SSL = True

    2、views里:

    from django.core.mail import send_mail
    from cnblog import settings

    # send_mail(
    # "您的文章%s新增了一条评论内容"%article_obj.title,
    # content,
    # settings.EMAIL_HOST_USER, #发送方邮箱
    # ["323223.qq.com"] # 作者邮箱,可以发给多个人
    # )

    # 用线程来的快
    import threading
    t=threading.Thread(target=send_mail,args=(
    "您的文章%s新增了一条评论内容"%article_obj.title,
    content,
    settings.EMAIL_HOST_USER, # 发送方邮箱
    ["323223.qq.com"] # 作者邮箱,可以发给多个人
    ))
    t.start()

    2.28(补) Django批量发现权限URL正则(发现反向解析表达式+url正则)

    from collections import OrderedDict
    from django.conf import settings
    from django.utils.module_loading import import_string
    from django.urls import URLResolver, URLPattern
    import re

    '''
    自动发现所有的URL,用于增加权限用
    返回字典:{'rbac:menu_list':{name:'rbac:menu_list', url:'/menu/list/'},...}
    使用方法:get_all_url_dict()
    思路:递归项目下的urls.py的patterns
    path(r"index/", views.index) # URLPattern对象
    re_path(r"^", include("web.urls.customer_urls")), # URLResolver对象
    '''

    def check_url_exclude(url):
    '''
    判断自动发现的url是否在过滤名单里。
    :param url:
    :return:
    '''
    exclude_url = settings.AUTO_DISCOVER_EXCLUDE # [r'^/admin/.*', r'^/login/.*', r'^/register/.*', ]
    for regex in exclude_url:
    if re.match(regex, url):
    return True

    def recursion_urls(pre_namespace, pre_url, urlpatterns, url_ordered_dict):
    '''
    递归获取URL(叶子节点路由必须有反向解析的name别名)
    :param pre_namespace: 路由分发的命名空间,以后用于拼接name
    :param pre_url: url前缀,以后用于拼接url
    :param urlpatterns: 路由关系列表
    :param url_ordered_dict: 保存递归中获取的所有路由
    :return: {'rbac:menu_list':{name:'rbac:menu_list', url:'/menu/list'},...}
    '''
    for item in urlpatterns:
    if isinstance(item, URLPattern): # 非路由分发,添加到url_ordered_dict
    if not item.name: # 没有反向解析别名,发现不了,跳过
    continue
    if pre_namespace:
    name = "%s:%s" % (pre_namespace, item.name)
    else:
    name = item.name
    # item.pattern = ^test/(?P<pk>d+)/$
    url = pre_url + item.pattern.__str__()
    url = url.replace('^', '').replace('$', '')
    if check_url_exclude(url):
    continue
    url_ordered_dict[name] = {'name': name, 'url': url}
    elif isinstance(item, URLResolver): # 路由分发,递归操作
    # 如果路由分发namespace有前缀,也要进行叠加
    if pre_namespace:
    if item.namespace: # 父级有,自己也有
    namespace = "%s:%s" % (pre_namespace, item.namespace)
    else: # 父级有,自己没有
    namespace = pre_namespace
    else:
    if item.namespace: # 父级没有, 自己有
    namespace = item.namespace
    else: # 父级没有,自己也没有
    namespace = None
    recursion_urls(namespace, pre_url + item.pattern.__str__(), item.url_patterns, url_ordered_dict)


    def get_all_url_dict():
    '''
    获取项目中所有的URL
    :return:
    '''
    url_ordered_dict = OrderedDict()
    md = import_string(settings.ROOT_URLCONF)
    recursion_urls(None, '/', md.urlpatterns, url_ordered_dict)
    return url_ordered_dict

    2.29(补) 同一个页面向后端同一个视图函数提交两种post请求,如何进行甄别?

    同一个前端页面向后端同一个视图函数提交post请求,如何进行甄别?

    使用post的action进行?参数拼接,后端判断type的类型

    <form method="post" action="?type=add">
    {% csrf_token %}
    {{ update_formset.management_form }}
       ....
    <button type="submmit">提交</div>
    </form>
    ....
    <form method="post" action="?type=update">
    {% csrf_token %}
    {{ update_formset.management_form }}
       ....
    <button type="submmit">提交</div>
    </form>

    123123



  • 相关阅读:
    关于EF中实体和数据表以及查询语句映射的问题
    流程设计(流程上下文法)
    流程设计(抽象节点法)
    第六章 跑马灯实验
    如何批量导入excel数据至数据库(MySql)--工具phpMyAdmin
    win10卸载瑞星
    如何在sublime上运行php
    Pycharm+django新建Python Web项目
    部署Asp.net Core 项目发生502.5 或者500 没有其他提示信息
    常用Windows DOS命令项目部署经常用到
  • 原文地址:https://www.cnblogs.com/staff/p/10669075.html
Copyright © 2011-2022 走看看