1. MVC框架和MTV框架
1. MVC框架
MVC,全名是Model View Controller,是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller),具有耦合性低、重用性高、生命周期成本低等优点。
M: model 模型 操作数据库
V: view 视图 展示页面 HTML
C: controller 控制器 调度 业务逻辑
2. MTV框架
Model(模型):负责业务对象与数据库的对象(ORM)
Template(模版):负责如何把页面展示给用户
View(视图):负责业务逻辑,并在适当的时候调用Model和Template
此外,Django还有一个urls分发器,它的作用是将一个个URL的页面请求分发给不同的view处理,view再调用相应的Model和Template
2. 语法
模板渲染的官方文档
关于模板渲染你只需要记两种特殊符号(语法):
{{ }}和 {% %}
变量相关的用{{}},逻辑相关的用{%%}。
3. 变量
1. 万能的点调用
{{ 变量名 }}
变量名由字母数字和下划线组成。
点(.)在模板语言中有特殊的含义,用来获取对象的相应属性值。
举例说明
views.py
from django.shortcuts import render
import datetime
def index(request):
name = "水手"
num = 100
lst = [1, 2, "aa", "bb"]
dic = {"xx": "oo", "xxx": "ooo"}
date = datetime.date(1993, 5, 2) # 日期对象
class Person():
n = "黑哥"
def p(self):
return "床前明月光"
obj = Person()
return render(request, "index.html", {"name": name, "num": num, "lst": lst, "dic": dic,"date":date, "obj": obj})
# print(locals()) 获得全局字典,传的数据多,效率低
# return render(request, "index.html", locals())
index.html 代码
<h1>24期官网</h1>
<h2>{{ name }}</h2> # 获得后端name的值渲染到页面
<h2>{{ num }}</h2> # 获得后端num的值渲染到页面
<h2>{{ lst }}</h2> # 获得后端lst的值渲染到页面
<h2>{{ dic }}</h2> # 获得后端dic的值渲染到页面
<h2>{{ obj }}</h2> # 获得后端obj的对象渲染到页面
<h2>{{ lst.1 }}</h2> # {#点索引获取对应的值#}
<h2>{{ dic.xx }}</h2> # {#点键获取字典的值#}
<h2>{{ obj.n }}</h2> # {#点属性获取值#}
<h2>{{ obj.p }}</h2> # {#点方法获取返回值,前端调用不加括号#}
<h2>{{ dic.keys }}</h2> {#先查找字典中的键,有此键就不调用方法了#}
<h2>{{ dic.items }}</h2> # {#点方法获取字典的键值对#}
# {# dic.values获取值,dic.keys获取键 #}
2. 查询顺序
字典查询(Dictionary lookup)
属性或方法查询(Attribute or method lookup)
数字索引查询(Numeric index lookup)
举例说明
dic = {"xx": "oo", "xxx": "ooo","keys":"我在执行我"}
<h2>{{ dic.keys }}</h2> # 前端执行时先在字典中查询此键,有键就不执行字典的方法了
# 结果是: 我在执行我 而不是: dict_keys(['xx', 'xxx']) 键的列表
3. 注意点
- 调用方法不支持传参数。
- 如果使用的变量不存在, 模版系统将插入 string_if_invalid 选项的值, 它被默认设置为'' (空字符串)
4. 过滤器 filter
作用:修改变量的显示结果
1. 语法
{{ value|filter_name }} # filter_name 过滤器名称
{{ value|filter_name:参数 }}
2. 具体举例
1. default 默认
注意:':'左右没有空格没有空格没有空格,nothing需要引号引起来
<h2>{{ age|default:"Nothing" }}</h2>
# 变量不存在,显示为空字符串/none时,都会调用default默认值
返回同样是nothing的还有有这个变量,但是值是False,空字典等值为空的
注:TEMPLATES的OPTIONS可以增加一个选项:string_if_invalid:'找不到',可以替代default的的作用
见上述做修改部分替代default的作用
在settings文件中TEMPLATES的'OPTIONS'字典中再添加一个键值对 "string_if_invalid":"不存在或为空" 为空时或不存在时执行
2. filesizeformat
将值格式化为一个 “人类可读的” 文件格式大小 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)
{{ value|filesizeformat }} #如果 value 是 123456789,输出将会是 117.7 MB
{{ 1024|filesizeformat }} # 1.0kb
3. add 添加
用于数字的加减,字符串拼接,列表相加等
<h2>{{ num|add:22 }}</h2> # html语法加不加引号效果相同
<h2>{{ num|add:"22" }}</h2> # html语法加不加引号效果相同 num=100 结果:122
<h2>{{ lst|add:lst }}</h2> #
<h2>{{ name|add:"bsb" }}</h2> # 也可以用于字符串和列表等的相加
4. widthratio 实现加减乘除
<h2>{{ num|add:10 }}</h2> #等同于 num+10
<h2>{{ num|add:-10 }}</h2> #等同于 num-10
<h2>{% widthratio num 1 5 %}</h2> #等同于 num/1*5
<h2>{% widthratio num 5 1 %}</h2> #等同于 num/5*1 必须3个参数 格式 参数1/参数2*参数3
<h2>{% widthratio 10 5 2 %}</h2> #等同于 10/5*2=4
5. length 长度
<h2>{{ name|length }}</h2> # 字符长度
<h2>{{ lst|length }}</h2> # 列表长度
<h2>{{ dic|length }}</h2> # 字典长度
<h2>{{ name|add:"bsb"|length }}</h2> # 多重操作
6. slice 切片
<h2>{{ name|add:"abcdefg"|slice:"2:-1:2" }}</h2> # ace 支持切片和步长
7. first 取第一个值
<h2>{{ name|first }}</h2> # 获取第一个值
<h2>{{ value|last }}</h2> # 获取最后一个值
8. join 拼接
<h2>{{ name|join:"//" }}</h2> # 结果 水//手 相当于字符串拼接
<h2>{{ dic|join:"//" }}</h2> # 键拼接 xx//xxx
<h2>{{ lst|join:"//" }}</h2> # 迭代拼接 1//2//aa//bb
9. truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾
<h2>{{ "abcdefghigk"|truncatechars:3 }}</h2> # ... 保留几个字符,小于三也是三个点
<h2>{{ "abcdefghigk"|truncatechars:6 }}</h2> # abc... 保留6个,有三个点
10. date日期格式化
date = datetime.date(1993, 5, 2) # 日期对象
now = datetime.datetime.now() # 现在时间
<h2>{{ date|date:"Y-m-d"}}</h2> #
<h2>{{ now|date:"Y-m-d H:i:s"}}</h2> # 设置显示格式
扩展,不过滤,在设置settings中修改
# USE_L10N = True
USE_L10N = False
DATETIME_FORMAT = "Y-m-d H:i:s"
DATE_FORMAT = "Y-m-d"
TIME_FORMAT = "H:i:s"
<h2>{{ date }}</h2> #
<h2>{{ now }}</h2> # 设置显示格式
11. lower 小写
<h2>{{ "NAme"|lower }}</h2> # 全部小写 name
12. upper 大写
<h2>{{ "NAme"|upper }}</h2> # 全部大写 NAME
13. title 标题,首字母大写
<h2>{{ "woShiShui"|title }}</h2> # 首字母大写 Woshishui
14. safe 告诉django不需要转义
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。
# 语法:{{ value|safe }}
'jjss':'<script> for (var i=0 ;i<5;i++){alert("123")}</script>'
{{ jjss|safe }} # 告诉浏览器这是安全的代码
{{ jjss }} # Django这个代码当做字符串处理 <script> for (var i=0 ;i<5;i++){alert("123")}</script>
# 或者使用mark_save()方法
from django.utils.safestring import mark_safe
'jjss':mark_safe('<script> for (var i=0 ;i<5;i++){alert("123")}</script>')
bd = '<a href="http://www.baidu.com">百度</a>' # 后端
前端:
<h2>{{ bd }}</h2> # 显示字符串 <a href="http://www.baidu.com">百度</a>
<h2>{{ bd|safe }}</h2> # 设置safe,显示为a标签 百度
通过自定义标签,实现网址
from django import template
register = template.Library()
@register.filter
def show_a(v1,v2):
return f'<a href="http://{v1}">{v2}</a>'
{% load my_tags %}
{{ 'www.baidu.com'|show_a:'百度'|safe }}
# 第二种方法
# index函数传 a = "www.taobao.com" 通过 render{"a":a}传到前端,前段通过{{ a|show_a:'百度'|safe }}传参到自定义标签
# 第三种方法
@register.filter(is_safe=True) #不能使用index传参
def show_a(v1,v2):
return f'<a href="http://{v1}">{v2}</a>'
{% load my_tags %}
{{ 'www.baidu.com'|show_a:'百度' }}
# 第四种方法
导入一个模块 mark_safe,用mark_safe将返回的结果包起来
from django.utils.safestring import mark_safe
@register.filter
def show_a(v1,v2):
return mark_safe(f'<a href="http://{v1}">{v2}</a>')
#return mark_safe('<a href="http://{}">{}</a>'.format(v1,v2)) 传参
15. truncatewords
msg = "我 是 i love you my baby"
<h3>{{ msg|truncatewords:2 }}</h3> # {# 以空格区分截断,不是字符字节 #} 我 是 ...
<h3>{{ msg|truncatechars:2 }}</h3> # 注意二者的区分 ...
16. divisibleby 是否整除
<h1>{{ 10|divisibleby:2 }}</h1> # True
<h1>{{ 10|divisibleby:3 }}</h1> # False
5. 自定义过滤器
1. 自定义过滤器
1.在app下创建一个名叫templatetags的包 (templatetags名字不能改)
2.在包内创建py文件 (文件名可自定义 my_tags.py)
3.在my_tags.py中写代码:
from django import template
register = template.Library() # register名字不能错,固定
4. 写上一个函数 + 加装饰器
@register.filter
def xx(v1,v2):
ret = v1+v2
return ret.upper()
5. 使用
index.html 中
{% load my_tags %} # 加载py文件
{{ "alex"|xx:"dsb" }} # alex和dsb 分别对应两个参数,渲染返回值
# 参数至多两个,至少一个 name = "alex"可以通过index函数传递
6. 其他写法
from django import template
register = template.Library()
@register.filter
def xx(v1,v2="dsb"):
ret = v1 + v2
return ret.upper()
{% load my_tags %} # html文件调用,先加载py文件
{{ "alex"|xx }} # 后端添加默认参数,得到返回值 ALEXDSB
只有一个参数
from django import template
register = template.Library()
@register.filter
def xx(v1):
ret = v1
return ret.upper()
{% load my_tags %}
{{ "alex"|xx }} # 后端添加默认参数,得到返回值,页面展示 ALEX
给函数起一个别名
from django import template
register = template.Library()
@register.filter(name="xxsb")
def xx(v1,v2):
ret = v1 + v2
return ret.upper()
{% load my_tags %}
{{ "alex"|xxsb:"dsb" }} # 注意用别名xxsb,而不是xx ALEXDSB
6. 模板中的标签
1. 语法{% tags %}
2. for 循环标签
1. for的一些参数
{{ forloop.counter }} 循环的索引 从1开始
{{ forloop.counter0 }} 循环的索引 从0开始
{{ forloop.revcounter }} 循环的索引(倒叙) 到1结束
{{ forloop.revcounter0 }} 循环的索引(倒叙) 到0结束
{{ forloop.first }} 判断是否是第一次循环 是TRUE
{{ forloop.last }} 判断是否是最后一次循环 是TRUE
{{ forloop.parentloop }} 当前循环的外层循环的相关参数
# forloop其实是一个字典,上边是通过.键取的值
2. 举个栗子
table = [[1,2,3],["a","b","c"],[7,8,9]] # 后端传导前端
# 使偶数行偶数列变红
<table border="2">
<tbody>
{% for lst in table %}
<tr>
{% for foo in lst %}
<td style="{% if forloop.counter|divisibleby:2 and forloop.parentloop.counter|divisibleby:2 %}color: red{% endif %}">{{ forloop.counter }}{{ foo }}</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
3. for ... empty
如果没有lst1时则显示empty的内容
<ul>
{% for i in lst1 %}
<li>{{ i }}</li>
{% empty %}
<li>列表不存在</li>
{% endfor %}
</ul>
3. if 条件标签
{% if num > 100 or num < 0 %}
<p>无效</p> <!--不满足条件,不会生成这个标签-->
{% elif num > 80 and num < 100 %}
<p>优秀</p>
{% else %} <!--也是在if标签结构里面的-->
<p>凑活吧</p>
{% endif %}
- if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。
- if支持过滤器语法 {% if 10|add:2 %} {{ "成功" }} {% endif %}
- if 不支持算数运算 + - * / %
- 不支持连续判断 10 > 5 > 1 false 与Python不同,先判断10>5 的True,无法与1比较
4. with 给变量起别名
方法1
{% with total=business.employees.count %} #注意等号两边不能有空格
{{ total }} <!--只能在with语句体内用-->
{% endwith %}
方法2
{% with business.employees.count as total %}
{{ total }}
{% endwith %} # 就是名字太长时节省写法,只能在with标签内使用
7. {% csrf_token %} 防止跨站请求伪造
<form action="" method="post">
# name="csrfmiddlewaretoken"
{% csrf_token %} # 会添加一个隐藏的input框. 当提交数据是,协同csrf令牌一同提交.
<input type="text" name="name">
<button>提交</button>
</form>
8. 模板和继承
1. 模板继承(母版继承)
1. 创建一个base.html页面(作为母版,其他页面来继承它使用)
2. 在母版中定义block块(可以定义多个,整个页面任意位置)
{% block content %} <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
{% endblock %}
3 其他页面继承写法
{% extends 'base.html' %} 必须放在页面开头
4 页面中写和母版中名字相同的block块,从而来显示自定义的内容
{% block content %} <!-- 预留的钩子,共其他需要继承它的html,自定义自己的内容 -->
{{ block.super }} #这是显示继承的母版中的content中的原内容
这是xx1
{% endblock %}
2. 继承问题
{% extends 'base.html' %} 必须放在页面开头
- 建base文件,可以命名多个block,不同名即可
- 想自定义自己继承后文件的属性,style要放在block中即可
- {{ block.super }} 继承父类,注意格式,双括号
3. 组件
网站 www.jq22.com
1 创建html页面,里面写上自己封装的组件内容,title.html
2 新的xx.html页面使用这个组件
{% include 'title.html' %}
9. 静态文件
1. 标签法
# 引用静态文件
{% load static %} # 自动寻找到static对应的静态资源.
<link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/dashboard.css' %}">
# 引用js
{% load static %} # 自动寻找到static对应的静态资源.
<script src="{% static "mytest.js" %}"></script> #'/static/mytest.js'
# 多出引用同一个资源时, 可以重新命名.
{% load static %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}"></img>
2. 别名法
<link rel="stylesheet" href="/static/css/dashboard.css">
3. get_static_prefix
{% load static %}
<link rel="stylesheet" href="{% get_static_prefix %}css/dashboard.css">
{% get_static_prefix %} == /static/ 别名
4. 静态文件配置
1 项目目录下创建一个文件夹,例如名为jingtaiwenjianjia,将所有静态文件放到这个文件夹中
2 settings配置文件中进行下面的配置
# 静态文件相关配置
STATIC_URL = '/abc/' #静态文件路径别名
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'jingtaiwenjianjia'),
]
10. 自定义标签
1. 自定义标签和过滤器
1 在应用app01下创建一个叫做templatetags的包(名称不能改),在里面创建一个py文件,例如my_tags.py
2 在my_tags.py文件中引用django提供的template类,写法
from django import template
#register名称不可变,library大写
register = template.Library()
#自定义过滤器
@register.filter # 参数至多两个
def xx(v1,v2):
return v1+v2
#使用:
{% load my_tags %} # 加载上文件名xxoo,相当于引入xxoo.py文件
{{ name|xx:'oo' }} # xx是函数名 传两个参数
# 自定义标签 没有参数个数限制
@register.simple_tag
def num_my(*args,**kwargs): # 给的参数没有限制
return '_'.join(args) + "*".join(kwargs.values()) #返回的值是已经处理过的.
#使用
{% load my_tags %}
{% num_my "1" "2" "3" a="alex" b="taibai" %} # 注意返回值时join,可迭代,不能是数字,其他方法可以返回数字
# inclusion_tag 返回html片段的标签
@register.inclusion_tag('result.html')
def res(n1): #n1 : ['aa','bb','cc']
return {'li':n1 }
使用:
{% res a %}
2. 自定义标签和过滤器大前提
1 在应用下创建一个叫做templatetags的文件夹(名称不能改),在里面创建一个py文件,例如my_tags.py
2 在xx.py文件中引用django提供的template类,写法
from django import template
#register名称不可变,library大写
register = template.Library()
3.自 定义过滤器执行流程
1. 页面请求 127.0.0.1/new/
2. 找到 url(r'^test/', views.test) 执行 test函数
3. test有第三个参数,render{"name":"Alex"}传到test.html文件中
4. {{ name|addoo:"dsb"}}加载my_tags.py文件,调用my_tags中的addoo函数
5. 执行my_tags中的addoo函数 return n1+n2,参数一一对应
6.替换test.html中内容,响应浏览器,显示结果
4. 自定义标签执行流程
1. 页面请求 127.0.0.1/new/
2. 找到 url(r'^new/', views.new) 执行 new函数
3. new有第三个参数,{"name":"Alex"}传到new.html文件中
4. {% load xx %}加载xx.py文件,调用{% res name "dsb" %}函数
5. 执行xx中的res函数 return n1+n2,参数一一对应
6.替换new.html中内容,响应浏览器,显示结果
5. inclusion_tag 返回html片段的标签执行流程
1. 页面请求 127.0.0.1/new/
2. 找到 url(r'^test/', views.test) 执行 test函数
3. test有第三个参数,{"a":a}传到test.html文件中
4. {% load xx %}加载xx.py文件,调用{% res a "s2" %}函数
5. 执行xx中的res函数
6. 将return值返回给@register.inclusion_tag("result.html")的result.html文件
7. 修饰后依次跳回test.html文件,响应浏览器,显示结果
11. 自定义过滤器和标签的区别
1. 前者最多接受2个参数,后者没有限制 {% if 10|add:2 %}
2. 前者可以在作为if的判断条件,后者不可以 不能识别
12. 面试题
cdn地址:https://www.bootcdn.cn/
模板中使用{% sqr_list 3 %},生成如下的dropdown list 控件(下拉菜单)
key text
1 1的平方是1
2 2的平方是4
3 3的平方是9
请写出sqr_list的实现
views.py
from django.shortcuts import render
def index(request):
return render(request,"index.html")
使用inclusion_tag标签
在app应用中创建templatetags的my_tags.py
from django import template
register = template.Library()
# 第一中方法,列表
@register.inclusion_tag('dropdown_list.html')
def sqr_list(num):
data = [f'{i} ---{i}的平方是{i**2}' for i in range(1,num+1)]
return {'data':data} # 传给showhome.html
#第二种方法,字典
@register.inclusion_tag('dropdown_list.html')
def sqr_list(num):
return {"i":{i:f'{i}的平方是{i**2}' for i in range(1,num+1)}}
index.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
{# <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"> #}
</head>
<body>
{% load my_tags %} # 加载my_tags.py文件
{% sqr_list 10 %} # 调用了my_tags文件,传参
<script src="{% static 'jquery.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
{#<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>#}
{##}
</body>
</html>
showhome.html
# 第一种方法
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
key text
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
{% for foo in data %}
<li><a href="#">{{ foo }}</a></li>
{% endfor %}
</ul>
</div>
# 第二种方法
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu2" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
key text
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
{% for k,v in i.items %}
<li><a href="#">{{ k }} {{ v }}</a></li>
{% endfor %}
</ul>
</div>