Django --总结 之URL路由控制 视图相应,视图请求,和模板语法&过滤器
URL路由控制
- Djaong第一站就是进控制路由,找到访问路径需要分发到那个视图中去,找到路径就去调用相应的视图函数
re_path(r"my_app/",include("my_app.urls")) #去父路径是my_app,然后去my_app的路由中找接下来的路径请求的常用方法
#假设下面是my_app应用中的Urls,那么完整的路径是/my_app/mytest/2003,然后执行my_app下的view视图中的my_test函数
path("mytest/2003",my_test,name="mytest_reverse"),
#路由路径如果不能写死,还要用==**分组**==进行对路径正则的匹配 或者用path内置的自定义
re_path(r"^case3/([0-9]{4,})/([0-9]{2})/$",case03)
#然后默认有名分组是按照变量名的定义先后顺序传参,如果获取参数时候要按照键值对传参,那就需要==**有名分组**== 语法格式为`?P<year>`
re_path(r"^case3/(?P<year>[0-9]{4,})/(?P<mon>[0-9]{2})/$",case03)
path("^case3$/<int:year>/<int:month>")
# 当我们考虑到路由路径可能会存在更改,但是又想后期让这个路径可以自己动态更新,不手动去改,那么我们就赋予一个name参数,用来反向解析用.通过这个name的值指向这条url的路径
re_path(r"^case3/(?P<year>[0-9]{4,})/(?P<mon>[0-9]{2})/$",case03,name="myurl")
#当反向解析的问题解决了,那么一个项目多个应用中可能存在name参数重复的问题,因此我们又需要命名空间这个东西.
#规则如下↓↓↓
视图响应
HttpResponse()
def zongjie(request):
return HttpResponse("总结测试") # HttpResponse是返回字符串
return render(request,"test.html") # render 方法本质还是调用 HttpResponse返回字符串,只不过在之前会通过模板语法对一些数据显示进行替换.
JsonResponse()
from django.http import JsonResponse
该类是用来返回json数据的,注意只能返回字典,如果要返回列表等其他数据类型,需要将safe设置为false
return JsonResponse([1,2,3,4],safe = False)
有时候也需要直接显示中文,原理和json处理中文乱码一样 添加json_dumps_params参数
JsonResponse({"status":"ok","name":stu_obj.name,},json_dumps_params={"ensure_ascii":False})
视图请求
请求的常用方法
request.body 当content type为application/json时候使用,需要decode并转换成json数据类型
request.POST 只获取表单数据,一般content type为application/application/x-www-form-urlencoded,对a=1&b=2&c=3 进行转换成字典
request.POST.dict() 将所有数据转换成字典形式,这里有个小坑,如果POST数据某个值是列表,转换后只会保留最后一个值,因此这种情况转换前先用request.POST.getlist("key")提取出来
request.GET 是一个queryDict类型的对象,默认是不允许更改里面的参数的,如果要修改里面参数设置request.GET_mutable=True
Reuqest.GET.setlist("name",[123]) url的请求就变成了http://.com?name=123
Reuqest.GET.setlist("name",[123,456]) url的请求就变成了http://.com?name=123&name=456
request.GET.urlencode() urlencode方法可以将request.GET的queryDict对象转换成?name=123&name=456
类型的字符串
request.method
request.POST.get(key)
request.POST.getlist(key) 当传的urlencode类型时候,同一个key有多个value,浏览器会把values写成列表形式.
https://www.baidu.com/p?name=a&age=18
request.path 返回的是路径部分 /p
request.get_full_path() 返回的是路径+参数部分 /p?name=a&age=18
request.META 请求头数据
request.resolver_match 提供了有关请求的URL解析信息的访问。可以提取访问的url路径,用来后续的鉴权功能
def zongjie(request):
print(request.method) #request.method 获取请求方式,根据不同方式又可以提取不同的内容
#http://127.0.0.1:8000/my_app/zongjie/?a=1&b=2,
# request.GET= <QueryDict: {'a': ['1'], 'b': ['2']}>
# request.GET.get("a") = 1
print(request.GET,request.GET.get("a"))
print(request.POST)
#request常用方法 get_full_path() 和is_ajax
#重点看下path和get_full_path()的区别,以URL=http://127.0.0.1:8000/my_app/zongjie/?a=1&b=2 为例
print(request.path) #结果为 /my_app/zongjie/
print(request.get_full_path()) #结果为/my_app/zongjie/?a=1&b=2
print(request.is_ajax()) # False
return HttpResponse("总结测试")
利用视图请求设置回退参数
假如操作的数据分页数很多,要实现对第100页的第二条数据进行编辑,提交编辑保存后再回退到第100页的分页数据
如果这个分页上之前还带有?name="aa"&age=19等过滤参数,我们也实现回退的url上继续带有这些过滤参数
要实现这个功能主要要用QueryDict
对象 导包路径from django.http import QueryDict
其实这个请求的request.GET 就是一个QueryDict对象,被赋值了而已,
这个对象的mutable
参数默认为false,表示对象的属性不可更改,修改为True则允许更改
# 先设置允许修改 也可以考虑复制request.GET,如果复制用深拷贝.
new_query_dict = QueryDict(mutable=True)
# 将初始的请求url写到next字段中
new_query_dict['next'] = param
# 对url进行url编码
filter_string = new_query_dict.urlencode()
这样之前的url参数都保存在next字段中.可以用request.GET.get("next")获取.
模板语法和过滤器
模版的寻找原理
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
# 'django.contrib.auth.context_processors.auth',
# 'django.contrib.messages.context_processors.messages',
],
},
},
]
优先去项目根目录 > 每个已注册的app的templates目录找。
- 简单的项目,模板都放在根目录。
- 复杂的项目,模板放在各自的app中,公共部分放在templates目录。
比如,下面这个路径,django会优先去找pro_test/templates/login.html
,如果没找到则去pro_test/api/templates/login.html和pro_test/web/templates/login.html
路径下去找
模板的渲染原理
用模板渲染的叫DTL模板渲染
render 渲染页面是通过get_template函数实现的
get_template函数是直接渲染静态页面. from django.template.loader import get_template
如果有动态数据渲染,则是用get_template.render(context,request)方法实现的
静态渲染
render(request,"index.html") = get_template("index.html")
动态渲染
render(request,"index.html",{"name":"alex"}) = get_template("index.html").render({"name":"alex"},request)
常见的2种模板渲染
- {{data}} 这种是渲染变量,这里又有2个知识点,分别是深度查询和过滤器
- 深度查询是 指的是对上面部分数据内再次查询的操作,比如字典,列表,类等,深度查询都是用.完成的
- 常用的内置过滤器,主要是用在网页上的模板语法,用来过滤显示的数据的,语法:
{{obj|过滤器名称:过滤器参数}}
关于详细的内置过滤器介绍: https://www.liujiangblog.com/course/django/147
- {% %} 这种渲染标签用
- for 循环的标签语法
语法总结
{% for i in obj_list %}
{% for k,v in obj_list.items %}
{{ forloop.first }} 返回布尔值,
{{ forloop.counter }} 从1开始计数
{{ forloop.counter0 }} 从0开始计数
{% endfor %}
- 其他forloop的属性描述
属性 描述
forloop.counter 显示循环的次数,从1开始
forloop.counter0 显示循环的次数,从0开始
forloop.revcounter0 倒数显示循环的次数,从0开始
forloop.revcounter 倒数显示循环的次数,从1开始
forloop.first 判断如果本次是循环的第一次,则结果为True
forloop.last 判断如果本次是循环的最后一次,则结果为True
forloop.parentloop 在嵌套循环中,指向当前循环的上级循环 - if 逻辑判断标签语法
和for一样 以{%if %}开头 {%endif%}结尾
语法总结
{% if score > 90 %}
{% elif score > 80%}
{% else score > 60%}
{% endif %}
-
with 标签语法,用来给添加变量指定特点较长的数据的
-
csrf_token 标签语法
-
url 标签语法 用来反向生成url中的命名空间对应的路由地址
{% url 'some-url-name' arg1=v1 arg2=v2 %}
some-url-name 是路由的命名空间 比如path('sms_login/',views.sms_login,name=" some-url-name"),
后面的arg1 arg2是可以传参
如果路由存在多层嵌套,比如
path('rbac/',include("rbac.urls"),name=" rbac"),
path('sms_login/',views.sms_login,name=" some-url-name"),
访问路由则是/rbac/sms_login/
在模版标签的url使用则在层和层之间加冒号
{% url 'rbac:some-url-name' arg1=v1 arg2=v2 %}
更多的标签语法可以参考 https://www.liujiangblog.com/course/django/146
自定义模版功能
使用步骤前面几步都相同
- 在setting中的INSTALLED_APPS配置当前app,不然框架找不到自定义的simple_tag
- 在app或者全局中创建一个templatetags的模块(packages,不是文件夹)(模块名字不允许自定义),在哪里创建取决于INSTALLED_APPS添加的信息,如果要全局,则无需在INSTALLED_APPS添加信息,反之则添加
- 创建任意py文件,例如我们创建的是define_tags.py
- 导包
from django import template
并且创建实例register=template.Library()
- 自定义函数上面分别按需求
@register.filter
或@register.simple_tag
或@register.inclusion_tag("tag_test.html")
注意:
@register.filter()和@register.simple_tag()
这里不填参数,默认这个自定义过滤器就是函数名,有参数,则视为别名,在模块渲染中要选这个别名作为自定义过滤器,
比如@register.filter("abc")
此时不能用模板渲染里不能用multi_filter而是要用abc替代,这个功能意义不大.
@register.inclusion_tag(渲染的html模版地址)
注意 inclusion_tag里的模版路径通常是templates下的,而templates这层路径可以省略,比如上面tag_test.html,是在/templates/tag_test.html
下的
f. 使用前导入自定义的过滤器py文件 {% load define_tags%},然后再导入过滤器文件中的各种自定义函数
三者的使用区别
-
filter
- 数据处理,参数:1~2个 - 数据处理,if条件 - 模版渲染用{{ }}
-
simple_tag
参数无限制 & 返回文本 - 模版渲染用{% %}
-
inclusion_tag
参数无限制 & HTML片段 - 模版渲染用{% %}
三者的代码示例合集
- 项目的settings的installapps加入app的name为web
- web应用下创建templates和templatetags
- templatetags的packages创建define_tags, 分别自定义了过滤器,simpletag,inclusion_tag
from django import template
register=template.Library()
@register.filter
def filter_tag(mystr):
return mystr[2:4]
@register.simple_tag
def simple_tag_show(x,y):
return x+y
@register.inclusion_tag("role.html")
def include_tag_show(age):
if age<=10:
return {"role":"小学生"}
else:
return {"role":"初中生"}
- templates文件夹下创建a.html和role.html
a.html代码
<body>
<p>{{ name }}</p>
<p>{{ name|filter_tag }}</p>
<p>{% simple_tag_show name "world" %}</p>
<p>{% include_tag_show age %}</p>
</body>
role.html代码
<h2>{{ role }}</h2>
文件目录层级关系如下
项目文件
-web
--templates
---a.html
---role.html
--templatetags
---init.py
---define_tags.py
- web应用下的视图代码:
def test(request):
con={
"name":"hello",
"age":9
}
return render(request,"a.html",con)
模板语法之include&extend
include是模板嵌入, 主要用来找出多个页面共有的部门,然后用include进行嵌入渲染展示
而extend是继承, 和include相反,如果公有的部分很多页面都要用,与其一个个嵌入,不如去继承这部分共有的内容.减少模块碎片化
我们要做的功能大致如下,做一个模板base.html展示黑色部分的框架,再做一个family.html去继承base.html,然后展示里面的家人信息
如果要继承模板,有2种模式extend和include,但是都要在首行输入
{%include "base.html"%}或者{%extends "base.html"%}
区别:include会把所有的html代码全部导入,
而extend在模板中会设置block标签,然后block标签以外的会引入进来,block标签就是自定义更改模板的地方
{% block con %} # 语法{%block 变量%}
{% endblock con %} #endblock 后面也建议加入变量,代码阅读会方便,知道一整块属于那个block
还可以用在css上
{% block css %}
<style>
.test{
padding:0;
}
</style>
{% endblock %}
练习的代码截图如下:
-
urls和views代码
-
base.html代码如下
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>关系图</title>
<link rel="stylesheet" href="/static/my_app/base.css">
</head>
<body>
<div id="top">
</div>
<div id="body">
<div id="left">
<div class="function">
<p>功能分布</p>
</div>
<div id="checkret">
<!-- 这里添加功能查询-->
{% block function_check %}
<p>功能查询</p>
{% endblock %}
</div>
</div>
<div id="right">
<div id="check_title">
<p>查询框</p>
</div>
<div id="check_result">
<p>查询结果</p>
<div id="show_check_result">
<div id="topshow">
<ul>
<li>姓名</li>
<li>性别</li>
<li>年纪</li>
</ul>
</div>
<div id="bodyshow">
<!-- 这里添加查询结果-->
<p>功能查询</p>
{% block relationship %}
{# <ul>#}
{# <li>姓名</li>#}
{# <li>性别</li>#}
{# <li>年纪</li>#}
{# </ul>#}
{# <ul>#}
{# <li>姓名</li>#}
{# <li>性别</li>#}
{# <li>年纪</li>#}
{# </ul>#}
{% endblock relationship%}
</div>
</div>
</div>
</div>
</div>
</body>
</html>
-
base.html和family.html的导入和block对应图
-
效果图
正常情况下base.html模板中显示的内容会被family.html自定义的block内容给覆盖,但是如果想保留模板的block区域的内容,又想添加family的自定义内容
那么就在family.html自定义模块中添加{{block.super}}
{%block con%}
{{block.super}}
{%endblock con%}
- family.html添加
{{block.super}}
的示例图
效果图如下
模板继承总结
继承2种模式
{% include "模板文件名"%} # 模板嵌入
{% extends "base.html" %} # 模板继承
include可以实现复用,但是会有大量碎片化,因为一个页面有可能要Include 左侧区域,也要其他区域,要几个就要include几个.
因此更多是使用extends
- 子模块语法
{% extends "base.html" %} 先声明继承哪个父模板
定义显示在父模块的哪个位置
{% block title %}index3的标题{% endblock %} 定义显示在父模块一个定义title的地方
{% block content %} 定义显示在父模块一个定义content的地方
{{ block.super }} {# 父级模板同名block标签的内容 #}
<h1>index3.html的独立内容</h1>
{{ block.super }} 定义父模块content的内容也一起展示
{% endblock %}
- 父模块语法
<body>
<h1>base.html的头部</h1>
{% block content %} 定义子模块显示位置,取别名为content
<h1>base.html的内容</h1>
{% endblock %}
<h1>base.html的脚部</h1>
</body>
不能在一个模版中定义多个相同名字的 block
标签。
模板语法转义失效
在模版中如果就是要输出{{ }}
这种模版语法
可以先用{% verbatim %}
标签进行包裹
例如 网页就会渲染{{ info }}
字样
{% verbatim %}
<p>{{ info }}</p>
{% endverbatim %}
模版继承,子模板使用自定义js和css
背景是我们在继承父模版的时候如果子模版需要使用js或者css样式,但是导入信息都写在了父模版中.
我们需要再父模版中加载css或者js的地方用模版语法写入
注意我们可以渲染static这个配置文件中的变量,但是用之前需要再模版上加载一下{% load static %}
父模版
{% load static %}
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
....代码省略..
<script type="text/javascript" src="{% static 'bootstrap-3.4.1/dist/js/bootstrap.js' %}"></script>
{% block js %} {% endblock %} 此处加模版语法
<link rel="stylesheet" type="text/css" href="{% static 'css/home.css' %}">
</head>
子模版
{% extends "home.html" %}
{% block customer %}
<div>
<a class="btn btn-success" href={% url 'customer_add' %}>新增</a>
</div>
<table class="table table-striped">
...省略...
</table>
{% endblock %}
{% block js %} 此处在子模版中加入语法
<script>
$(function () {
console.log("1")
})
</script>
{% endblock %}