Django-C004-模板
此文章完成度【100%】留着以后忘记的回顾。多写多练多思考,我会努力写出有意思的demo,如果知识点有错误、误导,欢迎大家在评论处写下你的感想或者纠错。
【Django version】: 2.1
【pymysql version】:0.9.3
【python version】: 3.7
模板
作为web框架,Django提供了模板,用于编写html代码,还可以嵌入模板代码更快捷、更方便的完成页面开发,在通过在视图中的渲染,将生成最终的HTML字符串返回给客户端,模板致力于表达外观,而不是程序逻辑,模板的设计实现了业务逻辑与显示内容的分离,一个视图可以使用任意的模板,一个模板可以供多个视图使用。
模板包含两个部分:
-
静态:html、css、js
-
动态:模版语言
Django处理模板分为两个阶段:
-
加载:根据给定的路径找到模板文件,编译后保存在内存中
-
渲染:使用上下文数据对模板插入值并返回生成的字符串
为了减少加载和渲染,Django提供了简单函数render用于调用模板
Django模板语言,定义在django.template,创建完项目后,在项目文件下的settings.py文件中定义关于模板的配置
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
DIRS定义一个目录列表,模板引擎按列表顺序搜索这些目录以便找到模板文件,通常是在项目的根目录下创建templates目录。
按照咱们的习惯,每当新开始的章节,当然是创建一个新的项目,温故而知新。
1)创建项目test4
django-admin startproject test4
2)进入项目目录,创建应用school
cd test4
python manage.py startapp school
3) 在项目的settings.py文件中,添加应用
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'school', # 添加应用 ]
4)在项目的settings.py文件中,更改使用的数据库引擎
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 't_school', 'USER': 'root', 'PASSWORD': 'toor', 'HOST': 'localhost', 'PORT': 3306, } }
5)在项目下的__init__.py文件中导入pymysql
import pymysql pymysql.install_as_MySQLdb()
6)在项目下的settings.py文件中添加模本文件夹
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], # 添加模板文件夹路径 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
7)创建模板文件夹
8)配置项目下的urls.py指向我们应用的urls.py, 并且创建应用专属urls.py
项目下的urls.py
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('', include("school.urls")), ]
应用下的urls.py
from django.urls import re_path from school import views urlpatterns = [ re_path(r"^$", views.index), ]
9)在应用下的views.py,定义index视图
10)在templates的t_school中创建index.html 模板文件
11)在school/models.py文件中,定义ClassInfoModel类
from django.db import models class ClassInfoModel(models.Model): """班级信息模型类""" name = models.CharField(max_length=20) class_date = models.DateField() class_size = models.IntegerField(default=0) reported_size = models.IntegerField(default=0) isDelete = models.BooleanField(default=False)
12)测试
模板语言
模版语言的四种类型:变量、标签、过滤器、注释
模版变量
模板班里那个的作用是计算并输出,变量名必须由字母、数字、下划线(但是不能作为开头)和点组成
{{ 变量 }}
当模版引擎遇到点如 c.name .这里的c是ClassInfoModel的对象,会按照以下顺序解析
-
字典 c['name'] 方式解析
-
先属性后方法,c当做对象,查找name属性,如果没有在查找name()方法
-
如果个为c.0则解析为c[0]
如果对象不存在则插入空字符串,在模板调用方式时,不能传递参数
视图:views.py,创建pass_on
def pass_on(request): po_dict = {'key' : 'po_dict_var'} c = ClassInfoModel.objects.get(id=1) context = {'po_dict': po_dict, 'c': c} return render(request, "t_school/pass_on.html", context)
URL : urls.py
urlpatterns = [ re_path(r"^$", views.index), re_path(r"^pass_on$", views.pass_on), ]
模板:templates>t_school>pass_on.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> dict = : {{po_dict.key}}<br> ClassInfoModel = : {{c.name}}<br> </body> </html>
展示效果
标签
{% 代码段 %}
for循环标签
{%for item in 列表%} 循环逻辑 {{forloop.counter}}表示当前是第几次循环,从1开始 {%empty%} 列表为空或不存在时执行此逻辑 {%endfor%}
if判断标签
{%if ...%} 逻辑1 {%elif ...%} 逻辑2 {%else%} 逻辑3 {%endif%}
比较运算符
== # 注意:运算符左右两侧不能紧挨变量或常量,必须有空格。 != < > <= >=
布尔运算符
and or not
视图:views.py 创建视图 use_tag
def use_tag(request): context = {'class_list': ClassInfoModel.objects.all()} return render(request, 't_school/use_tag.html', context)
URL: urls.py
re_path(r"^use_tag$", views.use_tag),
模板:templates>t_school>use_tag.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> {%for class in class_list %} {%if class.id <= 3 %} <li style="color:pink;">{{ class.name }}</li> {%else%} <li style="color:#666;">{{ class.name }}</li> {%endif%} {%empty%} error: 没找到班级信息 {%endfor%} </ul> </body> </html>
展示效果
过滤器
语法
- 使用管道符 | 来应用过滤器,用于进行计算、转换操作、可以使用在变量、标签中
- 如果过滤器需要参数,则使用冒号:传递参数
变量|过滤器:参数
长度length,返回字符串包含字符的个数,或列表、元祖、字典的元素个数
默认值default,如果变量不存在时则返回默认值
data|default:'默认值'
日期date, 用于对日期类型的值进行字符串格式化,常用的格式化标签
- Y表示年,格式为4位,y表示两位的年。
- m表示月,格式为01,02,12等。
- d表示日, 格式为01,02等。
- j表示日,格式为1,2等。
- H表示时,24进制,h表示12进制的时。
- i表示分,为0-59。
- s表示秒,为0-59。
alue|date:"Y年m月j日 H时i分s秒"
视图 : views.py 定义视图use_filter
def use_filter(request): context = {'class_list': ClassInfoModel.objects.all()} return render(request, 't_school/use_filter.html', context)
URL: urls.py
re_path(r"^use_filter$", views.use_filter),
模板:templates>t_school>use_filter.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> {%for class in class_list %} {%if class.id <= 3 %} <li style="color:pink;"> {{ class.name }}---> {{ class.class_date}} </li> {%else%} <li style="color:#666;"> {{ class.name }}---> {{ class.class_date|date:"Y-m-j"}} </li> {%endif%} {%empty%} error: 没找到班级信息 {%endfor%} </ul> </body> </html>
展示效果
自定义过滤器
过滤器就是Python中的函数,注册后就可以在模板中当做过滤器使用,下面以求余为例开发一个自定义过滤器mod
首先在应用中创建templatetags目录,创建__init__文件和filters.py文件
去settings里面的INSTALLED_APPS中注册:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'school', # 添加应用 'school.templatetags' # 作为APP注册 ]
在filters.py中编写代码:
# 导入Library类 from django.template import Library # 创建一个Library对象 register = Library() # 使用装饰器进行注册 @register.filter def mod(value): # 定义求余函数,返回布尔值 return value % 2 == 0
使用这个自定义的过滤器之前还需要在模板文件中导入,去模板文件中使用自定义的过滤器
{%load filters%} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> {%for class in class_list %} {%if class.id|mod %} <li style="color:pink;"> {{ class.name }}---> {{ class.class_date}} </li> {%else%} <li style="color:#666;"> {{ class.name }}---> {{ class.class_date|date:"Y-m-j"}} </li> {%endif%} {%empty%} error: 没找到班级信息 {%endfor%} </ul> </body> </html>
展示效果
其实自定义的过滤器还是可以指定参数的,接下来把上面的例子改进一下,不让固定只能取余2
1)首先到filters.py中定义过滤器函数
@register.filter def mod_num(value, num): # 定义求余函数,返回布尔值 return value % num == 0
2)去模板中使用
{%load filters%} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <ul> {%for class in class_list %} {%if class.id|mod_num:3 %} <li style="color:pink;"> {{ class.name }}---> {{ class.class_date}} </li> {%else%} <li style="color:#666666;"> {{ class.name }}---> {{ class.class_date|date:"Y-m-j"}} </li> {%endif%} {%empty%} error: 没找到班级信息 {%endfor%} </ul> </body> </html>
效果展示
注释
在模板中使用如下模板注释,这段代码不会被编译,不会输出到客户端;html注释只能注释html内容,不能注释模板语言
1)单行注释
{#...#}
2)多行注释
{%comment%}
...
{%endcomment%}
模板继承
模板继承和类的继承含义是一样的,主要是为了提高代码的重用
典型应用 : 网站头部、尾部
父模板
如果发现在多个模板中内容相同,那就应该把这段内容定义在父模板中。
标签block:用于在父模板中预留区域,留给紫墨般填充差异性的内容,名字不能相同,为了更好的可读性,建议给endblock 标签上写名字,这个名字与对应的block名字相同,父模板中也可以使用上下文中传递过来的来的数据
{%block 名称%}
预留区域,可以编写默认内容,也可以没有默认内容
{%endblock 名称%}
子模板
标签extends: 继承写在模版文件的第一行
{% extends "父模板路径"%}
子模板不用填充父模板中的所有预留区域,如果子模板没有填充,则使用父模板定义的默认值。
填充父模板中指定名称的预留区域
{%block 名称%}
实际填充内容
{{block.super}}用于获取父模板中block的内容
{%endblock 名称%}
视图 views.py 定义temp_inherit.
def temp_inherit(request):
context = {'title': '模板继承', 'class_list': ClassInfoModel.objects.all()}
return render(request, 't_school/temp_inherit.html', context)
URL urls.py
re_path(r"^temp_inherit$", views.temp_inherit),
模板 创建一个名为temp_inherit_base.html父模板,在创建一个temp_inherit.html子模板
temp_inherit_base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> </head> <body> <h1>网页头部</h1> <hr> {%block qu1%} 这是区域一,默认值 {%endblock qu1%} <hr> {%block qu2%} {%endblock qu2%} <hr> <h1>网页尾部</h1> </body> </html>
temp_inherit.html
{%extends 't_school/temp_inherit_base.html'%} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> {%block qu1%} {{block.super}} <br> 这是区域一,添加的内容 {%endblock qu1%} {%block qu2%} <ul> {% for class in class_list%} <li> {{class.name}} </li> {%endfor%} </ul> {%endblock qu2%} </body> </html>
展示效果
HTML转义
模板对上下文传递的字符串进行输出时,会对以下字符自动转义。
小于号< 转换为 < 大于号> 转换为 > 单引号' 转换为 ' 双引号" 转换为 " 与符号& 转换为 &
视图 views.py 修改index视图的context
def index(request): context = {'h1': '<h1> index ok </h1>'} return render(request, "t_school/index.html", context)
访问127.0.0.1:8000
转义后标记代码不会被直接解释执行,而是被直接呈现,防止客户端通过嵌入js代码攻击网站.
过滤器escape可以实现对变量的html转义,默认模板就会转义,一般省略。
{{数据|escape}}
关闭转义
过滤器safe:禁用转义,告诉模板这个变量是安全的,可以解释执行。
{{数据|safe}}
模板 修改index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 自动转义 : {{ h1 }} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 关闭转义 : {{h1|safe}} </body> </html>
autoescape标签:设置一段代码都禁用转义,接受on、off参数。
{%autoescape off%}
...
{%endautoescape%}
模板修改index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 自动转义:{{h1}} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 过滤器safe关闭转义:{{h1|safe}} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 标签autoescape关闭转义: {%autoescape off%} {{h1}} {%endautoescape%} </body> </html>
字符串字面值
对于在模板中硬编码的html字符串,不会转义。如果希望出现转义的效果,则需要手动编码转义。
模板修改index.html文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> 自动转义:{{h1}} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 过滤器safe关闭转义:{{h1|safe}} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 标签autoescape关闭转义: {%autoescape off%} {{h1}} {%endautoescape%} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 模板硬编码不转义:{{data|default:'<b>hello</b>'}} <HR style="FILTER: alpha(opacity=100,finishopacity=0,style=2)" width="100%" color=#987cb9 SIZE=10> 模板硬编码手动转义:{{data|default:"<b>123</b>"}} </body> </html>
CSRF
CSRF(Cross Site Request Forgery)跨站请求伪造。CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发送消息,盗取你的账号,甚至于购买商品,虚拟货币转账,个人隐私泄露以及财产安全。
CSRF示意图
如果想防止CSRF,首先是重要的信息传递都采用POST方式而不是GET方式,接下来就说POST请求的攻击方式以及在Django中的避免。
视图:views.py 创建视图login、login_check、post、post_action
def login(request): return render(request, 't_school/login.html') def login_check(request): username = request.POST.get('username') # 获取用户名 password = request.POST.get('password') # 获取密码 # 校验 if username == 'circle' and password == '123': request.session['username'] = username request.session['isLogin'] = True return redirect('/post') else: return redirect('/login') def post(request): return render(request, 't_school/post.html') def post_action(request): if request.session['isLogin']: username = request.session['username'] title = request.POST.get('title') # 获取标题 content = request.POST.get('content') # 获取内容 return HttpResponse(username + '发了一篇%s : %s' % (title, content)) else: return HttpResponse('失败')
URL : urls.py
re_path(r"^login$", views.login), re_path(r"^login_check$", views.login_check), re_path(r"^post$", views.post), re_path(r"^post_action$", views.post_action),
模板:在templates/school中创建login.html 和 post.html
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <form method="post" action="/login_check"> username:<input type="text" name="username"><br> password:<input type="text" name="password"><br> <input type="submit" value="提交"> </form> </body> </html>
post.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>发帖页</title> </head> <body> <form method="post" action="/post_action"> 标题:<input type="text" name="title"/><br/> 内容:<textarea name="content"></textarea> <input type="submit" value="发帖"/> </form> </body> </html>
运行服务,username : circle password : 123 就可以后续的操作
login.html
post.html
post_action
这些都是正常的操作,但是如果直接输入127.0.0.1:8000/post.html 也是可以的,所以这个就是有问题的,在生产环境中也是不允许的,所以我们需要验证post.html 是否已经登录
需要修改视图的post函数
def post(request):
if request.session.has_key('isLogin'):
return render(request, 't_school/post.html')
else:
return redirect('/login')
验证效果,但是我们需要清除浏览器中的Cookie否则你的登录状态是登录过的
当你再次访问127.0.0.1:8000/post.html,会直接跳转到login这个页面,问题来了,如果重复的页面都需要使用这个访问限制。难道我们需要在每一个函数中添加一条判断语句吗?当然不用,我们可以用到python中的装饰器
视图中定义一个装饰器
# 定义一个登录的装饰器,用来验证是否这个页面需要先登录在访问 def login_required(view_func): def wrapper(request, *args, **kwargs): if request.session.has_key('isLogin'): return view_func(request, *args, **kwargs) else: return redirect('/login') return wrapper
@login_required def post(request): return render(request, 't_school/post.html') @login_required def post_action(request): if request.session['isLogin']: username = request.session['username'] title = request.POST.get('title') # 获取标题 content = request.POST.get('content') # 获取内容 return HttpResponse(username + '发了一篇%s : %s' % (title, content)) else: return HttpResponse('失败')
但是到底什么是csrf,当黑客制作了一个钓鱼网站,当你在没有删除Cookie和你登录状态的时候,你通过这个浏览器访问了他的钓鱼网站B,但是他的站点设置一些对你操作的A网站里面的信息进行修改,他就可以跳过登录,直接操作A网站
那么咱们自己的网站如果做到防护呢,在Django中有一个中间件就是CSRF
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', # 防止CSRF的中间件 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
这个中间件默认是开启,但是我们自己网站需要用到的post提交也会被阻止,这里就需要在你的表单提交post数据的时候,添加{% csrf_token %}标签
login.html中添加
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <form method="post" action="/login_check"> {% csrf_token %} username:<input type="text" name="username"><br> password:<input type="text" name="password"><br> <input type="submit" value="提交"> </form> </body> </html>
post.html中添加
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>发帖页</title> </head> <body> <form method="post" action="/post_action"> {% csrf_token%} 标题:<input type="text" name="title"/><br/> 内容:<textarea name="content"></textarea> <input type="submit" value="发帖"/> </form> </body> </html>
这里,就可以成功完成CSRF防护
防护原理
-
渲染模板文件时在页面生成一个名字叫做csrfmiddlewaretoken的隐藏域。
-
服务器交给浏览器保存一个名字为csrftoken的cookie信息。
-
提交表单时,两个值都会发给服务器,服务器进行比对,如果一样,则csrf验证通过,否则失败。
说明:当启用中间件并加入标签csrf_token后,会向客户端浏览器中写入一条Cookie信息,这条信息的值与隐藏域input元素的value属性是一致的,提交到服务器后会先由csrf中间件进行验证,如果对比失败则返回403页面,而不会进行后续的处理。
验证码
在用户注册、登录页面,为了防止暴力请求,可以加入验证码功能,如果验证码错误,则不需要继续处理,可以减轻业务服务器、数据库服务器的压力。
实现验证码
安装pillow
pip install Pillow
点击查看PIL模块API,以下代码中用到了Image、ImageDraw、ImageFont对象及方法。
视图 views.py 创建视图verify_code
- 提示1:随机生成字符串后存入session中,用于后续判断。
- 提示2:视图返回mime-type为image/png。
from PIL import Image, ImageDraw, ImageFont from django.utils.six import BytesIO def verify_code(request): #引入随机函数模块 import random #定义变量,用于画面的背景色、宽、高 bgcolor = (random.randrange(100), random.randrange( 20, 100), 255) width = 100 height = 35 #创建画面对象 im = Image.new('RGB', (width, height), bgcolor) #创建画笔对象 draw = ImageDraw.Draw(im) #调用画笔的point()函数绘制噪点 for i in range(0, 100): xy = (random.randrange(0, width), random.randrange(0, height)) fill = (random.randrange(0, 255), 255, random.randrange(0, 255)) draw.point(xy, fill=fill) #定义验证码的备选值 str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0' #随机选取4个值作为验证码 rand_str = '' for i in range(0, 4): rand_str += str1[random.randrange(0, len(str1))] #构造字体对象 windows下的字体路径'C:\\Windows\\Fonts\\arial.ttf' 26是字体大小 font = ImageFont.truetype('C:\\Windows\\Fonts\\arial.ttf', 26) #构造字体颜色 fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255)) #绘制4个字 draw.text((5, 2), rand_str[0], font=font, fill=fontcolor) draw.text((25, 2), rand_str[1], font=font, fill=fontcolor) draw.text((50, 2), rand_str[2], font=font, fill=fontcolor) draw.text((75, 2), rand_str[3], font=font, fill=fontcolor) #释放画笔 del draw #存入session,用于做进一步验证 request.session['verifycode'] = rand_str #内存文件操作 buf = BytesIO() #将图片保存在内存中,文件类型为png im.save(buf, 'png') #将内存中的图片数据返回给客户端,MIME类型为图片png return HttpResponse(buf.getvalue(), 'image/png')
配置URL urls.py
re_path(r'^verify_code$', views.verify_code),
访问127.0.0.1:8000/verify_code
在login页面中调用verify_code
在templates中找到login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <form method="post" action="/login_check"> {% csrf_token %} username:<input type="text" name="username"><br> password:<input type="password" name="password"><br> <img src="/verify_code"><input type="text" name="verify_code"><br> <input type="submit" value="提交"> </form> </body> </html>
在视图views.py的login_check中添加验证
def login_check(request): username = request.POST.get('username') # 获取用户名 password = request.POST.get('password') # 获取密码 verifycode_input = request.POST.get('verify_code') # 获取验证码 verifycode_session = request.session['verifycode'] # 获取session中存储的验证码 # 校验验证码 if verifycode_input != verifycode_session: return redirect('/login') # 校验用户名密码是否正确 if username == 'circle' and password == '123': request.session['username'] = username request.session['isLogin'] = True return redirect('/post') else: return redirect('/login')
访问127.0.0.1:8000/login,如果验证码输入错误会重新返回到login这个页面
反向解析
如果在视图,模板中使用硬编码连接(也就说把地址写死),在url配置发生改变时,需要变更的代码会非常多,这样导致我们的代码结构不是很容易维护,使用反向解析可以提高我们代码的扩展性和可维护性。
第一步:在项目中urls.py中的include添加namespace
path('', include("school.urls", namespace='school')),
结果悲剧发生,报错信息如下:
django.core.exceptions.ImproperlyConfigured: Specifying a namespace in include() without providing an app_name is not supported.
Set the app_name attribute in the included module,
or pass a 2-tuple containing the list of patterns and app_name instead.
# 翻译过来大概意思
在include()指定一个 namespace必须提供app_name,在include模块中设置一个app_name属性,
或者传递一个包含规则和app_name的2个元素的元祖
修改结果为:
from django.contrib import admin from django.urls import path, include school_app_patterns = ("school.urls",'school') urlpatterns = [ path('admin/', admin.site.urls), # path('', include("school.urls", namespace='school')), path('', include(school_app_patterns, namespace='school')), ]
第二步:在应用下的urls.py中为url定义name属性
re_path(r"^$", views.index, name='index'),
视图 views.py 定义reverse_url
def reverse_url(request): return render(request, 't_school/reverse_url.html')
URL urls.py 添加 url
re_path(r'^reverse_url$', views.reverse_url),
Templates 创建reverse_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>反向解析</title> </head> <body> 普通链接<a href="/">首页</a><br> 反向链接<a href="{% url 'school:index' %}">首页</a><br> </body> </html>
访问127.0.0.1:8000/reverse_url,查看网页源码,会发现反向链接自己动态的生成了url,这里如果我们更改一下urls.py中的正则,对比一下普通和反向的区别
# re_path(r"^$", views.index, name='index'), re_path(r"^index$", views.index, name='index'),
这里我们可以看出反向的链接自动生成了,更改配置后的路径
URL的参数
有些url配置项正则表达式中是有参数的,接下来讲解如何传递参数。
应用下的urls.py 添加url
re_path(r'^reverse_url_args/(\d+)/(\d+)$', views.reverse_url_args, name='reverse_url_args'),
视图 定义reverse_url_args
def reverse_url_args(request, a, b): return HttpResponse(a + ":" + b)
Templates 下修改reverse_url.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>反向解析</title> </head> <body> 普通链接<a href="/">首页</a><br> 反向链接<a href="{% url 'school:index' %}">首页</a><br> 普通链接<a href="/reverse_url_args/2/3">/views.reverse_url_args/2/3</a><br> 反向链接<a href="{% url 'school:reverse_url_args' 2 3 %}">/views.reverse_url_args/2/3</a><br> </body> </html>
{% url 'school:reverse_url_args' 2 3 %} 这里school是namespace中的值 这里reverse_url_args是URL中name的值 这里 'school:reverse_url_args'之后加上空格加上第一个参数2,在加上空格加上第二个参数3
访问127.0.0.1:8000/reverse_url
查看源码同样反向链接中的href也是动态生成的
URL的关键字参数
首先在urls.py 中添加带有关键字参数的路径
re_path(
r'^reverse_url_kwargs/(?P<username>\w+)$',
views.reverse_url_kwargs,
name='reverse_url_kwargs'),
视图 定义reverse_url_kwargs
def reverse_url_kwargs(request, username): return HttpResponse(username)
Templates中添加刚才的路径a标签,
普通链接<a href="/reverse_url_kwargs/circle">/views.reverse_url_args/2/3</a><br> 反向链接<a href="{% url 'school:reverse_url_kwargs' username='circle' %}">/views.reverse_url_args/2/3</a><br>
访问127.0.0.1:8000/reverse_url
1.视图里使用反向解析
视图views.py中定义reverse_redirect
from django.urls import reverse def reverse_redirect(request): # 重定向到index # 写死路径的方式 # return redirect('/index') # 使用反向解析 url = reverse('school:index') return redirect(url)
应用下的urls.py 中配置reverse_redirect
re_path(r'^reverse_redirect$', views.reverse_redirect)
访问127.0.0.1:8000/reverse_redirect,会直接返回index页面
2.视图里使用反向解析带有位置参数的url
视图中修改reverse_redirect
def reverse_redirect(request): # 重定向到reverse_url_args url = reverse('school:reverse_url_args', args=(2,3)) return redirect(url)
访问127.0.0.1:8000/reverse_redirect,会直接返回reverse_url_args
页面
3.视图里使用反向解析带有关键字参数的url
视图中修改reverse_redirect
from django.urls import reverse def reverse_redirect(request): # 重定向到reverse_url_kwargs url = reverse('school:reverse_url_kwargs', kwargs={'username' : 'circle'}) return redirect(url)
访问127.0.0.1:8000/reverse_redirect,会直接返回reverse_url_kwargs
页面