2010-05-06 10 views
24

Ich habe URLs wie http://example.com/depict?smiles=CO&width=200&height=200 (und mit mehreren anderen optionalen Argumenten)Wie konstruiere ich eine Django Reverse/URL mit Abfrage Args?

Mein urls.py enthält:

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

kann ich zu dieser URL gehen und die 200x200 PNG erhalten, die gebaut wurde, damit ich weiß, dieser Teil funktioniert.

In meiner Vorlage aus der "cansmi.cansmi" -Antwort möchte ich eine URL für die benannte Vorlage "cyclops-depict" mit einigen Abfrageparametern erstellen. Ich dachte, ich

tun konnte

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

wo „input_smiles“ auf die Vorlage eine Eingabe über ein Formular Vorlage ist. In diesem Fall ist es die Zeichenfolge "CO" und ich dachte, es würde eine URL wie die oben erstellen.

Diese Vorlage schlägt mit einem TemplateSyntaxError:

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

Dies ist eine ziemlich häufig Fehlermeldung beide hier auf Stackoverflow und anderswo. In jedem Fall habe ich festgestellt, dass Leute sie mit Parametern im URL-Pfad regexp verwenden, was ich nicht habe, wenn die Parameter in die Abfrage eingehen.

Das bedeutet, ich mache es falsch. Wie mache ich es richtig? Das heißt, ich möchte die vollständige URL einschließlich Pfad- und Abfrageparametern mit etwas in der Vorlage erstellen.

Als Referenz

% 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

ich eine Feature-Anfrage erstellt: https://code.djangoproject.com/ticket/25582 – guettli

Antwort

18

Ihre regelmäßige expresion keine Platzhalter hat (das ist, warum Sie NoReverseMatch bekommen):

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

Man könnte es wie folgt tun:

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

URLconf search does not include GET or POST parameters

Oder wenn Sie {% url%} verwenden möchten markieren Sie Ihre URL-Muster zu so etwas wie

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

dann könnte man so etwas wie

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

Follow-up tun Umstrukturierung sollte :

Einfaches Beispiel für benutzerdefinierte Tag:

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

Sie diesen Tag in Ihren Vorlagen wie so verwenden:

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

und hoffentlich soll es Ausgabe etwas wie

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

Das ist eher unelegant, weshalb ich es gedacht sein, muss bessere Lösung. Einige der Parameter stammen aus der Formulareingabe. Der eigentliche Template-Ausdruck wäre {% url cyclops-depict%}? Smiles = {{input_smiles}} & width = {{Größe}} & height = {{Size}}. Das ist schwerer zu verstehen und zu erklären als das ungültige/hypothetische {% query-url cyclops-depict smiles = input_smiles width = Größe height = size%}. Die Muster in der URL sind natürlich möglich, aber 7 der 8 Parameter sind optional und es gibt keine natürliche Reihenfolge, was es eher erzwungen macht. (Grrr. Und Django sollte für Perfektionisten sein.) –

+0

Wenn Sie die Logik zum Erstellen von URLs kapseln möchten, könnten Sie einfach Ihre eigenen [Custom TemplateTag] [1] schreiben. Nehmen Sie Parameter wie "entry" oder sogar einen vollständigen Kontext und geben Sie eine gebaute URL zurück. Auf diese Weise können Sie sogar das Tag "url" emulieren und die von Ihnen gewünschte Syntax haben. [1]: http://docs.djangoproject.com/de/dev/howto/custom-template-tags/#custom- Template-Tags-and-filters –

+0

Ich komme zurück zu diesem Projekt. Eine Sache ist, dass ich das Nicht-Software-Entwicklern beibringe (sie sind Computerchemiker, die etwas programmieren) und ich möchte das alles nicht erklären. Ich muss darüber noch etwas nachdenken. Danke für die Fortsetzung! –

42

Aufbau eine URL mit Abfrage-String von String-Verkettung von einige als vorgeschlagen Antworten sind genauso eine schlechte Idee wie das Erstellen von SQL-Abfragen durch String-Verkettung. Es ist kompliziert, unelegant und besonders gefährlich mit einem vom Benutzer bereitgestellten (nicht vertrauenswürdigen) Eingang. Leider bietet Django keine einfache Möglichkeit, Abfrageparameter an die reverse-Funktion zu übergeben.

Python-Standard urllib bietet jedoch die gewünschte Abfragezeichenfolgencodierungsfunktionalität.

In meiner Anwendung ich eine Hilfsfunktion erstellt haben:

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

Dann in der Ansicht Ich nenne es wie folgt: wie Raum

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 

Bitte beachten Sie die richtige Codierung von Sonderzeichen und Ausrufezeichen!

+0

Einverstanden, besser als einfache Verkettung. –

+1

Ich möchte die URL innerhalb der Vorlage generieren. Wenn ich Sie richtig verstehe, während dies hilft, die URL zu machen, benötigt es immer noch den Template-Tag-Hook. –

+0

@Andrew Dalke Sie haben Recht, Sie müssen noch implementieren ein benutzerdefiniertes Tag mit einer Implementierung basierend auf urllib.urlencode – geekQ

11

Keine der ursprünglichen Antworten befasst sich mit dem verwandten Problem beim Auflösen von URLs im Ansichtscode. Für zukünftige Forscher, wenn Sie versuchen, dies zu tun, verwenden Sie kwargs, so etwas wie:

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

+2

Dies sollte die akzeptierte Antwort sein. – Amyth

+40

Umgekehrt mit Kwargs funktioniert nur für Pfadparameter, nicht Abfrageparameter. – Pace

8

Ich empfehle QueryDict builtin Djangos zu verwenden. Es behandelt auch Listen ordnungsgemäß.Beenden automatisch entkommt einige Sonderzeichen (wie =, ?, /, '#'):

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' 

Um dies in Vorlagen zu verwenden, müssen Sie eigenen Template-Tag

4

Die Antwort, die verwendete urllib ist in der Tat erstellen gut, aber während es versuchte, Strings Verkettung zu vermeiden, verwendete es es in path + '?' + urllib.urlencode(kwargs). Ich glaube, dass dies Probleme verursachen kann, wenn die path bereits einige Abfrage Parmas hat.

Eine modifizierte Funktion würde wie folgt aussehen:

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)