• Django基础之


    一 引子

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

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

      模板渲染的官方文档

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

      {{  }}和 {% %}

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

    二、变量

    2.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.2 语法

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

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

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

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

    2. 3 万能的点 . 

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

    views:

    def index(request):
      
        name_list = ['王阔', '天琪', '傻强', '志晨', '健身哥']
        dic = {'classname': '人工智能', 'since': 2019}
        lis = [1, ['冠状病毒', '武汉加油'],3]
    
        # 使用locals是用于测试,实际生产环境中是不可以的。
        return render(request, 'index.html', locals())

    locals()用法:locals()可以直接将函数中所有的变量全部传给模板。当然这可能会传递一些多余的参数,有点浪费内存的嫌疑。

    tml:

    <!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>    

    三、过滤器

    3.1 什么是过滤器

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

    3.2 语法

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

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

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

      注意事项:

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

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

    3.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>"

      很多网站,都会对你提交的内容进行过滤,一些敏感词汇、特殊字符、标签、黄赌毒词汇等等,你一提交内容,人家就会检测你提交的内容,如果包含这些词汇,就不让你提交,其实这也是解决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即可。

    4.1 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>

     4.2 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的值。

    4.3 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>

    注意: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那个中间件注释掉,那么就可以成功访问了:

    五、模版继承

    5.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重复代码太多了,如果领导不瞎,最晚后天你就可以领盒饭了。

    5.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。

    5.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的母版继承我们就不再这里尝试了,自己私下可以尝试一下。

    5.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.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;
                 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为我们提供了一个很牛逼的功能就是我们可以自定义标签和过滤器。接下来我们看看这个怎么玩。 

    7. 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 + '噢里给!'

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

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

    7. 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 %}

    7.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;
                 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标签中引入此文件的相对路径,是行不通的。

    8.1 引入静态文件方式一

     

    我们在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。

    8.1.1 别名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页面时,依然还是可以访问的。

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

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

    8.2 引入静态文件方式二

      我们还可以通过第二种方式引入静态文件,第一种方式是有漏洞的,假如我们已经完成了一期项目,你的项目中已经创建了多个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>

    8.3 引入静态文件方式三

      我们也可以通过 {% 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>
  • 相关阅读:
    Maven Docker镜像使用技巧
    Dockerfile 最佳实践
    无状态服务
    Docker 镜像加速器
    如何设置Docker容器中Java应用的内存限制
    k8s的容器监测探针
    (部署)使用kubernetes的deployment进行RollingUpdate
    linux下brctl配置网桥
    代码高亮插件
    Docker容器
  • 原文地址:https://www.cnblogs.com/dongye95/p/13423469.html
Copyright © 2020-2023  润新知