Django基础之:模版系统

一 引子

什么是模版系统?这里做一个简单解释。要想明白什么是模版系统,那么我们得先分清楚静态页面和动态页面。我们之前学过的都是静态页面,所谓的静态页面就是浏览器向后端发送一个请求,后端接收到这个请求,然后返回给浏览器一个html页面,这个过程不涉及从数据库取出数据渲染到html页面上,只是单纯的返回一个页面(数据全部在html页面上)。而动态页面就是在给浏览器返回html页面之前,需要后端与数据库之间进行数据交互,然后将数据渲染到html页面上在返回给浏览器。言外之意静态页面不涉及数据库,动态页面需要涉及从数据库取出数据。那么模版系统是什么呢?如果你只是单纯的写静态页面,也就没必有必要用模版系统了,只用动态页面才需要模版系统。

简单来说,模版系统就是在html页面想要展示的数据库或者后端的数据的标签上做上特殊的占位(类似于格式化输出),通过render方法将这些占位的标签里面的数据替换成你想替换的数据,然后再将替换数据之后的html页面返回给浏览器,这个就是模版系统。

模板渲染的官方文档

关于模板渲染你只需要记两种特殊符号(语法):

{{  }}和 {% %}

变量相关的用{{}},逻辑相关的用{%%}。

 

二、变量

1. 简单示例

接下来,我们先搭一个简单流程:浏览器访问https://127.0.0.1:8000:/index,返回一个index.html页面,我们在views函数中设置一些变量,然后通过模版系统渲染,最终返回给浏览器。

url:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^index/',views.index),
]

views:

render第三个参数接受一个字典的形式,通过字典的键值对index.html页面进行渲染,这也就是模块渲染。

def index(request):
    name = '太白金星'
    age = 18
    name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
    dic = {'classname': '人工智能', 'since': 2019}

    return render(request, 'index.html',{'name': name, 'age': age,'name_list':name_list, 'dic_class':dic})

