美文网首页
URLConf 调度器

URLConf 调度器

作者: 木叶苍蓝 | 来源:发表于2023-02-02 11:09 被阅读0次

    一、URL 调度器

    1. 工作原理

    Django 通过 urlconf 来映射视图函数,只区分路径,不区分 http 方法、

    • Django 确定要使用的根 URLconf 模块,一般是在 settings 中的 ROOT_URLCONF 设置的值,但是如果传入 HttpRequest 对象具有一个 urlconf 属性(由中间件设置),则其值将用于代替 ROOT_URLCONF 设置。
    • Django 加载该 URLconf 模块并查找变量 urlpatterns,它是一个列表 django.urls.path() 和 / 或者 django.urls.re_path() 实例。
    • Django 按顺序遍历每一个 URL 模式,并停在与请求的 URL 匹配的第一个 URL 模式,需要特别注意编写的顺序。
    • 一旦某一个 URL 模式匹配,Django 就会导入并调用给定的视图,该视图是一个简单的 Python 函数(或基于类的视图方法)。该视图通过以下参数传递。
      1. 一个HttpRequest 实例
      2. 如果匹配的 URL 模式没有返回任何命名组,则来自正则表达式的匹配作为位置参数提供。
      3. 关键字参数有路径表达式匹配的任何命名部分组成,并由可选的 kwargs 参数传给 django.urls.path() 或 django.urls.re_path()。
    • 如果没有 URL 模式匹配,或者在此过程中的任何点发送异常,Django 将调用适当的错误处理视图。
    2. 简单示例
    from django.urls import path
    
    from . import views
    
    urlpatterns = [
      path('articles/2003/', views.special_case_2003),
      path('articles/<int:year>/', views.year_archive),
      path('articles/<int:year>/<int:month>/', views.month_archive),
      path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
    ]
    

    笔记:

    • 从 URL 中捕获值,请使用尖括号。
    • 捕获的值可以选择包含转换器类型。例如,用于<int:name>捕获,前面的int指整数参数,name 是参数的名称。
    • 没有必要添加一个前导斜杆,因为没有 URL 都有,例如 使用 articles 而不是 /articles。
    • 示例请求说明:
      1. /articles/2005/03/ 匹配列表中的第三个条目。Django 会调用这个函数 views.month_archive(request, year=2005, month=3)
      2. /articles/2003/ 会匹配列表中的第一个模式,而不是第二模式,因为模式是按照顺序测试的,而第一个模式是第一个要传递的测试。看看利用匹配顺序插入像这样的特殊情况。在这里,Django 会调用这个函数 views.special_case_2003(request)
      3. /articles/2003 不匹配任何这些模式,因为每种模式都要求 URL 以斜线结尾,不过在浏览器访问时,会自动添加 /。
      4. **/articles/2003/03/building-a-django-site/ **将匹配最终模式。Django 会调用这个函数 views.article_detail(request, year=2003, month=3, slug="building-a-django-site")
    3. 路径转换器
    • str: 匹配任何非空字符串,不包括路径分隔符'/'。如果转换器不包含在表达式中,这是默认值。
    • int: 匹配零或任何正整数。返回一个int。
    • slug: 匹配有ASCII 字母或数字组成的字符串,以及横线和下划线字符。例如。buliding-your-1st-django_site。
    • uuid: 匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号,并且字母必须小写。例如 075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。
    • path: 匹配任何非空字符串,包括路径分隔符 '/',可以匹配完整的URL路径,而不仅仅是URL路径的一部分 str,使用时要谨慎,因为可能造成后续的所有url匹配都失效。
    4. 自定义路径转换器

    转换器是一个包含以下内容的类:

    • 一个 regex 类属性,作为一个 re 匹配字符串。
    • to_python(self, value)方法,它处理匹配的字符串转换成要传递到视图函数的类型。
    • to_url(self, value)方法,用于处理将Python 类型转换为 URL 中使用的字符串。
      定义方式如下:
    1. 新建一个 converters.py 文件,在文件中定义一个 FourDigitYearConverter 类:
    class FourDigitYearConverter(object):
        regex = '[0-9]{4}'
        
        def to_python(self, value):
            return int(value)
    
        def to_url(self, value):
            return '%04d' % value
    
    1. 使用 register_converter() 方法在 URLconf 中注册自定义转换器类:
    from django.urls import register_converter, path
    
    from . import converters, views
    
    register_converter(converters.FourDigitYearConverter, 'yyyy')
    
    urlpatterns = [
        path('articles/2003/', views.special_case_2003),
        path('articles/<yyyy:year>/', view.year_archive)
    
    ]
    
    5. 使用正则表达式

    使用正则表达式匹配路径,请使用 re_path() 而不是 path()
    在 Python 正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中 name 是组的名称,并且 pattern 是一些要匹配的模式
    实例:

    from django.urls import path, re_path
    
    from . import views
    
    urlpatterns = [
        path('articles/2003/', views.special_case_2003),
        path('articles/<int:year>/', views.year_archive),
        re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
        re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
        re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail),
    ]
    

    注意事项:

    • 匹配的URL会受到一些限制。例如,年份10000将不在匹配,因为年份整数限制为四位数
    • 无论正则表达式匹配什么类型,每个捕获的参数都以字符串的形式发送到视图。
    • 除了命名的组语法,例如(?P<year>[0-9]{4}),也可以使用较短的未命名组,例如([0-9]{4}),但是不建议这样使用,会引起未知匹配

    嵌套参数

    from django.urls import re_path
    
    urlpatterns = [
        # 不推荐,匹配 blog/page-3/
        re_path(r'^blog/(page-(\d+)/)?%', blog_articles),
        # 推荐,匹配:comments/page-2/ 路径到 comments(request, page_number)
        re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments)
    ]
    
    6. 使用默认值

    URLConf 中:

    from django.urls import path
    
    from . import views
    
    urlpatterns = [
        # http://127.0.0.1:8000/polls/blog/ 等同于 http://127.0.0.1:8000/polls/blog/1/
        path('blog/', views.page),
        # http://127.0.0.1:8000/polls/blog/1/
        # http://127.0.0.1:8000/polls/blog/10/
        # http://127.0.0.1:8000/polls/blog/99/
        path('blog/<int:num>', views.page),
    ]
    

    views 中:

    def page(request, num=1):
        # 编写对应的业务逻辑
        pass
    
    7. 错误处理
    • handler400- 状态码400
    • handler403- 状态码403
    • handler404- 状态码404
    • handler500- 状态码500
    1. 在settings中修改配置:
    DEBUG = False
    ALLOWED_HOSTS = ['*', ]
    
    1. 在主应用的 urls 中配置:
    # polls 是子应用
    handler404 = "polls.views.page_not_found"
    
    1. 在polls应用的views中添加函数 page_not_found
    def page_not_found(request, exception):
        return HttpResponse('自定义的404错误页面')
    

    8. 引用其他URL调度器

    1. 多个 patterns
    from django.urls import include, path
    
    extra_patterns = [
        path('reports, credit_views.report'),
        path('reports/<int:id>/', credit_views.report),
        path('charge/', credit_views.charge),
    ]
    
    urlpatterns = [
        path('', main_views.homepage),
        path('help/', include('apps.help.urls')),
        path('credit/', include(extra_patterns)),
    ]
    
    1. 使用 include 消除重复前缀
    from  django.urls import path
    from . import views
    
    urlpatterns = [
        path('<page_slug>-<page_id>/history/', views.history),
        path('<page_slug>-<page_id>/edit/', views.edit),
        path('<page_slug>-<page_id>/discuss/', views.discuss),
        path('<page_slug>-<page_id>/permissions/', views.permissions),
    ]
    # 修改为:
    from django.urls import include, path
    from . import views 
    
    urlpatterns = [
        path('<page_slug>-<page_id>', include([
        path('history/', views.history),
        path('edit/', views.edit),
        path('discuss/', views.discuss),
        path('permissions/', views.permissions),
        ]))
    ]
    
    1. 传递捕获的参数
      在主 urls 中配置
    urlpatterns = [
        path('admin/', admin.site.urls),
        # 这里捕获 username 参数,类型为字符串
        path('<username>/polls/', include('polls.urls'))
    ]
    

    对应的 polls 应用下的 urls 中配置

    urlpatterns = [
        path('arg_test/', views.arg_test),
    ]
    

    对应的 polls 应用下的 views 中编写函数:

    def arg_test(request, username):
        # 编写对应的业务逻辑
        pass
    
    1. 额外的参数
    from django.urls import path
    from . import views
    
    urlpatterns = [
        # 会传递给 views.year_archive(request, year=2005, foo='bar')
        path('blog/<int:year>/', view.year_archive, {'foo': 'bar'}),
    ]
    
    1. URL 反向解析
      url 调度器除了从用户发起请求,到匹配对应的view,还能在 python 程序中调用进行匹配,通过 path 或 re_path 中的 name 属性进行解析
    • 在模板中,使用 url 模板标签
    • 在 Python 代码中(主要是 views),使用 reverse() 函数
    • 在模型实例中,使用 get_absolute_url() 方法

    实例:
    urls中配置:

    from django.urls import path
    
    from . import views
    
    urlpatterns = [
        path('articles/<int:year>/', views.year_archive, name='news-year-archive'),
    ]
    
    1. 在模板中
    # 模板中
    <a href="{% url 'news-year-archive' 2012 %}"2012 Archive></a>
    <ul>
    {% for yearvar in year_list %}
    <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li>
    {% endfor %}
    </ul>
    
    1. 在Python代码中
    from django.urls import reverse
    from django.http import HttpResponseRedirect
    
    def redirect_to_year(request):
        # ...
        year = 2006
        # ...
        return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
    
    1. 在模型中
    """
        在模型中实现方法:
        def get_absolute_url(self):
            from django.urls import reverse
            return reverse('news-year-archive', args=[str(self.id)])
    
        然后在 模板 中如下使用:
    """
    <a href="{{ object.get_absolute_url }}">{{ object.name }}</a>
    
    11. 命名空间

    主要用于配合第 10 点 url反向解析 使用,多个不同的urls文件中可能配置同名的 name,那么为了进行区分,给不同的urls进行不同的命名,切记同一个项目下命名空间不能重复!

    通过在 url调度器的模块中,定义 app_name = ‘polls’ 来命名

    
    
    from django.urls import path
    
    from . import views
    # 定义,一般命名空间和子应用名相同,便于记忆
    app_name = 'polls'
    urlpatterns = [
        path('', views.IndexView.as_view(), name='index'),
        path('<int:pk>/', views.DetailView.as_view(), name='detail'),
        ...
    ]
    
    # 调用,一旦有了命名空间,调用时就必须使用 polls: 前缀
    reverse('polls:index', current_app=self.request.resolver_match.namespace)
    

    命名空间可以进行嵌套:

    # 在 urls 中配置如下:
    from django.urls import path
    
    from . import views
    # 定义命名空间,一般命名空间名和子应用名相同,便于记忆
    app_name = 'polls'
    
    extra_patterns = (
        [
            path('app_name/', views.app_name, name='app_name'),
        ],
        # 此处就是嵌套的命名空间
        'extra'
    )
    
    urlpatterns = [
        path('', views.IndexView.as_view(), name='index'),
        path('<int:pk>/', views.DetailView.as_view(), name='detail'),
        path('extra/', include(extra_patterns)),
        ...
    ]
    
    
    # 在模板中使用:
    <a href="{% url 'polls:extra:app_name' %}">点击链接</a>
    
    

    相关文章

      网友评论

          本文标题:URLConf 调度器

          本文链接:https://www.haomeiwen.com/subject/psuihdtx.html