我们可以把Django框架里的视图想象成餐厅里的服务员,帮助我们理解它的作用和工作方式。
视图就像服务员
在餐厅里,顾客(用户)进入餐厅(访问网站)后,会提出各种需求(发送请求),比如点菜(获取特定信息)。这时,服务员(视图)就会出现,接收顾客的需求(HttpRequest对象)。服务员会把顾客的需求传达给厨房(模型层和模板层),让厨房准备相应的菜品(处理数据)。最后,服务员把准备好的菜品(响应内容)端给顾客(返回HttpResponse对象)。
视图的工作流程
视图的分类
视图有两种类型,就像餐厅里有不同类型的服务员:
示例说明
假设我们有一个简单的餐厅,顾客可以询问当前的时间。这时,服务员(视图)会看一下手表(获取当前时间),然后告诉顾客现在的时间(返回包含时间信息的响应)。以下是对应的代码示例:
from django.http import HttpResponse
import datetime
def current_datetime(request):
# 获取当前时间
now = datetime.datetime.now()
# 生成包含时间信息的HTML代码
html = "It is now %s." % now
# 返回响应
return HttpResponse(html)
在这个示例中,current_datetime
函数就是一个视图,它接收顾客的请求(request
参数),获取当前时间,生成包含时间信息的HTML代码,并返回一个HttpResponse对象给顾客。
urls.py
文件完成。一个项目含多个 App 时会有多个 urls.py
文件,但不能放在同一目录。一般在项目根目录设根路由 urls.py
,每个 App 下再定义各自的 urls.py
,这是先进的解耦模式。Django的路由系统工作流程
客户端用户的http请求:图的左侧是客户端用户发出的http请求,这些请求通过箭头指向Django路由系统。
from django.contrib import admin # 调用模块导入对象,自带的管理员模块
from django.urls import path # 调用django.urls导入path,这是一个负责URL路由配置的模块
urlpatterns = [
path("admin/", admin.site.urls), # 定义了一个数组,通过path定义具体的路径配置信息
path('hello/', views.hello),
...
]
wsgi.py
接收,传递给URL解析器。urls.py
,按顺序匹配urlpatterns
中的条目,找到匹配的URL后,将请求派发给对应的视图函数或类视图。settings.py
中MIDDLEWARE
顺序经过中间件,中间件可执行多种操作,如修改请求、处理异常等。HttpRequest
对象,处理业务逻辑,查询数据库等,最终返回HttpResponse
对象,可能会使用模板引擎渲染模板。HttpResponse
对象经WSGI层返回给用户浏览器。DEBUG=True
可显示详细错误,生产环境则配置友好错误页面 。在Django中,PATH路径转换器是URL路由里用于匹配和捕获URL特定模式,并将匹配的路径段传递给视图函数的工具 。
内置PATH路径转换器
Django提供了多种内置的PATH路径转换器 :
/
以外的任何非空字符串,若未专门指定转换器,此为默认使用的类型。例如path('v1/users/', views.py)
可匹配v1/users/zyyy
。int
类型。如path('page/', views.py)
能匹配/page/100
。path('detail/', views.py)
可匹配detail/this-is-django
。075194d3 - 6885 - 417e - a8a8 - 6c931e272f00
,返回一个UUID对象。/
。比如path('v1/users/', views.py)
可匹配/v1/goods/a/b/c
。自定义PATH路径转换器
在某些复杂需求下,可自定义路径转换器 :
blog/converters.py
中编写如下代码:class UppercaseConverter:
# 定义正则表达式,仅匹配大写字母
regex = '[A-Z]+'
# 将路径段字符串转换为Python对象(可选,直接返回即可)
def to_python(self, value):
return value
# 将Python对象转换为URL使用的字符串(可选,直接返回即可)
def to_url(self, value):
return value
urls.py
文件中导入并注册:from django.urls import register_converter, path
from blog.converters import UppercaseConverter
# 注册自定义路径转换器
register_converter(UppercaseConverter, 'uppercase')
# 定义一个视图函数作为示例
from django.http import HttpResponse
def greet(request, name):
return HttpResponse(f"Hello, {name}!")
# 使用自定义路径转换器
urlpatterns = [
path('blog/greet//' , greet),
]
启动服务器后,http://127.0.0.1:8000/blog/greet/HELLO/
可匹配成功,返回“Hello, HELLO!”;http://127.0.0.1:8000/blog/greet/hello/
匹配失败,返回404页面 。
在Django中,使用正则表达式可实现复杂的URL模式匹配,主要通过re_path
函数来完成,其提供了基于正则表达式的强大匹配能力,能处理更复杂的URL模式需求,通过自定义正则表达式匹配,可定义灵活且精确的路由规则 。
re_path
基本语法
re_path
的语法格式如下 :
from django.urls import re_path
urlpatterns = [
re_path(r'^pattern$', view_function, name='route_name'),
]
其中:
r’^pattern$’
是正则表达式,用于匹配URL 。^
表示匹配字符串的开始位置,$
表示匹配字符串的结束位置,确保整个URL与正则表达式完全匹配 。view_function
是对应的视图函数,当URL匹配成功时,Django会调用该视图函数处理请求 。name
是路由的名称(可选),可用于反向解析URL 。示例
# urls.py
from django.urls import re_path
from django.http import HttpResponse
# 示例视图函数
def user_profile(request, username):
return HttpResponse(f"User profile: {username}")
# 使用re_path定义路由
urlpatterns = [
re_path(r'^profile/(?P[a-zA-Z0-9_]{3,15})/$' , user_profile),
]
测试时,访问http://127.0.0.1:8000/profile/john_doe/
,会返回User profile: john_doe
;访问http://127.0.0.1:8000/profile/john - doe/
,匹配失败,返回404页面 。这里(?P
是一个命名分组,?P
指定组名为username
,[a-zA-Z0-9_]{3,15}
表示匹配由字母、数字、下划线组成,长度在3到15个字符之间的字符串,并将匹配到的内容作为关键字参数传递给user_profile
视图函数 。
# urls.py
from django.urls import re_path
from django.http import HttpResponse
from datetime import datetime
# 示例视图函数
def event_view(request, year, month, day):
date = f"{year}-{month}-{day}"
return HttpResponse(f"Event date: {date}")
# 使用re_path定义路由
urlpatterns = [
re_path(r'^event/(?P\d{4})/(?P\d{2})/(?P\d{2})/$' , event_view),
]
测试时,访问http://127.0.0.1:8000/event/2024/11/17/
,返回Event date: 2024 - 11 - 17
;访问http://127.0.0.1:8000/event/24/11/17/
,匹配失败。这里(?P
、(?P
和(?P
分别匹配4位数字的年份、2位数字的月份和2位数字的日期,并作为关键字参数传递给event_view
视图函数 。
#RRGGBB
或#RGB
,示例代码如下 :# urls.py
from django.urls import re_path
from django.http import HttpResponse
# 示例视图函数
def color_view(request, color):
return HttpResponse(f"Color: #{color}")
# 使用re_path定义路由
urlpatterns = [
re_path(r'^color/(?P[A - Fa - f0 - 9]{6}|[A - Fa - f0 - 9]{3})/$' , color_view),
]
测试时,访问http://127.0.0.1:8000/color/FF5733/
,返回Color: #FF5733
;访问http://127.0.0.1:8000/color/123/
,返回Color: #123
;访问http://127.0.0.1:8000/color/GG5733/
,匹配失败 。
匹配自定义Slug格式:假设自定义Slug格式由字母、数字、连字符组成,示例代码如下 :
# urls.py
from django.urls import re_path
from django.http import HttpResponse
# 示例视图函数
def article_view(request, slug):
return HttpResponse(f"Article: {slug}")
# 使用re_path定义路由
urlpatterns = [
re_path(r'^article/(?P[a-zA-Z0-9 -]+)/$' , article_view),
]
测试时,访问http://127.0.0.1:8000/article/django - tutorial/
,返回Article: django - tutorial
;访问http://127.0.0.1:8000/article/django_tutorial/
,匹配失败 。
与基于path
的路径转换器相比,re_path
提供了更高的灵活性,适用于复杂的URL模式需求 。
URLconf在请求的URL上查找,将其当作一个普通的Python字符串,查找时不包括GET和POST参数以及域名 。例如,对于请求地址
http://www.example.com/myapp/
,URLconf查找的是myapp/
;对于请求地址http://www.example.com/myapp/?page=3
,URLconf查找的同样是myapp/
。通常情况下,Django确定要使用的根URLconf模块,一般是
settings
中ROOT_URLCONF
设置的值 。但如果传入的HttpRequest
对象具有urlconf
属性(由中间件设置),则其值将用于代替ROOT_URLCONF
设置 。接着,Django加载该Python模块并查找变量urlpatterns
,它是django.conf.urls.url()
实例(在Django 2.x+版本中通常是django.urls.path()
或django.urls.re_path()
实例)的一个Python列表 。Django按顺序遍历每个URL模式,并在与请求的URL匹配的第一个模式停下来 。
在Django中指定视图参数默认值是一个方便的技巧,当URL没有为视图函数的参数提供值时,将使用默认值。以下通过示例说明:
from django.conf.urls import url
from. import views
urlpatterns = (
url(r'^blog/$', views.page),
url(r'^blog/page(?P(0-9)+)/$' , views.page),
)
在视图函数(如blog/views.py)中定义:
def page(request, num="1"):
# Output the appropriate page of blog entries, according to num.
#...
pass
这里两个URL表达式都指向views.page
视图 。第一个表达式没有传递任何参数,若匹配到第一个样式,page()
函数将会对参数num
使用默认值"1"
;如果第二个表达式匹配成功,page()
函数将使用正则表达式传递过来的num
的值 。注意设置默认参数值为字符串"1"
,因为捕捉给num
的值总是字符串 。
from django.urls import path
from. import views
urlpatterns = [
path('blog/', views.page),
path('blog/page/' , views.page),
]
视图函数为:
def page(request, num = 1):
pass
此时第一个模式不会从URL中捕获任何值,如果第一个模式匹配,page()
函数将使用num
参数的默认值1
;如果第二个模式匹配,page()
将使用捕获的num
值 。
path('device_id//readings/, views.results, name='results')
,若想在用户未输入读数时避免404,可在视图中指定默认读数 。视图函数定义为def results(request, device_id="", readings=10):
,同时使用re_path
以便用正则表达式声明num_readings
是可选的 ,如urlpatterns = ( re_path( 'device_id/(?P\w+)/readings/(?P\d+)?',views.results,name='results' ),)
。综上,通过在视图函数中直接为参数指定默认值,结合URL的匹配规则,可实现视图参数默认值的设定 。
在Django中,将每个应用程序设置一个URLConf模块,并将其包含在根URLConf模块中,是一种良好的开发实践 。这样做有助于实现代码的模块化和可维护性,特别是在大型项目中。具体步骤和要点如下:
在根URLConf模块中包含应用的URLConf模块:在根urls.py
文件中,使用include
函数来包含其他应用的URLConf模块。例如:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('books.urls')),
]
上述代码中,path('', include('books.urls'))
表示Django会在books/urls.py
文件中搜索URL模式 。
应用的URLConf模块配置:以books/urls.py
为例,它可以定义该应用特有的URL模式,示例代码如下:
from django.urls import path
from. import views
urlpatterns = [
path('books//' , views.book_detail),
path('books//' , views.books_by_genre),
path('books/', views.book_index),
]
当请求的URL与这些模式匹配时,会调用相应的视图函数。比如,/books/crime/
的URL请求将与path('books/
匹配,Django会调用函数views.books_by_genre(request, genre = “crime”)
;/books/25/
的URL请求将匹配path('books/
,Django将调用函数views.book_detail(request, pk =25)
。
注意事项:
根URLConf模块设置:通常在settings.py
文件中通过ROOT_URLCONF
指定根级url的配置,默认情况下,它被设置为项目的urls.py
。根urls.py
中针对应用配置的URL名称,是该应用所有URL的总路径 。
正则表达式相关:在编写URL模式时,若使用正则表达式,要注意一些规则。例如,若要从url中捕获一个值,需要在它周围设置一对圆括号;不需要添加前导反斜杠;每个正则表达式前面的r
表示字符串不转义;urlpatterns中的每个正则表达式在第一次访问时被编译,可提高系统速度。另外,指向include()
的正则表达式通常不包含$
(字符串结尾匹配符),但会包含一个斜杆。每当Django遇到include()
时,它将截断匹配的URL,并把剩余的字符串发往被包含的URLconf作进一步处理 。
参数传递:被包含的URLconf会接收来自父URLconfs的被捕获的参数,并传递给其中的视图函数 。例如在root urls.py
中有(r'^(?P
,在foo/urls/blog.py
中的视图函数就能接收到username
这个参数 。
在Django中传递额外参数给视图函数,常见方式如下:
使用path()
函数捕获URL参数:可以在path()
函数的路由中使用尖括号捕获URL中的部分作为参数传递给视图函数。例如:
# urls.py
from django.urls import path
from.views import my_view
urlpatterns = [
path('example//' , my_view, name='my_view'),
]
# views.py
from django.http import HttpResponse
def my_view(request, extra_param):
return HttpResponse(f'This is my view with extra_param: {extra_param}')
当访问/example/some_value/
时,some_value
会作为extra_param
的值传递给my_view
函数 。
传递多个参数:在URL中使用多个捕获组来传递多个参数。例如:
# urls.py
from django.urls import path
from.views import my_view
urlpatterns = [
path('example///' , my_view, name='my_view'),
]
# views.py
from django.http import HttpResponse
def my_view(request, extra_param, number_param):
return HttpResponse(f'This is my view with extra_param: {extra_param} and number_param: {number_param}')
访问/example/some_value/42/
时,some_value
作为extra_param
的值,42
作为number_param
的值传递给my_view
函数 。
提供默认参数:为避免客户端请求因缺少参数导致404错误,可在URL配置中为参数提供默认值。例如:
# urls.py
from django.urls import path
from.views import my_view
urlpatterns = [
path('example///' , my_view, name='my_view'),
path('example/', my_view, {'extra_param': 'default_value', 'number_param': 0}, name='my_view_default'),
]
# views.py
from django.http import HttpResponse
def my_view(request, extra_param='default_value', number_param=0):
return HttpResponse(f'This is my view with extra_param: {extra_param} and number_param: {number_param}')
此时访问http://mysite.com/example/
,视图函数将使用默认值 。
使用字典传递额外参数:在path()
函数中包含Python字典类型的参数,再传递给视图函数 。例如:
# urls.py
from django.urls import path
from.views import my_view
urlpatterns = [
path('example/', my_view, {'key': 'value'}, name='my_view'),
]
# views.py
from django.http import HttpResponse
def my_view(request, key):
return HttpResponse(f'This is my view with key: {key}')
通过GET请求传递参数:在URL中通过?参数名字=value&参数名字=value
的形式传递多个参数,后端通过request.GET.get('参数名字')
获取传递的参数。例如:
# views.py
def div_page(request):
page_num = request.GET.get('page')
content = request.GET.get('content')
print(content)
print(page_num)
return HttpResponse(int(page_num))
URL为http://127.0.0.1:8000/page/?page=5&content=3
。这种方式后端无需在函数中定义额外参数,较为灵活 。
使用正则表达式传递参数:
- 无名分组:通过未命名的正则表达式组捕获参数,按顺序作为位置参数传递给视图函数。例如:
# urls.py
from django.urls import re_path
from.views import my_view
urlpatterns = [
re_path(r'^example/(\d+)/(\w+)/', my_view),
]
# views.py
from django.http import HttpResponse
def my_view(request, param1, param2):
return HttpResponse(f'param1: {param1}, param2: {param2}')
- **有名分组**:使用命名的正则表达式组`(?P<name>pattern)`捕获参数,按关键字参数传递给视图函数。例如:
# urls.py
from django.urls import re_path
from.views import my_view
urlpatterns = [
re_path(r'^example/(?P\d+)/(?P\w+)/' , my_view),
]
# views.py
from django.http import HttpResponse
def my_view(request, param1, param2):
return HttpResponse(f'param1: {param1}, param2: {param2}')
需要注意,当混杂命名正则和未命名正则两种样式时,未命名的组会被忽略,只有命名的组才会传递给视图函数 。
在Django中,反向解析指通过一些方法得到一个结果,该结果可以访问到对应的url并触发视图函数运行 。
反向解析的用途
在软件开发初期,url地址路径设计可能不完善,后期若需调整,若项目中多处使用该路径,直接修改路径会很繁琐。通过反向解析,在编写 url(regex, view, kwargs=None, name=None)
时,利用参数 name
为url路径起别名,项目中通过别名获取路径。日后路径变化,只要别名与路径对应关系不变,就无需修改使用该路径的各处代码 。
反向解析的实现方式
{% url '别名' %}
的方式进行反向解析。例如在 index.html
中,反向解析fan2
,这里 test_app
是命名空间,test2
是路由别名。如果路由配置了命名空间,格式通常为 命名空间:别名
。django.urls
引入 reverse
函数,使用 redirect(reverse('别名'))
进行反向解析重定向。如 def redirect_view(request): return redirect('test_app:test2')
。若路由存在参数,传递方式为 redirect(reverse('别名', args=(参数值,)))
(位置参数)或 redirect(reverse('别名', kwargs={'参数名':参数值}))
(关键字参数) 。url(r'^aritcle/(\d+)/$',views.article,name='article_page')
,反向解析时后端可使用 reverse('article_page', args=(1,))
;对于有名分组 url(r'^user/(?P\d+)/$',views.article,name='user_page')
,后端反向解析写法可以是 reverse('user_page', kwargs={'uid': 1})
。前端写法类似,如 链接
。反向解析的相关函数
reverse
:通过路由命名空间或可调用视图对象生成路由地址。有 viewname
(路由命名或可调用视图对象)、urlconf
(设置反向解析的URLconf模块,默认使用主路由)、args
(以列表方式传递路由地址变量)、kwargs
(以字典方式传递路由地址变量)、current_app
(提示当前正在执行的视图所在的项目应用)等参数 。resolve
:通过路由地址获取路由对象信息,有 path
(代表路由地址)、urlconf
(设置反向解析的URLconf模块,默认使用主路由)两个参数,返回的路由对象有 func
(视图函数或视图类对象)、args
(以列表格式获取路由变量信息)、kwargs
(以字典格式获取路由变量信息)、url_name
(获取路由命名name)等内置函数方法来获取具体路由信息 。在Django中,命名空间(Namespace)是表示标识符可见范围的概念,它能有效避免因不同应用中定义相同 name
而导致的URL反解错误 。
由于 name
没有作用域,Django反解URL时会在项目全局顺序搜索,一旦找到第一个 name
指定的URL就立即返回。所以在开发项目时,若不同应用的 urls
中不小心定义了相同的 name
,可能致使URL反解出错。为解决此问题,引入了命名空间 。
在Django中,include
函数可用于设置命名空间,其形式如 include(module(, namespace=None, app_name=None ))
、include(pattern_list)
、include((pattern_list, app_namespace, instance_namespace))
,其中 app_namespace
即应用命名空间,instance_namespace
即实例命名空间 。带有命名空间的URL组成方式为 “命名空间:名称”,以 :
为分隔符,且命名空间URL支持嵌套,例如 “foo:bar:whiz
” 。
解析命名空间URL时,假设要解析 “myapp:index
” 这样的命名空间URL :
myapp
)的应用命名空间,返回一个符合条件的实例命名空间列表 。Context
或者 RequestContext
中的 current_app
属性被设置,那么该值就是当前应用实例,使用该实例 。例如在项目的 urls.py
中,可通过如下方式设置命名空间:
from django.urls import path
from myapp import views
from django.conf.urls.static import static
from django.conf import settings
from django.conf.urls import re_path,include
from myapp import *
from myapp02 import *
urlpatterns = [
# path('admin/', admin.site.urls),
path('hello/',views.hello),
path('login/',views.login,name = 'Log'),
re_path(r"^myapp/",include(("myapp.urls","myapp"))), #这里使用元组设置命名空间为myapp
re_path(r"^myapp02/",include(("myapp02.urls","myapp02"))),
] + static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
在应用的 urls.py
中,例如 app1 urls.py
:
# !/usr/bin/env python
# -*- coding:utf-8 -*-
from django.conf.urls import url
from myapp import views
from django.urls import re_path
urlpatterns = [
re_path(r'^index/',views.index,name='index'),
url(r'^hello/([0-9]{4})/$', views.year_archive,name='y'),
url(r'^hello/([0-9]{4})/([0-9]{2})/$', views.month_archive),
url(r'^hello/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
在视图函数中,如 app1 views.py
,可通过命名空间反向解析URL :
from django.shortcuts import render
from django.http import HttpResponse
from django.urls import reverse
# Create your views here.
# request 是固定写法
def index(request):
return HttpResponse(reverse("myapp:index"))
在模板中使用 {% url %}
标签访问时,若存在多级命名空间,如在 portfolio_menu.urls
、store.urls
、product.urls
分别设置了命名空间 portfolio
、store
、product
,且 product.urls
中有视图的 name
为 list
,理论上可通过类似 {% url portfolio:store:product:list %}
的方式访问,但需注意配置正确,避免出现如 django.core.exceptions.ImproperlyConfigured
等错误 。