html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>
    {#  通过render进行模版语言渲染然后替换成后端的数据,最终发给浏览器  #}
    <li>{{ name }}</li>
    <li>{{ age }}</li>
    <li>{{ name_list }}</li>
    <li>{{ dic_class }}</li>
</ul>
</body>
</html>

最终浏览器显示的结果为:

 

这样,你后端这些变量全部都渲染到前端了。

2. 语法

在Django的模板语言中按此语法使用:{{ 变量名 }}。

  当模版引擎遇到一个变量,它将计算这个变量,然后用结果替换掉它本身。 变量的命名包括任何字母数字以及下划线 ("_")的组合。 变量名称中不能有空格或标点符号。

  深度查询据点符(.)在模板语言中有特殊的含义。当模版系统遇到点("."),它将以这样的顺序查询:

    字典查询(Dictionary lookup)
    属性或方法查询(Attribute or method lookup)
    数字索引查询(Numeric index lookup)

3. 万能的点 . 

通过简单示例我们已经知道模版系统对于变量的渲染是如何做到的,非常简单,接下来我们研究一些深入的渲染,我们不想将整个列表或者字典渲染到html,而是将列表里面的元素、或者字典的某个值渲染到html页面中,那怎么做呢?就是通过万能的点。

views:

def index(request):
  
    name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
    dic = {'classname': '人工智能', 'since': 2019}
    lis = [1, ['冠状病毒', '武汉加油'],3]

    # 使用locals是用于测试,实际生产环境中是不可以的。
    return render(request, 'index.html', locals())

html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>

    {# 万能的点 #}
    <li>{{ name_list.2 }}</li>
    <li>{{ dic.classname }}</li>
    <li>{{ lis.1.0 }}</li>
</ul>
</body>
</html>

浏览器的显示结果:

刚才我们尝试的数据类型,那么在一切皆对象的python世界中,我们一个对象是否可以通过模版渲染到html页面中呢?

views:

def index(request):
  
    class A:

        def __init__(self):
            self.name = 'barry'

        def func(self):
            return '太白教你学python'

    obj = A()
    return render(request, 'index.html', locals())

html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>

<ul>
   
    {# 万能的点 #}

    <li>{{ obj.name }}</li>
    <li>{{ obj.func }}</li>
</ul>
</body>
</html>

浏览器显示的结果:

 

如图所示,对象也是可以通过模版渲染到html页面中的,但是这里要注意一个点:obj.func是不可以加括号的,所以我们后端设置的函数是不可以有参数的(类中的方法self自动传递)。

注意我们直接在js代码中使用模板语法的时候,模板渲染的时候会有个转义的动作,将s = ['哈哈','xx']这种数据中的元素的引号变为一个特殊符号:这个我们后面会讲到

    <script>
        // 不加safe的话,引号会被转义。
// var a = {{ s }} // var a = [&#39;哈哈&#39;, &#39;xx&#39;]; // console.log(a[0]) // 加上safe就正常了 var a = {{ s|safe }}; console.log(a[0])
// 还要注意,当我们模板渲染的时候,后端返回的数据是字符串的话,我们需要将{{ s }}外面加上引号
比如s = '哈哈'
js中的写法
var a = '{{ s }}' </script>

三、过滤器

1.什么是过滤器

有的时候我们通过render渲染到html的数据并不是我们最终想要的数据,比如后端向前端传递的数据为hello,但是我们html想要显示为HELLO,当然这个在后端是可以提前处理的,但是诸如此类的需求我们可以通过过滤器解决,过滤器给我们提过了很多便捷的方法,加工你传递到html的数据,便于灵活开发。

2.语法

  过滤器的语法: {{ value| filter_name:参数 }}

  使用管道符"|"来应用过滤器。

  例如:{{ name| lower }}会将name变量应用lower过滤器之后再显示它的值。lower在这里的作用是将文本全都变成小写。

  注意事项:

  1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
  2. 过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。
  3. 过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}
  4. '|'左右没有空格!没有空格!没有空格!

  Django的模板语言中提供了大约六十个内置过滤器。

3.常用过滤器

default: 如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。

views:
a = ''  # 没有变量a或者变量a为空

html: # 显示啥也没有 {{ value|default:"啥也没有"}}

length:返回值的长度,作用于字符串和列表。

views:
name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
s = '太白金星讲师'
html:
{{ name_list|length }}  # 显示为5
{{s|length }}  # 显示为6

filesizeformat: 将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB''4.1 MB''102 bytes', 等等)。

views:
value = 1048576

html:
{{ value|filesizeformat }}  # 显示为1.0MB

slice:切片,支持pyhton中可以用切片的所有数据类型

views:
name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
s = '太白金星讲师'

html:
{{ name_list|slice:'1:3' }}  # ['天琪', '傻强']
{{ s|slice:'1::2' }}  # '白星师'

date:时间格式化

views:
time = datetime.datetime.now()

html:
{{ t|date:"Y-m-d H:i:s" }}  # 2020-02-11 07:31:29

 关于时间日期的可用的参数(除了Y,m,d等等)还有很多,有兴趣的可以去查查看看。

truncatechars:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。

参数:截断的字符数

views:
describe = '1999年3月,马云正式辞去公职,后来被称为18罗汉的马云团队回到杭州,凑够50万元人民币'

html:
{{ describe|truncatechars:9 }}  # 1999年3...

# 截断9个字符,三个点也算三个字符

这个我们在浏览网页时经常见到,描述的内容很多,只能用...显示,比如:

 

 

 

truncatewords:在一定数量的字后截断字符串,是截多少个单词。

views:
words = 'i love you my country china'

html:
{{ words|truncatewords:3 }}  # i love you...

cut: 移除value中所有的与给出的变量相同的字符串

views:
words = 'i love you my country china'

html:
{{ words|cut:3 }}  # iloveyou

join: 设定连接符将可迭代对象的元素连接在一起与字符串的join方法相同。

views:
name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
dic = {'name':'太白','age': 18}
tu = (1, 2, 3)

html:
<li>{{ name_list|join:'_'}}</li>
<li>{{ dic|join:','}}</li>
<li>{{ tu|join:'+'}}</li>
'''
王阔_天琪_傻强_志晨_健身哥
name,age
1+2+3
'''

safe

  Django的模板中在进行模板渲染的时候会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全,django担心这是用户添加的数据,比如如果有人给你评论的时候写了一段js代码,这个评论一提交,js代码就执行啦,这样你是不是可以搞一些坏事儿了,写个弹窗的死循环,那浏览器还能用吗,是不是会一直弹窗啊,这叫做xss攻击,所以浏览器不让你这么搞,给你转义了。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。

  我们去network那个地方看看,浏览器看到的都是渲染之后的结果,通过network的response的那个部分可以看到,这个a标签全部是特殊符号包裹起来的,并不是一个标签,这都是django搞得事情。

    

  比如:value = "<a href='#'>点我</a>"   和   value="<script>alert('123')</script>"

{{ value|safe}}

    很多网站,都会对你提交的内容进行过滤,一些敏感词汇、特殊字符、标签、黄赌毒词汇等等,你一提交内容,人家就会检测你提交的内容,如果包含这些词汇,就不让你提交,其实这也是解决xss攻击的根本途径,例如博客园:

      

timesince(了解)

将日期格式设为自该日期起的时间(例如,“4天,6小时”)。

采用一个可选参数,它是一个包含用作比较点的日期的变量(不带参数,比较点为现在)。 例如,如果since_12是表示2012年6月28日日期实例,并且comment_date是2018年3月1日日期实例:

views:
  year_12 = datetime.datetime.now().replace(year=2012, month=6, day=28)
  year_18 = datetime.datetime.now().replace(year=2018, month=3, day=1)

html:
 <li>{{ year_12|timesince:year_18}}</li>

'''
5 years, 8 months
'''

如果year_18不写,默认就是距现在的时间段。

timeuntil(了解)

  似于timesince,除了它测量从现在开始直到给定日期或日期时间的时间。 例如,如果今天是2006年6月1日,而conference_date是保留2006年6月29日的日期实例,则{{ conference_date | timeuntil }}将返回“4周”。

  使用可选参数,它是一个包含用作比较点的日期(而不是现在)的变量。 如果from_date包含2006年6月22日,则以下内容将返回“1周”:

{{ conference_date|timeuntil:from_date }}

  这里简单介绍一些常用的模板的过滤器,更多详见

更多内置过滤器(此链接页面最下面的内置过滤器):https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#filters 

四、标签Tags

现在我们已经可以从后端通过模版系统替换掉前端的数据了,但是如果只是替换掉数据,确实不够灵活,比如,我们要想在前端页面展示可迭代对象name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']每个元素,你如和展示呢?

# 前端页面
<ul>
    <li>{{ name_list.0 }}</li>
    <li>{{ name_list.1 }}</li>
    <li>{{ name_list.2 }}</li>
    <li>{{ name_list.3 }}</li>
</ul>

这样写明显很low,我们要是可以用上for循环就好了。Django这么强大的框架,不可能想不到这点的,这里模版系统给我们提供了另一种标签,就是可以在html页面中进行for循环以及if判断等等。

标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。与python语法不同的是:一些标签需要开始和结束标签 (例如{% tag %} ...标签 内容 ... {% endtag %})。

学习下面几种标签之前,我们要重新写一个url、views以及html,方便分类学习不与上面的变量产生冲突。

urls:
    url(r'^tags/',views.tags),


views:
def tags(request):
    num = 10
value = '' name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥'] dic = {'name': '太白金星', 'age': 18} return render(request, 'tags.html', locals()) 先创建一个简单的tags.html即可。

for标签

基本语法:

{% for 变量 in render的可迭代对象 %}
        {{ 变量 }}
{% endfor %}

例如:
{% for foo in name_list %}
        {{ foo }}
{% endfor %}

便捷用法:

遍历每一个元素:  写个for,然后 tab键自动生成for循环的结构,循环很基础,就这么简单的用,没有什么break之类的,复杂一些的功能,你要通过js。可以利用{% for obj in list reversed %}反向完成循环。

遍历一个列表

<ul>
    {% for foo in name_list %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

反向遍历一个列表

{% for foo in name_list reversed %}
        <li>{{ foo }}</li>
{% endfor %}

遍历一个字典:有items、keys、values参数

<ul>

    {% for key,value in dic.items %}
        <li>{{ key }}: {{ value }}</li>
    {% endfor %}

    {% for key in dic.keys %}
        <li>{{ key }}</li>
    {% endfor %}

    {% for value in dic %}
        <li>{{ value }}</li>
    {% endfor %}

</ul>

forloop的使用

模版系统给我们的for标签还提供了forloop的功能,这个就是获取循环的次数,有多种用法:

forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(从1开始)
forloop.revcounter0        当前循环的倒序索引值(从0开始)
forloop.first              当前循环是不是第一次循环(布尔值)
forloop.last               当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示外层循环的计数等

我们通过name_list示例:

<ul>
    {% for foo in name_list %}

        <li>{{ forloop.counter }} {{ foo }}</li>
    {% endfor %}

    {% for foo in name_list %}
        {{ forloop.counter0 }}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

 forloop.counter0数字与元素不在一行是因为我html标签摆放的问题,与方法无关。

{% for foo in name_list %}
    <li>{{ forloop.revcounter }} {{ foo }}</li>
 {% endfor %}

{% for foo in name_list %}
        <li>{{ forloop.first }} {{ foo }}</li>
{% endfor %}

 

forloop.first、forloop.last多用于下面我们讲到if判断条件。 forloop.parentloop我们讲到if标签在演示。

for...empty...组合 

如果遍历的可迭代对象是空的或者就没有这个对象,利用这个组合可以提示用户。

<ul>        
    {% for foo in aaa %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查询的内容啥也没有</li>
    {% endfor %}

    {% for foo in value %}
        <li>{{ foo }}</li>
    {% empty %}
        <li>查询的内容啥也没有</li>
     {% endfor %}
</ul>

if标签

基本语法:

 {% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。

{% if 条件 %}
    结果  <!--不满足条件,不会生成这个标签-->
{% elif 条件 %}
    结果
{% else %}  <!--也是在if标签结构里面的-->
    结果
{% endif %}

elif和else一定要在if endif标签里面,设置多个elif或者没有elif、有没有else都可以。

示例:

{% if dic.age > 18 %}
    <p>可以干点儿该做的事儿了~</p>
{% elif dic.age < 18 %}
    <p>小孩子,懂什么</p>
{% else %}
    <p> 风华正茂的年龄~</p>
{% endif %}

条件也可以与过滤功能配合

% if name_list|length > 4 %}
    <p>列表元素超过4个</p>
{% else %}
    <p>列表元素太少!</p>
{% endif %}

条件也可以加逻辑运算符

if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。

{% if name_list|length > 4 and '王阔' in name_list %}
    <p>条件都满足</p>
{% endif %}

with

使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名,当你需要使用一个“昂贵的”方法(比如访问数据库)很多次的时候是非常有用的,记住!等号的左右不要加空格!!

{% with total=business.employees.count %}
    {{ total }} <!--只能在with语句体内用-->
{% endwith %}

{% with business.employees.count as total %}
    {{ total }}
{% endwith %}

forloop.first、forloop.last、forloop.parentloop

当时讲for循环时有三个方法,我们没有尝试,因为需要与if条件配合。

forloop.first

{% for foo in name_list %}
    {% if forloop.first %}
        {{ foo }}
    {% else %}
        <p>只有第一次循环打印</p>
    {% endif %}
{% endfor %}

forloop.parentloop : 一定注意!他是返回本此循环的外层循环对象,这个对象可以调用forloop的各种方法进行获取相应的数据。

测试此方法,我们要在views函数中加一个数据类型:lis = [['A', 'B'], ['C', 'D'], ['E', 'F']]

{% for i in lis %}
     {% for j in i %}
{#         <p>{{ forloop.parentloop }}</p>#}
        <p>{{ forloop.parentloop.counter }} {{ forloop.counter }} {{ j }}</p>
     {% endfor %}
{% endfor %}

注意事项

1. Django的模板语言不支持连续判断,即不支持以下写法:

{% if a > b > c %}
...
{% endif %}

 2. Django的模板语言中属性的优先级大于方法(了解)

def xx(request):
    d = {"a": 1, "b": 2, "c": 3, "items": "100"}
    return render(request, "xx.html", {"data": d})

如上,我们在使用render方法渲染一个页面的时候,传的字典d有一个key是items并且还有默认的 d.items() 方法,此时在模板语言中:

{{ data.items }}

默认会取d的items key的值。

csrf_token标签

  这个标签是不是非常熟悉?之前我们以post方式提交表单的时候,会报错,还记得我们在settings里面的中间件配置里面把一个csrf的防御机制给注销了啊,本身不应该注销的,而是应该学会怎么使用它,并且不让自己的操作被forbiden,通过这个标签就能搞定。

接下来我们重写构建一个流程:

urls、views、html:

urlpatterns = [
    url(r'^login/',views.login),
]


from django.shortcuts import render, HttpResponse, redirect
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        print(request.POST)  # 这里我们就不进行验证了,只是研究csrf_token
        return HttpResponse('登录成功!!!')


<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
<h3>登录页面</h3>

<form action="" method="post">
    用户名:<input type="text" name="username">
    密码:<input type="text" name="password">
    <button>提交</button>
</form>
</body>
</html>
View Code

注意:post、get请求不一定非要写在一个视图函数中,那么我们之前为什么写在一个视图函数中,就是因为post、get都是请求相关的消息,所以我们都用一个视图函数去处理,你也可以写在两个视图函数中,自己可以尝试一下。

此时我们开启项目,通过浏览器进行访问,当你点击提交时,浏览器返回这样一个画面:

 

这就是csrf安全机制给你做出的响应。我们之前都是找到settings给对应的中间件注释掉,但是现在我们要研究一下如何遵循这个安全机制,只要我们在你的form表单中加上一个csrf_token标签就行了。

 

那么这个标签起到了什么作用呢?他的原理是什么?我们再次刷新这个页面,看一下源码:

当我们加上此标签之后,再次发出get请求,render返回给我们的页面中多了一个隐藏的input标签,并且这个标签里面有个一键值对:

键:name,值:随机的一堆密文。 那么这个是干什么用的呢?其实他的流程是这样的:

第一次发送get请求,在views视图函数返回给你login.html页面之前,render会将csrf_token标签替换成一个隐藏的input标签,此标签的键值对就是上面那个键值对并且Django将这个键值对保存在内存;当你再次进行post请求时,他会验证你的form表单里面的隐藏的input标签的键值对是否与我内存中存储的键值对相同,如果相同,你是合法的提交,允许通过;如果不相同,则直接返回给你forbidden页面。这就好比说,第一次get请求,他返回你一个盖戳的文件,当你在进行post请求时他会验证是不是那个盖戳的文件。他的目的就是你提交post请求时,必须是从我给你的get请求返回的页面提交的。为什么这么做呢?是因为有人登录你的页面时是可以绕过的get请求返回的页面直接进行post请求登录的,比如说爬虫。直接通过requests.post('/login/')直接请求的,这样是没有csrftoken的,这样就直接拒绝了。

说了这么多,目的就是一个:验证当你post提交请求时,是不是从我给你(你通过get请求的)页面上提交的数据。

那么接下来,我们写一个简单的爬虫验证一下:

import requests

ret = requests.post('http://127.0.0.1:8000/login/',
                    data={'username': 'taibai', 'password': '123'}
                    )

print(ret.content)

如果你保留这这个验证,则通过爬虫是登录不成功的,只能返回你一个forbidden的html页面。

如果你将settings那个中间件注释掉,那么就可以成功访问了:

五、模版继承

1. 引子

什么是模版继承?将这两个字拆开我们都清楚,模版就是django提供的用于html发送浏览器之前,需要在某些标签进行替换的系统,而继承我们立马就会想到这是面向对象的三大特性之一。其实模版继承就是拥有这两个特性的模版的高级用法。先不着急直接上知识点,我们用一个例子引出模版继承。

我们经常访问一些网站,你会发现只要是一个网站的多个html页面,他们有一部分是一模一样的,剩下的部分是本页面独有的。比如我们经常使用的博客园:

这个是我博客园的首页:

 

 

然后我们随机点一篇博客进去:

个人博客园设定了博客园的样式之后,你所有的博客都是按照这个样式创建的,在我选择的主题这里:导航条和侧边栏布局是一样的。再比如如果是公司内部的xx管理系统,更是如此。我们打开bootstraps网站(起步):

一般管理系统都是这样的布局,这个就是固定的导航条和侧边栏。无论我点击侧边栏里的那个按钮,这两部分不会更换只会改变中间的内容。

那么接下来我们实现一个这样的布局。

我们要准备4个html页面:base.html、menu1.html、menu2.html、menu3.html,这四个页面的导航条与左侧侧边栏一样,每个页面对应一个url。并且每个页面的左侧侧边栏菜单一、菜单二、菜单三可以实现跳转:跳转到menu1.html、menu2.html、menu3.html三个页面。而顶端导航条只是样式即可。接下来借助于Django,我们实现这四个页面并对应urls可以跑通流程。

urls:

urlpatterns = [
    url(r'^base/', views.base),
    url(r'^menu1/', views.menu1),
    url(r'^menu2/', views.menu2),
    url(r'^menu3/', views.menu3),

]

views:

def base(request):
    return render(request, 'base.html')


def menu1(request):
    return render(request, 'menu1.html')


def menu2(request):
    return render(request, 'menu2.html')


def menu3(request):
    return render(request, 'menu3.html')

html:

base.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆儿堂</a>
    <a href="">局部护理</a>
    <a href="">关于我们</a>
    <a href="">预约电话</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜单一</a></li>
        <li><a href="/menu2/">菜单二</a></li>
        <li><a href="/menu3/">菜单三</a></li>
    </ul>
</div>

<div class="menu">
    首页
</div>


</body>
</html>


menu1.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆儿堂</a>
    <a href="">局部护理</a>
    <a href="">关于我们</a>
    <a href="">预约电话</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜单一</a></li>
        <li><a href="/menu2/">菜单二</a></li>
        <li><a href="/menu3/">菜单三</a></li>
    </ul>
</div>

<div class="menu">
    菜单一首页
</div>


</body>
</html>

menu2.html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆儿堂</a>
    <a href="">局部护理</a>
    <a href="">关于我们</a>
    <a href="">预约电话</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜单一</a></li>
        <li><a href="/menu2/">菜单二</a></li>
        <li><a href="/menu3/">菜单三</a></li>
    </ul>
</div>

<div class="menu">
    菜单2首页
</div>


</body>
</html>

menu3.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆儿堂</a>
    <a href="">局部护理</a>
    <a href="">关于我们</a>
    <a href="">预约电话</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜单一</a></li>
        <li><a href="/menu2/">菜单二</a></li>
        <li><a href="/menu3/">菜单三</a></li>
    </ul>
</div>

<div class="menu">
    菜单三首页
</div>


</body>
</html>
base menu1 menu2 menu3

上面我的的需求虽然完成了但是有没有什么没问题?你会发现html重复代码太多了,如果领导不瞎,最晚后天你就可以领盒饭了。

2. 母版继承示例

所以针对与我上面的需求,很显然你现在所拥有的知识点已经解决不了了。那么接下来就是本节的重点:模版继承。根据这个知识点名字的特点,我们应该想到,我们可不可以建立一个父类,然后让所有的子孙类都继承我的父类,这样我就可以节省很多代码了,让我的代码非常的清新、简单。这里的父类就不叫父类了他有一个专有名词:母版。

接下来我们先创建一个母版。(最好不要将base页面直接作为母版,母版就是只设置公用的部分,其他一概不要。)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆儿堂</a>
    <a href="">局部护理</a>
    <a href="">关于我们</a>
    <a href="">预约电话</a>
    <input type="text">搜索
</div>

<div class='sidebar'>
    <ul>
        <li><a href="/menu1/">菜单一</a></li>
        <li><a href="/menu2/">菜单二</a></li>
        <li><a href="/menu3/">菜单三</a></li>
    </ul>
</div>

<div class="menu">

</div>
</body>
</html>

接下来,我们将base menu1 menu2 menu3这四个页面全部清空,然后在每个页面的最上面加上这么一行代码:

{% extends 'master_edition.html' %}

这个就实现了我要继承母版master_edition.html。

3. 自定制效果

现在已经完成了继承母版,这个只是减少了重复代码,还没有实现每个页面自定制的一些内容,如果你想要实现自定制的内容怎么做?类似于模版系统你是不是应该在母版的具体位置做一个标识,然后在自己的页面对应的地方进行自定制?那么这个类似于%占位符的特定的标识叫做钩子。这几个页面只是在menu div不同,所以我们就在这里做一个钩子就行了。

在母版的html对应的位置:

<div class="menu">
    {% block content %}
    {% endblock %}
</div>

block  endblock就是对应的钩子,content是此钩子的名字。

然后在base menu1 menu2 menu3的页面上(此时就以base页面举例):

{% block content %}
     base页面首页
{% endblock %}

这样你的代码是不是非常的简单了? 

那么我们不仅可以在对应的html标签设置钩子,还可以在css、js设定对应的钩子。所以母版继承中一般设定钩子的地方就是三部分: html、css、js。

以css举例:

我们将base页面的顶端导航条的背景颜色设置成红色:

首先现在母版页面对应的位置设置钩子:

 <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }
        .sidebar{
            background-color: #b2b2b2;
            color: #435dff;
            width: 20%;
            height: 1000px;
            float: left;
        }
        .sidebar ul{
            margin: 0;
        }

        .menu{
            width: 80%;
            float: right;

        }
        {% block nav %}
        {% endblock %}
    </style>

然后找到base页面:

{% block nav %}
    .nav{
    background-color: red;
    }
{% endblock %}

这样你的base页面的导航条就变成红色啦!

js的母版继承我们就不再这里尝试了,自己私下可以尝试一下。

4. 保留母版内容并添加新特性

还有一个情况我们也会遇到,就是我既要留住母版的内容,又要在自己的html添加一些新的标签。这个我们在面向对象时是不是也遇到过?当时用什么方法既执行父类方法又可以执行子类方法?super!在这里我们也用super!

母版html:

<div class="menu">

    {% block content %}
        <div>这是母版测试页面</div>
    {% endblock %}
</div>

base.html:

{% block content %}
    {{ block.super }}
     base页面首页
{% endblock %}

在钩子里面加上{{ block.super }}即可。

5. 注意

  • 如果你在模版中使用 {% extends %} 标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作,模板渲染的时候django都不知道你在干啥。

  • 在base模版中设置越多的 {% block %} 标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。

  • 如果你发现你自己在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。

  • If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }} will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.  将子页面的内容和继承的母版中block里面的内容同时保留。

  • 不能在一个模版中定义多个相同名字的 block 标签。
  • 结束一个钩子时可以标注此钩子的名字,比如 {% endblock content%}这样就可以结束此钩子不至于将其他的block钩子一并结束。

 六、组件

组件就是将一组常用的功能封装起来,保存在单独的html文件中,(如导航条,页尾信息等)其他页面需要此组功能时,按如下语法导入即可。 这个与继承比较类似,但是‘格局’不同,继承是需要写一个大的母版,凡是继承母版的一些html基本上用的都是母版页面的布局,只有一部分是自己页面单独展现的,这好比多以及排布好的多个组件。

{% include 'xx.html' %}

比如我们写一个nav导航条组件:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }

    </style>

</head>

<body>

<div class="nav clearfix">
    <a href="">普通洗浴</a>
    <a href="">盆儿堂</a>
    <a href="">局部护理</a>
    <a href="">关于我们</a>
    <a href="">预约电话</a>
    <input type="text">搜索
</div>



</body>
</html>

然后我们在创建流程去使用我们的组件:

urls:
url(r'^component/', views.component),


views:
def component(request):
    return render(request,'component.html')

component页面:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
{% include 'nav.html' %}
<h1>你好,世界!</h1>
<div>我是componet页面</div>


</body>
</html>

组件和插件的区别:

组件是提供某一完整功能的模块,如:编辑器组件,QQ空间提供的关注组件 等。

而插件更倾向封闭某一功能方法的函数。

这两者的区别在 Javascript 里区别很小,组件这个名词用得不多,一般统称插件。

 七 自定义标签和过滤器

 我们都学过了Django给我们提供的标签和过滤器,但是这些不能满足我们日常开发的所有需求,所以DJango为我们提供了一个很牛逼的功能就是我们可以自定义标签和过滤器。接下来我们看看这个怎么玩。 

1. 自定义过滤器

  Django的模版系统给我们提供了很多过滤器,比如切割、全大写、获取元素个数,联合join,安全性质的safe等等,这些都是对数据(尤其是字符串类型的数据)进行加工,但是这些都是模版系统提供给我们的,不能满足应对工作中的所有需求,所以Django给我们提供了口子:让我们可以自己定义过滤器,这样我们开发起来更加的灵活。接下来我们讲解如何自己定义过滤器。

1. 在应用的目录下创建templatetags文件夹(文件夹必须这样命名)。

 

2. 在templatetags文件下创建py文件(任意命名即可)。

我这里命名为network_language.py。

 3. 在py文件中引入template模块并创建注册器。

from django import template


register = template.Library()

4. 自定义过滤器函数。

下面讲到的自定义标签以及simple_tags,与前三步一模一样,并且都是在本文件中操作。

@register.filter
def adjoin(v1):
    pass

# adjoin就是我们自定义的过滤器函数。
# 此函数必须被register.filter装饰,这样才能生效。
# 我们现在先不做任何功能,一会在定义功能。

完成上面这个四步,我们就算是自定义了一个过滤器函数。那么如何使用呢?模版系统自带的过滤器如何使用?他是通过对render要渲染的变量加上管道符,然后在管道符后面加上具体的过滤器的名字,这样就可以对变量进行简单加工了。我们自定义的过滤器也是这样用法。接下来我们重新设计一个url流程:

urls:

    url(r'^templatetag/', views.template_tag),

views:

def template_tag(request):
    name = '天琪'
    return render(request,'templatetag.html', {'name': name})


html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">


</head>
<body>
{% load network_language %}  # 引入templatetags文件夹下面的network_language.py文件
{{ name|adjoin }}  # name就是render渲染的变量,adjoin就是自定义的过滤器的函数名

</body>
</html>

此时你自定义的过滤器里面的adjoin代码:

from django import template


register = template.Library()


@register.filter
def adjoin(v1):
    return v1 + '噢里给'

  我们自定义的过滤器就是完成了简单的功能:给name变量拼接了一个网络用语:奥利给。那么整个他的执行流程是什么呢?

  首先浏览器发送请求至url,url通过请求路径找到对应的views视图函数template_tag,然后执行到return时先不会给浏览器马上返回html页面,而是通过render进行模版渲染,当渲染到 {% load network_language %}这一行,render会自动找到templatetags文件夹下面的network_language文件,然后在向下读取,执行自定义过滤器adjoin函数,并且将变量name对应的'天琪'传递给adjoin函数的形参v1,得到返回值'天琪噢里给'之后,替换{{ name|adjoin }},渲染完毕之后,将此html页面返回给浏览器。

  这个就是整体的过程。那么有人可能会问了,我自定义的过滤器函数只能设定一个形参么?不是的,自定的过滤器函数设定形式参数至多两个。接下来我们演示一个两个参数的。

# html:

{% load network_language %}
{{ name|_adjoin:'微笑的面对它' }}


# templatetags/network_language.py:

@register.filter
def _adjoin(v1, v2):
    return v1 + v2 + '噢里给!'

第二个参数是通过过滤器的冒号后面传递的。

至此,我们自定义过滤器这部分就已经给大家讲完了,可以练习一下~

2. 自定义标签

我们学习完自定义过滤器之后,再来看看自定义标签,自定义标签与自定义过滤器差不多,前三个步骤是一样,接下来就是定义自定义标签函数不同:

views:我们还是使用template_tag函数,只是增加了一些变量。

def template_tag(request):
    name = '天琪'
    content = '我太难了'
    status = '在线学习'
    return render(request,'templatetag.html', locals())

html:

{% load network_language %}
{% all_join name status content '不要怕'%}

network_language.py:

@register.simple_tag
def all_join(v1, v2, v3, v4):
    return v1 + v2 + v3 + v4 + '噢里给!'

其实自定义标签和自定义过滤器差不多,都是我们设计一些函数,增加一些自定制的功能,也有一些细微的区别,我们总结一下:

  1. 自定制过滤器至多只能接受两个参数,而自定制标签可以接受多个参数,这样看来自定制标签更加灵活。
  2. 自定制过滤器必须依赖于 变量,他的目的就是对变量进行加工{{ name|adjoin }} ;而自定制标签可以不依赖于变量直接使用{% all_join %}.
  3. 自定制过滤器可以用在if、for等语句后,自定制标签不可以。
{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

3.inclusion_tag

这个标签不同于上面的两种,这个标签是将一段html代码插入到我们html页面中,有点儿类似于组件,但是比组件还灵活。上一节我们讲组件时,我们将自己设计的一个顶端的导航条nav.html作为一个组件,然后让另一个页面去使用,还记得这个例子吧?这次我们用inclusion_tag去展示一下,对比分析一下这两个有什么不同。

 

views:我们加了一个show_list变量

def template_tag(request):
    name = '天琪'
    content = '我太难了'
    status = '在线学习'
    show_list = ['课程分类', '在线学习', '解答疑问', '讲师介绍', '网站介绍']
    return render(request,'templatetag.html', locals())

teamplatetag.html: 我设计在network_language中自定制inclusion标签函数,函数名为nav_data,然后将show_list传递进去。

{% load network_language %}
{% nav_data show_list %}

network_language.py:

@register.inclusion_tag('nav.html')
def nav_data(argv):
    argv += [datetime.datetime.now()]return {'data': argv}

此时我在这个自定制函数中对show_list进行了加工,并且通过return(这里的return有点儿类似于render)将数据传递到nav.html页面对应的位置。

nav.html:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>

    <style>

        body{
            margin: 0;
            padding: 0;

        }

        .nav{
            background-color: black;
            color: #eaeaea;
            height: 30px;
            width: 100%;
        }
        .clearfix{
            content: '';
            display: block;
            clear: both;
        }

    </style>

</head>

<body>

<div class="nav clearfix">
    {% for foo in data %}
        <a href="">{{ foo }}</a>
    {% endfor %}
    <input type="text">搜索
</div>



</body>
</html>

然后将数据展示到导航条上面相应的位置上,最终的效果如下:

然后我们对比分析组件,组件是只能将html里面的一部分(导航条、左右侧栏等)原封不动被引入到相应的其他html页面中,而

inclusion_tag这个自定制标签,不仅可以完成组件的功能,而且还可以操作被导入的html组件,使其更加灵活。

 八、静态文件

截止到目前为止,我们还没有应用到css、js等文件,只是使用了html页面,现在我们的页面可算是不要太丑了。所以项目中我们肯定是要引入静态文件的,什么是静态文件?静态文件就是我们之前学过的css、js、图片文件、视频文件等等,我们在html想要使用他们就是引用静态文件,之前我们写页面是需要通过标签引入静态文件,并且需要各种路径的配置,现在我们利用Django做一个简单的配置就可以直接引入静态文件了。

我们在重新创建一个项目static_pro:

url:
url(r'^index/', views.index),

views:
def index(request):
    return render(request, 'index.html')

html:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html> 

  我们的静态文件不只是一个文件,这里面包含很多的文件,所以我们应该创建一个文件夹,将所有的静态文件都放置在此目录中,所以,我们在整个项目的目录下创建一个专门放置静态文件的文件夹:我们取名'jingtaiwenjian'(为了教学使用,工作中决不允许出现拼音形式),然后我们在此静态文件里面创建一个css1.css文件,专门放置css代码。

 

接下来我们如何引用我们的css1静态文件呢?按照我们之前的操作,在html页面的link标签中引入此文件的相对路径,是行不通的。

引入静态文件方式一

我们在Django框架中,就遵循Django规范,那么Django是如何配置css。

1. 在settings配置文件中,配置静态文件路径

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'jingtaiwenjian'),
] 
#
STATICFILES_DIRS 此常量名设置可以任意构建,一般约定俗成都是这样命名的。

