102

I have looked a lot on google for answers of how to use the 'url' tag in templates only to find many responses saying 'You just insert it into your template and point it at the view you want the url for'. Well no joy for me :( I have tried every permutation possible and have resorted to posting here as a last resort.

So here it is. My urls.py looks like this:

from django.conf.urls.defaults import *
from login.views import *
from mainapp.views import *
import settings

# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    # Example:
    # (r'^weclaim/', include('weclaim.foo.urls')),
    (r'^login/', login_view),
    (r'^logout/', logout_view),
    ('^$', main_view),

    # Uncomment the admin/doc line below and add 'django.contrib.admindocs' 
    # to INSTALLED_APPS to enable admin documentation:
    # (r'^admin/doc/', include('django.contrib.admindocs.urls')),

    # Uncomment the next line to enable the admin:
    (r'^admin/', include(admin.site.urls)),
    #(r'^static/(?P<path>.*)$', 'django.views.static.serve',{'document_root': '/home/arthur/Software/django/weclaim/templates/static'}),
    (r'^static/(?P<path>.*)$', 'django.views.static.serve',{'document_root': settings.MEDIA_ROOT}),
)

My 'views.py' in my 'login' directory looks like:

from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.contrib import auth

def login_view(request):
    if request.method == 'POST':
        uname = request.POST.get('username', '')
        psword = request.POST.get('password', '')
        user = auth.authenticate(username=uname, password=psword)
        # if the user logs in and is active
        if user is not None and user.is_active:
            auth.login(request, user)
            return render_to_response('main/main.html', {}, context_instance=RequestContext(request))
            #return redirect(main_view)
        else:
            return render_to_response('loginpage.html', {'box_width': '402', 'login_failed': '1',}, context_instance=RequestContext(request))
    else:
        return render_to_response('loginpage.html', {'box_width': '400',}, context_instance=RequestContext(request))

def logout_view(request):
    auth.logout(request)
    return render_to_response('loginpage.html', {'box_width': '402', 'logged_out': '1',}, context_instance=RequestContext(request))

and finally the main.html to which the login_view points looks like:

<html>
<body>
test! <a href="{% url logout_view %}">logout</a>
</body>
</html>

So why do I get 'NoReverseMatch' every time?

(on a slightly different note I had to use 'context_instance=RequestContext(request)' at the end of all my render-to-response's because otherwise it would not recognise {{ MEDIA_URL }} in my templates and I couldn't reference any css or js files. I'm not to sure why this is. Doesn't seem right to me)

Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
Robert Johnstone
  • 5,431
  • 12
  • 58
  • 88
  • 1
    What you are saying about the `context_instance=RequestContext(request)` is correct, this is needed to allow the template access to the context variables provided to all templates. This is done by default for all generic views, but you need to do it yourself in your custom ones. – Marcus Whybrow Jan 04 '11 at 23:22
  • Seems a bit strange to me because you are going to be accessing your css and js files all the time from your templates to keep consistency across your site. Therefore shouldn't you be able to access {{ MEDIA_URL }} by default? – Robert Johnstone Jan 05 '11 at 09:11
  • 1
    The accepted answer here is no longer valid – Dan Gayle Feb 02 '18 at 01:23
  • Add a new answer and then I'll accept that – Robert Johnstone Feb 22 '18 at 16:28

7 Answers7

118

The selected answer is out of date and no others worked for me (Django 1.6 and [apparantly] no registered namespace.)

For Django 1.5 and later (from the docs)

Warning Don’t forget to put quotes around the function path or pattern name!

With a named URL you could do:

(r'^login/', login_view, name='login'),
...
<a href="{% url 'login' %}">logout</a>

Just as easy if the view takes another parameter

def login(request, extra_param):
...
<a href="{% url 'login' 'some_string_containing_relevant_data' %}">login</a>
Jonathan
  • 8,453
  • 9
  • 51
  • 74
Mike S
  • 1,537
  • 2
  • 11
  • 15
62

Instead of importing the logout_view function, you should provide a string in your urls.py file:

So not (r'^login/', login_view),

but (r'^login/', 'login.views.login_view'),

That is the standard way of doing things. Then you can access the URL in your templates using:

