Необов’язкові параметри URL-адреси Django


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 це буде передано, то я можу його використовувати.

На сьогоднішній день я отримую 404, коли я отримую доступ до URL без project_idпараметра.

Відповіді:


381

Є кілька підходів.

Перший - використовувати групу, яка не захоплює, в регулярному вираженні: (?:/(?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-адрес у вашому коді (завдяки використанню відступів)
Патрік

Проблема з вкладеною є, якщо у вас є декілька необов’язкових параметрів, ви, в кінцевому підсумку, не є ДУХОЮ, оскільки, наприклад, з 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?
iamkhush

Ви можете просто додати? після косої риси або просто включіть косу рису в шаблон проекту_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-адрес .


1
Я думаю, ви змішали project_id і product_id у своєму коді, правда?
Andreas Bergström

@ AndreasBergström дякую дуже за те, що вказав на це! ви з цим абсолютно праві! Виправлено його поспіхом, але пізніше його погляньте. Сподіваюся, що зараз добре! Існував також project_idще шлях у випадку, коли за замовчуванням використовується a dict. Це може призвести до, здавалося б, дивної поведінки, оскільки аргумент, поданий у dictзаповіті, завжди буде використовуватися (якщо я правильно пам’ятаю).
Jojo

@jojo Це означає, що "project_config / foo / bar" у 2-й опції автоматично передасть {'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+)/)?$']
Патрік

2

Джанго = 2,2

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

0

Використовувати? добре працювати, можна перевірити на pythex . Не забудьте додати параметри * args та ** kwargs у визначенні методів перегляду

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.