2. 在index.html文件中,引入css文件

<link href="/static/css1.css" rel="stylesheet">

注意:这里面的路径如果你写的是/jingtaiwenjian/css1.css,这样css文件是引用不到的,你要像我上面写的一样。那么为什么呢?这个'static'是什么东西?接下来跟你简单聊聊这个static。

别名static

  这个static是给静态文件(比我们的项目的静态文件的名字为:jingtaiwenjian)起的别名,他在哪里可以配置呢?在我们settings里面就可以配置:

 

 Django默认给静态文件起了别名,当然你也可以自己设定别名,比如:

STATIC_URL = '/olddriver/'

如果你要是更改这个别名,你在index.html中的引用就需要更改,所以一般我们不去更改别名:

这样也可以成功访问,那么Django为什么要给静态文件起别名呢?这样有什么好处呢?

  1. 使用别名代替真实的静态文件名字,防止有人对你的静态文件发起恶意攻击,保证静态文件的安全。

    因为请求一个web网站时,都会对css、js发起请求,并且这些请求都是可以访问到的url:

    

 

     我们访问一下这个URL:

    

    再比如我们打开京东,看一下京东的网络请求:

    

 

 

     通过这个网址我们可以拿到他的css代码:

    

 

 

    这种css代码是压缩混淆处理的,有些内容你是看不懂的,就相当于加密了,但是我们可以获取到。虽然可以让你获取到,但是由于你的static是别名,他是不能对你的静态文件发起攻击的。

  2. 使用别名代替真实的静态文件名字,保持了代码的拓展性。

    如果以后遇到了需求,必须将静态文件的名字改掉(比如我们本次取的名字jingtaiwenjian就不规范),如果没有别名的话,你必须将所有的引用静态文件的相关link路径都必须改掉,这样太麻烦了,但是我们有了别名的概念,无论你以后如何更改静态文件的名字,都不会造成影响的。

    

