我有一个这样的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。


有几种方法。

一种是在正则表达式中使用非捕获组:(?:/(?P<title>[a- za -z]+)/)? Regex Django URL令牌可选

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

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

我想再补充一点答案。

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

另一种使用regex来容纳可选参数的方法:

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

可以使用嵌套路由

Django < 1.8

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")
        ])),
    ])),
]

这更加DRY(假设你想将product kwarg重命名为product_id,你只需要更改第4行,它将影响下面的url。

为Django 1.8及以上版本编辑


更简单的方法是:

(?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'
),

Django > 2.0版本:

这种方法基本上与富田裕治的《富田回答》中给出的方法相同。然而,受影响的是语法:

# 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(),你还可以通过可选参数kwargs (dict类型)向视图传递额外的参数。在这种情况下,你的视图将不需要一个默认的属性project_id:

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

关于在最新的Django版本中是如何做到这一点的,请参阅有关URL调度的官方文档。


Django = 2.2

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

使用?工作顺利,可以检查一下pythex。记住在视图方法的定义中添加参数*args和**kwargs

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