Django 带参数的路由

path对象

Django使用path对象来定义路由列表,path有四个参数:

  • 模式串:匹配用户请求路径的字符串
  • 视图函数:匹配上用户请求路径后调用的视图函数
  • kwaergs:可选参数,需要额外传递的参数,是一个字典
  • 名称(name):给路由命名,在代码中可以使用name进行方向解析(由name获取用户请求路径)

当path中模式串不能满足你的路由规则,还可以使用re_path对象,re_path对象中模式串是正则表达式,其他三个参数和path对象一致。

path注意

  • Django从上往下进行匹配,一旦匹配成功就返回
  • 一个视图函数可以有多个模式匹配
  • 模式前面不需要加’/‘
  • 如果匹配失败,则引起异常,Django会调用错误处理视图函数(关闭DEBUG模式)

path匹配规则

  • str 字符串参数可以匹配除/和空字符的其他字符串
  • int 匹配0和整整数,视图函数的参数将得到一个整型值
  • slug 匹配由数字、字母、和_组成的字符串参数
  • path 匹配任何非空字符串,包括/

str写法:path('user/',include("App.urls")),
匹配成功:http://127.0.0.1:8000/user/
匹配失败:http://127.0.0.1:8000/login/

int写法:path('show/<int:age>/',views.show,name="show")
如果匹配字符串写了参数,在views里定义函数就需要参数来接收:

1
2
def show(request,age):
return HttpResponse(str(age))

匹配成功:http://127.0.0.1:8000/user/show/90/
匹配失败:http://127.0.0.1:8000/user/show/ac/

slug写法:path('list/<slug:name>/',views.list,name="list"),
views:

1
2
3
def list(request,name):
print(type(name))
return HttpResponse(name)

只要list/后是大小写字母、数字、下划线、-都可以匹配成功
匹配成功:http://127.0.0.1:8000/user/list/A1_/
匹配失败:http://127.0.0.1:8000/user/list/A1.

path写法:path('path/<path:path>/',views.path,name="path"),
views:

1
2
def path(request,path):
return HttpResponse(path)

该类型匹配除空格外的所有字符串。

re_path

在re_path中,()部分是正则的组,Django在进行url匹配时,就会自动把匹配成功的内容,作为参数传递给视图函数。

re_path无参数匹配手机号:

1
2
3
4
5
#urls路由,无参数
re_path(r'^phone/(1[3-9]\d{9})/$',views.phone,name="phone"),
#views函数
def phone(requestn,phone):
return HttpResponse(phone)

re_path有参数匹配手机号:

1
2
3
4
5
#urls路由,有参数:tel,使用?P标志参数
re_path(r'^phone/(?P<tel>1[3-9]\d{9})/$',views.tel,name="tel"),
#views函数
def tel(request,tel):
return HttpResponse(tel)

Django 视图

视图本质上是一个函数(类)。这个函数第一个参数的类型时Httpreuest(Djiango传参);它返回一个HttpResponse实例。为了使一个Python的函数成为一个Django可以识别的视图,它必须满足这两个条件。

视图作用:接收并处理请求,调用模型和模板,响应请求

  • 响应模板
  • 重定向
  • 直接响应字符串
  • 响应错误模板
  • json数据

HttpRequest

HttpRequest是从web服务器传递过来的请求对象,经过Django框架封装产生的,封装了原始的Http请求。

  • 服务器接收到Http请求后,Django框架会自动根据服务器传递的环境变量创建HttpRequest对象
  • 视图的第一个参数必须是HttpRequest类型的对象
  • 在Django.http模块中定义了HttpPeruest对象的API
  • 使用HttpRequest对象的不同属性值,可以获取请求中多种信息

常用类型都可以使用这样的方式获取:
获取COOKIES数据:print(request.COOKIES)
获取POST的username的值:print(request.GET.get('username'))
获取POST的username数组字典:print(request.GET.getlist('username'))

QuertDict=>Dict:
print(request.GET.dict())

HttpResponse

每一个视图函数必须返回一个响应对象,HttpResponse对象由程序员创建并返回。

参数 说明
content 字节字符串
charset 字符编码
status_code Http状态码
conten_type 指定输出的MIME类型

