【问题标题】:Django optional url parametersDjango 可选 url 参数
【发布时间】:2012-12-30 08:05:53
【问题描述】:

我有一个这样的 Django URL:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

views.py:

def ProjectConfig(request, product, project_id=None, template_name='project.html'):
    ...
    # do stuff

问题是我希望project_id 参数是可选的。

我希望 /project_config//project_config/12345abdce/ 是同样有效的 URL 模式,以便 如果 project_id 被传递,然后我可以使用它。

就目前而言,当我访问没有 project_id 参数的 URL 时,我得到一个 404。

【问题讨论】:

    标签: python django django-views django-urls


    【解决方案1】:

    使用 ?运行良好,您可以查看pythex。记得在视图方法的定义中添加参数 *args 和 **kwargs

    url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
    

    【讨论】:

      【解决方案2】:

      Django > 2.0 版本

      该方法与Yuji 'Tomita' Tomita's Answer 中给出的方法基本相同。然而,受影响的是语法:

      # URLconf
      ...
      
      urlpatterns = [
          path(
              'project_config/<product>/',
              views.get_product, 
              name='project_config'
          ),
          path(
              'project_config/<product>/<project_id>/',
              views.get_product,
              name='project_config'
          ),
      ]
      
      
      # View (in views.py)
      def get_product(request, product, project_id='None'):
          # Output the appropriate product
          ...
      

      使用path(),您还可以将pass extra arguments to a viewkwargs 类型的可选参数dict 结合使用。在这种情况下,您的视图不需要 project_id 属性的默认值:

          ...
          path(
              'project_config/<product>/',
              views.get_product,
              kwargs={'project_id': None},
              name='project_config'
          ),
          ...
      

      在最新的 Django 版本中如何做到这一点,请参阅the official docs about URL dispatching

      【讨论】:

      • 我认为您在代码中混淆了 project_id 和 product_id,对吧?
      • @AndreasBergström 非常感谢您指出这一点!你说得对!匆忙更正了它,但稍后会再看第二次。希望现在一切都好!如果默认使用dict,路径中还有project_id。这可能会导致看似奇怪的行为,因为将始终使用 dict 中提供的参数(如果我没记错的话)。
      • @jojo 这是否意味着第二个选项中的 'project_config/foo/bar' 会自动将 {'project_id': 'bar'} kwargs 传递给视图?
      【解决方案3】:

      有几种方法。

      一种是在正则表达式中使用非捕获组:(?:/(?P&lt;title&gt;[a-zA-Z]+)/)?
      Making a Regex Django URL Token Optional

      另一种更容易遵循的方法是拥有多个符合您需求的规则,所有规则都指向同一个视图。

      urlpatterns = patterns('',
          url(r'^project_config/$', views.foo),
          url(r'^project_config/(?P<product>\w+)/$', views.foo),
          url(r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$', views.foo),
      )
      

      请记住,在您看来,您还需要为可选的 URL 参数设置默认值,否则您会收到错误消息:

      def foo(request, optional_parameter=''):
          # Your code goes here
      

      【讨论】:

      • 投票支持多路线选项。 +1
      • @Yuji -- 你不能通过命名每个 url 模式来解决反转问题吗?
      • 我们可以为每个视图命名吗?
      • @Yuji'Tomita'Tomita 我知道,所以不幸的是,尤金的问题的答案是,不,我们不能理智地拥有多个具有相同名称的视图,即使我们将它们作为一种方式来实现获取可选参数。
      • @eugene 是的,我们可以有两个同名的 url,根据 args 的不同,反转会巧妙地选择适用的 URL
      【解决方案4】:

      Django = 2.2

      urlpatterns = [
          re_path(r'^project_config/(?:(?P<product>\w+)/(?:(?P<project_id>\w+)/)/)?$', tool.views.ProjectConfig, name='project_config')
      ]
      

      【讨论】:

        【解决方案5】:

        您可以使用嵌套路由

        Django

        urlpatterns = patterns(''
            url(r'^project_config/', include(patterns('',
                url(r'^$', ProjectConfigView.as_view(), name="project_config")
                url(r'^(?P<product>\w+)$', include(patterns('',
                    url(r'^$', ProductView.as_view(), name="product"),
                    url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
                ))),
            ))),
        )
        

        Django >=1.8

        urlpatterns = [
            url(r'^project_config/', include([
                url(r'^$', ProjectConfigView.as_view(), name="project_config")
                url(r'^(?P<product>\w+)$', include([
                    url(r'^$', ProductView.as_view(), name="product"),
                    url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
                ])),
            ])),
        ]
        

        这要干得多(假设您想将 product kwarg 重命名为 product_id,您只需更改第 4 行,它将影响以下 URL。

        为 Django 1.8 及更高版本编辑

        【讨论】:

        • 嵌套很好。此外,它更清楚地分隔代码中的不同 URL 部分(由于使用缩进)
        • 嵌套的问题是,如果你有多个可选参数,那么你最终不会是 DRY,因为例如,如果有 3 个可选参数,你就有 8 种不同的可能 URL 组合。您必须处理参数 1 出现,参数 1 不出现但参数 2 出现,以及参数 1 和 2 不出现但参数 3 出现。 URL 段落将比具有多个可选参数的单个字符串更难阅读。对可选参数子字符串使用符号常量将使其非常易于阅读,并且只有一个 URL。
        • 我认为您是对的,但这更多是由于视图/URL 设计不佳所致。这个例子可以修改得更好。
        • '平面优于嵌套'
        【解决方案6】:

        使用起来更简单:

        (?P<project_id>\w+|)
        

        “(a|b)”表示 a 或 b,因此在您的情况下,它可能是一个或多个单词字符 (\w+) 或什么都没有。

        所以它看起来像:

        url(
            r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
            'tool.views.ProjectConfig',
            name='project_config'
        ),
        

        【讨论】:

        • 我喜欢这个解决方案的简单性,但要注意:这样做,视图仍会收到参数的值,即None。这意味着您不能依赖视图签名中的默认值:您必须在内部显式测试它并在结果中分配。
        • 这是我要找的 =)
        • 如果 project_id 不存在,最后一个斜杠呢?
        • 您可以添加一个 ?在斜杠之后或仅在 project_id 模式中包含斜杠
        【解决方案7】:

        我想我会在答案中添加一点。

        如果您有多个 URL 定义,则必须分别命名它们。所以你在调用 reverse 时失去了灵活性,因为一个 reverse 会需要一个参数,而另一个不会。

        另一种使用正则表达式来容纳可选参数的方法:

        r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'
        

        【讨论】:

        • 在 Django 1.6 中,这对我来说是一个异常。我会远离它Reverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P&lt;pk&gt;\\d+)/)?$']
        猜你喜欢
        • 2018-01-25
        • 1970-01-01
        • 2018-03-05
        • 1970-01-01
        • 1970-01-01
        • 2020-08-23
        • 2019-01-15
        相关资源
        最近更新 更多