创建项目:
- 通过命令行的方式:首先要进入到安装了django的虚拟环境中。然后执行命令:
django-admin startproject [项目的名称]
- 通过pycharm的方式:文件->新建项目->选择django。然后指定项目所在的路径,以及Python解释器,再点击Create就可以创建项目了。
运行项目:
- 终端:进入到项目文件夹中,然后执行以下命令即可运行:
python manage.py runserver
- pycharm:直接点击右上角的绿色三角箭头按钮就可以了。注意:用pycharm运行项目,要避免一个项目运行多次。。在项目配置中,把“只用单一实例”那个选项勾选上,避免以上的问题。
改变端口号:
- 在终端:运行的时候加上一个端口号就可以了。命令为:
python manage.py runserver 9000
。 - 在pycharm中:右上角->项目配置->port。改成你想要的端口号,重新运行。
让同局域网中的其他电脑访问本机的项目:
-
让项目运行到额时候,host为0.0.0.0。
- 在终端,使用命令:
python manage.py runserver 0.0.0.0:8000
。 - 在pycharm,右上角->项目配置->host。改成
0.0.0.0
。
- 在终端,使用命令:
-
在
settings.py
文件中,配置ALLOWED_HOSTS
,将本机的ip地址添加进去。示例代码如下:ALLOWED_HOSTS = ['192.168.0.103']
注意:要关闭自己电脑的防火墙才行。
项目结构分析:
manange.py
:以后和项目交互基本上都是基于这个文件。一般都是在终端输入python manage.py [子命令]。可以输入python manage.py help看下能做什么事情。除非你知道你自己在做什么,一般情况下不应该编辑这个文件。settings.py
:保存项目所有的配置信息。urls.py
:用来做url与视图函数映射的。以后来了一个请求,就会从这个文件中找到匹配的视图函数。wsig.py
:专门用来做部署的。不需要修改。
django推荐的项目规范:
按照功能或者模块进行分层,分成一个个app。所有和某个模块相关的视图都写在对应的app的views.py中,并且模型和其他的也是类似。然后django已经提供了一个比较方便创建app的命令叫做python manage.py startapp [app的名称]
。把所有的代码写在各自的app中。
DEBUG模式:
- 如果开启了DEBUG模式,那么以后我们修改了Django项目的代码,然后按下ctrl+s,那么Django就会自动的给我们重启项目,不需要手动重启。
- 如果开启了DEBUG模式,那么以后Django项目中的代码出现bug了,那么在浏览器中和控制台会打印出错信息。
- 在生产环境中,禁止开启DEBUG模式,不然有很大的安全隐患。
- 如果将DEBUG设置为False,那么必须要设置ALLOWED_HOSTS.
ALLOWED_HOSTS:
这个变量是用来设置以后别人只能通过这个变量中的ip地址或者域名来进行访问。
视图函数
- 视图函数的第一个参数必须是request。这个参数绝对不能少。
- 视图函数的返回值必须是
django.http.response.HttpResponseBase
的子类的对象。
url相关:django2.0
url映射:
不使用缓存去加载页面ctrl+shift+r
- 为什么会去urls.py文件中寻找映射呢? 是因为在
settings.py
文件中配置了ROOT_URLCONF
为urls.py
。所有django会去urls.py
中寻找。 - 在
urls.py
中我们所有的映射,都应该放在urlpatterns
这个变量中。 - 所有的映射不是随便写的,而是使用
path
函数或者是re_path
函数进行包装的。
url传参数:
-
采用在url中使用变量的方式:在path的第一个参数中,使用
<参数名>
的方式可以传递参数。然后在视图函数中也要写一个参数,视图函数中的参数必须和url中的参数名称保持一致,不然就找不到这个参数。另外,url中可以传递多个参数。path("book/detail/<book_id>/<category_id>/",views.book_detail)
多个参数 -
采用查询字符串的方式:在url中,不需要单独的匹配查询字符串的部分。只需要在视图函数中使用
request.GET.get('参数名称')
的方式来获取。示例代码如下:def author_detail(request): author_id = request.GET['id'] text = '作者的id是:%s' % author_id return HttpResponse(text)
因为查询字符串使用的是
GET
请求,所以我们通过request.GET
来获取参数。并且因为GET
是一个类似于字典的数据类型,所有获取值跟字典的方式都是一样的。
url参数的转换器:
path("book/publisher/<path:publisher_id>/",views.publisher_detail)
- str:除了斜杠
/
以外所有的字符都是可以的。 - int:只有是一个或者多个的阿拉伯数字。
- path:所有的字符都是满足的。
- uuid:只有满足
uuid.uuid4()
这个函数返回的字符串的格式。 - slug:英文中的横杆或者英文字符或者阿拉伯数字或者下划线才满足
urls模块化(路由转发):
如果项目变得越来越大。那么url会变得越来越多。如果都放在主urls.py
文件中,那么将不太好管理。因此我们可以将每个app自己的urls放到自己的app中进行管理。一般我们会在app中新建一个urls.py文件用来存储所有和这个app相关的子url。 需要注意的地方:
- 应该使用
include
函数包含子urls.py
,并且这个urls.py
的路径是相对于项目的路径。示例代码如下:# 父url urlpatterns = [ path('admin/', admin.site.urls), path('book/',include('book.urls')) ] # 子url urlpatterns = [ path('reports/', credit_views.report), path('reports/<int:id>/', credit_views.report), ]
- 在
app
的urls.py
中,所有的url匹配也要放在一个叫做urlpatterns
的变量中,否则找不到。 url
是会根据主urls.py
和app中的urls.py
进行拼接的,因此注意不要多加斜杠。
include函数的用法:
- include(module,namespace=None):
- module:子url的模块字符串。
- namespace:实例命名空间。这个地方需要注意一点。如果指定实例命名空间,那么前提必须要先指定应用命名空间。也就是在子
urls.py
中添加app_name
变量。
- include((pattern_list, app_namespace), namespace=None):
include
函数的第一个参数既可以为一个字符串,也可以为一个元组,如果是元组,那么元组的第一个参数是子urls.py
模块的字符串,元组的第二个参数是应用命名空间。这样的话,namespace就可传可不传。也就是说,应用命名空间既可以在子urls.py
中通过app_name
指定,也可以在include
函数中指定。 - include(pattern_list):
pattern_list
是一个列表。这个列表中装的是path
或者re_path
函数。也就是说,可以把子url中的path放在这个列表中,实例代码如下:path('movie/',include([ path('',views.movie), path('list/',views.movie_list), ]))
url命名:
为什么需要url命名?
因为url是经常变化的。如果在代码中写死可能会经常改代码。给url取个名字,以后使用url的时候就使用他的名字进行反转就可以了,就不需要写死url了。
如何给一个url指定名称?
在path
函数中,传递一个name
参数就可以指定。示例代码如下:
urlpatterns = [
path('',views.index,name='index'), path('login/',views.login,name='login') ]
应用命名空间:
在多个app之间,有可能产生同名的url。这时候为了避免反转url的时候产生混淆,可以使用应用命名空间,来做区分。定义应用命名空间非常简单,只要在app
的urls.py
中定义一个叫做app_name
的变量,来指定这个应用的命名空间即可。示例代码如下:
# 应用命名空间
app_name = 'front' urlpatterns = [ path('',views.index,name='index'), path('login/',views.login,name='login') ]
以后在做反转的时候就可以使用应用命名空间:url名称
的方式进行反转。示例代码如下:
login_url = reverse('front:login')
应用(app)命名空间和实例命名空间:
一个app,可以创建多个实例。可以使用多个url映射同一个app。所以这就会产生一个问题。以后在做反转的时候,如果使用应用命名空间,那么就会发生混淆。为了避免这个问题。我们可以使用实例命名空间。实例命名空间也是非常简单,只要在include
函数中传递一个namespace
变量即可。示例代码如下:
urlpatterns = [
path('',include('front.urls')), # 同一个app下有两个实例 path('cms1/',include('cms.urls',namespace='cms1')), path('cms2/',include('cms.urls',namespace='cms2')), ]
以后在做反转的时候,就可以根据实例命名空间来指定具体的url。示例代码如下:
def index(request): username = request.GET.get("username") if username: return HttpResponse('CMS首页') else: # 获取当前的命名空间 current_namespace = request.resolver_match.namespace return redirect(reverse("%s:login"%current_namespace))
re_path
-
re_path和path的作用都是一样的。只不过
re_path
是在写url的时候可以用正则表达式,功能更加强大。 -
写正则表达式都推荐使用原生字符串。也就是以
r
开头的字符串。 -
在正则表达式中定义变量,需要使用圆括号括起来。这个参数是有名字的,那么需要使用
?P<参数的名字>
。然后在后面添加正则表达式的规则。示例代码如下:from django.urls import re_path from . import views urlpatterns = [ # r"":代表的是原生字符串(raw) re_path(r'^$',views.article), # /article/list/<year>/ re_path(r"^list/(?P<year>\d{4})/$",views.article_list), re_path(r"^list/(?P<month>\d{2})/$",views.article_list_month) ]
-
如果不是特别要求。直接使用
path
就够了,省的把代码搞的很麻烦(因为正则表达式其实是非常晦涩的,特别是一些比较复杂的正则表达式,今天写的明天可能就不记得了)。除非是url中确实是需要使用正则表达式来解决才使用re_path
。
reverse
- 如果在反转url的时候,需要添加参数,那么可以传递
kwargs
参数到revers
函数中。示例代码如下:detail_url = reverse('detail',kwargs={ "article_id":1,'page':2})
- 如果想要添加查询字符串的参数,则必须手动的进行拼接。示例代码如下:
login_url = reverse('login') + "?next=/"
在“文章分类”参数传到视图函数之前要把这些分类分开来存储到列表中。 比如参数是python+django
,那么传到视图函数的时候就要变成['python','django']
。
以后在使用reverse反转的时候,限制传递“文章分类”的参数应该是一个列表,并且要将这个列表变成python+django
的形式。
自定义URL转换器
第一种办法:
之前已经学到过一些django内置的url转换器,包括有int、uuid等。有时候这些内置的url转换器并不能满足我们的需求,因此django给我们提供了一个接口可以让我们自己定义自己的url转换器。
自定义url转换器按照以下五个步骤来走就可以了:
- 为了模块化,在app里建一个单独的文件如:converters.py,在其中定义一个类,直接继承自object就可以了。
- 在类中定义一个属性regex,这个属性是用来限制url转换器规则的正则表达式。
- 实现to_python(self,value)方法,这个方法是将url中的值转换一下,然后传给视图函数的。
- 实现to_url(self,value)方法,这个方法是在做url反转的时候,将传进来的参数转换后拼接成一个正确的url。
- 将定义好的转换器,使用
django.urls.converters.register_converter
方法注册到django中。 - 需要在当前app包中的
__init__.py
中引入一下这个包from . import converters
- 源码参照
from django.urls import converters
中已经定义好的去写。
示例代码如下:
from django.urls import register_converter class CategoryConverter(object): regex = r'\w+|(\w+\+\w+)+' def to_python(self,value): # python+django+flask # ['python','django','flask'] result = value.split("+") return result def to_url(self,value): # value:['python','django','flask'] # python+django+flask if isinstance(value,list): result = "+".join(value) return result else: raise RuntimeError("转换url的时候,分类参数必须为列表!") register_converter(CategoryConverter,'cate')
第二种办法:
写一个类,并包含下面的成员和属性:
- 类属性regex:一个字符串形式的正则表达式属性;
- to_python(self, value) 方法:一个用来将匹配到的字符串转换为你想要的那个数据类型,并传递给视图函数。如果转换失败,它必须弹出ValueError异常;
- to_url(self, value)方法:将Python数据类型转换为一段url的方法,上面方法的反向操作。
例如,新建一个converters.py文件,与urlconf同目录,写个下面的类:
class FourDigitYearConverter:
regex = '[0-9]{4}'
def to_python(self, value):
return int(value) def to_url(self, value): return '%04d' % value
写完类后,在URLconf 中注册,并使用它,如下所示,注册了一个xxxx:
from django.urls import register_converter, path from . import converters, views register_converter(converters.FourDigitYearConverter, 'xxxx') # 注册 urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<xxxx:year>/', views.year_archive), ... ]
URL映射的时候指定默认参数
使用path或者是re_path的后,在route中都可以包含参数,而有时候想指定默认的参数,这时候可以通过以下方式来完成。示例代码如下:
from django.urls import path from . import views urlpatterns = [ path('blog/', views.page), path('blog/page<int:num>/', views.page), ] # View (in blog/views.py) def page(request, num=1): # Output the appropriate page of blog entries, according to num. ...
当在访问blog/的时候,因为没有传递num参数,所以会匹配到第一个url,这时候就执行view.page这个视图函数,而在page函数中,又有num=1这个默认参数。因此这时候就可以不用传递参数。而如果访问blog/1的时候,因为在传递参数的时候传递了num,因此会匹配到第二个url,这时候也会执行views.page,然后把传递进来的参数传给page函数中的num。
使用正则表达式
Django2.0的url虽然改‘配置’了,但它依然向老版本兼容。而这个兼容的办法,就是用re_path()
方法代替path()
方法。re_path()
方法在骨子里,根本就是以前的url()
方法,只不过导入的位置变了。下面是一个例子,对比一下Django1.11时代的语法,有什么太大的差别?
from django.urls import path, re_path from . import views urlpatterns = [ path('articles/2003/', views.special_case_2003), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail), ]
与path()
方法不同的在于两点:
- year中匹配不到10000等非四位数字,这是正则表达式决定的
- 传递给视图的所有参数都是字符串类型。而不像
path()
方法中可以指定转换成某种类型。在视图中接收参数时一定要小心。
自定义错误页面
当Django找不到与请求匹配的URL时,或者当抛出一个异常时,将调用一个错误处理视图。Django默认的自带的错误视图包括400、403、404和500,分别表示请求错误、拒绝服务、页面不存在和服务器错误。它们分别位于:
- handler400 —— django.conf.urls.handler400。
- handler403 —— django.conf.urls.handler403。
- handler404 —— django.conf.urls.handler404。
- handler500 —— django.conf.urls.handler500。
这些值可以在根URLconf中设置。在其它app中的二级URLconf中设置这些变量无效。
Django有内置的HTML模版,用于返回错误页面给用户,但是这些403,404页面实在丑陋,通常我们都自定义错误页面。
首先,在根URLconf中额外增加下面的条目,并导入views模块:
from django.contrib import admin from django.urls import path from app import views urlpatterns = [ path('admin/', admin.site.urls), ] # 增加的条目 handler400 = views.bad_request handler403 = views.permission_denied handler404 = views.page_not_found handler500 = views.error
然后在,app/views.py文件中增加四个处理视图:
def bad_request(request): return render(request, '400.html') def permission_denied(request): return render(request, '403.html') def page_not_found(request): return render(request, '404.html') def error(request): return render(request, '500.html')
再根据自己的需求,创建对应的400、403、404、500.html四个页面文件,就可以了(要注意好模板文件的引用方式,视图的放置位置等等)。
模板
在之前的章节中,视图函数只是直接返回文本,而在实际生产环境中其实很少这样用,因为实际的页面大多是带有样式的HTML代码,这可以让浏览器渲染出非常漂亮的页面。目前市面上有非常多的模板系统,其中最知名最好用的就是DTL和Jinja2。DTL是Django Template Language三个单词的缩写,也就是Django自带的模板语言。当然也可以配置Django支持Jinja2等其他模板引擎,但是作为Django内置的模板语言,和Django可以达到无缝衔接而不会产生一些不兼容的情况。因此建议大家学习好DTL。
DTL与普通的HTML文件的区别
DTL模板是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化。在编译完成后,生成一个普通的HTML文件,然后发送给客户端。
渲染模板:
渲染模板有多种方式。这里讲下两种常用的方式。
render_to_string
:找到模板,然后将模板编译后渲染成Python的字符串格式。最后再通过HttpResponse类包装成一个HttpResponse对象返回回去。示例代码如下:
from django.template.loader import render_to_string from django.http import HttpResponse def book_detail(request,book_id): html = render_to_string("detail.html") return HttpResponse(html)
- 以上方式虽然已经很方便了。但是django还提供了一个更加简便的方式,直接将模板渲染成字符串和包装成HttpResponse对象一步到位完成。示例代码如下:
from django.shortcuts import render def book_list(request): return render(request,'list.html')
模版查找路径
在项目的settings.py文件中。有一个TEMPLATES配置,这个配置包含了模板引擎的配置,模板查找路径的配置,模板上下文的配置等。模板路径可以在两个地方配置。
DIRS
:这是一个列表,在这个列表中可以存放所有的模板路径,以后在视图中使用render或者render_to_string渲染模板的时候,会在这个列表的路径中查找模板。APP_DIRS
:默认为True,这个设置为True后,会在INSTALLED_APPS的安装了的APP下的templates文件加中查找模板。- 查找顺序:比如代码render('list.html')。先会在DIRS这个列表中依次查找路径下有没有这个模板,如果有,就返回。如果DIRS列表中所有的路径都没有找到,那么会先检查当前这个视图所处的app是否已经安装,如果已经安装了,那么就先在当前这个app下的templates文件夹中查找模板,如果没有找到,那么会在其他已经安装了的app中查找。如果所有路径下都没有找到,那么会抛出一个TemplateDoesNotExist的异常。
- DIRS > 当前APP > 其他APP
模版变量
- 在模版中使用变量,需要将变量放到
{ { 变量 }}
中。 - 如果想要访问对象的属性,那么可以通过
对象.属性名
来进行访问。class Person(object): def __init__(self,username): self.username = username context = { 'person': p }
person
的username
,那么就是通过person.username
来访问。 - 如果想要访问一个字典的key对应的value,那么只能通过
字典.key
的方式进行访问,不能通过中括号[]
的形式进行访问。context = { 'person': { 'username':'zhiliao' } }
username
。就是以下代码person.username
- 因为在访问字典的
key
时候也是使用点.
来访问,因此不能在字典中定义字典本身就有的属性名当作key
,否则字典的那个属性将编程字典中的key了。context = { 'person': { 'username':'zhiliao', 'keys':'abc' } }
keys
作为person
这个字典的key
了。因此以后在模版中访问person.keys
的时候,返回的不是这个字典的所有key,而是对应的值。 - 如果想要访问列表或者元组,那么也是通过
点.
的方式进行访问,不能通过中括号[]
的形式进行访问。这一点和python中是不一样的。示例代码如下:{ { persons.1 }}
if语句
- 所有的标签都是在
{%%}
之间。 - if标签有闭合标签。就是
{% endif %}
。 - if标签的判断运算符,就跟python中的判断运算符是一样的。
==、!=、<、<=、>、>=、in、not in、is、is not
这些都可以使用。 - 还可以使用
elif
以及else
等标签。
for...in...
for...in…
标签
for...in...
类似于Python
中的for...in...
。可以遍历列表、元组、字符串、字典等一切可以遍历的对象。示例代码如下:
{
% for person in persons %} <p>{
{ person.name }}</p> {
% endfor %}
如果想要反向遍历,那么在遍历的时候就加上一个reversed
。示例代码如下:
{
% for person in persons reversed %} <p>{
{ person.name }}</p> {
% endfor %}
遍历字典的时候,需要使用items
、keys
和values
等方法。在DTL
中,执行一个方法不能使用圆括号的形式。遍历字典示例代码如下:
{
% for key,value in person.items %} <p>key:{
{ key }}</p> <p>value:{
{ value }}</p> {
% endfor %}
在for
循环中,DTL
提供了一些变量可供使用。这些变量如下:
forloop.counter
:当前循环的下标。以1作为起始值。forloop.counter0
:当前循环的下标。以0作为起始值。forloop.revcounter
:当前循环的反向下标值。比如列表有5个元素,那么第一次遍历这个属性是等于5,第二次是4,以此类推。并且是以1作为最后一个元素的下标。forloop.revcounter0
:类似于forloop.revcounter。不同的是最后一个元素的下标是从0开始。forloop.first
:是否是第一次遍历。forloop.last
:是否是最后一次遍历。forloop.parentloop
:如果有多个循环嵌套,那么这个属性代表的是上一级的for循环。
** 模板中的for...in...没有continue和break语句,这一点和Python中有很大的不同,一定要记清楚! **
for...in...empty`标签
这个标签使用跟for...in...
是一样的,只不过是在遍历的对象如果没有元素的情况下,会执行empty
中的内容。示例代码如下:
{
% for person in persons %} <li>{
{ person }}</li> {
% empty %} 暂时还没有任何人 {
% endfor %}
with标签
- 在模板中,想要定义变量,可以通过
with
语句来实现。 with
语句有两种使用方式,第一种是with xx=xxx
的形式,第二种是with xxx as xxx
的形式。- 定义的变量只能在with语句块中使用,在with语句块外面使用取不到这个变量。 示例代码如下:
{
% with zs=persons.0%} <p>{
{ zs }}</p> <p>{
{ zs }}</p> {
% endwith %} 下面这个因为超过了with语句块,因此不能使用 <p>{
{ zs }}</p> {
% with persons.0 as zs %} <p>{
{ zs }}</p> {
% endwith %}
url标签
url
标签:在模版中,我们经常要写一些url
,比如某个a
标签中需要定义href
属性。当然如果通过硬编码的方式直接将这个url
写死在里面也是可以的。但是这样对于以后项目维护可能不是一件好事。因此建议使用这种反转的方式来实现,类似于django
中的reverse
一样。示例代码如下:
<a href="{
% url 'book:list' %}">图书列表页面</a>
如果url
反转的时候需要传递参数,那么可以在后面传递。但是参数分位置参数和关键字参数。位置参数和关键字参数不能同时使用。示例代码如下:
# path部分
path('detail/<book_id>/',views.book_detail,name='detail') # url反转,使用位置参数 <a href="{
% url 'book:detail' 1 %}">图书详情页面</a> # url反转,使用关键字参数 <a href="{
% url 'book:detail' book_id=1 %}">图书详情页面</a>
如果想要在使用url
标签反转的时候要传递查询字符串的参数,那么必须要手动在在后面添加。示例代码如下:
<a href="{
% url 'book:detail' book_id=1 %}?page=1">图书详情页面</a>
如果需要传递多个参数,那么通过空格的方式进行分隔。示例代码如下:
<a href="{
% url 'book:detail' book_id=1 page=2 %}">图书详情页面</a>
autoescape自动转义
- DTL中默认已经开启了自动转义。会将那些特殊字符进行转义。比如会将
<
转义成<
等。 - 如果你不知道自己在干什么,那么最好是使用DTL的自动转义。这样网站才不容易出现XSS漏洞。
- 如果变量确实是可信任的。那么可以使用
autoescape
标签来关掉自动转义。示例代码如下:{ % autoescape off %} { { info }} { % endautoescape %}
verbatim标签
verbatim
标签:默认在DTL
模板中是会去解析那些特殊字符的。和其他模板相冲突的时候,需要关闭解析的时候,比如{%
和%}
以及{
{
等。如果你在某个代码片段中不想使用DTL
的解析引擎。那么你可以把这个代码片段放在verbatim
标签中。示例代码下:
{
% verbatim %} {
{
if dying}}Still alive.{
{
/if}} // 这样就是原始字符不会被当成变量解析 {
% endverbatim %}
Django模板过滤器
为什么需要过滤器?
因为在DTL中,不支持函数的调用形式()
,因此不能给函数传递参数,这将有很大的局限性。而过滤器其实就是一个函数,可以对需要处理的参数进行处理,并且还可以额外接收一个参数(也就是说,最多只能有2个参数)。
add过滤器:
将传进来的参数添加到原来的值上面。这个过滤器会尝试将值
和参数
转换成整形然后进行相加。如果转换成整形过程中失败了,那么会将值
和参数
进行拼接。如果是字符串,那么会拼接成字符串,如果是列表,那么会拼接成一个列表。示例代码如下:
{
{ value|add:"2" }}
如果value
是等于4,那么结果将是6。如果value
是等于一个普通的字符串,比如abc
,那么结果将是abc2
。add
过滤器的源代码如下:
def add(value, arg): """Add the arg to the value.""" try: return int(value) + int(arg) except (ValueError, TypeError): try: return value + arg except Exception: return ''
cut过滤器
移除值中所有指定的字符串。类似于python
中的replace(args,"")
。示例代码如下:
{
{ value|cut:" " }}
以上示例将会移除value
中所有的空格字符。cut
过滤器的源代码如下:
def cut(value, arg): """Remove all values of arg from the given string.""" safe = isinstance(value, SafeData) value = value.replace(arg, '') if safe and arg != ';': return mark_safe(value) return value
date
过滤器
将一个日期按照指定的格式,格式化成字符串。示例代码如下:
# 数据
context = { "birthday": datetime.now() } # 模版 {
{ birthday|date:"Y/m/d" }}
那么将会输出2018/02/01
。其中Y
代表的是四位数字的年份,m
代表的是两位数字的月份,d
代表的是两位数字的日。
还有更多时间格式化的方式。见下表。
格式字符 | 描述 | 示例 |
---|---|---|
Y | 四位数字的年份 | 2018 |
m | 两位数字的月份 | 01-12 |
n | 月份,1-9前面没有0前缀 | 1-12 |
d | 两位数字的天 | 01-31 |
j | 天,但是1-9前面没有0前缀 | 1-31 |
g | 小时,12小时格式的,1-9前面没有0前缀 | 1-12 |
h | 小时,12小时格式的,1-9前面有0前缀 | 01-12 |
G | 小时,24小时格式的,1-9前面没有0前缀 | 1-23 |
H | 小时,24小时格式的,1-9前面有0前缀 | 01-23 |
i | 分钟,1-9前面有0前缀 | 00-59 |
s | 秒,1-9前面有0前缀 | 00-59 |
default
如果值被评估为False
。比如[]
,""
,None
,{}
等这些在if
判断中为False
的值,都会使用default
过滤器提供的默认值。为True 时,则会使用value的值,示例代码如下:
{
{ value|default:"nothing" }}
如果value
是等于一个空的字符串。比如""
,那么以上代码将会输出nothing
。
default_if_none
如果值是None
,那么将会使用default_if_none
提供的默认值。这个和default
有区别,default
是所有被评估为False
的都会使用默认值。而default_if_none
则只有这个值是等于None
的时候才会使用默认值。示例代码如下:
{
{ value|default_if_none:"nothing" }}
如果value
是等于""
也即空字符串,那么以上会输出空字符串。如果value
是一个None
值,以上代码才会输出nothing
。
first
返回列表/元组/字符串中的第一个元素。示例代码如下:
{
{ value|first }}
如果value
是等于['a','b','c']
,那么输出将会是a
。
last
返回列表/元组/字符串中的最后一个元素。示例代码如下:
{
{ value|last }}
如果value
是等于['a','b','c']
,那么输出将会是c
。
floatformat
使用四舍五入的方式格式化一个浮点类型。如果这个过滤器没有传递任何参数。那么只会在小数点后保留一个小数,如果小数后面全是0,那么只会保留整数。当然也可以传递一个参数,标识具体要保留几个小数。
- 如果没有传递参数:
value | 模版代码 | 输出 |
---|---|---|
34.23234 | `{ { value | floatformat }}` |
34.000 | `{ { value | floatformat }}` |
34.260 | `{ { value | floatformat }}` |
- 如果传递参数:
value | 模版代码 | 输出 |
---|---|---|
34.23234 | `{ {value | floatformat:3}}` |
34.0000 | `{ {value | floatformat:3}}` |
34.26000 | `{ {value | floatformat:3}}` |
join
类似与Python
中的join
,将列表/元组/字符串用指定的字符进行拼接。示例代码如下:
{
{ value|join:"/" }}
如果value
是等于['a','b','c']
,那么以上代码将输出a/b/c
。
length
获取一个列表/元组/字符串/字典的长度。示例代码如下:
{
{ value|length }}
如果value
是等于['a','b','c']
,那么以上代码将输出3
。如果value
为None
,那么以上将返回0
。
lower
将值中所有的字符全部转换成小写。示例代码如下:
{
{ value|lower }}
如果value
是等于Hello World
。那么以上代码将输出hello world
。
upper
类似于lower
,只不过是将指定的字符串全部转换成大写。
random
在被给的列表/字符串/元组中随机的选择一个值。示例代码如下:
{
{ value|random }}
如果value
是等于['a','b','c']
,那么以上代码会在列表中随机选择一个。
safe
标记一个字符串是安全的。也即会关掉这个字符串的自动转义。示例代码如下:
{
{
value|safe}}
如果value
是一个不包含任何特殊字符的字符串,比如<a>
这种,那么以上代码就会把字符串正常的输入。如果value
是一串html
代码,那么以上代码将会把这个html
代码渲染到浏览器中。
也可以把返回的字符串通过导入from django.utils.safestring import mark_safe
mark_safe(字符串)
slice
类似于Python
中的切片操作。示例代码如下:
{
{ some_list|slice:"2:" }}
以上代码将会给some_list
从2
开始做切片操作。
stringtags
删除字符串中所有的html
标签。示例代码如下:
{
{ value|striptags }}
如果value
是<strong>hello world</strong>
,那么以上代码将会输出hello world
。
truncatechars
如果给定的字符串长度超过了过滤器指定的长度。那么就会进行切割,并且会拼接三个点来作为省略号。示例代码如下:
{
{ value|truncatechars:5 }}
如果value
是等于北京欢迎您~
,那么输出的结果是北京...
。可能你会想,为什么不会北京欢迎您...
呢。因为三个点也占了三个字符,所以北京
+三个点的字符长度就是5。
truncatechars_html
类似于truncatechars
,只不过是不会切割html
标签。示例代码如下:
{
{ value|truncatechars:5 }}
如果value
是等于<p>北京欢迎您~</p>
,那么输出将是<p>北京...</p>
。
查看源码:from django.template import defaultfilters
自定义过滤器
- 首先在某个app中,创建一个python包,叫做
templatetags
,注意,这个包的名字必须为templatetags
,不然就找不到。 - 在这个
templatetags
包下面,创建一个python文件用来存储过滤器。 - 在新建的python文件中,定义过滤器(也就是函数),这个函数的第一个参数永远是被过滤的那个值,并且如果在使用过滤器的时候传递参数,那么还可以定义另外一个参数。但是过滤器最多只能有2个参数。
- 在写完过滤器(函数)后,要使用
register=django.template.Library() register.filter(过滤器名子,函数名子)
进行注册。 - 还要把这个过滤器所在的这个app添加到
settings.INSTALLED_APS
中,不然Django也找不到这个过滤器。 - 在模板中使用
load
标签加载过滤器所在的python包。 - 可以使用过滤器了。
django.template.Library.filter
还可以当作装饰器来使用。如果filter
函数没有传递任何参数,那么将会使用这个函数的名字来作为过滤器的名字。当然如果你不想使用函数的名字来作为过滤器的名字,也可以传递一个name
参数。示例代码如下:@register.filter('my_greet') def greet(value,word): return value + word
@register.filter
def time_since(value): """ time距离现在的时间间隔 1.如果时间间隔小于1分钟以内,那么就显示“刚刚” 2.如果是大于1分钟小于1小时,那么就显示“xx分钟前” 3.如果是大于1小时小于24小时,那么就显示“xx小时前” 4.如果是大于24小时小于30天以内,那么就显示“xx天前” 5.否则就是显示具体的时间 2017/10/20 16:15 """ if not isinstance(value,datetime): return value now = datetime.now() # timedelay.total_seconds timestamp = (now - value).total_seconds() if timestamp < 60: return '刚刚' elif timestamp >= 60 and timestamp < 60*60: minutes = int(timestamp/60) return '%s分钟前' % minutes elif timestamp >= 60*60 and timestamp < 60*60*24: hours = int(timestamp/60/60) return '%s小时前' % hours elif timestamp >= 60*60*24 and timestamp < 60*60*24*30: days = int(timestamp/60/60/24) return '%s天前' % days else: return value.strftime("%Y/%m/%d %H:%M")
include
- 有些模版代码是重复的。因此可以单独抽取出来,以后哪里需要用到,就直接使用
include
进来就可以了。 - 如果想要在
include
子模版的时候,传递一些参数,那么可以使用with xxx=xxx
的形式。示例代码如下:{ % include 'header.html' with username='zhiliao' %}
模版继承
在前端页面开发中。有些代码是需要重复使用的。这种情况可以使用include
标签来实现。也可以使用另外一个比较强大的方式来实现,那就是模版继承。模版继承类似于Python
中的类,在父类中可以先定义好一些变量和方法,然后在子类中实现。模版继承也可以在父模版中先定义好一些子模版需要用到的代码,然后子模版直接继承就可以了。并且因为子模版肯定有自己的不同代码,因此可以在父模版中定义一个block接口,然后子模版再去实现。以下是父模版的代码:
{% load static %}
<!DOCTYPE html>
<html lang="en"> <head> <link rel="stylesheet" href="{% static 'style.css' %}" /> <title>{% block title %}我的站点{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">首页</a></li> <li><a href="/blog/">博客</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %}{% endblock %} </div> </body> </html>
这个模版,我们取名叫做base.html
,定义好一个简单的html
骨架,然后定义好两个block
接口,让子模版来根据具体需求来实现。子模板然后通过extends
标签来实现,示例代码如下:
{% extends "base.html" %}
{% block title %}博客列表{% endblock %}
{% block content %} {% for entry in blog_entries %} <h2>{
{ entry.title }}</h2> <p>{
{ entry.body }}</p> {% endfor %} {% endblock %}
需要注意的是:extends标签必须放在模版的第开始的位置 子模板中的代码必须放在block中,否则将不会被渲染。 如果在某个block
中需要使用父模版的内容,那么可以使用{
{block.super}}
来继承。比如上例,{%block title%}
,如果想要使用父模版的title
,那么可以在子模版的title block
中使用{
{ block.super }}
来实现。
在定义block
的时候,除了在block
开始的地方定义这个block
的名字,还可以在block
结束的时候定义名字。比如{% block title %}{% endblock title %}
。这在大型模版中显得尤其有用,能让你快速的看到block
包含在哪里。
加载静态文件
在一个网页中,不仅仅只有一个html
骨架,还需要css
样式文件,js
执行文件以及一些图片等。因此在DTL
中加载静态文件是一个必须要解决的问题。在DTL
中,使用static
标签来加载静态文件。要使用static
标签,首先需要{% load static %}
。加载静态文件的步骤如下:
-
首先确保
django.contrib.staticfiles
已经添加到settings.INSTALLED_APPS
中,创建时已经默认添加好了。 -
确保在
settings.py
中设置了STATIC_URL
。STATIC_URL=\static\
-
在已经安装了的
app
下创建一个文件夹叫做static
,然后再在这个static
文件夹下创建一个当前app
的名字的文件夹,再把静态文件放到这个文件夹下。例如你的app
叫做book
,有一个静态文件叫做zhiliao.jpg
,那么路径为book/static/book/zhiliao.jpg
。(为什么在app
下创建一个static
文件夹,还需要在这个static
下创建一个同app
名字的文件夹呢?原因是如果直接把静态文件放在static
文件夹下,那么在模版加载静态文件的时候就是使用zhiliao.jpg
,如果在多个app
之间有同名的静态文件,这时候可能就会产生混淆。而在static
文件夹下加了一个同名app
文件夹,在模版中加载的时候就是使用<img src="{% static 'app/zhiliao.jpg' %}">
,这样就可以避免产生混淆。) -
如果有一些静态文件是不和任何
app
挂钩的。那么可以在settings.py
中添加STATICFILES_DIRS
,以后DTL
就会在这个列表的路径中查找静态文件。那么查找的有顺序就变为,先在自己的app里找,找不到就会这个目录下去找。比如可以设置为:
STATICFILES_DIRS = [
os.path.join(BASE_DIR,"static") ]
- 在模版中使用
load
标签加载static
标签。比如要加载在项目的static
文件夹下的style.css
的文件。那么示例代码如下:
{% load static %}
<link rel="stylesheet" href="{% static 'style.css' %}">
-
如果不想每次在模版中加载静态文件都使用
load
加载static
标签,那么可以在settings.py
中的TEMPLATES/OPTIONS
添加'builtins':['django.templatetags.static']
,这样以后在模版中就可以直接使用static
标签,变成一个内置的标签,而不用手动的load
了。 -
如果没有在
settings.INSTALLED_APPS
中添加django.contrib.staticfiles
。那么我们就需要手动的将请求静态文件的url
与静态文件的路径进行映射了。示例代码如下:
from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # 其他的url映射 ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)<