2010-05-06 13 views
24

J'ai URL comme http://example.com/depict?smiles=CO&width=200&height=200 (et avec plusieurs autres arguments en option)Comment construire un reverse/url Django en utilisant des arguments de requête?

Mon urls.py contient:

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

je peux aller à cette URL et obtenir le 200x200 PNG qui a été construit, donc je sais cette partie fonctionne.

Dans mon modèle de la réponse "cansmi.cansmi", je veux construire une URL pour le modèle nommé "cyclops-dépeindre" donné quelques paramètres de requête. Je pensais que je pouvais faire

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

où « input_smiles » est une entrée au modèle via une soumission de formulaire. Dans ce cas, c'est la chaîne "CO" et je pensais que cela créerait une URL comme celle en haut.

Ce modèle échoue avec 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.

Ceci est un message d'erreur assez commune à la fois ici sur StackOverflow et ailleurs. Dans tous les cas, j'ai trouvé que les gens les utilisaient avec des paramètres dans l'URL regexp, ce qui n'est pas le cas où les paramètres entrent dans la requête.

Cela signifie que je me trompe. Comment puis-je le faire correctement? Autrement dit, je veux construire l'URL complète, y compris les paramètres de chemin et de requête, en utilisant quelque chose dans le modèle.

Pour référence,

% 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

J'ai créé une demande de fonctionnalité: https://code.djangoproject.com/ticket/25582 – guettli

Répondre

18

Votre expresion régulière n'a pas détenteurs de place (c'est pourquoi vous obtenez NoReverseMatch):

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

Vous pouvez le faire comme ceci:

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

URLconf search does not include GET or POST parameters

Ou si vous souhaitez utiliser {% url%} tag vous devez restructurer votre modèle d'URL pour quelque chose comme

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

alors vous pourriez faire quelque chose comme

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

Suivi :

Exemple simple pour une balise personnalisée:

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

Vous pouvez utiliser cette balise dans vos modèles comme ceci:

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

et nous espérons qu'il devrait quelque chose de sortie comme

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

C'est un peu frustes, ce qui est la raison pour laquelle je pensais qu'il doit y avoir un meilleure solution. Certains des paramètres proviennent de l'entrée de formulaire. L'expression de modèle réelle serait {% url cyclops-dépeindre%}? Smiles = {{input_smiles}} & width = {{size}} & height = {{size}}. C'est plus difficile à comprendre et à expliquer que l'invalide/hypothétique {% query-url cyclops-dépeint smiles = input_smiles width = taille height = taille%}. Avoir les patrons dans l'URL est bien sûr possible mais 7 des 8 paramètres sont optionnels et il n'y a pas d'ordre naturel, ce qui le rend plutôt forcé. (Et Django est censé être pour les perfectionnistes.) –

+0

Si vous voulez encapsuler la logique de construction des URL, vous pouvez simplement écrire votre propre [custom templatetag] [1]. Faites-lui prendre des paramètres tels que 'entry' ou même un contexte complet et renvoyez l'URL construite. De cette façon, vous pouvez même émuler la balise 'url' et avoir la syntaxe que vous aimez. [1]: http://docs.djangoproject.com/fr/dev/howto/custom-template-tags/#custom- template-tags-and-filters –

+0

Je reviens à ce projet. Une chose est que j'enseigne ceci aux développeurs non-logiciels (ce sont des chimistes computationnels qui font de la programmation) et je ne veux pas expliquer tout cela. Je vais devoir y réfléchir un peu plus. Merci pour le suivi! –

42

Construire une URL avec la chaîne de requête par concaténation de chaîne comme suggéré par certains réponses est aussi mauvaise idée que la construction de requêtes SQL par concaténation de chaînes. C'est compliqué, peu élégant et particulièrement dangereux avec une entrée fournie par l'utilisateur (non fiable). Malheureusement, Django n'offre pas une possibilité facile de passer des paramètres de requête à la fonction reverse.

La norme Python urllib fournit cependant la fonctionnalité de codage de chaîne de requête souhaitée.

Dans mon application, j'ai créé une fonction d'assistance:

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

Alors je l'appelle dans la vue comme suit:

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 

S'il vous plaît noter le bon codage de caractères spéciaux tels que l'espace et point d'exclamation!

+0

D'accord, mieux que concaténation simple. –

+1

Je voudrais générer l'URL depuis l'intérieur du modèle. Si je vous comprends bien, alors que cela aide à faire l'URL, il faut toujours le crochet de balise de modèle. –

+0

@Andrew Dalke Vous avez raison, vous devrez toujours implémenter un tag personnalisé avec une implémentation basée sur urllib.urlencode – geekQ

11

Aucune des réponses d'origine n'aborde le problème lié à la résolution des URL dans le code de vue. Pour les chercheurs futurs, si vous essayez de le faire, utilisez kwargs, quelque chose comme:

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

+2

Ceci devrait être la réponse acceptée. – Amyth

+40

reverse avec kwargs ne fonctionne que pour les paramètres de chemin, pas les paramètres de requête. – Pace

8

Je recommande d'utiliser QueryDict de builtin django. Il gère également les listes correctement.Fin échappe automatiquement certains caractères spéciaux (comme =, ?, /, « # »):

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' 

Pour utiliser dans les modèles que vous devez créer étiquette de modèle personnalisé

4

La réponse qui a utilisé urllib est en effet bon, cependant, alors qu'il essayait d'éviter la concaténation des chaînes, il l'a utilisé dans path + '?' + urllib.urlencode(kwargs). Je crois que cela peut créer des problèmes lorsque le path a déjà quelques requêtes parmas.

Une fonction modifiée ressemblerait à ceci:

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)