Parametri url opzionali di Django


162

Ho un URL Django come questo:

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

Il problema è che voglio che il project_idparametro sia facoltativo.

Voglio /project_config/e /project_config/12345abdce/essere ugualmente validi pattern URL, in modo che se project_id viene passato, allora posso usarlo.

Allo stato attuale, ottengo un 404 quando accedo all'URL senza il project_idparametro.

Risposte:


382

Esistono diversi approcci.

Uno è quello di utilizzare un gruppo non di acquisizione nella regex: (?:/(?P<title>[a-zA-Z]+)/)?
rendere facoltativo un token URL Regex Django

Un altro modo più semplice da seguire è avere più regole che soddisfino le tue esigenze, tutte puntando alla stessa vista.

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),
)

Tieni presente che a tuo avviso dovrai anche impostare un valore predefinito per il parametro URL facoltativo, altrimenti visualizzerai un errore:

def foo(request, optional_parameter=''):
    # Your code goes here

68
Vota per l'opzione percorsi multipli. +1
Burhan Khalid,

4
@Yuji: non riesci a risolvere il problema di inversione nominando ogni modello di url?
Ted

8
possiamo dare ad ogni vista lo stesso nome?
Eugene,

2
@ Yuji'Tomita'Tomita Lo so, quindi la risposta alla domanda di Eugene è purtroppo, no non possiamo senz'altro avere più viste con lo stesso nome, anche se le stiamo implementando come un modo per ottenere parametri opzionali.
nnyby,

2
@eugene Sì, possiamo avere due URL con lo stesso nome, la retromarcia prenderà in modo intelligente qualunque sia applicabile a seconda degli argomenti
Arpit Singh

37

È possibile utilizzare percorsi nidificati

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")
        ])),
    ])),
]

Questo è molto più ASCIUTTO ( productsupponiamo che tu voglia rinominare il kwarg in product_id, devi solo cambiare la linea 4 e influenzerà gli URL sotto.

A cura di Django 1.8 e versioni successive


1
Nidificato è buono. Inoltre, separa più chiaramente le diverse sezioni di URL nel codice (a causa dell'uso dei rientri)
Patrick,

Il problema con il nidificato è se hai più parametri opzionali, quindi finisci per non essere SECCO, poiché con, ad esempio, 3 parametri opzionali, hai 8 diverse combinazioni di possibili URL. È necessario gestire il parametro 1 che si verifica, il parametro 1 non si verifica ma il parametro 2 si verifica e i parametri 1 e 2 non si verificano ma si verifica il parametro 3. Il paragrafo URL sarà MOLTO più difficile da leggere rispetto a una singola stringa con più parametri opzionali. L'uso di costanti simboliche per le sottostringhe di parametri opzionali faciliterebbe la lettura e ci sarebbe un solo URL.
Bogatyr,

Penso che tu abbia ragione, ma questo è più il risultato di una cattiva visualizzazione / progettazione URL. Questo esempio potrebbe essere rielaborato per essere molto migliore.
Jacob Valenta,

'appartamento è meglio che nidificato'
pjdavis,

30

Ancora più semplice è usare:

(?P<project_id>\w+|)

"(A | b)" significa a o b, quindi nel tuo caso sarebbe uno o più caratteri di parole (\ w +) o niente.

Quindi sembrerebbe:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

9
Mi piace la semplicità di questa soluzione, ma attenzione: facendo ciò, la vista riceverà comunque un valore per l'argomento, che sarà None. Ciò significa che non puoi fare affidamento su un valore predefinito nella firma della vista per questo: devi testarlo esplicitamente all'interno e assegnare di conseguenza.
Anto,

Questo è quello che stavo cercando =)
Mike Brian Olivera,

3
che dire dell'ultima barra nel caso in cui project_id non sia presente?
iamkhush,

Puoi solo aggiungere un? dopo la barra o includi semplicemente la barra nel modello project_id
Juan José Brown,

18

Django> versione 2.0 :

L'approccio è essenzialmente identico a quello indicato nella risposta di Tomji "Tomita" di Yuji . Interessato, tuttavia, è la sintassi:

# 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
    ...

Usando path()puoi anche passare argomenti extra a una vista con l'argomento opzionale kwargsche è di tipo dict. In questo caso la tua vista non avrebbe bisogno di un valore predefinito per l'attributo project_id:

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

Per come eseguire questa operazione nella versione Django più recente , consulta i documenti ufficiali sulla distribuzione degli URL .


1
Penso che tu abbia confuso project_id e product_id nel tuo codice, giusto?
Andreas Bergström,

@ AndreasBergström grazie mille per averlo sottolineato! hai ragione su questo! L'ho corretto in fretta, ma ci sarà un secondo sguardo più tardi. Spero vada bene adesso! C'era anche il project_idfermo nel percorso in caso di default usando a dict. Questo può portare a comportamenti apparentemente strani, poiché l'argomento fornito in dictsarà sempre usato (se ricordo bene).
jojo,

@jojo Significa che un 'project_config / foo / bar' nella seconda opzione passerà automaticamente i kwargs {'project_id': 'bar'}?
Original BBQ Sauce

9

Ho pensato di aggiungere un po 'alla risposta.

Se hai più definizioni URL, dovrai nominarle ognuna separatamente. Quindi si perde la flessibilità quando si chiama reverse poiché un reverse si aspetta un parametro mentre l'altro no.

Un altro modo di usare regex per accogliere il parametro opzionale:

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

2
In Django 1.6 questo rappresenta un'eccezione per me. Starei lontano da essoReverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P<pk>\\d+)/)?$']
Patrick,

2

Django = 2.2

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

0

Uso ? funziona bene, puoi controllare su pythex . Ricorda di aggiungere i parametri * args e ** kwargs nella definizione dei metodi di visualizzazione

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.