2010-05-06 12 views
24

que tienen URLs como http://example.com/depict?smiles=CO&width=200&height=200 (y con varios otros argumentos opcionales)¿Cómo construyo un Django reverse/url usando query args?

Mi urls.py contiene:

urlpatterns = patterns('', 
    (r'^$', 'cansmi.index'), 
    (r'^cansmi$', 'cansmi.cansmi'), 
    url(r'^depict$', cyclops.django.depict, name="cyclops-depict"), 

puedo ir a esa URL y obtener el PNG de 200x200 que se construyó, por lo que sé esa parte funciona

En mi plantilla de la respuesta "cansmi.cansmi" quiero construir una URL para la plantilla con nombre "cyclops-represente" dado algunos parámetros de consulta. Pensé que podía hacer

{% url cyclops-depict smiles=input_smiles width=200 height=200 %}

donde "input_smiles" es una entrada a la plantilla a través de un envío del formulario. En este caso, es la cadena "CO" y pensé que crearía una URL como la de arriba.

Esta plantilla falla con un TemplateSyntaxError:

Caught an exception while rendering: Reverse for 'cyclops-depict' with arguments '()' and keyword arguments '{'smiles': u'CO', 'height': 200, 'width': 200}' not found.

Este es un mensaje de error bastante común tanto aquí en StackOverflow y en otros lugares. En todos los casos que encontré, la gente los usaba con parámetros en la expresión regular de la ruta URL, que no es el caso en el que los parámetros entran en la consulta.

Eso significa que lo estoy haciendo mal. ¿Cómo lo hago bien? Es decir, quiero construir la URL completa, incluidos los parámetros de ruta y consulta, usando algo en la plantilla.

Como referencia,

% python manage.py shell 
Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
(InteractiveConsole) 
>>> from django.core.urlresolvers import reverse 
>>> reverse("cyclops-depict", kwargs=dict()) 
'/depict' 
>>> reverse("cyclops-depict", kwargs=dict(smiles="CO")) 
Traceback (most recent call last): 
    File "<console>", line 1, in <module> 
    File "/Library/Python/2.6/site-packages/django/core/urlresolvers.py", line 356, in reverse 
    *args, **kwargs))) 
    File "/Library/Python/2.6/site-packages/django/core/urlresolvers.py", line 302, in reverse 
    "arguments '%s' not found." % (lookup_view_s, args, kwargs)) 
NoReverseMatch: Reverse for 'cyclops-depict' with arguments '()' and keyword arguments '{'smiles': 'CO'}' not found. 
+0

creé una solicitud de función: https://code.djangoproject.com/ticket/25582 – guettli

Respuesta

18

Su expresion regular tiene no hay marcadores de posición (que es por eso que está recibiendo NoReverseMatch):

url(r'^depict$', cyclops.django.depict, name="cyclops-depict"), 

que podría hacerlo de esta manera:

{% url cyclops-depict %}?smiles=CO&width=200&height=200 

URLconf search does not include GET or POST parameters

O si lo desea utilizar {% url%} etiqueta que debe reestructurar su patrón de URL para algo así como

r'^depict/(?P<width>\d+)/(?P<height>\d+)/(?P<smiles>\w+)$' 

entonces se podría hacer algo como

{% url cyclops-depict 200 200 "CO" %} 

Seguimiento :

Ejemplo simple de etiqueta personalizada:

from django.core.urlresolvers import reverse 
from django import template 
register = template.Library() 

@register.tag(name="myurl") 
def myurl(parser, token): 
    tokens = token.split_contents() 
    return MyUrlNode(tokens[1:]) 

class MyUrlNode(template.Node): 
    def __init__(self, tokens): 
     self.tokens = tokens 
    def render(self, context): 
     url = reverse('cyclops-depict') 
     qs = '&'.join([t for t in self.tokens]) 
     return '?'.join((url,qs)) 

Usted podría utilizar esta etiqueta en sus plantillas, así:

{% myurl width=200 height=200 name=SomeName %} 

y es de esperar que debe hacer salir algo así como

/depict?width=200&height=200&name=SomeName 
+2

Eso es poco elegante, por lo que pensé que debe ser una mejor solución. Algunos de los parámetros provienen de la entrada del formulario. La expresión de la plantilla real sería {% url cyclops-depict%}? Smiles = {{input_smiles}} & width = {{size}} & height = {{size}}. Es más difícil de entender y explicar que el inválido/hipotético {% query-url cyclops-representa sonrisas = input_smiles ancho = tamaño alto = tamaño%}. Tener los patrones en la URL es, por supuesto, posible, pero 7 de los 8 parámetros son opcionales y no hay un orden natural, por lo que es bastante forzado. (Grrr. Y se supone que Django es para perfeccionistas) –