{% url login.views.login_view %}
Marcus Whybrow
  • 19,578
  • 9
  • 70
  • 90
  • 2
    yeah, definitely use strings. this way, you can also use prefixes, and you don't have to import all of your view functions into your URLConf. – Sri Raghavan Jan 04 '11 at 23:53
  • I tried this as well and got 'Caught NoReverseMatch while rendering: Reverse for 'login.views.login_views' with arguments '()' and keyword arguments '{}' not found.' again :( – Robert Johnstone Jan 05 '11 at 09:15
  • Wait... Scratch that! I waited 15 mins, tried it again and it worked (yippeeee!!!). Nice 1. Next question. If I only have one site that I have added in the admin page, how can I suffix this to {% url ??? %} – Robert Johnstone Jan 05 '11 at 10:12
  • 1
    Yes this is a necro, but the URL tag is still biting me in 2015. It would help if they didn't keep changing syntax: – Dave Aug 06 '15 at 23:04
  • 6
    Just because I came here from google, I should say that as for django 1.8+, passing strings as view argument is deprecated, and will be soon removed. You should actually pass the callable like in this post. – user3599803 May 15 '16 at 18:24
51

Make sure (django 1.5 and beyond) that you put the url name in quotes, and if your url takes parameters they should be outside of the quotes (I spent hours figuring out this mistake!).

{% url 'namespace:view_name' arg1=value1 arg2=value2 as the_url %}
<a href="{{ the_url }}"> link_name </a>
Bogatyr
  • 19,255
  • 7
  • 59
  • 72
  • I know this is an old answer, but this really helped me. I'm using django-norel, which is a fork of Django 1.6, which must also suffer this problem because encapsulating the url name in quotes fixed the TypeError I was getting. – robobrobro Jun 26 '15 at 18:44
  • 3
    Using the correct documentation helps too, since they keep changing the syntax: `{% url app_views.client client.id %}` (no quotes) in 1.4, `{% url 'app_views.client' client.id %}` (with quotes) in 1.5 -1.7, and `{% url 'app-views-client' client.id %}` (no underscores or dots, just dashes) in 1.8. – Dave Aug 06 '15 at 23:09
  • Oh Lord and I was planning to upgrade to 1.8 soon. – Bogatyr Sep 03 '15 at 05:59
18

The url template tag will pass the parameter as a string and not as a function reference to reverse(). The simplest way to get this working is adding a name to the view:

url(r'^/logout/' , logout_view, name='logout_view')
Bernhard Vallant
  • 49,468
  • 20
  • 120
  • 148
12

I run into same problem.

What I found from documentation, we should use namedspace.

in your case {% url login:login_view %}

Alist
  • 169
  • 2
  • 3
1

Judging from your example, shouldn't it be {% url myproject.login.views.login_view %} and end of story? (replace myproject with your actual project name)

Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
  • Same as the above 'Caught NoReverseMatch while rendering: Reverse for 'weclaim.login.views.login_views' with arguments '()' and keyword arguments '{}' not found.' (I'm presuming that my project name is the same name as the root directory all my code is stored in) – Robert Johnstone Jan 05 '11 at 09:19
0

For example, there are 4 views in my_app1/views.py as shown below. *You can see the doc explaining URL namespaces in detail:

# "my_app1/views.py"

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
    return render(request, 'index.html')

def test1(request):
    return HttpResponse("Test1")

def test2(request):
    return HttpResponse("Test2")

def test3(request):
    return HttpResponse("Test3")

And, there are 2 paths in my_app1/urls.py as shown below:

# "my_app1/urls.py"

from django.urls import path
from . import views

app_name = "my_app1"

urlpatterns = [
    path('test1/', views.test1, name="test1"),
    path('test2/', views.test2, name="test2"),
]

And, there are 4 paths in core/urls.py as shown below:

# "core/urls.py"

from django.contrib import admin
from django.urls import path, include
from my_app1 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index),
    path('my_app1/', include('my_app1.urls')),
    path('test3/', views.test3, name='test3'),
]

Now, you can set these URL namespaces to url tag in index.html as shown below:

{% "index.html" %}

<a href="{% url 'admin:index' %}">Admin</a>
<a href="{% url 'my_app1:test1' %}">Test1</a>
<a href="{% url 'my_app1:test2' %}">Test2</a>
<a href="{% url 'test3' %}">Test3</a>
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129