Django入门必经之路:如何掌握路由、视图和模板?
- 内容介绍
- 文章标签
- 相关推荐
本文共计4596个文字,预计阅读时间需要19分钟。
Django基础三之路由、视图、模板
1. Django请求和返回周期 1.1 路由层之路由匹配 1.2 有名分组 1.3 无名分组
2. 反射解析
3. 路由分部开发
4. 命名空间
5. JS对象
Django基础三之路由、视图、模板 目录- Django基础三之路由、视图、模板
- 1. Django 请求和返回周期
- 1.1 路由层之路由匹配
- 1.2 有名分组
- 1.3 无名分组
- 2. 反射解析
- 3. 路由分发
- 4 名称空间
- 5. JsonResponse
- 6. 上传文件
- 7. FBV和CBV
- 8. 模板语法传值
- 8.1 传基本数据类型
- 8.2 传函数名
- 8.3 传类名
- 9. 模板语法获取值
- 10. 模板语法过滤器
- 11. 模板语法标签(流程控制)
- 12. 自定义过滤器、标签、inclusion_tag
- 12.1 自定义过滤器:
- 12.2 自定义标签
- 12.3 自定义inclusion_tag
- 13. 模板的导入
- 14. 模板的继承
- 1. Django 请求和返回周期
Django默认使用wsgiref模块但是该模块并发量特别小(大约1000),不适用于线上环境,所以在Django项目上线之后会使用uwsgi。
主要是在ursl.py文件里书写。
1.11版本:
urlpatterns = [
url('^admin/', admin.site.urls),
]
3.2版本:
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.test),
path('testadd/', views.testadd),
]
1版本中使用url方法:
url()方法:
1,第一个参数为一个正则
2,只要能匹配上就会执行后面的视图函数
3版本中使用path
path()方法
第一个参数是一个字符串
如果使用正则,则要使用 re_path() 而不是 path() 。
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
]
test/和testadd/ 在匹配的时候如果不写后面的斜杠(/),发现也能匹配上,是因为Django在做的时候如果test匹配不上,它会让浏览器后面自动加上斜杠(/)再试一次。
这个是用settings里面的APPEND_SLASH参数控制,默认为True,如果只想匹配一次则设置为False.
APPEND_SLASH=False
在Django3.x在匹配时有了路径转换器:
1.2 有名分组
str- 匹配除了'/'之外的非空字符串。如果表达式内不包含转换器,则会默认匹配字符串。
int- 匹配 0 或任何正整数。返回一个int。
path('articles/<int:year>/', views.year_archive), <int:year>是个整型参数
slug- 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。比如,building-your-1st-django-site。
uuid- 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个UUID实例。
path- 匹配非空字段,包括路径分隔符'/'。它允许你匹配完整的 URL 路径而不是像str那样匹配 URL 的一部分。
命名正则表达式组的语法是 (?P<name>pattern) 其中 name 是组名,pattern 是要匹配的模式
在Django3中路由匹配使用正则:
ursl.py文件:
from django.contrib import admin
from django.urls import path,re_path #要手动导入re_path
from orm import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.test),
path('testadd/', views.testadd),
re_path(r'test/(?P<year>[0-9]{4})/', views.testadd),
]
在views.py:
def testadd(request,year):
print(year)
return HttpResponse("from test")
// 分组名必须要传给后面的视图函数,否则会报错。
如上面的例子,分组名为year,如果不传给后端的views.testadd函数,报错信息:
testadd() got an unexpected keyword argument 'year'
有名分组
将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数
1.3 无名分组
有命名组语法,例如 (?P<year>[0-9]{4}) ,你也可以使用更短的未命名组,例如 ([0-9]{4}) 。
在Django3中路由匹配使用正则:
ursl.py文件:
from django.contrib import admin
from django.urls import path,re_path #要手动导入re_path
from orm import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.test),
path('testadd/', views.testadd),
re_path(r'test/([0-9]{4})/$', views.test),
]
启动访问:
127.0.0.1:8000/test/1234/
报错:
test() takes 1 positional argument but 2 were given
解决方法:
在views.py:
def test(request,what):
print(what)
return HttpResponse("from test")
再执行访问成功。
控制台打印的结果:
1234
无名分组:
将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数。
总结:
- 有名分组和无名分组不能混合使用。
- 单个种类可以重复使用
当路由频繁变化的时候,HTML界面上的连接地址如何做到动态解析。
"""
1. 给路由与视图函数对应关系添加一个别名(名字自己定义,名字之间不要冲突)
path('show/', views.show, name='showtime'),
2. 根据这个别名动态解析出一个结果,该结果可以直接访问到对应的路由
前端使用别名:
<a href="{% url 'showtime' %}"><h1>Hello Django</h1></a>
这样不管path里面的show怎么变,只要name='showtime'不变,那么访问就没问题
后端使用别名:
ursl.py:
urlpatterns = [
path('show/', views.show, name='showtime'),
]
views.py
from django.shortcuts import render, HttpResponse,redirect,reverse
def delete(request):
......
print(reverse('showtime')) # 打印url
return redirect('showtime') # 也可以直接在重定向里写别名
"""
无名和有名分组指向解析
ursl.py
"""
from django.urls import path,re_path
urlpatterns = [
re_path(r'test/([0-9]{4})/$', views.test, name='index_test'),
]
"""
views.py
"""
def delete(request):
......
print(reverse('index_test',args=(1,))) # 打印url
args=(1,) args后面跟一个元组,里面这写的是1,推荐写主键的值
r'test/([0-9]{4})/([0-9]{4})/$ 如果有两个分组,则args后面必须写两个值,(1,2)第二个值可以随便写
"""
前端也一样:
<a href="{% url 'index_test' 123 %}"><h1>Hello Django</h1></a>
这里123也是随便写的,只要写个数字就行
有名:
后端
reverse('index_test',kwargs={'id':123})
前端
<a href="{% url 'index_test' id=123 %}"><h1>Hello Django</h1></a>
总结
无名和有名都可以使用一种(无名)反向解析的形式
3. 路由分发其实Django中的每一个应用都可以有自己的urls.py、static文件夹、templates文件夹,这样使用Django做分组开发非常的简便。每个人只需要写息的应用即可,最后汇总到一个空的Django项目中然后使用路由分发将多个应用关联。
示例:
创建一个项目,并创建两个应用(app01,app02).
在每个应用里面都创建一个urls.py文件。
app01 urls.py:
"""
from django.urls import path
from app01 import views
urlpatterns = [
path('index', views.index),
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(request):
return HttpResponse("from app01 index")
"""
app02 urls.py:
"""
from django.urls import path
from app02 import views
urlpatterns = [
path('index', views.index),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(request):
return HttpResponse("from app02 index")
"""
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
# 导入应用的urls
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include(app01_urls)),
path('app02/', include(app02_urls)),
]
"""
注意:
需要在总的urls.py里导入include
from django.urls import path,include
在总的路由里面不能加$符号,否则没办法分发
还有一种在总的urls.py里不需要导入应用urlsr 的方法:
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls')),
]
"""
4 名称空间
当多个应用在反射解析的时候如果出现了别名冲突的情况,那么将会无法自动识别
示例:
app01 urls.py:
"""
from django.urls import path
from app01 import views
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login)
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app01 index")
def login(request):
print(reverse('index_name'))
return HttpResponse("from app01 login")
"""
app02 urls.py:
"""
from django.urls import path
from app02 import views
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app02 index")
def login(request):
print(reverse('index_name'))
return HttpResponse("from app02 login")
"""
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls')),
]
"""
虽然访问页面:
127.0.0.1:8000/app01/login
127.0.0.1:8000/app02/login
的时候能正常拿到对应的页面,但是在后端发现拿到的是同一个:
/app02/index
/app02/index
要解决这个问题就用到了名称空间
解决方法一:使用名称空间
在总路上加上namespace这个参数:
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls',namespace='app01')),
path('app02/', include('app02.urls',namespace='app02')),
]
"""
app01 urls.py:
"""
from django.urls import path
from app01 import views
app_name='app01'
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login)
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app01 index")
def login(request):
print(reverse('app01:index_name'))
return HttpResponse("from app01 login")
"""
app02 urls.py:
"""
from django.urls import path
from app02 import views
app_name='app02'
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app02 index")
def login(request):
print(reverse('app02:index_name'))
return HttpResponse("from app02 login")
"""
访问页面:
127.0.0.1:8000/app01/login
127.0.0.1:8000/app02/login
拿到的就是
/app01/index
/app02/index
注意在Django3.2版本中使用名称空间的时候,一定要给每个应用设置应用名,否则会报错:
'''pecifying 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.'''
解决方法:
app01 urls.py:
'''
app_name='app01'
'''
app02 urls.py:
'''app_name='app02''''
这两个必须要设置。
前端使用名称空间:
<a href="{% url 'app01:index_name' %}">app01_index</a>
<a href="{% url 'app02:index_name' %}">app02_index</a>
注意:
虽然我们现在可以将模板文件直接放在
app01/templates文件夹中(而不是再建立一个app01子文件夹),但是这样做不太好。Django 将会选择第一个匹配的模板文件,如果你有一个模板文件正好和另一个应用中的某个模板文件重名,Django 没有办法 区分 它们。我们需要帮助 Django 选择正确的模板,最好的方法就是把他们放入各自的 命名空间 中,也就是把这些模板放入一个和 自身 应用重名的子文件夹里。(app01/templates/app01/login.html)同理:多个应用下的静态文件也是这样。
所以在前端使用名称空间的时候,HTML文件的路径为:
app01/templates/app01/login.html
app02/templates/app02/login.html
后端app01 views.py:
from django.shortcuts import render,HttpResponse,reverse
def login(request):
print(reverse('app01:index_name'))
return render(request, "app01/login.html")
后端app02 views.py:
from django.shortcuts import render,HttpResponse,reverse
def login(request):
print(reverse('app02:index_name'))
return render(request, "app02/login.html")
解决方法二:别名别冲突
写别名的时候要加上自己应用名做前缀。
5. JsonResponse给前端返回一个json格式的数据
方法一:自己序列化
views.py:
from django.shortcuts import render,HttpResponse,reverse
import json
def index(request):
d = {'user':'Hans', 'password':123}
d_json = json.dumps(d)
return HttpResponse(d_json)
# json默认不能直接识别的字符直接返回对应的unicode编码,如上面的汉字要正确返回则需要设置ensure_ascii=False
d = {'user':'Hans你好', 'password':123}
d_json = json.dumps(d,ensure_ascii=False)
方法二: 使用JsonResponse
views.py:
from django.shortcuts import render,HttpResponse,reverse
from django.cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form>
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</body>
</html>
模板导入:
需要用到模板的页面:
index.html
<body>
<div>
{% include 'form.html' %}
</div>
</body>
14. 模板的继承
示例:
主页home.html(模板)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<!--左侧-->
<div class="list-group col-md-2">
<a href="#" class="list-group-item active">
Cras justo odio
</a>
<a href="/compute/" class="list-group-item">电脑</a>
<a href="/phone/" class="list-group-item">手机</a>
<a href="/beauty/" class="list-group-item">beauty</a>
<a href="#" class="list-group-item">Vestibulum at eros</a>
</div>
<!--右侧-->
{% block content %}
<div class="jumbotron col-md-10">
<h1>Hello, world!</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
{% endblock %}
</div>
</div>
</body>
</html>
<!--
{% block content %}
这个区域做了标记,这个区域是可以修改的。
content 这个名字可以随意起
{% endblock %}
-->
电脑(compute.html)页面继承home.html
{% extends 'home.html' %}
{% block content %}
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="www.apple.com.cn/shop/buy-mac/macbook-pro/MK1A3CH/A" class="thumbnail">
<img src="store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/mbp16-spacegray-gallery1-202110_GEO_CN?wid=4000&hei=3072&fmt=jpeg&qlt=80&.v=1633656602000">
</a>
</div>
</div>
{% endblock %}
<!--
{% extends 'home.html' %} 继承home.html
{% block content %}
这个区域写homecompute自己的内容
{% endblock %}
-->
手机(phone.html)页面继承home.html
{% extends 'home.html' %}
{% block content %}
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="gimg2.baidu.com/image_search/src=gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2110021F21V024-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1648559897&t=c5b2a29fac9a42cb0f7019ea13b40d21">
</a>
</div>
</div>
{{ block.super }}
{% endblock %}
<!--
{{ block.super }} 为模板内被标记的区域
-->
模板在标记区域的时候一般有三个区域
- CSS区域
- HTML区域
- JS区域
目的是为了让继承的子模板具有独立的CSS和JS,增加扩展性
<head>
{% balock css %}
css 样式
{% endblock %}
</head>
<body>
{% balock html %}
html内容
{% endblock %}
{% balock js %}
js 内容
{% endblock %}
</body>
子板也可以使用模板标记的区域的内容:
{{ block.super }}
本文共计4596个文字,预计阅读时间需要19分钟。
Django基础三之路由、视图、模板
1. Django请求和返回周期 1.1 路由层之路由匹配 1.2 有名分组 1.3 无名分组
2. 反射解析
3. 路由分部开发
4. 命名空间
5. JS对象
Django基础三之路由、视图、模板 目录- Django基础三之路由、视图、模板
- 1. Django 请求和返回周期
- 1.1 路由层之路由匹配
- 1.2 有名分组
- 1.3 无名分组
- 2. 反射解析
- 3. 路由分发
- 4 名称空间
- 5. JsonResponse
- 6. 上传文件
- 7. FBV和CBV
- 8. 模板语法传值
- 8.1 传基本数据类型
- 8.2 传函数名
- 8.3 传类名
- 9. 模板语法获取值
- 10. 模板语法过滤器
- 11. 模板语法标签(流程控制)
- 12. 自定义过滤器、标签、inclusion_tag
- 12.1 自定义过滤器:
- 12.2 自定义标签
- 12.3 自定义inclusion_tag
- 13. 模板的导入
- 14. 模板的继承
- 1. Django 请求和返回周期
Django默认使用wsgiref模块但是该模块并发量特别小(大约1000),不适用于线上环境,所以在Django项目上线之后会使用uwsgi。
主要是在ursl.py文件里书写。
1.11版本:
urlpatterns = [
url('^admin/', admin.site.urls),
]
3.2版本:
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.test),
path('testadd/', views.testadd),
]
1版本中使用url方法:
url()方法:
1,第一个参数为一个正则
2,只要能匹配上就会执行后面的视图函数
3版本中使用path
path()方法
第一个参数是一个字符串
如果使用正则,则要使用 re_path() 而不是 path() 。
urlpatterns = [
re_path(r'^admin/', admin.site.urls),
]
test/和testadd/ 在匹配的时候如果不写后面的斜杠(/),发现也能匹配上,是因为Django在做的时候如果test匹配不上,它会让浏览器后面自动加上斜杠(/)再试一次。
这个是用settings里面的APPEND_SLASH参数控制,默认为True,如果只想匹配一次则设置为False.
APPEND_SLASH=False
在Django3.x在匹配时有了路径转换器:
1.2 有名分组
str- 匹配除了'/'之外的非空字符串。如果表达式内不包含转换器,则会默认匹配字符串。
int- 匹配 0 或任何正整数。返回一个int。
path('articles/<int:year>/', views.year_archive), <int:year>是个整型参数
slug- 匹配任意由 ASCII 字母或数字以及连字符和下划线组成的短标签。比如,building-your-1st-django-site。
uuid- 匹配一个格式化的 UUID 。为了防止多个 URL 映射到同一个页面,必须包含破折号并且字符都为小写。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个UUID实例。
path- 匹配非空字段,包括路径分隔符'/'。它允许你匹配完整的 URL 路径而不是像str那样匹配 URL 的一部分。
命名正则表达式组的语法是 (?P<name>pattern) 其中 name 是组名,pattern 是要匹配的模式
在Django3中路由匹配使用正则:
ursl.py文件:
from django.contrib import admin
from django.urls import path,re_path #要手动导入re_path
from orm import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.test),
path('testadd/', views.testadd),
re_path(r'test/(?P<year>[0-9]{4})/', views.testadd),
]
在views.py:
def testadd(request,year):
print(year)
return HttpResponse("from test")
// 分组名必须要传给后面的视图函数,否则会报错。
如上面的例子,分组名为year,如果不传给后端的views.testadd函数,报错信息:
testadd() got an unexpected keyword argument 'year'
有名分组
将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数
1.3 无名分组
有命名组语法,例如 (?P<year>[0-9]{4}) ,你也可以使用更短的未命名组,例如 ([0-9]{4}) 。
在Django3中路由匹配使用正则:
ursl.py文件:
from django.contrib import admin
from django.urls import path,re_path #要手动导入re_path
from orm import views
urlpatterns = [
path('admin/', admin.site.urls),
path('test/', views.test),
path('testadd/', views.testadd),
re_path(r'test/([0-9]{4})/$', views.test),
]
启动访问:
127.0.0.1:8000/test/1234/
报错:
test() takes 1 positional argument but 2 were given
解决方法:
在views.py:
def test(request,what):
print(what)
return HttpResponse("from test")
再执行访问成功。
控制台打印的结果:
1234
无名分组:
将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数。
总结:
- 有名分组和无名分组不能混合使用。
- 单个种类可以重复使用
当路由频繁变化的时候,HTML界面上的连接地址如何做到动态解析。
"""
1. 给路由与视图函数对应关系添加一个别名(名字自己定义,名字之间不要冲突)
path('show/', views.show, name='showtime'),
2. 根据这个别名动态解析出一个结果,该结果可以直接访问到对应的路由
前端使用别名:
<a href="{% url 'showtime' %}"><h1>Hello Django</h1></a>
这样不管path里面的show怎么变,只要name='showtime'不变,那么访问就没问题
后端使用别名:
ursl.py:
urlpatterns = [
path('show/', views.show, name='showtime'),
]
views.py
from django.shortcuts import render, HttpResponse,redirect,reverse
def delete(request):
......
print(reverse('showtime')) # 打印url
return redirect('showtime') # 也可以直接在重定向里写别名
"""
无名和有名分组指向解析
ursl.py
"""
from django.urls import path,re_path
urlpatterns = [
re_path(r'test/([0-9]{4})/$', views.test, name='index_test'),
]
"""
views.py
"""
def delete(request):
......
print(reverse('index_test',args=(1,))) # 打印url
args=(1,) args后面跟一个元组,里面这写的是1,推荐写主键的值
r'test/([0-9]{4})/([0-9]{4})/$ 如果有两个分组,则args后面必须写两个值,(1,2)第二个值可以随便写
"""
前端也一样:
<a href="{% url 'index_test' 123 %}"><h1>Hello Django</h1></a>
这里123也是随便写的,只要写个数字就行
有名:
后端
reverse('index_test',kwargs={'id':123})
前端
<a href="{% url 'index_test' id=123 %}"><h1>Hello Django</h1></a>
总结
无名和有名都可以使用一种(无名)反向解析的形式
3. 路由分发其实Django中的每一个应用都可以有自己的urls.py、static文件夹、templates文件夹,这样使用Django做分组开发非常的简便。每个人只需要写息的应用即可,最后汇总到一个空的Django项目中然后使用路由分发将多个应用关联。
示例:
创建一个项目,并创建两个应用(app01,app02).
在每个应用里面都创建一个urls.py文件。
app01 urls.py:
"""
from django.urls import path
from app01 import views
urlpatterns = [
path('index', views.index),
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(request):
return HttpResponse("from app01 index")
"""
app02 urls.py:
"""
from django.urls import path
from app02 import views
urlpatterns = [
path('index', views.index),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(request):
return HttpResponse("from app02 index")
"""
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
# 导入应用的urls
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include(app01_urls)),
path('app02/', include(app02_urls)),
]
"""
注意:
需要在总的urls.py里导入include
from django.urls import path,include
在总的路由里面不能加$符号,否则没办法分发
还有一种在总的urls.py里不需要导入应用urlsr 的方法:
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls')),
]
"""
4 名称空间
当多个应用在反射解析的时候如果出现了别名冲突的情况,那么将会无法自动识别
示例:
app01 urls.py:
"""
from django.urls import path
from app01 import views
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login)
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app01 index")
def login(request):
print(reverse('index_name'))
return HttpResponse("from app01 login")
"""
app02 urls.py:
"""
from django.urls import path
from app02 import views
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app02 index")
def login(request):
print(reverse('index_name'))
return HttpResponse("from app02 login")
"""
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls')),
path('app02/', include('app02.urls')),
]
"""
虽然访问页面:
127.0.0.1:8000/app01/login
127.0.0.1:8000/app02/login
的时候能正常拿到对应的页面,但是在后端发现拿到的是同一个:
/app02/index
/app02/index
要解决这个问题就用到了名称空间
解决方法一:使用名称空间
在总路上加上namespace这个参数:
项目中总的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/', include('app01.urls',namespace='app01')),
path('app02/', include('app02.urls',namespace='app02')),
]
"""
app01 urls.py:
"""
from django.urls import path
from app01 import views
app_name='app01'
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login)
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app01 index")
def login(request):
print(reverse('app01:index_name'))
return HttpResponse("from app01 login")
"""
app02 urls.py:
"""
from django.urls import path
from app02 import views
app_name='app02'
urlpatterns = [
path('index', views.index,name='index_name'),
path('login', views.login),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse
# Create your views here.
def index(request):
return HttpResponse("from app02 index")
def login(request):
print(reverse('app02:index_name'))
return HttpResponse("from app02 login")
"""
访问页面:
127.0.0.1:8000/app01/login
127.0.0.1:8000/app02/login
拿到的就是
/app01/index
/app02/index
注意在Django3.2版本中使用名称空间的时候,一定要给每个应用设置应用名,否则会报错:
'''pecifying 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.'''
解决方法:
app01 urls.py:
'''
app_name='app01'
'''
app02 urls.py:
'''app_name='app02''''
这两个必须要设置。
前端使用名称空间:
<a href="{% url 'app01:index_name' %}">app01_index</a>
<a href="{% url 'app02:index_name' %}">app02_index</a>
注意:
虽然我们现在可以将模板文件直接放在
app01/templates文件夹中(而不是再建立一个app01子文件夹),但是这样做不太好。Django 将会选择第一个匹配的模板文件,如果你有一个模板文件正好和另一个应用中的某个模板文件重名,Django 没有办法 区分 它们。我们需要帮助 Django 选择正确的模板,最好的方法就是把他们放入各自的 命名空间 中,也就是把这些模板放入一个和 自身 应用重名的子文件夹里。(app01/templates/app01/login.html)同理:多个应用下的静态文件也是这样。
所以在前端使用名称空间的时候,HTML文件的路径为:
app01/templates/app01/login.html
app02/templates/app02/login.html
后端app01 views.py:
from django.shortcuts import render,HttpResponse,reverse
def login(request):
print(reverse('app01:index_name'))
return render(request, "app01/login.html")
后端app02 views.py:
from django.shortcuts import render,HttpResponse,reverse
def login(request):
print(reverse('app02:index_name'))
return render(request, "app02/login.html")
解决方法二:别名别冲突
写别名的时候要加上自己应用名做前缀。
5. JsonResponse给前端返回一个json格式的数据
方法一:自己序列化
views.py:
from django.shortcuts import render,HttpResponse,reverse
import json
def index(request):
d = {'user':'Hans', 'password':123}
d_json = json.dumps(d)
return HttpResponse(d_json)
# json默认不能直接识别的字符直接返回对应的unicode编码,如上面的汉字要正确返回则需要设置ensure_ascii=False
d = {'user':'Hans你好', 'password':123}
d_json = json.dumps(d,ensure_ascii=False)
方法二: 使用JsonResponse
views.py:
from django.shortcuts import render,HttpResponse,reverse
from django.cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form>
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</body>
</html>
模板导入:
需要用到模板的页面:
index.html
<body>
<div>
{% include 'form.html' %}
</div>
</body>
14. 模板的继承
示例:
主页home.html(模板)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<nav class="navbar navbar-inverse">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul>
</li>
</ul>
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li role="separator" class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul>
</li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /.container-fluid -->
</nav>
<!--左侧-->
<div class="list-group col-md-2">
<a href="#" class="list-group-item active">
Cras justo odio
</a>
<a href="/compute/" class="list-group-item">电脑</a>
<a href="/phone/" class="list-group-item">手机</a>
<a href="/beauty/" class="list-group-item">beauty</a>
<a href="#" class="list-group-item">Vestibulum at eros</a>
</div>
<!--右侧-->
{% block content %}
<div class="jumbotron col-md-10">
<h1>Hello, world!</h1>
<p>...</p>
<p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
</div>
{% endblock %}
</div>
</div>
</body>
</html>
<!--
{% block content %}
这个区域做了标记,这个区域是可以修改的。
content 这个名字可以随意起
{% endblock %}
-->
电脑(compute.html)页面继承home.html
{% extends 'home.html' %}
{% block content %}
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="www.apple.com.cn/shop/buy-mac/macbook-pro/MK1A3CH/A" class="thumbnail">
<img src="store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/mbp16-spacegray-gallery1-202110_GEO_CN?wid=4000&hei=3072&fmt=jpeg&qlt=80&.v=1633656602000">
</a>
</div>
</div>
{% endblock %}
<!--
{% extends 'home.html' %} 继承home.html
{% block content %}
这个区域写homecompute自己的内容
{% endblock %}
-->
手机(phone.html)页面继承home.html
{% extends 'home.html' %}
{% block content %}
<div class="row">
<div class="col-xs-6 col-md-3">
<a href="#" class="thumbnail">
<img src="gimg2.baidu.com/image_search/src=gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2110021F21V024-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1648559897&t=c5b2a29fac9a42cb0f7019ea13b40d21">
</a>
</div>
</div>
{{ block.super }}
{% endblock %}
<!--
{{ block.super }} 为模板内被标记的区域
-->
模板在标记区域的时候一般有三个区域
- CSS区域
- HTML区域
- JS区域
目的是为了让继承的子模板具有独立的CSS和JS,增加扩展性
<head>
{% balock css %}
css 样式
{% endblock %}
</head>
<body>
{% balock html %}
html内容
{% endblock %}
{% balock js %}
js 内容
{% endblock %}
</body>
子板也可以使用模板标记的区域的内容:
{{ block.super }}

