Django进阶
一、模板语法
1. 本质
|
1
2
3
4
5
6
7
|
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>' |
注:模板中取元组或列表变量的时候通过.取
|
1
2
3
4
5
6
7
8
9
10
|
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
(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
2
3
4
5
6
7
8
9
10
11
12
|
(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
|
1
2
3
4
5
6
|
自定义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失效策略。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
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
2
3
4
5
6
7
8
9
10
11
12
|
1. 请求进来,到达wsgi,wsgi就是一个socket服务端,它用来接收用户请求并对请求初次封装,然后将请求交给web框架2. 请求到达django,django对请求再次进行封装3. 执行中间件中的方法(process_request),对请求中的数据进行校验或放值,比如请求对请求中的csrf进行验证,比如请求中原来没有session,经过中间件赋值和session4. 根据请求头的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条。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
原生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>