Pregunta

Tengo una vista en Django que puede aceptar varios parámetros de filtro diferentes, pero todos son opcionales. Si tengo 6 filtros opcionales, ¿realmente tengo que escribir URL para cada combinación de los 6 o hay alguna forma de definir qué partes de la URL son opcionales?

Para darle un ejemplo con solo 2 filtros, podría tener todas estas posibilidades de 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>/

Todas estas URL apuntan a la misma opinión y los únicos parámetros requeridos son la ciudad y el estado. Con 6 filtros, esto se vuelve inmanejable.

¿Cuál es la mejor manera de hacer lo que quiero lograr?

¿Fue útil?

Solución

Un método sería hacer que la expresión regular lea todos los filtros dados como una sola cadena, y luego dividirlos en valores individuales en la vista.

Se me ocurrió la siguiente URL:

(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$',
 'views.my_view'),

Hacer coincidir la ciudad y el estado requeridos es fácil. los filters Parte es un poco más complicado. La parte interna - (?:/[^/]+/[^/]+)* - coincide con los filtros dados en el formulario /name/value. sin embargo, el * Cuantificador (como todos los cuantificadores de expresión regular de Python) solo devuelve la última coincidencia encontrada, por lo que si la URL era /radius/80/company/mycompany/ solamente company/mycompany sería almacenado. En cambio, le decimos que no capture los valores individuales (el ?: al principio), y colóquelo dentro de un bloque de captura que almacenará todos los valores de filtro como una sola cadena.

La lógica de la vista es bastante sencilla. Tenga en cuenta que la expresión regular solo coincidirá con pares de filtros, así que /company/mycompany/radius/ no se combinará. Esto significa que podemos asumir con seguridad que tenemos pares de valores. La vista con la que probé esto es la siguiente:

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)

Dos cosas a tener en cuenta sobre esto. Primero, permite entradas de filtro desconocidas en su vista. Por ejemplo, /fakefilter/somevalue es válida. El código de vista anterior ignora estos, pero probablemente desee informar un error al usuario. Si es así, altere el código obteniendo los valores a

radius = filters.pop('radius', None)
company = filters.pop('company', None)

Cualquier entrada restante en el filters El diccionario son valores desconocidos sobre los que puede quejarse.

En segundo lugar, si el usuario repite un filtro, se utilizará el último valor. Por ejemplo, /radius/80/radius/50 establecerá el radio en 50. Si desea detectar esto, deberá escanear la lista de valores antes de convertirse en un diccionario:

given = set()
for name in split[::2]:
    if name in given:
        # Repeated entry, complain to user or something.
    else:
        given.add(name)

Otros consejos

Este es absolutamente el caso de uso para los parámetros GET. Tu urlconf debería ser solo /city/state/, luego los diversos filtros van al final como variables Get:

/city/state/?radius=5&company=google

Ahora, en tu opinión, aceptas city y state Como parámetros normales, pero todo lo demás se almacena en el request.GET QueryDict.

También puede hacer una sola URL (que solo verifica el comienzo del camino, que debería ser lo mismo) apuntando a su vista y luego analizar request.path en tu opinión. Por otro lado, si tiene realmente muchos parámetros de filtro opcional en varias combinaciones, la mejor solución es muy a menudo para hacer el filtrado a través de GET-Parámetros, especialmente si las URL utilizadas para el filtrado no necesitan ser optimizadas para ningún motor de búsqueda ...

Intente usar algo así en sus URLs.py:

url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*$', 'view')
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top