Django进阶
一、模板语法
1. 本质
python manage.py shell from django.template import Context,Template t = Template('<h1>hello{{name}}</h1>') c = Context({'name':'yuan'}) t.render(c) Out[4]: '<h1>helloyuan</h1>'
注:模板中取元组或列表变量的时候通过.取
def select(request): book_list = Book.objects.filter(name__icontains='p').values('name','price') return render(request,'book.html',{'book_list':book_list}) book.html {% for book in book_list %} <div> <p>{{ book.name }}{{ book.author }}{{ book.price }}</p> </div> {% endfor %}
2. 默认模板语法
(1) if {% if 条件1 %} ... {% elif 条件2 %} ... {% else %} ... {% endif} (2) for {% for i in list %} {{ forloop.counter}}{{i}} {% empty %} 列表为空 {% endfor %} 1,forloop.counter表示循环的次数,它从1开始计数,第一次循环设为1: 2,forloop.counter0 类似于forloop.counter,但它是从0开始计数,第一次循环设为0 3,forloop.revcounter 4,forloop.revcounter0 5,forloop.first当第一次循环时值为True,在特别情况下很有用: (3){%csrf_token%}:用于生成csrf_token的标签,用于防治跨站攻击验证。注意如果你在view的index里用的是render_to_response方法,不会生效 其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。 (4) {% url %}: 引用路由配置的地址,使用别名代替静态url路径 基础语法:<form action="{% url 'blog:login' %}" method="post"> from django.urls import reverse url传值 1.url中带参数 url(r'^all/(?<article_type_id>\d+).html$', home.index, name='index'), 在HTML中:{% url "index" article_type_id=1 %} => all/1.html 在views中:reverse('index',kwargs={"article_type_id":1}) =>all/1.html url(r'^all/(\d+).html$', home.index, name='index'), 在HTML中:{% url "index" 1 %} =>all/1.html 在views中:reverse('index',args=(1,)) =>all/1.html re_path('video2-(?P<direction_id>(\d+))-(?P<classification_id>(\d+))-(?P<level_id>(\d+)).html',views.video2,name='video2'), 在HTML中 {% url 'video2' direction_id=0 classification_id=kwargs.classification_id level_id=kwargs.level_id %}" "/video2-{{ kwargs.direction_id }}-{{ item.id }}-{{ kwargs.level_id }}.html" 2.get方式传参 url(r'^discover.html/$', views.discover,name='discover'), 在HTML中:{% url 'table:discover' %}?id={{ article.id }} ==> "/table/discover/?id={{ article.id }}" (5) {% with %}:用更简单的变量名替代复杂的变量名 {% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %} (6){% verbatim %}: 禁止render {% verbatim %} {{ hello }} {% endverbatim %} (7) {% load %}: 加载标签库 {% load myTag %} {% load staticfiles %}
3.模板继承
(1)extends {% extends 'base.html' %} {% block content %} ... {% endblock %} 注:{{ block.super }} 继承模板变量 (2)include {% load staticfiles %} {% include 'index.html' %} 注:django模板语言自动提示设置,将templates文件夹右键标记为django语言模板文件夹
4. 默认过滤器
过滤器: 语法格式: {{obj|filter:param}} 1 add : 给变量加上相应的值 2 addslashes : 给变量中的引号前加上斜线 3 capfirst : 首字母大写 4 cut : 从字符串中移除指定的字符 5 date : 格式化日期字符串:datetime取到的时间,转成指定格式 6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值 7 default_if_none: 如果值是None,就替换成设置的默认值,否则就使用本来的值 8 safe:页面渲染传递过来的变量标签 {{k|safe}} 不设置的话讲按源字符串输出,为了防止XSS攻击 注:XSS攻击全称跨站脚本攻击,XSS是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。 9 autoescape off 10.default 数据为空时设置默认值 11.length 取变量长度 12.filesizeformat 文件大小转成可读 13.slice 从指定位置到指定位切片 14.safe 防止XSS攻击、加上safe才能传标签 15.truncatechars 取摘显示一段剩下的…
{#格式 值|函数#} {# 如果没有值,那么使用默认值#} <p>{{ bucunzai|default:'空的哦' }}</p> {# 取出变量长度#} <q>{{ name }}--{{ name|length }}</q> {# 文件大小转换成可读型 kb 自动转成bm、g、tb#} <p>文件大小{{ file_size|filesizeformat }}</p> {# 切片 从指定位置到指定位 ,例:第3位到-2位#} <p>切片:{{ slice_str|slice:'3:-2' }}</p> {# 把datetime取到的时间,转成指定格式#} <p>格式化:{{ now|date:'Y-m-d H:i:s' }}</p> {# 如果后端内容包含标签,那么加上safe 才能转义(防止用户直接加script标签作弊)防XSS攻击#} <p>{{ h_html|safe }}</p> {# 取摘要只显示一段,指定取长度后面...例:120个字符 #} <p>长文本:{{ p_str|truncatechars:12 }}</p>
5.自定义filter和simple_tag
自定义filter和simple_tag a、在app中创建templatetags模块(必须的) b、创建任意 .py 文件,如:my_tags.py c、在使用自定义simple_tag(可以传多个参数,但不能用在控制语句里)和filter(只能传一个参数)的html文件中导入之前创建的 my_tags.py :{% load my_tags %} d、使用simple_tag和filter(如何调用) e、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
''' 1. 在app内创建固定名:templatetags文件夹 2. 在其中创建myfilter.py来装自定义 ''' myfilter.py # 一定要导入 from django import template register = template.Library() # 字符串加一个sb @register.filter(name="addSB") # 告诉模版这里有个自定义方法 名字叫:addSB def add_sb(value): return "{} SB".format(value) # 有参数 @register.filter(name="addstr") # 注册到模版语言 def cut(value, arg): return '{}{}'.format(value,arg) HTML中 {# 一定要导入才能使用#} {% load myfilter %} <p>自定义:{{ name|addSB}}</p> {# 参数用:参数传#} <p>自定义带参数:{{ name|addstr:'技术好'}}</p>
''' 1. 在app内创建固定名:templatetags文件夹 2. 在其中创建myfilter.py来装自定义 ''' myfilter.py from django.template import Library from django.utils.safestring import mark_safe register = Library() @register.simple_tag def render_paginator(querysets,admin_class,sorted_column): ele = ''' <ul class="pagination"> ''' #上一页 filter_ele = render_filtered_args(admin_class) sorted_index = '' if sorted_column: sorted_index = '&_o={}'.format(list(sorted_column.values())[0]) p_ele = '<li><a href="?_page=1{}{}">首页</a></li>'.format(filter_ele,sorted_index) ele += p_ele if querysets.has_previous: if querysets.number > 1: p_ele = '<li><a href="?_page={}{}{}">上一页</a></li>'.format(querysets.number-1,filter_ele,sorted_index) else: p_ele = '<li class="disabled"><a href="?_page=1{}{}">上一页</a></li>'.format(filter_ele,sorted_index) ele += p_ele #循环页码 for i in querysets.paginator.page_range: active = '' if abs(querysets.number - i) < 2: if querysets.number == i: active = 'active' p_ele = '<li class="{0}"><a href="?_page={1}{2}{3}">{4}</a></li>'.format(active,i,filter_ele,sorted_index,i) ele += p_ele #下一页 if querysets.has_next: if querysets.number < querysets.paginator.num_pages: p_ele = '<li><a href="?_page={}{}{}">下一页</a></li>'.format(querysets.number+1,filter_ele,sorted_index) else: p_ele = '<li class="disabled"><a href="?_page={}{}{}">下一页</a></li>'.format(querysets.paginator.num_pages,filter_ele,sorted_index) ele += p_ele p_ele = '<li><a>{}/{}</a></li>'.format(querysets.number,querysets.paginator.num_pages) ele += p_ele ele += '</ul>' return mark_safe(ele) HTML中 {# 一定要导入才能使用#} {% load myfilter %} ... <div class="pagination"> {% if querysets %} {% render_paginator querysets admin_class sorted_column %} {% else %} <span>数据为空</span> {% endif %} </div>
inclusion_tag返回html代码片段
''' 1. 在app内创建固定名:templatetags文件夹 2. 在其中创建myfilter.py来装自定义 ''' myfilter.py from django import template register = template.Library() <!--将下面得到的data值,当参数传给result.html处理,然后返回html代码传给调用者--> @register.inclusion_tag('result.html') def show_results(n): n = 1 if n < 1 else int(n) data = ["第{}项".format(i) for i in range(1, n+1)] return {"data": data}<!--该值传给注册的result.html处理--> templates/snippets/result.html <!--接收到data 后进行处理数据,得到li表--> <ul> {% for choice in data %} <li>{{ choice }}</li> {% endfor %} </ul> templates/index.html <body> {% load inclusion_tag_test %} <!--这里直接传参数10,自动运行结果并返回 一段html--> {% show_results 10 %} </body>
二、COOKIES和SESSION
COOKIES: 存储在本地客户端浏览器的键值对数据,访问的时候带着数据到服务器段。服务器端也可以设置
SESSION: 存储在服务器端的键值对
一、操作Cookie 获取cookie:request.COOKIES[key] 设置cookie:response.set_cookie(key,value,max_age,expires) 由于cookie保存在客户端的电脑上,所以,jquery也可以操作cookie。 <script src='http://830909.blog.51cto.com/static/js/jquery.cookie.js'></script> $.cookie("list_pager_num", 30,{ path: '/' });
二、操作Session(session默认在服务器端保存15天)
获取session:request.session[key]
设置session:reqeust.session[key] = value
删除session:del request.session[key]
这个删除其实就是把数据库的session_data更新为一个其他的值了,并没有立即删除)
request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。
COOKIES: def login(request): print('COOKIES:',request.COOKIES) if request.method == 'POST': name = request.POST.get('username') pwd = request.POST.get('pwd') if name=='yuan' and pwd=='123': ret = redirect('/index/') ret.set_cookie('username',name,max_age=10,expires=datetime.datetime.utcnow()+datetime.timedelta(days=3)) #随便命名 return ret return render(request,'login.html') def index1(request): if request.COOKIES.get('username',None)=='yuan': name = request.COOKIES.get('username',None) return render(request,'index1.html',locals()) else: return redirect('/login/') SESSION: def login(request): if request.method == 'POST': name = request.POST.get('username') pwd = request.POST.get('pwd') if name=='yuan' and pwd=='123': request.session['is_login'] = True request.session['user'] = name return redirect('/index/') def index1(request): if request.session.get('is_login',None): name = request.session.get('user') return render(request,'index1.html',locals()) else: return redirect('/login/') def logout(request): """ 直接通过request.session['is_login']回去返回的时候, 如果is_login对应的value值不存在会导致程序异常。所以 需要做异常处理 """ try: #删除is_login对应的value值 del request.session['is_login'] except KeyError: pass #点击注销之后,直接重定向回登录页面 return redirect('/login/')
三、Django的生命周期
生命周期概述
1. 请求进来,到达wsgi,wsgi就是一个socket服务端,它用来接收用户请求并对请求初次封装,然后将请求交给web框架 2. 请求到达django,django对请求再次进行封装 3. 执行中间件中的方法(process_request),对请求中的数据进行校验或放值,比如请求对请求中的csrf进行验证,比如请求中原来没有session,经过中间件赋值和session 4. 根据请求头的url在路由关系表中进行匹配(从上到下) 5. 匹配成功后,执行指定的的视图函数进行业务处理和模板渲染,可能会涉及到数据库和模板操作 URL -> 函数 ==> FBV(function base views)视图里面使用函数处理请求 URL -> 类 ==> CBV(class base views)视图里面使用类处理请求 业务处理:ORM,模板渲染:通过ORM取数据库中取数据 模板渲染:通过template获取模板,然后将数据和模板进行模板渲染 注:视图函数处理之后默认执行中间件中的process_view方法,对视图数据进行处理 6. 然后在经过中间件(process_response)对响应数据再次进行加工处理 7. 最后通过wsgi返回给浏览器
请求字符串 请求头: Request URL: https://blog.csdn.net/jeremyjone/article/details/80641321 Request Method: GET Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cache-Control: max-age=0 Connection: keep-alive Cookie: uuid_tt_dd=10_18657945660-1525931045570-105710; __yadk_uid=Fd95TNnEDZFknmrhyJ2js19FoGCsnBJI; kd_user_id=ca8b087f-7180-4513-9ec3-16d4be59c7a4; Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac=1788*1*PC_VC; UM_distinctid=16366e97fbc208-02ad6cb264566a-444a002e-100200-16366e97fbdfc; __utma=17226283.73752246.1529233646.1529233646.1529233646.1; __utmz=17226283.1529233646.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); smidV2=20180627010844f77014a25f0f78d638430e0f952131e300da9b12176e6ecb0; dc_session_id=10_1534470634139.579669; CNZZDATA1259587897=1755646523-1526821445-https%253A%252F%252Fwww.baidu.com%252F%7C1536488683; ARK_ID=JS13942097c85e7db2c6e04d03cabe36aa1394; dc_tos=pf8bug; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1537237241; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1537237241 Host: blog.csdn.net Referer: https://www.baidu.com/link?url=W-6cD29a6Ihsru69rs6d8BX798yrbAPJQ09tG1-WbJ8-lNqHY9M0R3qOteQ695lFNEhtKrKPZew11HzkPfmcLy4gPcymHOniZab23Pra4_W&wd=&eqid=bb5c25850002033b000000035b9fbbb9 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36 分隔:\r\n\r\n 请求体:nid=sb request.body字符串 => request.POST字典 响应字符串 响应头: Connection: keep-alive Content-Encoding: gzip Content-Type: text/html; charset=UTF-8 Date: Tue, 18 Sep 2018 02:25:45 GMT Keep-Alive: timeout=20 Server: openresty Strict-Transport-Security: max-age= 31536000 Transfer-Encoding: chunked Vary: Accept-Encoding 分隔:\r\n\r\n 响应体:...
CBV: 通过父类View的dispatch方式对request.method进行反射,执行响应的函数 def dispatch(self, request, *args, **kwargs): if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) #http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) CBV实例:urls.py: path('cbv/',views.CBV.as_view(),name='cbv') views.py from django.views import View class CBV(View): def dispatch(self, request, *args, **kwargs): print('dispatch......') #这部分可以自定制 扩展点 result = super(CBV, self).dispatch(request,*args,**kwargs) return result def get(self,request): #根据请求头的request.method进行自动执行 return render(request,'cbv.html') # return HttpResponse('CBV.GET') def post(self,request): ret = HttpResponse('CBV.POST') ret['h1'] = 'v1' ret['h2'] = 'v2' ret.set_cookie('c1','v1') ret.set_cookie('c2','v2') """ 头: h1=v1 h2=v2 cookies:c1=v1,c2=v2 体: CBV.POST """ return ret
四、分页
分页的作用:减少一次性从数据库里取太多的数据,100条数据分十次取就会快很多,而且用户也不一定需要一次看100条。
原生sql select * from table_name limit offset 50 limit 10 select * from table_name limit 5,10 #6,15 select * from table_name limit 5,-1 #5-最后 select * from table_name limit 5 #前5个 selete * from testtable limit 2,1; #第三个 selete * from testtable limit 2 offset 1; #第二三个 --Django内置分页:https://docs.djangoproject.com/en/1.11/topics/pagination/ -Paginator、Page -页面:include --扩展Django的内置分页 -CustomPaginator(Paginator) 传入: -所有数据 -当前页 -每页30条 -最多显示页面7个 自定义分页组件 -所有数据的个数 -当前页 -每页显示30条 -最多页面7个
django内置分页组件
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage def table_obj_list(request,app_name,model_name): admin_class = site.enable_admins[app_name][model_name] if request.method == 'POST': # print(request.POST) selected_action = request.POST.get('action') selected_ids = json.loads(request.POST.get('selected_ids')) # print(selected_action,selected_ids) if not selected_action: #如果有action参数,代表这是一个正常的action,如果没有,代表可能是一个删除动作 if selected_ids: #这些选中的数据都要删除 admin_class.model.objects.filter(id__in=selected_ids).delete() else: #走action流程 selected_objs = admin_class.model.objects.filter(id__in=selected_ids) admin_action_func = getattr(admin_class,selected_action) response = admin_action_func(request,selected_objs) if response: return response querysets = admin_class.model.objects.all().order_by('-id') querysets,filter_condition = get_filter_result(request,querysets,admin_class) admin_class.filter_condition = filter_condition #searched queryset_result querysets = get_search_result(request,querysets,admin_class) admin_class.search_key = request.GET.get('_q','') #sorted_querysets querysets,sorted_column = get_orderby_result(request,querysets,admin_class) # print(admin_class.model._meta.get_field('name').verbose_name) paginator = Paginator(querysets, admin_class.list_per_page) page = request.GET.get('_page') try: querysets = paginator.page(page) except PageNotAnInteger: querysets = paginator.page(1) except EmptyPage: querysets= paginator.page(paginator.num_pages) return render(request,'kingadmin/table_obj_list.html',{'querysets':querysets, 'admin_class':admin_class, 'sorted_column':sorted_column, 'app_name':app_name, 'model_name':model_name, })
{% extends 'kingadmin/index.html' %} {% load kingadmin_tags %} {% block right-content-container %} <ol class="breadcrumb"> <li><a href="{% url 'kingadmin:app_index' %}">HOME</a></li> <li><a href="{% url 'kingadmin:app_model_index' app_name%}">{{ app_name|upper }}</a></li> <li class="active">{% get_model_verbose_name admin_class %}</li> </ol> <h2 class="page-header">KingAdmin</h2> <div> <form class="navbar-form navbar-default" role="search" action="" style="margin-left: -16px;"> <input class="form-control" type="search" name="_q" value="{{ admin_class.search_key }}" placeholder="{% for s in admin_class.search_fields %} {{ s }},{% endfor %}"> <input class="btn btn-primary" type="submit" value="Search"> {% for k,v in admin_class.filter_condition.items %} <input type="hidden" name="{{ k }}" value="{{ v }}"> {% endfor %} <input type="hidden" name="_o" value="{% get_current_sorted_column_index sorted_column %}"> </form> <div class="row" style="margin-bottom: 5px;"> {% if admin_class.list_display %} <form action=""> {% for fileter_column in admin_class.list_filter %} {% build_filter_ele fileter_column admin_class %} {% endfor %} <div><input type="hidden" name="_o" value="{% get_current_sorted_column_index sorted_column %}"> </div> <div class="col-md-2" style="margin-top: 18px;"><input type="submit" value="过滤" class="btn btn-success"></div> </form> {% endif %} <div class="col-md-2" style="margin-top: 18px;float: right;"><a href="{% url 'kingadmin:table_obj_add' app_name model_name %}" class="btn btn-warning">添加</a></div> </div> <form onsubmit="return ActionCheck(this);" method="post">{% csrf_token %} <div class="row"> <div class="col-lg-3"> <select name="action" class="form-control"> <option value="">------------</option> {% for action in admin_class.actions %} <option value="{{ action }}">{{ action }}</option> {% endfor %} </select> </div> <div class="col-lg-2"> <input type="submit" class="btn btn-info" value="GO"> </div> </div> </form> <table class="table table-bordered"> <thead> <tr> <th><input type="checkbox" onclick="CheckAllObjs(this);"></th> {% if admin_class.list_display %} {% for column in admin_class.list_display %} <th><a href="?_o={% get_sorted_column column sorted_column forloop.counter0 %}{% render_filtered_args admin_class %}"> {% get_field_verbose_name admin_class column %}{% render_sorted_arrow column sorted_column %}</a> </th> {% endfor %} {% else %} <th>{% get_model_name admin_class %}</th> {% endif %} </tr> </thead> <tbody> {% for obj in querysets %} <tr> <td><input row-select="true" type="checkbox" value="{{ obj.id }}"></td> {% build_table_row obj admin_class %} </tr> {% endfor %} </tbody> </table> <div class="pagination"> {% if querysets %} {% render_paginator querysets admin_class sorted_column %} {% else %} <span>数据为空</span> {% endif %} </div> </div> <script> function ActionCheck(self) { var selected_action = $('select[name="action"]').val(); var selected_objs = $("input[row-select]:checked"); {#var selected_objs = $('input[row-select]').filter(':checked');#} console.log(selected_action); if (!selected_action) { alert('no action selected'); return false } if (selected_objs.length == 0) { alert('no objects selected'); return false } else { //生成一个标签,放到form里 var ele = '<input type="hidden" >'; var selected_ids = []; $.each(selected_objs, function () { //console.log($(this)); selected_ids.push($(this).val()); }); console.log(selected_ids); var input_ele = '<input type="hidden" name="selected_ids" value=' + JSON.stringify(selected_ids) + '>'; $(self).append(input_ele); } } function CheckAllObjs(self) { if ($(self).prop('checked')) { $('input[row-select]').prop('checked', true); } else { $('input[row-select]').prop('checked', false); } } </script> {% endblock %}
@register.simple_tag def render_paginator(querysets,admin_class,sorted_column): ele = ''' <ul class="pagination"> ''' #上一页 filter_ele = render_filtered_args(admin_class) sorted_index = '' if sorted_column: sorted_index = '&_o={}'.format(list(sorted_column.values())[0]) p_ele = '<li><a href="?_page=1{}{}">首页</a></li>'.format(filter_ele,sorted_index) ele += p_ele if querysets.has_previous: if querysets.number > 1: p_ele = '<li><a href="?_page={}{}{}">上一页</a></li>'.format(querysets.number-1,filter_ele,sorted_index) else: p_ele = '<li class="disabled"><a href="?_page=1{}{}">上一页</a></li>'.format(filter_ele,sorted_index) ele += p_ele #循环页码 for i in querysets.paginator.page_range: active = '' if abs(querysets.number - i) < 2: if querysets.number == i: active = 'active' p_ele = '<li class="{0}"><a href="?_page={1}{2}{3}">{4}</a></li>'.format(active,i,filter_ele,sorted_index,i) ele += p_ele #下一页 if querysets.has_next: if querysets.number < querysets.paginator.num_pages: p_ele = '<li><a href="?_page={}{}{}">下一页</a></li>'.format(querysets.number+1,filter_ele,sorted_index) else: p_ele = '<li class="disabled"><a href="?_page={}{}{}">下一页</a></li>'.format(querysets.paginator.num_pages,filter_ele,sorted_index) ele += p_ele p_ele = '<li><a>{}/{}</a></li>'.format(querysets.number,querysets.paginator.num_pages) ele += p_ele ele += '</ul>' return mark_safe(ele)
自定义分页组件
def index(request,*args,**kwargs): article_type_list = Article.type_choices print(kwargs) if kwargs: article_type_id = int(kwargs['article_type_id']) base_url = reverse('index',kwargs={'article_type_id':article_type_id}) else: article_type_id = None base_url = '/' articlecount = Article.objects.filter(**kwargs).count() page_obj = Pagination(articlecount,request.GET.get('p')) article_list = Article.objects.filter(**kwargs)[page_obj.start:page_obj.end] page_str = page_obj.page_str(base_url) context = {'article_type_list':article_type_list, 'article_type_id':article_type_id, 'article_list':article_list, 'page_str':page_str, } return render(request, 'index.html',context)
-*- coding: utf-8 -*- """ @Datetime: 2018/9/20 @Author: Zhang Yafei """ class Pagination(object): def __init__(self,total_count,current_page,per_page_num=10,max_page_num=7): #数据总个数 self.total_count = total_count #当前页 try: v = int(current_page) if v <= 0: v = 1 self.current_page = v except: self.current_page = 1 #每页显示的行数 self.per_page_num = per_page_num #每页显示的最多页码 self.max_page_num = max_page_num def start(self): return (self.current_page-1) * self.per_page_num def end(self): return self.current_page * self.per_page_num @property def num_pages(self): """ 总页数 :return: """ a,b = divmod(self.total_count,self.per_page_num) if b == 0: return a return a+1 def pager_num_range(self): if self.num_pages < self.max_page_num: return range(1, self.num_pages + 1) # 总页数特别多 part = int(self.max_page_num / 2) if self.current_page < part: return range(1, self.max_page_num + 1) if (self.current_page + part) > self.num_pages: return range(self.current_page - self.max_page_num + 1, self.num_pages + 1) return range(self.current_page - part, self.current_page + part + 1) def page_str(self): page_list = [] first = '<li><a href="index2.html?p=1">首页</a></li>' page_list.append(first) if self.current_page == 1: prev = '<li><a href="#">上一页</a></li>' else: prev = '<li><a href="index2.html?p={0}">上一页</a></li>'.format(self.current_page-1) page_list.append(prev) for i in self.pager_num_range(): if i == self.current_page: temp = '<li class="active"><a href="index2.html?p={0}">{1}</a></li>'.format(i,i) else: temp = '<li><a href="index2.html?p={0}">{1}</a></li>'.format(i, i) page_list.append(temp) if self.current_page == self.num_pages: next = '<li><a href="#">下一页</a></li>' else: next = '<li><a href="index2.html?p={0}">下一页</a></li>'.format(self.current_page + 1) page_list.append(next) end = '<li><a href="index2.html?p={}">尾页</a></li>'.format(self.num_pages) page_list.append(end) return ''.join(page_list)
@register.simple_tag def render_paginator(querysets,admin_class,sorted_column): ele = ''' <ul class="pagination"> ''' #上一页 filter_ele = render_filtered_args(admin_class) sorted_index = '' if sorted_column: sorted_index = '&_o={}'.format(list(sorted_column.values())[0]) p_ele = '<li><a href="?_page=1{}{}">首页</a></li>'.format(filter_ele,sorted_index) ele += p_ele if querysets.has_previous: if querysets.number > 1: p_ele = '<li><a href="?_page={}{}{}">上一页</a></li>'.format(querysets.number-1,filter_ele,sorted_index) else: p_ele = '<li class="disabled"><a href="?_page=1{}{}">上一页</a></li>'.format(filter_ele,sorted_index) ele += p_ele #循环页码 for i in querysets.paginator.page_range: active = '' if abs(querysets.number - i) < 2: if querysets.number == i: active = 'active' p_ele = '<li class="{0}"><a href="?_page={1}{2}{3}">{4}</a></li>'.format(active,i,filter_ele,sorted_index,i) ele += p_ele #下一页 if querysets.has_next: if querysets.number < querysets.paginator.num_pages: p_ele = '<li><a href="?_page={}{}{}">下一页</a></li>'.format(querysets.number+1,filter_ele,sorted_index) else: p_ele = '<li class="disabled"><a href="?_page={}{}{}">下一页</a></li>'.format(querysets.paginator.num_pages,filter_ele,sorted_index) ele += p_ele p_ele = '<li><a>{}/{}</a></li>'.format(querysets.number,querysets.paginator.num_pages) ele += p_ele ele += '</ul>' return mark_safe(ele)
-*- coding: utf-8 -*- """ @Datetime: 2018/9/20 @Author: Zhang Yafei """ from django.utils.safestring import mark_safe class Pagination(object): def __init__(self,total_count,current_page,per_page_num=10,max_page_num=7): #数据总个数 self.total_count = total_count #当前页 try: v = int(current_page) if v <= 0: v = 1 self.current_page = v except: self.current_page = 1 #每页显示的行数 self.per_page_num = per_page_num #每页显示的最多页码 self.max_page_num = max_page_num @property def start(self): return (self.current_page-1) * self.per_page_num @property def end(self): return self.current_page * self.per_page_num @property def num_pages(self): """ 总页数 :return: """ a,b = divmod(self.total_count,self.per_page_num) if b == 0: return a return a+1 def pager_num_range(self): if self.num_pages < self.max_page_num: return range(1, self.num_pages + 1) # 总页数特别多 part = int(self.max_page_num / 2) if self.current_page < part: return range(1, self.max_page_num + 1) if (self.current_page + part) > self.num_pages: return range(self.current_page - self.max_page_num + 1, self.num_pages + 1) return range(self.current_page - part, self.current_page + part + 1) def page_str(self,base_url): page_list = [] first = '<li><a href="{}?p=1">首页</a></li>'.format(base_url) page_list.append(first) if self.current_page == 1: prev = '<li><a href="#">上一页</a></li>' else: prev = '<li><a href="{0}?p={1}">上一页</a></li>'.format(base_url,self.current_page-1) page_list.append(prev) for i in self.pager_num_range(): if i == self.current_page: temp = '<li class="active"><a href="{0}?p={1}">{2}</a></li>'.format(base_url,i,i) else: temp = '<li><a href="{0}?p={1}">{2}</a></li>'.format(base_url,i, i) page_list.append(temp) if self.current_page == self.num_pages: next = '<li><a href="#">下一页</a></li>' else: next = '<li><a href="{0}?p={1}">下一页</a></li>'.format(base_url,self.current_page + 1) page_list.append(next) end = '<li><a href="{0}?p={1}">尾页</a></li>'.format(base_url,self.num_pages) page_list.append(end) return mark_safe(''.join(page_list))
五、Form组件
作用:1.对用户请求的验证
2.生成HTML代码+保存上次提交的数据
使用: a.创建一个类 b.类中创建字段(包含正则表达式) c.GET obj = Form() obj.user =>自动生成HTML d.POST obj = Fr(request.POST,request.FILES) #request.FILES 上传文件 if obj.is_valid(): obj.cleaned_data else: obj.errors return ...obj e:Form重点: --字段 用于保存正则表达式 ChoiceField(Field) ***** GenericIPAddressField CharField(Field) IntegerField(Field) DecimalField(IntegerField) DateField(BaseTemporalField) 格式:2015-09-01 TimeField(BaseTemporalField) 格式:11:12 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 EmailField(CharField) FileField(Field) RegexField(CharField) --HTML 用于生成HTML标签 --特殊的单选或多选时,是否是能实时更新? 方法一:利用python执行原理 from app01.models import UserInfo class LoveForm(forms.Form): price = fields.IntegerField() user_id = fields.IntegerField( # widget=widgets.Select(choices=[(0,'alex'),(1,'杨贵妃'),(2,'西施'),(3,'yuan'),]) widget=widgets.Select() ) def __init__(self,*args,**kwargs): #拷贝所有的静态变量,赋值给self.fields super(LoveForm,self).__init__(*args,**kwargs) self.fields['user_id'].widget.choices = UserInfo.objects.values_list('id','username') 方法二:Django内置 from app01.models import UserInfo from django.forms.models import ModelChoiceField class LoveForm(forms.Form): price = fields.IntegerField() user_id2 = ModelChoiceField( queryset=UserInfo.objects.all(), to_field_name='id' ) def __init__(self,*args,**kwargs): super(LoveForm,self).__init__(*args,**kwargs) self.fields['user_id'].widget.choices = UserInfo.objects.values_list('id','username') 注:依赖models中的__str__方法
-form组件(验证:保留上次内容)
-Ajax(验证:无需保留上次内容)
Form组件扩展 1.简单扩展 利用Form组件自带的正则扩展: a. 方式一 from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.CharField( error_messages={'invalid': '...'}, validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], ) b. 方式二 from django.forms import Form from django.forms import widgets from django.forms import fields from django.core.validators import RegexValidator class MyForm(Form): user = fields.RegexField(r'^[0-9]+$',error_messages={'invalid': '...'}) 2.基于源码流程 --三个扩展点 a. 单字段 from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class AjaxForm(forms.Form): username = fields.CharField() user_id = fields.IntegerField( widget=widgets.Select(choices=[(0,'alex'),(1,'刘皓宸'),(2,'杨建'),]) ) # 自定义方法 clean_字段名 # 必须返回值self.cleaned_data['username'] # 如果出错:raise ValidationError('用户名已存在') def clean_username(self): v = self.cleaned_data['username'] if models.UserInfo.objects.filter(username=v).count(): # 整体错了 # 自己详细错误信息 raise ValidationError('用户名已存在') return v def clean_user_id(self): return self.cleaned_data['user_id'] 注:用obj.errors.字段名.0 提取 b.整体错误验证 class AjaxForm(forms.Form): username = fields.CharField() user_id = fields.IntegerField( widget=widgets.Select(choices=[(0,'alex'),(1,'杨贵妃'),(2,'西施'),(3,'yuan'),]) ) # 自定义方法 clean_字段名 # 必须返回值self.cleaned_data['username'] # 如果出错:raise ValidationError('用户名已存在') def clean_username(self): v= self.cleaned_data['username'] if UserInfo.objects.filter(username=v).count(): #整体错了 #自己详细的错误信息 raise ValidationError('用户名已存在') return v def clean_user_id(self): return self.cleaned_data['user_id'] def clean(self): value_dict = self.cleaned_data v1 = value_dict.get('username') v2 = value_dict.get('user_id') if v1 == 'root1' and v2 == 1: raise ValidationError('整体错误信息') return self.cleaned_data PS:_post_clean 注:用obj.errors.__all__.0提取 $(function () { $('#btn').click(function () { $.ajax({ url:'/ajax/', type:'POST', data:$('#fm').serialize(), dataType:'JSON', success:function (arg) { //状态,错误信息 if(arg.status == '钱'){ window.location.href ='http://www.baidu.com' } console.log(arg.message.__all__[0]); } }) }) }) form表单验证流程:前台以post方式提交数据, v = MyFoorm(request.PORT,request.FILES) #1.每个字段正则表达式验证 2.clean_字段方法再次验证 3.clean方法整体验证 if v.is_valid():
六、Django序列化
--把一个对象转换成能写在硬盘上的字符串的过程称为序列化
--将一个存储在硬盘上的字符串转化成对象的过程称为反序列化
JavaScript: JSON.parse() JSON.stringify() Django: json.loads() json.dumps() 问题: serialize:UserInfo.objects.all() json:list(UserInfo.objects.values_list()) json:list(UserInfo:objects.value()) a.对象 from django.core import serializers user_list = UserInfo.objects.all() #QuerySet[obj,obj,obj...] ret['data'] = serializers.serialize('json',user_list) 页面 if (arg.status){ var d = JSON.parse(arg.data); console.log(d); b.字典 c.元组 user_list = UserInfo.objects.all().values_list('id','username') #QuerySet列表[{obj},{obj},..] ret['data'] = list(user_list) 页面 console.log(arg.data);
七、XSS攻击:跨站脚本攻击
<script> for(var i=0;i<9999;i++){ alert(i); } </script> <script> 获取本地cookie,发送到另外一个网站,然后可以支付宝支付等操作 </script> 防止: -别人输入的内容 不用safe -自己输入的内容 可用safe 前端渲染:|safe 后台渲染:txt = '<input type="text" />' from django.utils.safestring import mark_safe txt = mark_safe(txt)
#!/usr/bin/env python # -*- coding:utf-8 -*- from bs4 import BeautifulSoup class XSSFilter(object): __instance = None def __init__(self): # XSS白名单 self.valid_tags = { "font": ['color', 'size', 'face', 'style'], 'b': [], 'div': [], "span": [], "table": [ 'border', 'cellspacing', 'cellpadding' ], 'th': [ 'colspan', 'rowspan' ], 'td': [ 'colspan', 'rowspan' ], "a": ['href', 'target', 'name'], "img": ['src', 'alt', 'title'], 'p': [ 'align' ], "pre": ['class'], "hr": ['class'], 'strong': [] } def __new__(cls, *args, **kwargs): """ 单例模式 :param cls: :param args: :param kwargs: :return: """ if not cls.__instance: obj = object.__new__(cls, *args, **kwargs) cls.__instance = obj return cls.__instance def process(self, content): soup = BeautifulSoup(content, 'html.parser') # 遍历所有HTML标签 for tag in soup.find_all(recursive=True): # 判断标签名是否在白名单中 if tag.name not in self.valid_tags: tag.hidden = True if tag.name not in ['html', 'body']: tag.hidden = True tag.clear() continue # 当前标签的所有属性白名单 attr_rules = self.valid_tags[tag.name] keys = list(tag.attrs.keys()) for key in keys: if key not in attr_rules: del tag[key] return soup.decode() if __name__ == '__main__': html = """<p class="title"> <b>The Dormouse's story</b> </p> <p class="story"> <div name='root'> Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a> <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>; and they lived at the bottom of a well. <script>alert(123)</script> </div> </p> <p class="story">...</p>""" obj = XSSFilter() v = obj.process(html) print(v)
八、文件上传
一、普通文件上传 前端: <form action="/upload.html/" method="post" enctype="multipart/form-data"> {% csrf_token %} <p><input type="text" name="username"></p> <p><input type="file" name="img"></p> <p><input type="submit" value="提交"></p> </form> 后台: def upload(request): if request.method == 'GET': return render(request,'upload.html') else: username = request.POST.get('username') img = request.FILES.get('img') #img是一个对象(文件大小,文件名称,文件内容) print(img.name) print(img.size) f = open(img.name,'wb') for line in img.chunks(): f.write(line) f.close() return HttpResponse('...')
二、自定义页面上传按钮 前端: <script src="/static/js/jquery-3.1.1.js"></script> <link rel="stylesheet" type="text/css" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css"> <form action="/upload.html/" method="post" enctype="multipart/form-data"> {% csrf_token %} <p><input type="text" name="username"></p> <div STYLE="position: relative;height: 50px;"> <button type="button" class="btn btn-primary">上传</button> <input style="position: absolute;left: 0;top: 0;opacity: 0" type="file" name="img"> </div> <p><input type="submit" value="提交"></p> </form>
三、基于Form上传:多了一个验证功能 后台: class UploadForm(Form): username = fields.CharField() img = fields.FileField() def upload(request): if request.method == 'GET': return render(request,'upload.html') else: obj = UploadForm(request.POST,request.FILES) if obj.is_valid(): username = obj.cleaned_data['username'] img = obj.cleaned_data['img'] f = open(img.name,'wb') for line in img.chunks(): f.write(line) f.close() return HttpResponse('...') else: return HttpResponse(obj.errors)
Ajax上传
九、django系统登录验证
内置系统登录认证组件
from django.shortcuts import render, redirect from django.contrib.auth import authenticate, login, logout #auth系统验证 def acc_login(request): error_msg = '' if request.method == 'POST': username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username,password=password) if user: login(request,user) return redirect(request.GET.get('next','/')) else: error_msg = 'Wrong username or password' return render(request,'login.html',{'error_msg':error_msg}) def acc_logout(request): logout(request) return redirect('login') from django.contrib.auth.decorators import login_required #验证装饰器 @login_required def dashboard(request): return render(request,'crm/dashboard.html') #若装饰器验证失败,则跳转到http://127.0.0.1:8000/login/?next=/crm/页面 若想跳转到指定页面 settings:LOGIN_URL = '/login/'
自定义django系统登录验证方法
#自定义认证方法 from django.contrib.auth.backends import ModelBackend class CustomBackend(ModelBackend): def authenticate(self, request, username=None, password=None, **kwargs): try: user = models.UserProfile.objects.get(Q(username=username) | Q(email=username)) if user.check_password(password): return user except Exception as e: return None settings配置 AUTHENTICATION_BACKENDS = ( 'users.views.CustomBackend', ) 视图函数 class LoginView(View): """ 登录功能类 """ def get(self, request): return render(request, 'login.html') def post(self, request): context = {'msg': '用户名或密码错误'} login_form = LoginForm(request.POST) if login_form.is_valid(): username = request.POST.get('username') password = request.POST.get('password') user = authenticate(username=username, password=password) if user: login(request, user) return redirect(to='/') context['msg'] = '用户名或密码错误' return render(request, 'login.html', context) return render(request, 'login.html', context)
十、Django内置组件-content_type
情景再现:我们想象一个场景,当我们的网站有文章表和问题表,另外还有一张评论表,这两张表都可以评论,因此,我们需要将文章表和问题表分别和评论表建立关联。这时,我们会怎样做?
第一种想法是在评论表多加两个字段:第一个字段代表文章ID,第二个字段代表问题ID,每一列可以为空,且只有一个存在。多列FK
第二种想法是新建第三张表:评论id,对象ID和对象类型,比如某一个评论的id为10,对象ID为10,对象类型为1代表文章。两列FK
这两种想法哪一个更好 ?是不是感觉第二种更好一些,当评论表与多张表有Fk关联时,比如10个表都和评论表有关,那么我们就得多加10个字段,而第二种的话不管与多少个表有FK关系,都只需要两个字段。这时,我们会想,嗯,我们应该新建一个表,存放他们之间的关系。No,Django已经帮我们做好了,就是内置的ContentType表。
contenttype表默认有三个字段,存放了所有注册APP的model和app名称,我们可以利用这张表,添加几个字段就可以来做关联。
组件的作用:可以通过两个字段让表和N张表创建FK关系
比如:我们有三张表:课程表,学位课程,价格策略。课程表和学位课程都和价格策略相关
两个字段:content_type和object_id,这时就会将会和contenttype表建立关系,
GenericForeignKey不会在数据库生成列,只用于帮助你进行添加和查询,帮助我们快速创建记录,帮助我们快速查询关联表字段,比如快速查询某一个价格策略的课程名字,快速查询某一条评论的文章标题
GenericRelation不会在数据库生成列,只用于帮助你进行查询,帮助我们快速查询所有的关联字段信息,比如快速查询某一个课程的所有价格策略,快速查询所有某一篇文章的所有评论
class PricePolicy(models.Model): """价格与有课程效期表""" content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) # 关联course or degree_course object_id = models.PositiveIntegerField() # 不会在数据库生成列,只用于帮助你进行添加和查询 content_object = GenericForeignKey('content_type', 'object_id') class Course(models.Model): """专题课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255) # 不会在数据库生成列,只用于帮助你进行查询 policy_list = GenericRelation("PricePolicy") class DegreeCourse(models.Model): """学位课程""" name = models.CharField(max_length=128, unique=True) course_img = models.CharField(max_length=255, verbose_name="缩略图") brief = models.TextField(verbose_name="学位课程简介", ) # 不会在数据库生成列,只用于帮助你进行查询 policy_list = GenericRelation("PricePolicy")
使用
from django.shortcuts import render,HttpResponse from app01 import models from django.contrib.contenttypes.models import ContentType def test(request): # 1.在价格策略表中添加一条数据 # models.PricePolicy.objects.create( # valid_period=7, # price=6.6, # content_type=ContentType.objects.get(model='course'), # object_id=1 # ) # models.PricePolicy.objects.create( # valid_period=14, # price=9.9, # content_object=models.Course.objects.get(id=1) # ) # 2. 根据某个价格策略对象,找到他对应的表和数据,如:管理课程名称 # price = models.PricePolicy.objects.get(id=2) # print(price.content_object.name) # 自动帮你找到 # 3.找到某个课程关联的所有价格策略 obj = models.Course.objects.get(id=1) for item in obj.policy_list.all(): print(item.id,item.valid_period,item.price) return HttpResponse('...')
十一、django第三方分页组件django-pure-pagination
1 安装
pip install django-pure-pagination
2. django项目中进行配置
INSTALLED_APPS = ( ... 'pure_pagination', ) # 可以设置 也可以不设置 PAGINATION_SETTINGS = { 'PAGE_RANGE_DISPLAYED': 10, 'MARGIN_PAGES_DISPLAYED': 2, 'SHOW_FIRST_PAGE_WHEN_INVALID': True, }
PAGE_RANGE_DISPLAYED is the number of pages neighbouring the current page which will be displayed (default is 10)
MARGIN_PAGES_DISPLAYED is the number of pages neighbouring the first and last page which will be displayed (default is 2)
Set SHOW_FIRST_PAGE_WHEN_INVALID to True when you want to just show first page when provided invalid page instead of 404 error
3. 使用
from pure_pagination import Paginator, PageNotAnInteger from courses.models import Course class CourseListView(View): """ 课程列表页 """ @staticmethod def get(request): all_courses = Course.objects.all().order_by("-add_time") hot_courses = Course.objects.all().order_by("-click_nums")[:3] # 课程搜索 search_keywords = request.GET.get('keywords', "") if search_keywords: all_courses = all_courses.filter( Q(name__icontains=search_keywords) | Q(desc__icontains=search_keywords) | Q( detail__icontains=search_keywords)) # 课程排序 sort = request.GET.get('sort', "") if sort: if sort == "students": all_courses = all_courses.order_by("-students") elif sort == "hot": all_courses = all_courses.order_by("-click_nums") # 对课程进行分页 try: page = request.GET.get('page', 1) except PageNotAnInteger: page = 1 p = Paginator(all_courses, 12, request=request) courses = p.page(page) context = { "all_courses": courses, "sort": sort, "hot_courses": hot_courses } return render(request, 'course-list.html', context=context)
<div class="pageturn"> <ul class="pagelist"> {% if all_courses.has_previous %} <li class="long"><a href="?{{ all_courses.previous_page_number.querystring }}">上一页</a></li> {% endif %} {% for page in all_courses.pages %} {% if page %} {% ifequal page all_courses.number %} <li class="active"><a href="?{{ page.querystring }}">{{ page }}</a></li> {% else %} <li><a href="?{{ page.querystring }}" class="page">{{ page }}</a></li> {% endifequal %} {% else %} <li class="none"><a href="">...</a></li> {% endif %} {% endfor %} {% if all_courses.has_next %} <li class="long"><a href="?{{ all_courses.next_page_number.querystring }}">下一页</a></li> {% endif %} </ul> </div>
4. 文档
https://github.com/jamespacileo/django-pure-pagination
十二、Django第三方组件django-cors-headers
跨域:请求url包含协议、网址、端口,任何一种不同都是跨域请求。
浏览器的同源策略: 不允许通过ajax,href等发送跨域请求,但对src开放
-
- 开放:src
- 禁止:ajax
解决跨域
绕过浏览器同源策略就可以跨域。 巧妙的机制:JSONP 原理: 同源策略会阻止ajax请求;不阻止具有src属性的标签 JSONP:利用创建script块,在期中执行src属性为:远程url 函数(返回值) 动态创建script标签 <script src='xxxx'></script> 实现: jsonp,在客户端动态创建一个script标签 1.客户端:创建一个 <script src='http://www.jxntv.cn/data/jmd-jxtv2.html'></script> <script> function func(arg){ alert(arg); } </script> 2.服务端:接收到请求并处理并返回值 "func('success')" 相当于: <script> func('success') </script> PS: jsonp只能发送GET请求 - cors,设置响应响应响应响应响应头 - 简单请求 - 复杂请求 - options请求做预检 - PUT/POST....
django中解决方案
1. 中间件
class CORSMiddleware(MiddlewareMixin): """ cors跨域实现简答请求 跨域:向不同域名或端口不同的地址发送请求) """ def process_response(self, request, response): # 添加响应头 # 允许你的域名来获取我的数据 response['Access-Control-Allow-Origin'] = "*" # 允许你携带Content-Type请求头 response['Access-Control-Allow-Headers'] = "Content-Type" # 允许你发送DELETE,PUT response['Access-Control-Allow-Methods'] = "DELETE,PUT" return response
2. 第三方组件:django-cors-headers
安装
pip install django-cors-headers
添加应用
INSTALLED_APPS = ( ... 'corsheaders', ... )
设置中间件
MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ... ]
.添加允许访问的白名单,凡是出现在白名单的域名都可以访问后端接口
# CORS CORS_ORIGIN_WHITELIST = ( '127.0.0.1:8080', 'localhost:8080', ) CORS_ALLOW_CREDENTIALS = True # 指明在跨域访问中,后端是否支持对cookie的操作。
十三、发送邮件
自己实现
import smtplib from email.mime.text import MIMEText from email.utils import formataddr msg = MIMEText('邮件内容', 'plain', 'utf-8') msg['From'] = formataddr(["张亚飞",'1271570224@qq.com']) msg['To'] = formataddr(["走人",'2464392538@qq.com']) msg['Subject'] = "主题" server = smtplib.SMTP_SSL("smtp.qq.com", 25) server.login("1271570224@qq.com", "输入你的授权码") server.sendmail('1271570224@qq.com', ['2464392538@qq.com',], msg.as_string()) server.quit()
Django实现
settings设置 EMAIL_USE_SSL = True EMAIL_HOST = 'smtp.qq.com' # 如果是 163 改成 smtp.163.com EMAIL_PORT = 25 EMAIL_HOST_USER = '1271570224@qq.com' # 帐号 EMAIL_HOST_PASSWORD = ‘xxxxxxx’' # 你的授权码 DEFAULT_FROM_EMAIL = EMAIL_HOST_USER # DEFAULT_FROM_EMAIL = 'zhangyafei <zhangyafei@163.com>' AUTH_USER_MODEL = 'crm.UserProfile' views.py def send_qq_mail(user_obj): email_title = 'Pyhton SMTP 邮件服务测试' email_body = '恭喜你成为飞凡教育的学员,您的账号为{},密码为 feifan@123 \n请尽快登录网站修改密码。'.format(user_obj.username) # email = '1271570224@qq.com' # 对方的邮箱 send_status = send_mail(email_title, email_body, conf.settings.DEFAULT_FROM_EMAIL, [user_obj.email]) return send_status
十四、信号
1.Django内置信号
Model signals pre_init # django的modal执行其构造方法前,自动触发 post_init # django的modal执行其构造方法后,自动触发 pre_save # django的modal对象保存前,自动触发 post_save # django的modal对象保存后,自动触发 pre_delete # django的modal对象删除前,自动触发 post_delete # django的modal对象删除后,自动触发 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 创建数据库连接时,自动触发
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
from django.core.signals import request_finished from django.core.signals import request_started from django.core.signals import got_request_exception from django.db.models.signals import class_prepared from django.db.models.signals import pre_init, post_init from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_delete, post_delete from django.db.models.signals import m2m_changed from django.db.models.signals import pre_migrate, post_migrate from django.test.signals import setting_changed from django.test.signals import template_rendered from django.db.backends.signals import connection_created def callback(sender, **kwargs): print("xxoo_callback") print(sender,kwargs) xxoo.connect(callback) # xxoo指上述导入的内容
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
有时需要在添加记录和删除记录是写到日志,可以通过继承save和delete方法重写后实现,但更方便的方法是可以使用信号来完成。
2、自定义信号
a. 定义信号
import django.dispatch pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
b.注册信号
def callback(sender, **kwargs): print("callback") print(sender,kwargs)
pizza_done.connect(callback)
c.触发信号
from 路径 import pizza_done pizza_done.send(sender='seven',toppings=123, size=456)
由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发。
十五、配置404和500页面
1. 修改settings文件
DEBUG = False ALLOWED_HOSTS = ['*'] STATIC_URL = '/static/' STATIC_ROOT = 'static' # 新增行 STATICFILES_DIRS = ( os.path.join(BASE_DIR, '/static/'), # 修改 )
2. 修改urls
# 全局404页面配置 handler404 = 'users.views.page_not_found' # 群居500页面配置 handler500 = 'users.views.page_error'
3. 添加静态文件路由
# 配置静态文件的访问处理函数 path('static/<path:path>', serve, {'document_root': settings.STATIC_ROOT}, name='static'),