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



  • 相关阅读:
    Android开发 ViewConfiguration View的配置信息类
    Android 开发 倒计时功能 转载
    Android 开发 关于7.0 FileUriExposedException异常 详解
    Android 开发 实现文本搜索功能
    Android 开发 Activity里获取View的宽度和高度 转载
    Android 开发 存储目录的详解
    Android 开发 Fresco框架点击小图显示全屏大图实现 ZoomableDraweeView
    Android 开发 将window变暗
    Android 开发 DisplayMetrics获取Android设备的屏幕高宽与其他信息
    Android 开发 DP、PX、SP转换详解
  • 原文地址:https://www.cnblogs.com/staff/p/10669075.html
Copyright © 2011-2022 走看看