不调用模板

1
2
3
4
5
6
def post(request):
res = HttpResponse()
res.content = b'Hello Wolrd'
res.content_type = 'text/html'
res.status_code = 200
return res

调用模块返回

一般使用render函数返回,render只是HttpResponse的包装,还是会返回一个HttpResponse对象。

参数 说明
request HttpRequest的request参数
template_name HTTP渲染模板
context 渲染的字典文件,默认为空
content_type MIME类型,用于生成文档
status Http响应码,默认200

重定向

使用redirect函数来对网页进行重定向到网站首页。

1
2
def red(request):
return redirect('/')

反向引用

通过name取得路由表地址,首先在路由表”urls.py”中设置路由命名空间:app_name = "App"
调用的时候使用”命名空间:路由名称”调用,实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# urls.py
from django.urls import path, re_path

from App import views

app_name = "App"
urlpatterns = (
path('', views.hoxme, name='home'),
path('post/', views.post, name='post'),
path('red/', views.red, name='red'),

)

# views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
# Create your views here.
from django.urls import reverse

def hoxme(request):
return HttpResponse('Home')

def post(request):
res = HttpResponse("Hello Django")
return res

def red(request):
print(reverse("App:post"))
return redirect(reverse("App:post"))

有参调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# urls.py
from django.urls import path, re_path

from App import views

app_name = "App"
urlpatterns = (
path('', views.hoxme, name='home'),
path('post/<name>/<age>/', views.post, name='post'),
path('red/', views.red, name='red'),

)

# views.py
def post(request,name,age):
res = HttpResponse("Hello,{}<br>你是{}岁吗?".format(name,age))
return res

def red(request):
return redirect(reverse("App:post",kwargs={'name':'Lili','age':'20'}))

错误视图

Django内置了处理HTTP错误的视图(在django.views.defaults包下),主要错误视图包括:

  • 403错误:permission_denied (拒绝访问)
  • 404错误:page_not_found (文件不存在)
  • 500错误:server_error (服务器内部错误)

如果开启DEBUG模式,出错了Django会调用默认模板,如果DEBUG=False;在模板路径下创建404.html、500.html、403.heml,出错后Django会自动调用该模板。

Django 模板

模板用于快速生成动态页面返回给客户端,模板是一个文本。用于分离文档的表现形式和内容。模板定义了占位符以及各种用于规范文档该如何显示的模块标签。模板通常是用于产生HTML,但是Django的模板也能产生任何基于文本格式的文档。模板包含两部分:

  • HTML代码
  • 模板代码

模板位置

  • 在应用中建立templates目录,优点是不需要注册,缺点是多个应用的时候不能复用页面
  • 第二种是放在工程的目录下,优点是如果有多个应用,可以调用相同的页面,需要注册

需要修改项目的配置文件setings.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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',
],
},
},
]

模板渲染

渲染方式:

  • loader 先加载模板,再进行渲染,好处是多次渲染是不用重新加载
  • render 不用加载模板,自动加载,每次渲染都要重新加载一次模板

loader

1
2
3
4
def index(request):
temp = loader.get_template("user/index.html") #加载模板
res = temp.render(context={'title':'loader加载'}) #渲染模板
return HttpResponse(res)

render

1
2
def index(request):
return render(request,'user/index.html',context={'var':'render自动加载后渲染'})

模板语法

Django模板中包括两部分:变量和内置标签。变量会在模板渲染时被其值代替,内置标签负责逻辑控制。

变量

变量在模板中的表示为:{{ 变量名 }},变量名就是 render中 context中的键。变量可以基本类型中的数值、字符串、布尔,也可以是字典、对象、列表等。 django提供了点号来访问复杂数据结构。 - 列表、元组的元素可使用索引引用,不能使用负索引,语法:变量.索引 - 字典:字典变量.key - 对象:对象.属性 对象.方法名(方法不能有参数) 当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找: - 字典类型查找 - 属性查找 - 方法查找 - 列表类型索引 如果模板中引用变量未传值,则会被置为空,不会报错,除非你对其进行了操作。 #### 过滤器 过滤器是在变量显示之前修改它的值得一个方法,过滤器使用管道符。过滤器可以串联调用 > {{ 变量|方法 }} > 内置常见过滤器方法: ![](https://img.xpctf.cn/img/20201015093838.png) - 自定义过滤器 内置过滤器功能有限,如果不能满足需求,可以自己定义过滤器。 - 在应用(app)里创建一个包,templatetags,包名指定的 - 在包里创建一个自定义py文件
1
2
3
4
5
6
7
8
9
10
from django import template

