Django --总结 之URL路由控制 视图相应,视图请求,和模板语法&过滤器

URL路由控制

  1. 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种模板渲染

  1. {{data}} 这种是渲染变量,这里又有2个知识点,分别是深度查询和过滤器
  • 深度查询是 指的是对上面部分数据内再次查询的操作,比如字典,列表,类等,深度查询都是用.完成的

  1. {% %} 这种渲染标签用
  • 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

自定义模版功能

使用步骤前面几步都相同

  1. 在setting中的INSTALLED_APPS配置当前app,不然框架找不到自定义的simple_tag
  2. 在app或者全局中创建一个templatetags的模块(packages,不是文件夹)(模块名字不允许自定义),在哪里创建取决于INSTALLED_APPS添加的信息,如果要全局,则无需在INSTALLED_APPS添加信息,反之则添加
  3. 创建任意py文件,例如我们创建的是define_tags.py
  4. 导包from django import template 并且创建实例 register=template.Library()
  5. 自定义函数上面分别按需求 @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片段
    - 模版渲染用{% %}
    

三者的代码示例合集

  1. 项目的settings的installapps加入app的name为web
  2. web应用下创建templates和templatetags
  3. 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":"初中生"}
  1. 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
  1. 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

  1. 子模块语法
{% extends "base.html" %}    先声明继承哪个父模板
定义显示在父模块的哪个位置
{% block title %}index3的标题{% endblock  %}   定义显示在父模块一个定义title的地方
{% block content %}                            定义显示在父模块一个定义content的地方
    {{ block.super }} {# 父级模板同名block标签的内容 #}
    <h1>index3.html的独立内容</h1>
    {{ block.super }}                          定义父模块content的内容也一起展示
{% endblock %}
  1. 父模块语法
<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 %}
posted @ 2021-07-31 22:43  零哭谷  阅读(113)  评论(0编辑  收藏  举报