Django模板详解!如何利用Django写出前端界面?Django全栈工程师必看系列! ✧*。٩(ˊᗜˋ*)و✧*。 Django初体验

576 阅读13分钟

Django


在这里插入图片描述

Django模板简介

Django使用的是MVT模型,而模板就是MVT中的T: templates 模版,也是Django区别与常规的MVC模型不同的地方。模板是前后端交互的一个桥梁,Django就是依靠模板,将参数传入前端页面。下列我将介绍,模板一般放置于那些位置,如何让.html和.py能相互交互。

模板路径

模板所在路径,并非绝对的规定,只是一种大部人人都遵守的守则。一般模板都会放在名为templates的文件中,而这个文件,可以放在两个位置,分别是项目根目录中,或者放在APP的根目录中(一般项目根目录中的页面优先级会大于APP中的)
(如果使用pycharm创建项目,根目录中的templates会被自动创建,且os.path.join(BASE_DIR, 'templates')也会自行添加,但APP中的templates文件需要手动注册,在列表INSTALLED_APPS添加APP名即可,不过前提是要确定'APP_DIRS': True,(True为默认选项)
在这里插入图片描述

导入HTML

在Python中导入HTML也有两种方式。下述信息不在包含如何路由等信息,如何路由可以在Django路由机制详解中查看

  1. 方式一: 使用from django.template.loader import render_to_string
from django.template.loader import render_to_string
from django.http import HttpResponse


# 在此处创建视图。
def home(request):
    html = render_to_string('home.html')
    return HttpResponse(html)

这种方式使用较少,暂不详细讲解了,在绝大部分情况下我们都会使用下列方式导入HTML
2. 方式二: 使用from django.shortcuts import render

from django.shortcuts import render


# 在此处创建视图。
def home(request):
    return render(request, 'home.html')

相比于第一种方式,我们发现第二种方式会让代码显得更加简洁,可读性更好。

模板变量

变量我们可以简单看成传值,使用模板进行传值操作,在Python中,需要使用到context属性,这是render与render_to_string都具有的属性。在HTML中加入{{字典键}}
模板变量Python部分: 使用context=字典(context必须传入字典dict的数据类型)的方式,如果你需要传入列表或元组或者其他类型的值,将其当成字典键传入。此处字典的键,甚至可以穿入一个类

from django.shortcuts import render


class MyName:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        """"""
        return '我是类方法'


xunmi = MyName('我是类属性')

data = {
    '昵称': '寻觅',
    '字典': {
        '键1': 1,
        '键2': 2,
    },
    '列表': [
        'one',
        2,
        '三'
    ],
    '类': xunmi
}


# 在此处创建视图。
def home(request):
    # html = render_to_string('home.html')
    return render(request, 'home.html', context=data)

模板变量HTML部分: 接收使用{{字典键}}的方式,如下(如果你是一个前端,你可以将需要后端接收变量的地方使用{{}}事先预留,然后想后端询问字典键填入即可事先传值)

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <h1>这是{{昵称}}首页</h1>
    <h1>{{字典.keys}}{{字典.values}}可以使用keys和values等属性</h1>
    <h1>这是列表:{{列表}}</h1>
    <h1>这是列表第一个元素:{{列表.0}}</h1>
    <h1>这是列表第二个元素:{{列表.1}}</h1>
    <h1>这是列表第三个元素:{{列表.2}}</h1>
    <h1>这是一个类属性:{{类.name}}</h1>
    <h1>这是一个类方法:{{类.get_name}}</h1>
</body>
</html>

在这里插入图片描述

模块语言(DTL)

在写上述变量时候,我们如果需要取键为列表或其他可迭代对象时会比较麻烦,如果能使用for语句那将会如虎添翼,

添加代码模板

代码模板纯属是方便之后使用,Django的模板在pycharm专业版中自行携带,如果你是用的并非是专业版,可以在设置中执行添加
在这里插入图片描述
我们可以添加几个常用的模板,比如说if和for

# if模板
{% if $VARIABLE$ %}
$END$
{% endif %} 
# for模板
{% for $VAR$ in $COLLECTION$ %}
$END$
{% endfor %}

添加好模板后,我们如果需要使用只要输入相应的缩写,然后在按下tab键即会自动补齐

for与if标签

for与if和Python使用方式大相径庭,只是需要在使用时,用{% 标签 %}进行包裹即可,使用比较简单,这里就合并一起进行讲解。HTML和Python中唯一不同的是,在结束时候我们需要使用一个结束标签来标记,其中if的结束标签为{% endif %}而for的结束标签为{% endfor %}
在for循环中,模板语言提供了一些变量可供使用

变量作用
forloop.counter当前循环的次数(从1开始计算)
forloop.counter0当前循环的下标(从0开始计算)
forloop.revcounter剩余循环的次数(从总循环次数开始计算)
forloop.revcounter0剩余循环的次数(从总循环次数-1开始计算)
forloop.first是否为第一次循环
forloop.last是否为最后一次循环
forloop.parentloop输出父循环的信息(循环次数,剩余次数,是否为第一次、最后一次循环,如果其父循环存在父循环,则会一同输出)
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    {% for foo in 列表 %}
        {% for foo1 in 字典 %}
            {% for foo2 in 昵称 %}
                <p>{{ forloop.parentloop }}</p>
                <p>{{ foo2 }}</p>
            {% endfor %}
            <p>{{ foo1 }}</p>
        {% endfor %}
        
        {% if foo == '2' %}
            <h3>我是加粗的二货</h3>
        {% else %}
            <p>我是列表:{{ foo }}</p>
        {% endif %}
    {% endfor %}
</body>
</html>

再次需要注意的是for循环语句中的break与continue无法使用,且没有whlie循环
在这里插入图片描述

url标签

url标签将会是我们非常常用的标签之一,虽然我们可以将标签写死在页面中,但聪明的程序员绝对不会这样干,因为这样既不利于后期的维护。
这里url类似与Django路由中的反转机制,需要我们现在path中写入name,然后在基于此name进行跳转,在跳转同时,我们也可以携参跳转,两种传参方式下列代码中也都有演示。
url标签的Python代码
(Django项目APP中urls.py文件中代码):

from django.urls import path
from . import views

app_name = 'front'

urlpatterns = [
    path('home/', views.home, name='home'),
    path('home/<items>/', views.home, name='home'),
    path('index/', views.index, name='index'),
]

Django项目APP中views.py文件中代码

from django.shortcuts import render


# 在此处创建视图。
def home(request, items='默认值'):
    # html = render_to_string('home.html')
    data = {'参数': items}
    return render(request, 'home.html', context=data)


def index(request):
    uid = request.GET.get('id')
    data = {'参数': uid}
    return render(request, 'index.html', context=data)

url标签的HTML中代码
home.html:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<h1>携带了"{{ 参数 }}"归来了</h1>
<a href="{% url 'front:index' %}">立刻马上来点我</a>
<br>
<a href="{% url 'front:index' %}?id=1">我是问号传参</a>

</body>
</html>

index.html:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>我就是一测试页面</title>
</head>
<body>

    <p>我真的就是一个测试页面,不要过来啊</p>
    {% if 参数 %}
        <h1>你居然将参数"{{ 参数 }}"插入了我</h1>
    {% endif %}
    <a href="{% url 'front:home' '位置参数' %}">快按我跳回去(位置参数)</a>
    <br>
    <a href="{% url 'front:home' items='关键字参数' %}">快按我跳回去(关键字参数)</a>
    
</body>
</html>

在这里插入图片描述

模板过滤器

过滤器的作用是在HTML页面中将传入的参数进行过滤,留下有用的,这个操作虽然可以在Python代码中完成,但有时候我们会发现在HTML中放入过滤器效果会更好,下面我会简单介绍一些常用的过滤器。如果你想了解全部的过滤器可以自行去from django.template import defaultfilters此库中查看过滤器源码。

add(拼接)

此拼接主要用于字符串(str)与整数(int),浮点型(float)会自动被转换成整数(int),小数部分自动省去。
将传入的值拼接到原值上,这个过滤器比较智能,如果两个变量都为数字,他会将其相加,无论是否为整数类型(如果是字符串类型的数字会将其自动转换为整数)如果是字符串则会进行字符的拼接。这里需要注意的是Python是强类型语言,所以不同字符类型的内容无法相加,比如说2+'亿'为空值,须写成'2'+'亿'!

<!--列表.1: '2' ; 字典.键2: 2-->
<p>我是add过滤器:'{{ 列表.1 | add:字典.键2 }}'或'{{ '3' | add:'亿' }}'</p>

join(拼接)

将可迭代对象进行拼接(比如说列表,元组,字符串),用法类似与Python中的join

<!--列表=['one', '2', '三']-->
<p>我是join过滤器:{{ 列表 | join:' - ' }}</p>

silce(切片)

和Python中列表等可迭代对象切片方式类似,格式都为(起始角标:结束角标:步长),比如下列切片就将其中的两个逗号成功切分出来了

<!--'切片使用': '你好,我是,寻觅'-->
<p>我是slice过滤器:{{ 切片使用 | slice:'2:6:3' }}</p>

cut(移除)

移除值中所以指定的字符串,类似于Python中的reqlace(args,’’)

<!--昵称: '寻觅'-->
<p>我是cut过滤器:'{{ 昵称 | cut:'寻' }}'</p>

date(时间)

将一个日期按指定格式格式化成字符串

<!--'时间': datetime.now(), datetime.now()是Python内置库中的方法,会读取当前时间,格式样式(/ :)可以自定义-->
<p>我是date过滤器:原始样式-'{{ 时间 }}'过滤器转换后-'{{ 时间 | date:'Y/m/d G:i:s' }}'</p>
常用格式字符描述示例
Y四位数字年份2020
m两位数字月份01-12
n一位或两位数字月份1-12
d两位数字天数01-31
j一位或两位数字天数1-31
g带前缀0的十二小时制小时01-12
h不带前缀0的十二小时制小时1-12
G带前缀0的二十四小时制小时00-23
H不带前缀0的二十四小时制小时0-23
i带前缀0的分钟00-59
s带前缀0的秒钟00-59

default(默认值)

当值被评估为Falst时会使用default所提供的默认值([],{},None,Falst等都会被评估为Falst)

<!--空值=None-->
<p>我是default过滤器:{{ 空值 | default:'此处无值' }}</p>

flast(返回首元素)与last(返回尾元素)

返回可迭代对象的第一个元素|最后一个元素

<!--列表=['one', '2', '三']-->
<p>我是first与last过滤器:列表第一个元素-{{ 列表 | first }} | 列表最后一个元素-{{ 列表 | last }}</p>

floatformat(移除)

使用四舍五入的方式格式化一个浮点类型(默认保留一位小数,如果小数部分全为0则只保留整数,可在其后指定保留位数)

<p>我是floatformat过滤器:{{ 123.456 | floatformat }}或{{ 123.456 | floatformat:2 }}</p>

length(获取长度)

获取长度(可以看成可迭代对象的迭代次数),和Python中的len()作用相同

<!--昵称='寻觅', 列表=['one', '2', '三']-->
<p>我是length过滤器:昵称-{{ 昵称 | length }} | 列表-{{ 列表 | length }}</p>

lower(强制转换为小心)与upper(强制转换为大写)

移除值中所以指定的字符串,类似于Python中的reqlace(args,’’)

<!--'大小写转换': "AaBb"-->
<p>我是lower与upper过滤器:强制小写-{{ 大小写转换 | lower }} | 强制大写-{{ 大小写转换 | upper }}</p>

safe(标记安全字符串)

Django会默认将传入的值进行转义,以防SQL注入攻击,如果传入字符被标记为安全字符串,则Django不会再将其转义。

<!--'js脚本': '<script>alert(document.domain)</script>'-->
<p>我是safe过滤器:此脚本是{{ js脚本 }},在刷新网页时会执行{{ js脚本 | safe }}</p>

striptags(删除标签)

删除值中所带的HTML标签

<!--'js脚本': '<script>alert(document.domain)</script>'-->
<p>我是striptags过滤器:{{ js脚本 | striptags }}</p>

truncatechars(指定长度)

指定长度后只能显示指定长度的内容,如果超过指定内容,则会已省略号代替(比如说指定字符长度为5,如果传入的字符串大于等于5,那么第5个字将会被省略号代替,之后的字符将会被省略)

<!--'js脚本': '<script>alert(document.domain)</script>'-->
<p>我是truncatechars过滤器:{{ js脚本 | truncatechars:10 }}</p>

在这里插入图片描述

模板优化

引入模板

一个站点中的有页眉和页尾,为了避免重复性导入,我们可以写一个模板,在其他页面引入即可,这样可以避免重复写大量代码的同时,也增强了代码的可维护性,如果进行继承,这里就需要用到{% include 'HTML页面' %}将父类引入即可。
如果需要传参,可以通过with进行{% include 'HTML页面' with 参数名='参数' %}

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>我就是一测试页面</title>
</head>
<body>
    {% include 'header.html' %}
    <p>我真的就是一个测试页面,不要过来啊</p>
    {% include 'footer.html' with 昵称='xunmi' %}
<!--footer.html中的代码为: <p>我是尾部footer.html文件{{ 昵称 }}</p>-->   
</body>
</html>

继承模板

继承和上述的引用想要达成的目的大致相同,都是为了减少重复代码而出现的,不过继承比引入更加适用,模板中的继承和Python的继承有异曲同工之处,在父模板中定义一些子模板需要用到的代码,然后子模板自己继承即可

父模板使用方式

在HTML中可以预留<% block 填充名 %> <% endblock %>标注子模板需要填充的区域。
列如:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>父模板页面</title>
</head>
<body>
    <h1>我是父模板</h1>
    {% block 填充一 %}
    <p>这里的代码默认会被覆盖</p>
    {% endblock %}
    <h2>上方是第一个子模板</h2>
    {% block 填充二 %}
    <p>这里的代码默认会被覆盖</p>
    {% endblock %}
    <h3>上方是第二个子模板填充区</h3>
</body>
</html>

子模板使用方式

子模板需要先导入父模板,使用{% extends '父模板名.html' %} 即可导入父模板。 导入父模板后,同样是使用<% block 填充名 %> <% endblock %>方法即可在父模板中预留的位置填充内容(填充的内容会覆盖父模板中block区域中的内容,可以使用{{ block.super }}方法放在填充区中防止覆盖),填充名除了可以在block中写入,在endblock中也可以写入,此功能在大项目中可以增强代码的可读性。
例如:

{% extends 'inherit.html' %}

{% block 填充一 %}
<p>我是在父模板的填充一区</p>
{% endblock %}

{% block 填充二 %}
    {{ block.super }}
    <p>我是获得了填充二区内容的区域</p>
{% endblock 填充二 %}

引入静态文件

一个网站中除了有HTML,一些静态文件也是不可或缺的,比如css、js、图片等。这些今天文件我们可以直接写绝对路径,或相对路径来表示,但这种做法并不是明智的,如果一个静态文件被多次引入,在其后修改时就会造成巨大的工程量,而且可能会出现漏改的情况。

settings.py文件设置

下方设置都在settings.py下完成:

  1. 确定是否在INSTALLED_APPS中安装了'django.contrib.staticfiles',(高版本Django默认安装)
  2. 确保已经指定STATIC_URL = '/static/'(static为文件夹名,可以自定义,此设定在settings.py文件最下方,同样在高版本Django会默认设置)
    确保有上述两部,我们就可以正常使用静态文件了,但是在使用过程会较为麻烦,我们可以在进行接下来几部操作进行优化:
  3. 当前设置我们只能APP目录下的static文件夹中的内容,但有些静态文件是公用的,我们可能想把它放入根目录中,这时候就需要手动写入(推荐放在settings.py最下方STATIC_URL = '/static/'之后,方便查看)
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
  1. 默认情况下,我们在HTML中每次使用静态文件都需要在HTML中写入{% load static %}引入静态文件夹。比较麻烦,不过可以在设置中的TEMPLATES里的OPTIONS属性内添加'builtins': ['django.templatetags.static'],
    在这里插入图片描述
    操作完成上述步骤,我们使用{% static '静态文件名' %}的方式即可引入文件

引入示例

我现在项目的APP与根目录中的static文件夹中分别放入了一张图片与一个css(其中设置背景颜色为猛男色)
在这里插入图片描述

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="{% static 'css.css' %}">
</head>
<body>
    <img src="{% static '炮姐.png' %}" alt="" width="200">
</body>
</html>

在这里插入图片描述