Django необязательные параметры URL

132

У меня есть 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.

Теги:
django-views
django-urls

5 ответов

302
Лучший ответ

Есть несколько подходов.

Одним из них является использование группы без захвата в регулярном выражении: (?:/(?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
  • 60
    Голосуйте за вариант с несколькими маршрутами. +1
  • 0
    @BurhanKhalid спасибо, Бурхан! Да, я тоже предпочитаю этот метод ... просто легче последовать 1 год спустя. Единственным недостатком, который я вижу, является изменение URL.
Показать ещё 13 комментариев
36

Вы можете использовать вложенные маршруты

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 и выше

  • 1
    Вложенный это хорошо. Кроме того, он разделяет различные разделы URL в вашем коде более четко (из-за использования отступов)
  • 0
    Проблема с вложенностью заключается в том, что если у вас есть несколько необязательных параметров, то в итоге вы не будете СУХИМЫМИ, поскольку, например, с 3 необязательными параметрами у вас есть 8 различных комбинаций возможных URL. Необходимо обработать параметр 1, который встречается, параметр 1 не встречается, но параметр 2 встречается, а параметры 1 и 2 не встречаются, но параметр 3 встречается. Абзац URL будет НАМНОГО сложнее для чтения, чем одна строка с несколькими необязательными параметрами. Использование символических констант для необязательных подстрок параметров будет очень легко читать, и будет только один URL.
Показать ещё 2 комментария
23

Еще проще использовать:

(?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'
),
  • 7
    Мне нравится простота этого решения, но будьте осторожны: при этом представление все равно получит значение для аргумента, которое будет None . Это означает, что вы не можете полагаться на значение по умолчанию в сигнатуре представления для этого: вы должны явно проверить его внутри и назначить в последствии.
  • 0
    Это я искал =)
Показать ещё 3 комментария
8

Думаю, я добавлю немного ответа.

Если у вас несколько определений URL-адресов, вам придется указывать каждый из них отдельно. Таким образом, вы теряете гибкость при вызове reverse, поскольку один обратный будет ожидать параметра, а другой - нет.

Другой способ использования регулярного выражения для размещения необязательного параметра:

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+)/)?$'] подальше от этого. Reverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P<pk>\\d+)/)?$']
  • 0
    Достаточно интересно, это работает для меня. ;-)
0

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.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню