web-dev-qa-db-fra.com

Comment puis-je construire un Django reverse/url en utilisant des arguments de requête?

J'ai des URL du type http://example.com/depict?smiles=CO&width=200&height=200 (avec plusieurs autres arguments facultatifs)

Mon urls.py contient:

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

Je peux accéder à cette URL et obtenir le fichier PNG 200x200 qui a été construit. Je sais donc que cette partie fonctionne.

Dans mon modèle à partir de la réponse "cansmi.cansmi", je souhaite créer une URL pour le modèle nommé "cyclops-depict" en fonction de certains paramètres de requête. Je pensais pouvoir faire

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

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

Ce modèle échoue avec une erreur TemplateSyntaxError:

A capturé une exception lors du rendu: Inverse pour 'cyclops-dépict' avec les arguments '()' et les arguments de mot clé '{' smiles ': u'CO', 'hauteur': 200, 'largeur': 200} 'non trouvé.

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

Cela signifie que je le fais mal. Comment puis-je le faire correctement? C'est-à-dire que 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.
27
Andrew Dalke

Votre expression habituelle n'a pas de générique (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

La recherche d'URLconf n'inclut pas les paramètres GET ou POST

Ou, si vous souhaitez utiliser la balise {% url%}, vous devez restructurer votre modèle d’URL de la manière suivante: 

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 j'espère qu'il devrait produire quelque chose comme

/depict?width=200&height=200&name=SomeName
20
Davor Lucic

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

Le standard Python urllib fournit toutefois 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) # for Python 3, use urllib.parse.urlencode instead

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

Veuillez noter le codage approprié des caractères spéciaux tels que l'espace et le point d'exclamation!

44
geekQ

Je recommande d'utiliser le fichier QueryDict de Django intégré. Il gère également les listes correctement. End échappe automatiquement à certains caractères spéciaux (tels que =, ?, /, '#'):

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 ceci dans les modèles, vous devrez créer une balise de modèle personnalisée.

12
imposeren

Aucune des réponses originales ne résout le problème lié à la résolution des URL dans le code d'affichage. Pour les futurs chercheurs, si vous essayez de faire cela, utilisez kwargs, quelque chose comme:

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

9
Aitch

La réponse qui a utilisé urllib est effectivement bonne, cependant, alors qu’elle essayait d’éviter la concaténation de chaînes, elle l’utilisait dans path + '?' + urllib.urlencode(kwargs). Je pense que cela peut créer des problèmes lorsque la path a déjà des paramètres de requête.

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)
5
Nour Chawich

Variation de travail des réponses précédentes et mon expérience avec ce genre de choses.

from Django.urls import reverse
from Django.utils.http import urlencode

def build_url(*args, **kwargs):
    params = kwargs.pop('params', {})
    url = reverse(*args, **kwargs)
    if params:
        url += '?' + urlencode(params)
    return url

Comment utiliser:

>>> build_url('products-detail', kwargs={'pk': 1}, params={'category_id': 2})
'/api/v1/shop/products/1/?category_id=2'
0
michal-michalak