Django可选的url参数


161

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

Answers:


381

有几种方法。

一种是在正则表达式中使用非捕获组:使正则 (?:/(?P<title>[a-zA-Z]+)/)?
表达式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

68
为多路线选项投票。+1
Burhan Khalid

4
@Yuji-您不能通过命名每个url模式来解决反向问题吗?
Ted

8
我们可以给每个视图使用相同的名称吗?
尤金2014年

2
我知道@ Yuji'Tomita'Tomita,所以不幸的是,对eugene问题的回答是,不,即使我们将它们实现为获取可选参数的一种方式,我们也不能理智地拥有多个具有相同名称的视图。
nnyby 2014年

2
@eugene是的,我们可以有两个具有相同名称的url,反向转换将根据args巧妙地选择适用的网址
Arpit Singh 2015年

37

您可以使用嵌套路线

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要多得多(假设您要将productkwarg 重命名为product_id,只需更改第4行,它就会影响以下网址。

针对Django 1.8及更高版本进行了编辑


1
嵌套是好的。此外,由于使用缩进,它可以更清楚地分隔代码中的不同URL部分
Patrick

嵌套的问题是,如果您有多个可选参数,那么最终就不会变得干燥,因为例如,有了3个可选参数,您就有8种可能的URL组合。您必须处理参数1发生,参数1没发生但参数2发生,参数1和2没发生但参数3发生的情况。URL段落比带有多个可选参数的单个字符串更难读。对可选参数子字符串使用符号常量将使其非常易于阅读,并且只有一个URL。
Bogatyr 2015年

我认为您是对的,但这更多是由于视图/ URL设计不佳造成的。这个例子可以改写得更好。
Jacob Valenta 2015年

“扁平比嵌套更好”
pjdavis

30

更简单的是使用:

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

9
我喜欢此解决方案的简单性,但请注意:这样做,视图仍将收到参数的值,即None。这意味着您不能依赖于视图签名中的默认值:您必须在内部显式测试它并指定结果。
Anto 2014年

这是我正在寻找的=)
Mike Brian Olivera'2

3
如果project_id不存在,最后一个斜杠怎么办?
iamkhush

您可以添加一个?在斜杠之后或仅在project_id模式中包含斜杠
JuanJoséBrown布朗

18

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()您还可以使用类型为的可选参数将额外的参数传递给视图。在这种情况下,您的视图不需要该属性的默认值:kwargsdictproject_id

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

有关如何在最新的Django版本中完成此操作的信息,请参阅有关URL调度的官方文档


1
我认为您在代码中混合了project_id和product_id,对吗?
AndreasBergström'19年

@AndreasBergström非常感谢您指出这一点!您对此很正确!急忙进行了更正,但稍后将进行第二次研究。希望现在好!project_id如果使用的默认值,路径中还会有静止的图像dict。这可能导致看似奇怪的行为,因为dict将始终使用(如果我没有记错的话)中提供的参数。
jojo

@jojo这是否意味着第二个选项中的'project_config / foo / bar'将自动将{'project_id':'bar'} kwargs传递给视图?
原始的烧烤酱

9

以为我会在答案中加点。

如果您有多个URL定义,则必须分别命名每个。因此,当调用反向时,您会失去灵活性,因为一个反向将需要一个参数,而另一个则不会。

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

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

2
在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<pk>\\d+)/)?$']
Patrick


0

用 ?工作正常,您可以检查pythex。请记住在视图方法的定义中添加参数* args和** kwargs

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.