尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Django 路由组织、名称空间与虚拟环境

Django 路由组织、名称空间与虚拟环境 Django 路由组织、名称空间与虚拟环境本章围绕 Django 项目的 URL 设计展开如何为动态路由反向生成地址如何将不同 App 的路由拆分管理如何用名称空间消除同名路由冲突以及如何使用路径转换器约束参数。最后介绍虚拟环境与依赖清单保证不同项目可以使用各自独立的 Python 包版本。一、带参数路由的反向解析动态路由会从 URL 中提取参数例如/students/5/中的5。反向解析则根据“路由名称 参数”生成 URL避免在模板或视图中硬编码地址。动态路由有参数时反向解析必须提供所有必需参数。新项目应优先使用path()的命名参数和转换器re_path()主要用于需要正则表达式的复杂规则。# user/urls.pyfromdjango.urlsimportpathfrom.importviews urlpatterns[path(students/int:student_id/,views.student_detail,namestudent-detail),]# user/views.pyfromdjango.httpimportHttpResponsefromdjango.shortcutsimportredirectfromdjango.urlsimportreversedefstudent_detail(request,student_id):returnHttpResponse(f学生编号{student_id})defgo_to_student(request):urlreverse(student-detail,kwargs{student_id:5})returnredirect(url)代码说明路由参数名是student_id因此使用kwargs反向解析时键也必须是student_id。redirect(student-detail, student_id5)是更简洁的等价写法reverse()适合需要先获得 URL 字符串的场景。模板中的参数反向解析!-- student 是模板上下文中的对象 --ahref{% url student-detail student_idstudent.id %}查看学生/a!-- 也可以使用位置参数但命名参数更清晰 --ahref{% url student-detail student.id %}查看学生/a代码说明若缺少动态参数Django 会抛出NoReverseMatch。模板中应使用{% url %}而不是手写/students/{{ student.id }}/这样修改 URL 结构后引用位置不必同步改动。二、re_path() 的命名与非命名捕获组使用正则路由时捕获组会作为参数传给视图。无名组按位置参数传递命名组按关键字参数传递。为了可读性和维护性通常应优先使用命名捕获组。# user/urls.pyfromdjango.urlsimportre_pathfrom.importviews urlpatterns[re_path(r^legacy-pages/(\d)/$,views.legacy_page,namelegacy-page),re_path(r^named-pages/(?Ppage\d)/$,views.named_page,namenamed-page),]# user/views.pyfromdjango.httpimportHttpResponsedeflegacy_page(request,page_number):returnHttpResponse(f无名组参数{page_number})defnamed_page(request,page):returnHttpResponse(f命名组参数{page})反向解析时无名组使用args命名组可使用args或匹配名称的kwargs。实际开发中建议命名组统一用kwargs。fromdjango.urlsimportreverse legacy_urlreverse(legacy-page,args(5,))named_urlreverse(named-page,kwargs{page:5})print(legacy_url)# /legacy-pages/5/print(named_url)# /named-pages/5/代码说明kwargs{number: 5}会失败因为正则中定义的参数名是page不是number。不要在同一条re_path()规则中混用命名和非命名捕获组这会使视图调用方式不直观。三、按 App 分发路由小项目可以把所有路由写在项目的urls.py中但随着 App 和页面增多主路由文件会变得难以维护。推荐做法是每个 App 管理自己的urls.py项目级路由用include()挂载 App 路由并统一添加前缀。demo01/ ├── demo01/ │ └── urls.py ├── shop/ │ └── urls.py └── user/ └── urls.py# demo01/urls.py项目级路由fromdjango.contribimportadminfromdjango.urlsimportinclude,pathfrom.importviews urlpatterns[path(admin/,admin.site.urls),path(,views.index,nameindex),path(shop/,include(shop.urls)),path(users/,include(user.urls)),]# shop/urls.py商品或订单相关路由fromdjango.urlsimportpathfrom.importviews urlpatterns[path(orders/,views.order_list,nameorder-list),path(buy/,views.buy,namebuy),]# user/urls.py用户相关路由fromdjango.urlsimportpathfrom.importviews urlpatterns[path(login/,views.login,namelogin),path(register/,views.register,nameregister),]代码说明include(shop.urls)会把shop/前缀与子路由拼接因此path(orders/, ...)最终地址是/shop/orders/。App 内部路由不应重复写项目级前缀否则会形成/shop/shop/orders/这类冗余路径。四、应用名称空间多个 App 都可能有login、index、detail之类的路由名称。若不使用名称空间reverse(login)无法明确指向哪一个 App甚至可能因加载顺序而得到意外结果。解决方法是在每个 App 的urls.py中声明app_name然后通过应用名:路由名引用。# user/urls.pyfromdjango.urlsimportpathfrom.importviews app_nameuserurlpatterns[path(login/,views.login,namelogin),path(register/,views.register,nameregister),]# admin_portal/urls.pyfromdjango.urlsimportpathfrom.importviews app_nameadmin_portalurlpatterns[path(login/,views.login,namelogin),]# demo01/urls.pyfromdjango.urlsimportinclude,path urlpatterns[path(users/,include(user.urls)),path(admin-portal/,include(admin_portal.urls)),]fromdjango.shortcutsimportredirectfromdjango.urlsimportreverse user_login_urlreverse(user:login)admin_login_urlreverse(admin_portal:login)# 也可直接重定向到命名空间路由returnredirect(user:login)代码说明名称空间解决的是 URL 名称冲突不是 Python 模块或视图函数名称冲突。业务项目中App 内路由名称可以保持简洁例如都命名为login再用名称空间表达所属业务域。指定实例名称空间同一份 URLconf 被挂载多次时可以使用实例名称空间区分不同挂载位置。普通项目中只需app_name即可以下写法用于理解include()的三元组形式。# demo01/urls.pyfromdjango.urlsimportinclude,path urlpatterns[path(staff/,include((user.urls,user),namespacestaff)),path(customers/,include((user.urls,user),namespacecustomers)),]fromdjango.urlsimportreverse reverse(staff:login)reverse(customers:login)代码说明实例名称空间适用于同一个应用以不同前缀或配置重复挂载的情况。对于每个 App 只挂载一次的项目使用app_name和常规include(app.urls)更简单。五、path() 路径转换器路径转换器让path()同时完成匹配和类型转换比手写简单正则更易读。转换器参数都会按关键字传给视图函数参数名必须与视图形参匹配。转换器匹配规则传给视图的类型str不含/的非空字符串strint0 或正整数intslugASCII 字母、数字、-、_struuid带连字符的 UUIDuuid.UUIDpath可包含/的非空字符串str# order/urls.pyfromdjango.urlsimportpathfrom.importviews urlpatterns[path(names/str:name/,views.name_detail,namename-detail),path(orders/int:order_id/,views.order_detail,nameorder-detail),path(articles/slug:slug/,views.article_detail,namearticle-detail),path(files/path:file_path/,views.file_detail,namefile-detail),]# order/views.pyfromdjango.httpimportHttpResponsedeforder_detail(request,order_id):assertisinstance(order_id,int)returnHttpResponse(f订单编号{order_id})defname_detail(request,name):returnHttpResponse(f名称{name})代码说明path转换器会匹配斜杠因此应将含有path:...的路由放在更具体的路由之后避免它过早吞掉后续路径。int只匹配非负整数若需要负数、固定长度或其他复杂格式可使用自定义转换器或re_path()。六、自定义路径转换器自定义转换器需要提供regex、to_python()和to_url()。前者定义 URL 中允许出现的文本to_python()将匹配结果转为视图使用的值to_url()则在反向解析时把 Python 值转为 URL 字符串。# order/converters.pyclassFourDigitYearConverter:regexr[0-9]{4}defto_python(self,value):returnint(value)defto_url(self,value):returnf{int(value):04d}# order/urls.pyfromdjango.urlsimportpath,register_converterfrom.importviewsfrom.convertersimportFourDigitYearConverter register_converter(FourDigitYearConverter,year)urlpatterns[path(reports/year:report_year/,views.report,namereport),]# order/views.pyfromdjango.httpimportHttpResponsedefreport(request,report_year):returnHttpResponse(f报告年份{report_year})fromdjango.urlsimportreverseprint(reverse(report,kwargs{report_year:2024}))# /reports/2024/代码说明转换器属性名必须是regex不是reg。to_url()需要返回与正则规则匹配的字符串例如年份传入42时会格式化为0042仍符合四位数字规则。七、虚拟环境与依赖管理虚拟环境不是对现有环境的“备份”而是为项目创建隔离的 Python 解释器与第三方包目录。它允许项目 A 使用 Django 3.2而项目 B 使用 Django 5.x彼此不互相卸载或覆盖依赖。使用 venv 创建环境# 在项目根目录创建虚拟环境python-mvenv .venv# Windows PowerShell 激活.venv\Scripts\Activate.ps1# macOS / Linux 激活source.venv/bin/activate# 确认当前解释器与 Django 来源python-cimport sys; print(sys.executable)python-mdjango--version代码说明激活后再使用python -m pip install ...安装包可确保依赖进入当前项目的虚拟环境。.venv/通常应加入.gitignore不要提交整个环境目录。导出与安装依赖将可复现的依赖版本写入requirements.txt其他开发者或部署环境即可使用同一份清单安装依赖。# 导出当前虚拟环境中已安装的包与版本python-mpip freezerequirements.txt# 根据清单安装依赖python-mpipinstall-rrequirements.txt一个简化的依赖文件示例如下Django3.2.12 PyMySQL1.1.1代码说明pip freeze会列出当前环境中的全部包适合学习项目或简单部署。生产项目还应定期审查依赖、修复安全漏洞并根据团队实践选择pip-tools、Poetry、uv 等更严格的依赖锁定方案。八、实践要点给每条可复用的路由设置唯一的name并在模板与视图中使用反向解析。App 内维护自己的urls.py项目级urls.py只负责挂载和全局入口。存在同名路由时必须声明app_name使用app_name:url_name引用。新项目优先使用path()转换器复杂规则才使用re_path()。每个项目使用独立虚拟环境提交依赖清单不提交虚拟环境目录和密钥文件。掌握这些约定后URL 的组织方式会随项目规模增长而保持清晰也能让开发环境在不同机器上更稳定地复现。
返回列表