+0

Si desea encapsular la lógica para construir URL, puede escribir su propia [plantilla personalizada] [1]. Haga que tome parámetros como 'entry' o incluso un contexto completo y devuelva la URL construida. De esta forma, incluso podría emular la etiqueta 'url' y tener la sintaxis que desee .. [1]: http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#custom- template-tags-and-filters –

+0

Vuelvo a este proyecto. Una cosa es que estoy enseñando esto a desarrolladores que no son de software (son químicos computacionales que hacen algo de programación) y no quiero explicar todo esto. Tendré que pensar en esto un poco más. ¡Gracias por el seguimiento! –

42

La construcción de una URL con cadena de consulta mediante la concatenación de cadenas según lo sugerido por algunos respuestas es tan mala idea como crear consultas SQL por concatenación de cadenas. Es complicado, poco elegante y especialmente peligroso con una entrada proporcionada por el usuario (que no es de confianza). Desafortunadamente, Django no ofrece una posibilidad sencilla de pasar parámetros de consulta a la función reverse.

Python estándar urllib proporciona la función de codificación de cadena de consulta deseada.

En mi solicitud He creado una función auxiliar:

def url_with_querystring(path, **kwargs): 
    return path + '?' + urllib.urlencode(kwargs) 

Entonces me llaman en la vista de la siguiente manera:

quick_add_order_url = url_with_querystring(reverse(order_add), 
    responsible=employee.id, scheduled_for=datetime.date.today(), 
    subject='hello world!') 
# http://localhost/myapp/order/add/?responsible=5& 
#  scheduled_for=2011-03-17&subject=hello+world%21 

Tenga en cuenta la correcta codificación de caracteres especiales, como espacio y ¡signo de exclamación!

+0

De acuerdo, mejor que la concatenación simple. –

+1

Me gustaría generar la URL desde el interior de la plantilla. Si entiendo bien, aunque esto ayude a crear la URL, aún necesita el enlace de etiqueta de la plantilla. –

+0

@Andrew Dalke Tiene razón, aún necesitará implementar una etiqueta personalizada con una implementación basada en urllib.urlencode – geekQ

11

Ninguna de las respuestas originales aborda el problema relacionado con la resolución de URL en el código de vista. Para los futuros buscadores, si usted está tratando de hacer esto, utilizar kwargs, algo así como:

reverse('myviewname', kwargs={'pk': value})

+2

Esta debería ser la respuesta aceptada. – Amyth

+40

reverse con kwargs solo funciona para parámetros de ruta, no para parámetros de consulta. – Pace

8

Recomiendo el uso de Django orden interna de QueryDict. También maneja las listas correctamente.Terminar escapa automáticamente algunos caracteres especiales (como =, ?, /, '#'):

from django.http import QueryDict 
from django.core.urlresolvers import reverse 

q = QueryDict('', mutable=True) 
q['some_key'] = 'some_value' 
q.setlist('some_list', [1,2,3]) 
'%s?%s' % (reverse('some_view_name'), q.urlencode()) 
# '/some_url/?some_list=1&some_list=2&some_list=3&some_key=some_value' 

q.appendlist('some_list', 4) 
q['value_with_special_chars'] = 'hello=w#rld?' 
'%s?%s' % (reverse('some_view_name'), q.urlencode()) 
# '/some_url/?value_with_special_chars=hello%3Dw%23rld%3F&some_list=1&some_list=2&some_list=3&some_list=4&some_key=some_value' 

Para utilizar esta en plantillas que se necesitan para crear etiqueta de plantilla personalizada

4

La respuesta que utiliza urllib es de hecho bueno, sin embargo, mientras trataba de evitar la concatenación de cadenas, lo usó en path + '?' + urllib.urlencode(kwargs). Creo que esto puede crear problemas cuando el path ya tiene algunos parmas de consulta.

Una función modificada se vería así:

def url_with_querystring(url, **kwargs): 
    url_parts = list(urlparse.urlparse(url)) 
    query = dict(urlparse.parse_qsl(url_parts[4])) 
    query.update(kwargs) 
    url_parts[4] = urllib.urlencode(query) 
    return urlparse.urlunparse(url_parts) 
Cuestiones relacionadas