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>
自定义filter
'''
 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>
自定义simple_tag

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>
自定义inclusion_tag

二、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: '/' });
        
操作cookie
二、操作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失效策略。
c操作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
CBV实例

四、分页

  分页的作用:减少一次性从数据库里取太多的数据,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,
                                                           })
views.py
{% 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 %}
template
@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)
templatetags

自定义分页组件

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))
自定义分页组件2

五、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组件的使用

-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)
xss组件

八、文件上传

一、普通文件上传
        前端:
            <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)
基于form上传

  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('...')
views.py

十一、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

http://i.imgur.com/LCqrt.gif

 

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)
views.py
                  <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>
html

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指上述导入的内容
View Code
from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")
View Code

有时需要在添加记录和删除记录是写到日志,可以通过继承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'),

  

posted @ 2018-12-13 18:59  DreamBoy_张亚飞  阅读(287)  评论(0编辑  收藏  举报