我将静态文件名字改成了staticfile,当我再次访问index页面时,依然还是可以访问的。

 

请求路径还是别名作为路径。

所以,针对于上述情况,起别名还是又好处的。

引入静态文件方式二

  我们还可以通过第二种方式引入静态文件,第一种方式是有漏洞的,假如我们已经完成了一期项目,你的项目中已经创建了多个html文件,并且都一直通过第一种方式引入静态文件,此时你跳槽了,其他的人接收你的项目,他没有注意到你的别名为static,而当它二期项目已经进展了很长时间发现你的static别名与二期项目中的某些url冲突了,这时必须要更改你的别名才可以解决问题,那么怎么做?因为你通过第一种方式引入的静态文件,如果你把别名改了,你的很多html页面的link路径都必须要更改,所以这种方式还是有弊端的。那么接下来我介绍第二种引入静态文件的方式,就可以完美避开这个问题。

settings:

STATIC_URL = '/static/'

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'staticfile'),
]

index.html:

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="{% static 'css1.css' %}" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html>

通过这种方式引入静态文件,无论你更改别名,都没有影响,因为他是根据settings你配置的静态文件的路径找到你的静态文件的,不依赖于别名,所以这种方式也是我比较喜欢的一种方式。

了解:如果某个静态文件路径过长(比如一个图片)并且此图片多次被用到,我们每次引用这个图片写路径很麻烦时,这样可以给这个图片路径起个别名,以后在引用起来就简单了:

{% load static %}
{% static 'css1.css' as c %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="{% static 'css1.css' %}" rel="stylesheet">
    <link href="{{ c }}" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>

引入静态文件方式三

我们也可以通过 {% get_static_prefix %}方式引入静态文件,这样你的文件分隔符可以省略不写了。

{% load static %}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
    <link href="{% get_static_prefix %}css1.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html>

了解:如果某个静态文件路径过长(比如一个图片)并且此图片多次被用到,我们每次引用这个图片写路径很麻烦时,这样可以给这个图片路径起个别名,以后在引用起来就简单了:

{% load static %}
{% get_static_prefix as c %}

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap 101 Template</title>
{#    <link href="{% static 'css1.css' %}" rel="stylesheet">#}
{#    <link href="{{ c }}" rel="stylesheet">#}
    <link href="{{ c }}css1.css" rel="stylesheet">


</head>
<body>
<h1>你好,世界!</h1>


</body>
</html>

 

posted @ 2020-02-16 10:13  太白*金星  阅读(1767)  评论(0编辑  收藏  举报