Django - Regex per i parametri URL opzionali
-
28-10-2019 - |
Domanda
Ho una vista in Django che può accettare una serie di parametri di filtro diversi, ma sono tutti opzionali. Se ho 6 filtri opzionali, devo davvero scrivere URL per ogni combinazione del 6 o c'è un modo per definire quali parti dell'URL sono opzionali?
Per darti un esempio con solo 2 filtri, potrei avere tutte queste possibilità di URL:
/<city>/<state>/
/<city>/<state>/radius/<miles>/
/<city>/<state>/company/<company-name>/
/<city>/<state>/radius/<miles>/company/<company-name>/
/<city>/<state>/company/<company-name>/radius/<miles>/
Tutti questi URL indicano la stessa visione e gli unici parametri richiesti sono la città e lo stato. Con 6 filtri, questo diventa ingestibile.
Qual è il modo migliore per fare ciò che voglio ottenere?
Soluzione
Un metodo sarebbe quello di rendere l'espressione regolare leggere tutti i filtri indicati come una singola stringa, quindi dividerli in singoli valori nella vista.
Mi è venuto in mente il seguente URL:
(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$',
'views.my_view'),
Abbinare la città e lo stato richiesti è facile. Il filters
La parte è un po 'più complicata. La parte interna - (?:/[^/]+/[^/]+)*
- corrisponde ai filtri indicati nel modulo /name/value
. in ogni caso, il *
quantificatore (come tutti i quantificatori di espressione regolare di Python) restituisce solo l'ultima partita trovata, quindi se l'URL era /radius/80/company/mycompany/
solo company/mycompany
verrebbe immagazzinato. Invece, gli diciamo di non catturare i singoli valori (il ?:
All'inizio) e mettilo all'interno di un blocco di cattura che memorizzerà tutti i valori di filtro come una singola stringa.
La logica di visualizzazione è abbastanza semplice. Si noti che l'espressione regolare corrisponderà solo alle coppie di filtri - quindi /company/mycompany/radius/
non sarà abbinato. Ciò significa che possiamo tranquillamente supporre che abbiamo coppie di valori. La vista con cui ho testato è la seguente:
def my_view(request, city, state, filters):
# Split into a list ['name', 'value', 'name', 'value']. Note we remove the
# first character of the string as it will be a slash.
split = filters[1:].split('/')
# Map into a dictionary {'name': 'value', 'name': 'value'}.
filters = dict(zip(split[::2], split[1::2]))
# Get the values you want - the second parameter is the default if none was
# given in the URL. Note all entries in the dictionary are strings at this
# point, so you will have to convert to the appropriate types if desired.
radius = filters.get('radius', None)
company = filters.get('company', None)
# Then use the values as desired in your view.
context = {
'city': city,
'state': state,
'radius': radius,
'company': company,
}
return render_to_response('my_view.html', context)
Due cose da notare su questo. Innanzitutto, consente voci di filtro sconosciute nella tua vista. Per esempio, /fakefilter/somevalue
è valido. Il codice di visualizzazione sopra li ignora, ma probabilmente si desidera segnalare un errore all'utente. In tal caso, modifica il codice ottenendo i valori su
radius = filters.pop('radius', None)
company = filters.pop('company', None)
Eventuali voci rimaste in filters
Il dizionario sono valori sconosciuti su cui puoi lamentarti.
In secondo luogo, se l'utente ripete un filtro, verrà utilizzato l'ultimo valore. Per esempio, /radius/80/radius/50
imposterà il raggio su 50. Se si desidera rilevare questo, dovrai scansionare l'elenco dei valori prima che venga convertito in un dizionario:
given = set()
for name in split[::2]:
if name in given:
# Repeated entry, complain to user or something.
else:
given.add(name)
Altri suggerimenti
Questo è assolutamente il caso d'uso per i parametri GET. Il tuo URLConf dovrebbe essere solo /city/state/
, quindi i vari filtri vanno alla fine come Ottieni variabili:
/city/state/?radius=5&company=google
Ora, a tuo avviso, accetti city
e state
come parametri normali, ma tutto il resto è archiviato in request.GET
Querydict.
Potresti anche fare un solo URL (che controlla solo l'inizio del percorso, che dovrebbe essere lo stesso) indicando il tuo punto di vista e poi analizza request.path
a tuo avviso. D'altra parte, se hai davvero molti parametri di filtro opzionali in varie combinazioni, la soluzione migliore è molto spesso fare il filtro tramite GET
-Parameters, specialmente se gli URL utilizzati per il filtraggio non devono essere ottimizzati per qualsiasi motore di ricerca ...
Prova a usare qualcosa del genere nei tuoi urls.py:
url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*$', 'view')