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

162

У меня есть URL Django, как это:

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 будет принят, то я могу использовать его.

В настоящее время, я получаю 404, когда я получаю доступ к URL без project_idпараметра.

Дарвин Тех
источник

Ответы:

382

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

Одним из них является использование группы без захвата в регулярном выражении: (?:/(?P<title>[a-zA-Z]+)/)?
Создание необязательного маркера URL Django для Regex

Другой, более простой способ - иметь несколько правил, которые соответствуют вашим потребностям, и все они указывают на одно и то же представление.

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
Бурхан Халид
4
@Yuji - разве вы не можете решить проблему, назвав каждый шаблон URL?
Тед
8
мы можем дать каждому представлению одно и то же имя?
Евгений
2
@ Yuji'Tomita'Tomita Я знаю, поэтому ответ на вопрос Евгения, к сожалению, нет, мы не можем разумно иметь несколько представлений с одинаковым именем, даже если мы реализуем их как способ получения необязательных параметров.
nnyby
2
@eugene Да, у нас может быть два URL-адреса с одинаковым именем, реверсирование будет ловко подхватывать, в зависимости от того, что применимо в зависимости от аргументов
Арпит Сингх
37

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

Джанго <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")
        ))),
    ))),
)

Джанго> = 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")
        ])),
    ])),
]

Это намного более СУХОЙ (скажем, вы хотите переименовать productkwarg product_id, вам нужно только изменить строку 4, и это повлияет на приведенные ниже URL-адреса.

Отредактировано для Django 1.8 и выше

Джейкоб Валента
источник
1
Вложенный это хорошо. Кроме того, он более четко разделяет различные разделы URL в вашем коде (из-за использования отступов)
Patrick
Проблема с вложенностью заключается в том, что если у вас есть несколько необязательных параметров, то в итоге вы не будете СУХИМЫМИ, поскольку, например, с 3 необязательными параметрами у вас есть 8 различных комбинаций возможных URL-адресов. Вы должны обработать параметр 1, который встречается, параметр 1 не встречается, но параметр 2 встречается, а параметры 1 и 2 не встречаются, но параметр 3 встречается. Абзац URL будет НАМНОГО сложнее для чтения, чем одна строка с несколькими необязательными параметрами. Использование символических констант для необязательных подстрок параметров будет очень легко читаться, и будет только один URL.
Богатырь
Я думаю, что вы правы, но это скорее результат плохого дизайна просмотра / URL. Этот пример может быть переработан, чтобы быть намного лучше.
Джейкоб Валента
'квартира лучше вложенной'
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. Это означает, что вы не можете полагаться на значение по умолчанию в сигнатуре представления для этого: вы должны явно проверить его внутри и назначить в последствии.
Анто
Это я искал =)
Майк Брайан Оливера
3
как насчет последней косой черты в случае, если project_id нет?
Ямхуш
Вы можете просто добавить? после косой черты или просто
Хуан Хосе Браун
18

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 .

Jojo
источник
1
Я думаю, что вы смешали project_id и product_id в своем коде, верно?
Андреас Бергстрем
@ AndreasBergström большое спасибо за указание на это! Вы совершенно правы по этому поводу! Исправил это в спешке, но потом посмотрю второй. Надеюсь, теперь все в порядке! Также был project_idпуть в случае, если по умолчанию используется a dict. Это может привести к странному поведению, так как dictвсегда будет использоваться аргумент, приведенный в (если я правильно помню).
Jojo
@jojo Означает ли это, что 'project_config / foo / bar' во втором варианте автоматически передаст kwargs {'project_id': 'bar'} представлению?
Оригинальный соус для барбекю
9

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

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

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

r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'
tarequeh
источник
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+)/)?$']
Патрик
2

Джанго = 2,2

urlpatterns = [
    re_path(r'^project_config/(?:(?P<product>\w+)/(?:(?P<project_id>\w+)/)/)?$', tool.views.ProjectConfig, name='project_config')
]
AzizAhmad
источник
0

Использовать? работать хорошо, вы можете проверить на pythex . Не забудьте добавить параметры * args и ** kwargs в определение методов представления

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
franciscorode
источник