# 建立模板对象
register = template.Library()

# 注册一个过滤器,@符号是修饰符
@register.filter(name='sub1')

def sub1(value,num): #参数最多俩个
return value - num
调用需要在模板里加载自定义过滤器:
1
2
3
4
5
6
7
8
9
10
11
{% load mytag %} {# 加载自定义过滤器 #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>自定义过滤器</title>
</head>
<body>
<h1>{{ var|sub1:5 }}</h1>
</body>
</html>
#### 内建标签 > 语法:{% tag %} >
  1. if标签
1
2
3
4
5
6
7
{% if express1 %}
{# 为真执行 #}
{% elif express2 %}
{# 为真执行 #}
{% else %}
{# 为假执行 #}
{% endif %}
  • if表达式中使⽤以下运算符(优先级从⾼到低):

    • < >= <= == !=
    • is、is not
    • not
    • and
    • or
  • 不要在表达式中使⽤(),可以使⽤if嵌套实现功能

  • 不⽀持 if 3 < b < 5这种写法

  1. for
  • 遍历可迭代对象

    1
    2
    3
    {% for i in y%}
    #代码块
    {% endfor %}
  • 反向迭代(reversed)

1
2
3
{% for value in c [1,2,3,4,5] reversed %}
<span>{{ value }}---</span>
{% endfor %}
  • empty 当可迭代对象为空或不存在时执⾏,否则不执⾏
1
2
3
4
5
{% for value in c %}
<span>{{ value }}---</span>
{% empty %}
数据不存在
{% endfor %}
  • 字典迭代
1
2
3
4
e = {'a1':20,'b1':40}
{% for k,v in e.items %}
<div>{{ k }}---{{ v }}</div>
{% endfor %}
变量名称 变量说明
forloop.counter 获取迭代的索引 从1开始
forloop.counter0 获取迭代的索引 从0开始
forloop.revcounter 迭代的索引从最⼤递减到1
forloop.revcounter0 迭代的索引从最⼤递减到0
forloop.first 是否为第⼀次迭代
forloop.last 是否为最后⼀次迭代
forloop.parentloop 获取上层的迭代对象
1
2
3
4
5
6
7
8
{% for i in c %}
<li>{{ forloop.first }}</li>
<li>{{ forloop.last }}</li>
<li>{{ forloop.counter }}</li>
<li>{{ forloop.counter0 }}</li>
<li>{{ forloop.revcounter }}</li>
<li>{{ forloop.revcounter0 }}</li>
{% endfor %}
  1. ifequal/ifnotequal

⽤于判断两个值相等或不等的

1
2
3
4
{% ifequal var var %}
{% endifequal %}
{% ifnotequal var var %}
{% endifnotequal %}
  1. 注释
  • 单⾏注释
1
{# 注释内容 #}
  • 多⾏注释
1
2
3
{% comment %}
...
{% endcomment %}
  1. 跨站请求伪造 csrf

防⽌⽹站受第三⽅服务器的恶意攻击(确定表单到底是不是本⽹站的表单传递过来 的)。csrf相当于在表达中增加了⼀个隐藏的input框,⽤于向服务器提交⼀个唯⼀ 的随机字符串⽤于服务器验证表单是否是本服务器的表单。

编辑settings.py开启csrf

1
2
3
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]

表单调用:

1
2
3
4
5
<form action="" method="post">
{% csrf_token %}
<input type="text" name="username">
<p><input type="submit"></p>
</form>

全站禁⽤csrf:

1
2
3
4
5
6
7
8
9
10
#在settings中设置
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

局部禁⽤csrf:

1
2
3
4
5
#在不想检验csrf的视图函数前添加装饰器@csrf_exempt。
from django.views.decorators.csrf import csrf_exempt,csrf_protect
@csrf_exempt
def csrf1(request):
pass

ajax验证csrf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Ajax提交数据时候,携带CSRF:
a. 放置在data中携带
<form method="POST" action="/csrf1.html">
{% csrf_token %}
<input id="username" type="text" name="username" />
<input type="submit" value="提交"/>
<a onclick="submitForm();">Ajax提交</a>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
function submitForm(){
var csrf = $('input[name="csrfmiddlewaretoken"]').val();
var user = $('#user').val();
$.ajax({
url: '/csrf1.html',
type: 'POST',
data: { "user":user,'csrfmiddlewaretoken': csrf},
success:function(arg){
console.log(arg);
}
})
}
</script>

注意:

csrf的意义在于 给每⼀个表单都设置⼀个唯⼀的csrf的值 并且cookie也存储⼀份 当提交表单过来的时候 判断cookie中的值 和csrf_token中的值 是否都为本⽹站⽣ 成的 如果验证通过则提交 否则 403

  1. 模板导⼊标签( include)

可以把指定html⽂件代码导⼊到当前⽂件,实现模板代码的复⽤/重⽤。语法格式:

1
{% include '路径/xxx.html' %}
  1. url标签

在模板中url标签可⽤于反向解析

1
2
3
4
5
6
<h2><a href="{% url 'App:index' %}">动态⽣成路由地址不带参的跳转</a>
</h2>
<h2><a href="{% url 'App:args1' 1 2 %}">动态⽣成路由地址带参的跳转</a>
</h2>
<h2><a href="{% url 'App:args1' num1=1 num2=2 %}">动态⽣成路由地址带关
键字参数的跳转</a></h2>

Django 模板继承

在整个⽹站中,如何减少共⽤⻚⾯区域(⽐如站点导航)所引起的重复和冗余代 码?Django 解决此类问题的⾸选⽅法是使⽤⼀种优雅的策略—— 模板继承 。 本质上来说,模板继承就是先构造⼀个基础框架模板,⽽后在其⼦模板中对它所包 含站点公⽤部分和定义块进⾏重载。 - {% extends "父模板路径" %} 继承⽗模板 - {% block %} ⼦模板可以重载这部分内容 - {{ block.super }}调⽤⽗模板的代码 使⽤继承的⼀种常⻅⽅式是下⾯的三层法: - 创建base.html模板,在其中定义站点的主要外观感受。这些都是不常修改甚 ⾄从不修改的部分。 - 为每种类型的⻚⾯创建独⽴的模板,例如论坛⻚⾯或者图⽚库。这些模板拓展 相应的区域模板。 - ⾃⼰的⻚⾯继承⾃模板,覆盖⽗模板中指定block 注意事项: - 如果在模板中使⽤ {% extends %} ,必须保证其为模板中的第⼀个模板标记。 否则,模板继承将不起作⽤。 - ⼀般来说,基础模板中的 {% block %} 标签越多越好。 - 如果发觉⾃⼰在多个模板之间有重复代码,你应该考虑将该代码放置到⽗模板 的某个 {% block %} 中。 不在同⼀个模板中定义多个同名的 {% block %} 。 - 多数情况下, {% extends %} 的参数应该是字符,但是如果直到运⾏时⽅能确 定⽗模板名称,这个参数也 可以是个变量。

Django 静态资源配置

什么是静态资源:css、js、images 需要从外部导⼊的资源

创建static⽂件夹

在项目根目录下创建个static文件夹,用来存储css、js、images等静态资源。

在settings注册

1
2
3
4
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]

在模板中使⽤静态资源

1
2
3
{% load static %}  #放置到模板开头 
<img src="/static/img/img.jpeg" alt=""> #硬编码
<img src="{% static 'img/img.jpeg' %}" alt=""> #动态写法,建议⽤这种

Django 系列文章

Django基础学习:https://www.xpctf.cn/posts/24c5/
Django晋级学习:https://www.xpctf.cn/posts/2f99/
Django模型学习:https://www.xpctf.cn/posts/d0b4/
Django会话学习:https://www.xpctf.cn/posts/7490/
Django Form学习:正在写