django学习(二)
1.反向解析
什么是方向解析呢?
通过一些方法得到一个结果,该结果可以直接访问对应url出发视图函数。
先给一个路由和视图函数起一个别名。但是我们要注意的是反向解析的别名是不可以冲突的!!!不然会出现问题。
re_path(r'func_kkk/', views.func, name='ooo')
re_path(r'edit_info', views.edit_info, name='edit'),
后端反向解析
from django.shortcuts import render,HttpResponse,redirect,reverse
reverse('ooo')
reverse('edit')
前端反向解析
<a href='{% url 'foo' %}'>111</a>
<a href='{% url 'edit' %}'>编辑</a>
2.无名和有名的反向解析
我们在做反向解析的时候,如果是单纯的视图和路由之间的关系,那么我们做反向解析的时候是不用传入任何的参数值。但是对于有名和无名分组的方向解析就不一样了,这个时候是需要我们传入参数值。因为这个参数值是用来匹配视图后面的无名分组和有名分组使用的。我们先来get一下无名分组的反向解析,然后再来get一下有名分组的反向解析。
1)无名分组的反向解析
# 无名分组的反向解析
re_path(r'^index/(\d+)/',views.index,name='xxx')
# 前端
{% url 'xxx' 123 %}
# 后端
reverse('xxx',args=(1,))
在这里我们必须明确一个知识点,有名和无名分组反向解析要传入的参数值的数字应该放什么呢?数字一般情况下放的是数据的主键值,例如我们做过的案例关于数据的编辑和删除。同时还需要明白这个数字都不是写死的,在实际的项目开发的过程中都是动态获取这个数据值的。
如果我们在写代码的时候,没有给无名分组反向解析传入值的时候,我们会报错。报错的信息如下所示:
Reserver for 'xxx' with no arguments not found 1 pattern(s) tried:['index/(\\d+)/']
2)有名分组的反向解析
# 有名分组反向解析
re_path(r'^edit_info/(?P<book_id>\d+)/', views.edit_info, name='edit'),
re_path(r'^delete_info/(?P<book_id>\d+)/', views.delete_info, name='delete')
# 前端
# 第一种:了解
<a href="{% url 'edit' book_id=123 %}" class="button btn-success btn-xs">编辑</a>
<a href="{% url 'delete' book_id=123 %}" class="button btn-danger btn-xs">删除</a>
# 第二种:记忆
<a href="{% url 'edit' 123 %}" class="button btn-success btn-xs">编辑</a>
<a href="{% url 'delete' 123 %}" class="button btn-danger btn-xs">删除</a>
# 后端
print(reverse('edit',kwargs={'book_id':123}))
print(reverse('delete',kwargs={'book_id':123}))
# 简便的写法 减少你的脑容量消耗 记跟无名一样的操作即可
print(reverse('edit',args=(123,)))
print(reverse('delete',args=(123,)))
在这里我们必须明确一个知识点,有名和无名分组反向解析要传入的参数值的数字应该放什么呢?数字一般情况下放的是数据的主键值,例如我们做过的案例关于数据的编辑和删除。同时还需要明白这个数字都不是写死的,在实际的项目开发的过程中都是动态获取这个数据值的。
如果我们在写代码的时候,没有给有名分组反向解析传入值的时候,我们会报错。报错的信息如下所示:
NoReverseMatch at /
Reverse for 'ooo' whit no arguments not found. 1 pattern(s) tried: ['func/(?P<year>\\d+)/']
3.路由分发
django的每一个应用都可以有自己的templates文件夹,urls.py文件和static文件夹。正是基于上述的特点,django能够非常好我的做到分组开发(每个人只是写自己的app)。作为组长,只需要将手下书写的app应用全部拷贝到一个新的django项目中,然后在配置文件里面注册,所有的app应用再利用路由分发的特点将所有的app整合起来。
当一个django项目中的url(path)路由特别多的时候,总路由urls.py代码非常冗余不好维护,这个时候也可以利用路由分发来减轻总路由的压力。
利用路由分发之后,总路由不在干路由和视图函数的直接对应关系,而是做一个分发处理。(类似于前台)。识别当前的url是属于哪个应用下的,直接分发给对应的应用去处理。
from django.conf.urls import url, include
from django.contrib import admin
from app01 import urls as app01_urls
from app01 import urls as app01_urls
urlpattern = [
path('admin/',admin.site.urls),
# 路由分发
path('app01/', include(app01_urls)),# 只要是路由的前缀是app01开头的全部交给app01去处理
path('app02/', include(app02_urls)),# 只要是路由的前缀是app02开头的全部交给app02去处理
]
# 还有一种方式去写路由分发,这种方式变得更加的简单和使用方便。
urlpattern = [
path('admin/',admin.site.urls),
# 路由分发
path('app01/', include(app01.urls)),# 只要是路由的前缀是app01开头的全部交给app01去处理
path('app02/', include(app02.urls)),# 只要是路由的前缀是app02开头的全部交给app02去处理
]
# 注意:总路由的path(url)千万不能加$结尾,会出现问题的。
4.名称空间(了解)
我们知道我们的不同的应用可能是不同的人去开发和使用的,那么我们就可能会出现名字相同的情况,不同应用之间如果出现了名字相同的情况。在反向解析的时候,就会出现问题。那么为了使得我们的反向解析可以是识别是什么应用下的路由与视图的匹配,这里我们引出了名称空间的说法。
path('app01/',include('app01_urls',namespace='app01')),
path('app02/',include('app02_urls',namespace='app02')),
path('app01/', include('app01.urls',namespace='app01')),
path('app02/', include('app02.urls',namespace='app02')),
当多个应用出现了相同的别名,我们研究反向解析会不会自动识别应用的前缀。正常情况下的反向解析是没有办法自动识别应用前缀的。
# app01
urlpatterns = [
path('reg/',views.reg,name='reg')
]
# app02
urlpatterns = [
path('reg/',views.reg,name='reg')
]
# 总路由
path('app01/', include('app01.urls',namespace='app01')),
path('app02/', include('app02.urls',namespace='app02')),
# 解析的时候
reverse('app01:reg')
reverse('app02:reg')
{% url 'app01:reg' %}
{% url 'appp2:reg' %}
一般的情况下,有多个app的时候,我们在起别名的时候会加上app的名字为前缀,这样的话就能够确保多个app之间的名字不冲突的问题。
urlpatterns = [
path('reg/',views.reg,name='app01_reg')
]
urlpatterns = [
path('reg/',views.reg,name='app02_reg')
]
静态网页:数据是写死的,万年不变
伪静态:将一个动态网页伪装成一个静态网页
为什么要做伪装呢?
总结:无论你怎么优化,怎么处理,始终还是干不过人民币玩家。
from django.urls import path, include
from django.contrib import admin
urlpattern = [
path('reg.html',views.reg,name='app01_reg')
]
5.虚拟环境(了解)
虚拟环境基本知识
在实际的开发的过程中,正常开发中,我们会给每一个项目配备一个该项目独有的解释器环境,该环境内只有该项目用到的模块,用不到的一概不装。就类似于我们使用的linux一样:缺什么我们才装什么。
我们为什么要用到虚拟环境呢?因为每个项目的需求不一样,可能用到的python的解释器的版本也不一样,同时每个项目的所需要的包和模块也不一样。如果没有虚拟环境,我们有些包和模块用不到,但是加载的时候浪费资源和浪费时间。同时有些模块和包没有,我们需要自己去安装。这样会使得我们原本的python解释器环境的包和模块十分的多。有时候,我们用不到的包和模块,如果一个一个的去卸载,也是给自己的开发带来了麻烦。所以我们提出了虚拟环境的概念,为每一个项目提供一个安全可靠,干净的开发环境。
我们每次创建一个虚拟环境就类似于重新下载了一个纯净的python解释器,但是虚拟环境不要创建的太多,是需要消耗硬盘空间的。
扩展:每个项都需要用到很多模块,并且每个模块版本可能还不一样,那我们该如何安装呢?一个个看一个个装???
开发当中我们会给每一个项目配备一个requirements.txt文件,里面书写了该项目所有模块和版本。你只需要直接输入一条命令即可一键安装所有模块以及所对应的版本。
6.django版本区别(1.x和2.x,3.x之间的区别)
我们主要是将django1.X,2.X和3.X之间的区别。其实2.X和3.X是一样的。主要讲的是和1.X之间的区别即可。
django1.x路由层使用的是url方法,而在django2.x和3.x版本中路由层使用的是path方法。
url()第一个参数支持正则,path()第一个参数是不支持正则表达式的,匹配到什么就是什么。
如果你习惯使用path那么也给你提供了另外一种方法
from django.urls import path, re_path
from django.conf.urls import url
re_path(r'^index/',index),
url(r'^login/',login)
2.x和3.x里面的re_path就等价于1.x里面的url
虽然path不支持正则,但是它的内部支持五种转换器
path('index/<int:id>/',index)
将第二个路由里面的内容先转成整型然后以关键字的形式传递给后面的视图函数
def index(request,id):
print(id,type(id))
return HttpResponse('index')
1、path与re_path或者1.0中的url的不同之处是,传给path的第一个参数不再是正则表达式,而是一个完全匹配的路径,相同之处是第一个参数中的匹配字符均无需加前导斜杠 。2、使用尖括号(<>)从url中捕获值,相当于有名分组 3、<>中可以包含一个转化器类型(converter type),比如使用 <int:name>
使用了转换器int。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符。
下面是五种path下的转换器:
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
除了有默认的五个转换器之外,我们还支持自定义转换器。
1、在app01下新建文件path_converters.py,文件名可以随意命名
class MonthConverter:
regex='\d{2}' # 属性名必须为regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return value # 匹配的regex是两个数字,返回的结果也必须是两个数字
2、在urls.py中,使用register_converter将其注册到url配置中:
from django.urls import path,register_converter
from app01.path_converts import MonthConverter
register_converter(MonthConverter,'mon')
from app01 import views
urlpatterns = [
path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),
]
3、views.py中的视图函数article_detail
from django.shortcuts import render,HttpResponse,reverse
def article_detail(request,year,month,other):
print(year,type(year))
print(month,type(month))
print(other,type(other))
print(reverse('xxx',args=(1988,12,'hello'))) # 反向解析结果/articles/1988/12/hello/
return HttpResponse('xxxx')
# 1、在浏览器输入http://127.0.0.1:8000/articles/2009/12/hello/,path会成功匹配出参数year=2009,month=12,other='hello'传递给函数article_detail
# 2、在浏览器输入http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配失败,因为我们自定义的转换器mon只匹配两位数字,而对应位置的123超过了2位
模型层里面的1.x外键都是级联更新删除的,但是到了2.x和3.x中需要我们自己手动配置参数。
publish = models.ForeignKey(to='Publish')(1.x)
publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)(2.x和3.x)
7.三板斧
我们有时候在写代码的时候会出现下面这种报错的情况:
The view app01.views.index didn't return an HttpResponse object.It returned None instead.
HttpResponse
:返回字符串类型
render
:返回的是html页面,并且在返回给浏览器之前还可以给html传值
redirect
:重定向
视图函数必须返回一个HttpResponse对象,这句话是正确的。我们去研究三种的源码就可以得出这个结论。
8.JsonResponse对象
json格式的数据有什么用?
前后端数据交互需要使用到json作为过渡,实现跨语言的传输数据
前端序列化:JSON.stringify(),前端反序列化:JSON.parse()
后端序列化:json.dumps(),后端反序列化:json.loads()
我们看下面的例子来体会一下,json和JsonResponse的使用的方法。
def demo_json(request):
dic = {'username': 'zhouqian', 'password': '123456zhouqian', 'hobby': 'basketball', 'sport': '跑步'}
lst = ['zhouqina', 'AZ', 123, '你好']
dic = json.dumps(dic)
return HttpResponse(dic)
# 如果我们前端返回的是json数据,但是我们看到我们的json对于中文是无法显示的,默认是使用了ensure_ascii=True,转换为ASCII码
# {"username": "zhouqian", "password": "123456zhouqian", "hobby": "basketball", "sport": "\u8dd1\u6b65"}
# 我们使用下面的这种方法就可以显示中文
dic = json.dumps(dic, ensure_ascii=False)
return HttpResponse(dic) # {"username": "zhouqian", "password": "123456zhouqian", "hobby": "basketball", "sport": "跑步"}
# 我们还可以使用JsonResponse来做json的序列化的工作,会变得更加的简单和快捷
# return JsonResponse(dic)
# {"username": "zhouqian", "password": "123456zhouqian", "hobby": "basketball", "sport": "\u8dd1\u6b65"}
# 默认还是装换为ASCII码,我们还不无法显示中文,看不懂
# 我们如果要正常的显示中文,那么我们应该做下面这个措施。
# return JsonResponse(dic, json_dumps_params={'ensure_ascii': False})
# {"username": "zhouqian", "password": "123456zhouqian", "hobby": "basketball", "sport": "跑步"}现在我们可以正常的显示中文了
# 如果我们要使用JsonResponse来序列化除了字典以外的对象,那么我们应该做什么呢?
# return JsonResponse(lst, json_dumps_params={'ensure_ascii': False})
# 我们执行上面的代码会出现错误:显示我们如果要序列化一个不是字典的对象,我们应该设置safe参数为False
# 下面这样写才可以序列化一个列表
return JsonResponse(lst,json_dumps_params={'ensure_ascii':False},safe=False)
# ["zhouqina", "AZ", 123, "你好"]
9.form表单普通数据和文件上传
我们知道我们的form表单有两种数据编码格式,这两种分别是:第一种是:enctype="application/x-www-form-urlencoded"
。第二种是:enctype="multipart/form-data"
。
在这里我们对两种编码格式分别进行分析,分析每一种编码格式对普通数据和文件上传的存储和操作。
我们先讲解一下第一种编码格式,即form-urlencoded。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'jquery-3.5.1/jquery-3.5.1.min.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.js' %}"></script>
</head>
<body>
<form action="" method="post" enctype="application/x-www-form-urlencoded">
<p><input type="text" name="username" placeholder="请输入用户名:" class="form-control"></p>
<p><input type="password" name="password" placeholder="请输入密码:" class="form-control"></p>
<p><input type="file" name="file"></p>
<button type="submit" class="btn btn-primary btn-block">提交</button>
</form>
</body>
</html>
def form_files(request):
if request.method == 'POST':
print(request.POST) # <QueryDict: {'username': ['zhouqian'], 'password': ['123'], 'file': ['001849-15912011295e8a.jpg']}>
print(request.POST.get('username')) # zhouqian
print(request.POST.get('password')) # 123
file_obj = request.POST.get('file')
print(file_obj) # 001849-15912011295e8a.jpg
print(file_obj.name) # AttributeError: 'str' object has no attribute 'name'
with open(file_obj.name,'wb') as f:
for line in file_obj:
f.write(line)
return render(request, 'form_files.html')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
{% load static %}
<link rel="stylesheet" href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<script src="{% static 'jquery-3.5.1/jquery-3.5.1.min.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.js' %}"></script>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
<p><input type="text" name="username" placeholder="请输入用户名:" class="form-control"></p>
<p><input type="password" name="password" placeholder="请输入密码:" class="form-control"></p>
<p><input type="file" name="file"></p>
<button type="submit" class="btn btn-primary btn-block">提交</button>
</form>
</body>
</html>
def form_files(request):
if request.method == 'POST':
print(request.POST) # <QueryDict: {'username': ['zhouqian'], 'password': ['123']}>
print(request.POST.get('username')) # zhouqian
print(request.POST.get('password')) # 123
print(request.FILES) # <MultiValueDict: {'file': [<InMemoryUploadedFile: 001849-15912011295e8a.jpg (image/jpeg)>]}>
file_obj = request.FILES.get('file')
print(file_obj) # 001849-15912011295e8a.jpg
print(file_obj.name) # 001849-15912011295e8a.jpg
with open(file_obj.name,'wb') as f:
for line in file_obj:
f.write(line)
return render(request, 'form_files.html')
总结:对于form-urlencoded编码格式的form表单来说,普通数据和文件数据都存放在request.POST中。我们都可以拿到普通数据和文件。拿到的普通数据是字符串或者列表,文件是字符串,是文件的名字,不是文件对象。所以这里的文件不能进行写入了。对于form-data编码格式的form表单来说,普通数据存放在request.POST中,文件不行。文件是存放在request.FILES中。获取的普通数据是字符串或者是列表,获取的文件是文件对象。我们可以将文件写入到项目中。
10.request方法大汇总
我们在这里将我们的request方法做一个详细的总结和回顾。request方法很多,但是我们主要是掌握以下常见的request的方法即可。
request.method
:获取前端请求的方式,我们使用较多的是get和post两种请求方式。
request.path
:获取请求的url路径。这个方法等价于request.path_info
request.get_full_path
:获取请求的url路径以及问号后面的参数
request.POST
:获取到POST请求的所有普通数据。queryset
request.GET
:获取到GET请求的所有普通数据。queryset。get请求的数据会放在url链接的后面
request.FILES
:获取的是编码格式为form-data的文件数据,即可以得到我们的文件对象。
这里再讲一下get()和getlist()两个方法的区别。get()方法是获取关键字所对应列表的最后一项,返回的是字符串的类型。getlist()方法获取关键字所对应的列表,返回的是一个列表。
11.FBV和CBV
在这里我们讲解一下FBV和CBV的使用的差别。其实这两种在平时的项目开发中,我们都可以采用,但是只要采用其中的一种即可,没必要一个项目两种情况混合使用,有种鞋子乱穿的感觉。
FBV(function base views)<<>> CBV(class base views)
我们之前使用的方式都是基于FBV模式,那么在这里关于FBV就没有什么好说的了。我们这里主要是讲一下CBV。我们通过下面一个简单的实例,来讲解CBV的使用。
class cbv_demo(View):
def get(self, request):
return render(request, 'cbv_demo.html')
def post(self, request):
return HttpResponse('post')
# CBV路由
path('cbv_demo/', views.cbv_demo.as_view(), name='cbv_demo')
FBV和CBV各有千秋。CBV特点是能都直接根据请求方式的不同直接匹配到对应的方法执行。那这个的内部是怎么实现的呢?我们需要去看内部源码,才可以get到。
12.CBV源码解析
。。。。。。等待补充
13.模版语法传值
1)模版语法传值
模版的语法的传值我们可以总结为下面两句话:
-
{{ }}
:变量相关 -
{% %}
:逻辑相关--for循环、if循环、反向解析、加载静态文件
2)python后端的数据类型,我们的模版语法都是可以传递的,模版语法可以传递后端python的所有的基本数据类型。
views.py文件
def index(request):
a = 123
b = 10.9
c = 'hello world'
l = ['zhouqian', 'zhoukun', 'zhouzhou']
t = ('zhou', 'qian', 'kun')
d = {'zhou': 'qian', 'kun': 'baby'}
s = {'zhouqian', 'zhoukun', 'smallzhou'}
return render(request, 'index.html', locals())
index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模版语法的传值</title>
</head>
<body>
<ul>
<li>{{ a }}</li>
<li>{{ b }}</li>
<li>{{ c }}</li>
<li>{{ l }}</li>
<li>{{ t }}</li>
<li>{{ d }}</li>
<li>{{ s }}</li>
</ul>
</body>
</html>
3)函数名放到模版语法中去,会自动加上括号执行这个函数。传递函数名会自动加括号调用,但是模版语法不支持给函数传递额外的参数:{{func}}
def func():
print("hello,this is a book!")
return 'zhouqian'
<li>传递函数名会自动加括号调用,但是模版语法不支持给函数传额外的参数:{{ func }}</li>
4)模版层传类的时候也会自动加括号调用,也就是自动实例化。如果模版层传的是对象,那么这个时候我们接受的就是一个对象。如果这个对象所对应的类中有__str__
方法,那么模版语法接受的是这个方法输出的结果。
class MyClass(object):
def __init__(self):
print('__init__执行了')
@staticmethod
def add(a, b):
print(a + b)
def get_func(self):
print("我是self,你咬我")
obj = MyClass()
<li>传递类名的时候也会自动加括号调用(实例化的过程){{ MyClass }}</li>
<li>{{ obj }}</li>
def __str__(self):
print('打印对象执行了__str__方法')
通过模版语法接受到对象的时候,模版语法收到的是对象的地址。如果对象所对应的实例化的类中有__str__
方法的时候,那么模版对象接受对象的时候是将方法__str__
方法打印出来的结果的值展现出来。前端模版展示的过程类似于打印对象的过程。对象被展示到html页面上,就类似于执行了打印操作也会触发__str__
方法。
djangp模版语法内部能够自动判断出当前的变量名是否可以加括号调用,如果可以就会自动执行。针对的是函数名和类名。
模版语法的传值是指python的所有的基本的数据类型,函数名,类名,对象都可以使用模版语法进行传递。
5)django模版语法的取值是固定的格式,只能采用“句点符”。只能用点取值。既可以点键也可以点索引,还可以两者混用。
我们可以通过对象点出类中的方法名,然后模版语法会自动的加上(),去执行类中的方法。这样类中的方法就被执行了,显示出执行的结果如下所示,代码的情况如下所示。
class MyClass(object):
def __init__(self):
print('__init__执行了')
def __str__(self):
return '打印对象执行了__str__方法'
@staticmethod
def add(a, b):
print(a + b)
def get_func(self):
return "我是self,你咬我"
def get_class(self):
return "我是self,你咬我"
def get_self(self):
return "我是func,你咬我"
obj = MyClass()
<li>{{ obj.get_func }}</li>
<li>{{ obj.get_class }}</li>
<li>{{ obj.get_self }}</li>
只能用点取值。既可以点键也可以点索引,还可以两者混用。如下所示的代码的情况,模版语法的结果如下所示:
l = ['zhouqian', 'zhoukun', 'zhouzhou']
d = {'zhou': 'qian', 'kun': 'baby', 'hobby': [111, 222, 333, {'info': '这个人的爱好有点牛不'}]}
<li>{{ l.2 }}</li>
<li>{{ d.zhou }}</li>
<li>{{ d.hobby.3.info }}</li>
14.模版语法之过滤器
过滤器就类似于是模版语法内置的内置方法,django内置有60多个过滤器。我们不需要学这么多,了解十多个左右即可,后面碰到了再去记忆和学习。
过滤器的基本语法如下所示:{{数据|过滤器:参数}}
def index1(request):
a = 123
b = 10.9
c = 'hello world'
l = ['zhouqian', 'zhoukun', 'zhouzhou']
t = ('zhou', 'qian', 'kun')
d = {'zhou': 'qian', 'kun': 'baby', 'hobby': [111, 222, 333, {'info': '这个人的爱好有点牛不'}]}
s = {'zhouqian', 'zhoukun', 'smallzhou'}
filesize = 1245572
info = 'python学习之路 你想要的都在这里了 (根据自己的学习进度后期不断更新哟!!!)python基础--小数据池,代码块的最'
msg = 'this is a yellow book! where are you yellow book?'
import datetime
current_time = datetime.datetime.now()
return render(request, 'index1.html', locals())
<ul>
<li>统计长度:{{ c | length }}</li>
<li>默认值(第一个参数布尔值是True就展示第一个参数的值,否则展示的是冒号后面的值):{{ a| default:46 }}</li>
<li>文件大小:{{ filesize| filesizeformat }}</li>
<li>日期格式化:{{ current_time | date:'Y-m-d H:i:s' }}</li>
<li>切片的操作:{{ l | slice:'0:4:2' }}</li>
<li>切取字符(包含三个点):{{ info|truncatechars:9 }}</li>
<li>切取单词(不包含三个点 按照空格切){{ msg|truncatewords:9 }}</li>
<li>切取单词(不包含三个点 按照空格切){{ info| truncatechars:9 }}</li>
<li>移除特定的字符:{{ mgs|cut:' '}}</li>
<li>拼接的操作:{{ l | join:'$' }}</li>
<li>拼接操作(加法):{{ a|add:10 }}</li>
<li>拼接操作(加法):{{ c|add:msg }}</li>
</ul>
在过滤器这里我们还需要补充一个知识点就是取消转义的知识点。
res = '<h1>你是大帅哥</h1>'
<li>取消转义:{{ res|safe }}</li>
通过取消转义这个知识点我们明白了一个小的理论:
以后你在全栈项目的时候,前端代码不一定非要在前端页面书写,也可以在先在后端写好,然后通过取消转义传递给前端的页面。
15.模版语法之标签
1)for循环
for循环的第一个知识点是forloop的使用。
{% for foo in l %}
<p>{{ forloop }}</p>
{% endfor %}
for循环其实是不断的取值的过程
l = ['a', 'b', 'c', 'd', 'e']
{% for foo in l %}
<p>{{ foo }}</p>
{% endfor %}
取出来的值依次的模版的渲染页面,依次的渲染页面的p标签,一共渲染出5个p标签。
2)if判断
学习if判断,我们直接看下面的例子即可:
l = ['a', 'b', 'c', 'd', 'e']
s = 1
{% if l %}
<p>嘿嘿</p>
{% elif s %}
<p>哈哈</p>
{% else %}
<p>老司机</p>
{% endif %}
3)for循环和if判断的混合使用
{% for foo in l %}
{% if forloop.first %}
<p>这是我第一次来</p>
{% elif forloop.last %}
<p>这是我最后一次来</p>
{% else %}
<p>{{ foo }}</p>
{% endif %}
{% endfor %}
如果for循环的可迭代对象是空的,内部没有元素,根本没有办法循环。那么这次执行的是{% empty %}
下面的逻辑。我们举一个例子来说明这个问题。
k = []
{% for foo in k %}
{% if forloop.first %}
<p>这是我第一次来</p>
{% elif forloop.last %}
<p>这是我最后一次来</p>
{% else %}
<p>{{ foo }}</p>
{% endif %}
{% empty %}
<p>我好惨,没人要</p>
{% endfor %}
4)for标签对字典的使用
d = {'zhou': 'qian', 'kun': 'baby', 'hobby': [111, 222, 333, {'info': '这个人的爱好有点牛不'}]}
{% for foo in d.keys %}
<p>{{ foo }}</p>
{% endfor %}
{% for foo in d.values %}
<p>{{ foo }}</p>
{% endfor %}
{% for foo in d.items %}
<p>{{ foo }}</p>
{% endfor %}
5)with的使用
d = {'zhou': 'qian', 'kun': 'baby', 'hobby': [111, 222, 333, {'info': '这个人的爱好有点牛不'}]}
{% with d.hobby.3.info as nb %}
<p>{{ nb }}</p>
{# 在with语法内就可以通过as后面的别名快速的使用到前面非常复杂获取数据的方式#}
<p>{{ d.hobby.3.info }}</p>
{% endwith %}
16.自定义过滤器、标签以及inclusion_tag
自定义过滤器、标签以及inclusion_tag的时候,我们需要明白我们的操作步骤,操作的步骤分为下面三个:
-
在应用的文件夹下面必须创建一个
templatetags
文件夹。 -
在
templatetags
文件下创建一个任意名字的.py
文件。 -
必须在任意名字的
.py
写入下面两行的代码。
from django import template
register = template.Library()
1)自定义过滤器
# 自定义过滤器,在这里我们必须说明一下,过滤器最多只能传入两个参数,不能够传很多的参数
@register.filter(name='baby')
def my_sum(value1,value2)
return value1+value2
n=12
# 自定义过滤器
<p>{{n|baby:5}}</p>
2)自定义标签
# 自定义标签
@register.simple_tag(name='boy')
def my_simple_tag(a, b, c, d):
return f'{a}-{b}-{c}-{d}'
# 自定义标签
<p>{% boy 'zhouqian' 'AndreasZhou' 520 521 %}</p>
3)自定义inclusion_tag
# 自定义inclusion_tag
@register.inclusion_tag('left_tag.html')
def my_inclusion_tag(n):
lst = ['第{0}项'.format(i) for i in range(n)]
return locals()
<p>{% my_inclusion_tag 5 %}</p>
上面的代码的输出的结果如下所示:
总结:当html页面的某一个地方的页面需要传参数才能够动态的渲染出来,并且在多个页面上都需要使用到该局部,那么就考虑将该局部页面做成inclusion_tag形式。(在bbs的时候我们会用到这个知识点)
17.模版的继承
你们有没有见多一些网站,这些网站页面整体都大差不差,只是页面的某一些局部在做变化。那么什么是模版的继承呢?模版继承其实是减少代码的冗余程度,使得我们的页面书写的更加的简洁。维护起来也比较的简便,也变得更加的好维护。
模版的继承的基本的过程如下所示:
1)模版的继承之前,我们应该先选择好一个你想要继承的模版的页面。
{% extends 'home.html' %}
2)继承了之后子页面跟模版页面长的是一模一样的,你需要在模版页面上提前划定可以修改的区域。
{% block content %}
模版内容
{% endblock %}
# 子页面就可以声明想要修改哪块划定了的区域
{% block content %}
子页面内容
{% endblock %}
3)一般情况下模版页面上应该至少有三块可以被修改的区域,这三块区域分别是css区域,html区域和js区域。
1.css区域
2.html区域
3.js区域
{% block css %}
{% endblock %}
{% block html %}
{% endblock %}
{% block js %}
{% endblock %}
# 每一个子页面就都可以有自己独特的css代码 html代码 js代码
总结:一般的情况下,模版的页面上划定的区域越多,那么该模版的可扩展性就越高,但是如果太多,那还不如自己直接写。
18.模版的导入
将页面的某一个局部当成模块的形式,哪一个地方需要就可以直接导入使用即可。
{% include 'zhouqian.html' %}
19.补充数据库知识
下面的两篇文章是关于数据库的编码的文章,怎么将默认的数据库编码改为utf8的编码格式,不仅可以识别字母,还可以识别汉字等等,符合全世界的统一。
https://blog.csdn.net/xiaobendan_1989/article/details/90001667
https://my.oschina.net/sdlvzg/blog/2209575/
这里补充一下setting配置文件的配置的信息:
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'