У меня есть URL-адрес Django:
url(
r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$',
'tool.views.ProjectConfig',
name='project_config'
),
и my views.py:
def ProjectConfig(request, product, project_id=None, template_name='project.html'):
...
# do stuff
Проблема заключается в том, что я хочу, чтобы параметр project_id был необязательным. Я бы хотел, чтобы /project_config/
и /project_config/12345abdce/
были одинаково правильными шаблонами url, поэтому передается IF project_id
, тогда я могу его использовать. Поскольку он стоит на данный момент, я получаю 404, если попытаюсь получить доступ к URL-адресу без параметра project_id
.
Есть несколько подходов.
Одним из них является использование группы без захвата в регулярном выражении: (?:/(?P<title>[a-zA-Z]+)/)?
Создание URL-токена Regex Django необязательно
Другой, более простой способ - иметь несколько правил, которые соответствуют вашим потребностям, и все они указывают на одно и то же представление.
urlpatterns = patterns('',
url(r'^project_config/$', views.foo),
url(r'^project_config/(?P<product>\w+)/$', views.foo),
ulr(r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$', views.foo),
)
Имейте в виду, что, по вашему мнению, вам также необходимо установить значение по умолчанию для необязательного параметра URL, иначе вы получите сообщение об ошибке:
def foo(request, optional_parameter=''):
# Your code goes here
Вы можете использовать вложенные маршруты
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'
),
None
. Это означает, что вы не можете полагаться на значение по умолчанию в сигнатуре представления для этого: вы должны явно проверить его внутри и назначить в последствии.
Думаю, я добавлю немного ответа.
Если у вас несколько определений URL-адресов, вам придется указывать каждый из них отдельно. Таким образом, вы теряете гибкость при вызове reverse, поскольку один обратный будет ожидать параметра, а другой - нет.
Другой способ использования регулярного выражения для размещения необязательного параметра:
r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'
Reverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P<pk>\\d+)/)?$']
подальше от этого. Reverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P<pk>\\d+)/)?$']
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, project, product_id='None'):
# Output the appropriate product
...
Используя path
вы также можете передать дополнительные аргументы представлению с помощью dict
качестве необязательного аргумента. В этом случае вашему представлению не понадобится значение по умолчанию для атрибута product_id
:
...
path(
'project_config/<product>/<project_id>/',
views.get_product,
name='project_config',
{'product_id': None}
),
...
Как это сделать в самой последней версии Django, смотрите в официальных документах об отправке URL.