tags:

views:

1657

answers:

2

I’ve got a brand new Django project. I’ve added one minimal view function to views.py, and one URL pattern to urls.py, passing the view by function reference instead of a string:

# urls.py
# -------

# coding=utf-8

from django.conf.urls.defaults import *

from myapp import views


urlpatterns = patterns('',
    url(r'^myview/$', views.myview),
)


# views.py
----------

# coding=utf-8

from django.http import HttpResponse


def myview(request):
    return HttpResponse('MYVIEW LOL',  content_type="text/plain")

I’m trying to use reverse() to get the URL, by passing it a function reference. But I’m not getting a match, despite confirming that the view function I’m passing to reverse is the exact same view function I put in the URL pattern:

>>> from django.core.urlresolvers import reverse
>>> import urls
>>> from myapp import views

>>> urls.urlpatterns[0].callback is views.myview
True

>>> reverse(views.myview)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Library/Python/2.5/site-packages/django/core/urlresolvers.py", line 254, in reverse
    *args, **kwargs)))
  File "/Library/Python/2.5/site-packages/django/core/urlresolvers.py", line 243, in reverse
    "arguments '%s' not found." % (lookup_view, args, kwargs))
NoReverseMatch: Reverse for '<function myview at 0x6fe6b0>' with arguments '()' and keyword arguments '{}' not found.

As far as I can tell from the documentation, function references should be fine in both the URL pattern and reverse().

I’m using the Django trunk, revision 9092.

+6  A: 

Got it!! The problem is that some of the imports are of myproject.myapp.views, and some are just of myapp.views. This is confusing the Python module system enough that it no longer detects the functions as the same object. This is because your main settings.py probably has a line like:

ROOT_URLCONF = `myproject.urls`

To solve this, try using the full import in your shell session:

>>> from django.core.urlresolvers import reverse
>>> from myproject.myapp import views
>>> reverse(views.myview)
'/myview/'

Here's a log of the debugging session, for any interested future readers:

>>> from django.core import urlresolvers
>>> from myapp import myview
>>> urlresolvers.get_resolver (None).reverse_dict
{None: ([(u'myview/', [])], 'myview/$'), <function myview at 0x845d17c>: ([(u'myview/', [])], 'myview/$')}
>>> v1 = urlresolvers.get_resolver (None).reverse_dict.items ()[1][0]
>>> reverse(v1)
'/myview/'
>>> v1 is myview
False
>>> v1.__module__
'testproject.myapp.views'
>>> myview.__module__
'myapp.views'

What happens if you change the URL match to be r'^myview/$'?


Have you tried it with the view name? Something like reverse ('myapp.myview')?

Is urls.py the root URLconf, or in the myapp application? There needs to be a full path from the root to a view for it to be resolved. If that's myproject/myapp/urls.py, then in myproject/urls.py you'll need code like this:

from django.conf.urls.defaults import patterns
urlpatterns = patterns ('',
    (r'^/', 'myapp.urls'),
)
John Millikin
Good question. Ah, same thing, no change.
Paul D. Waite
Yeah, it works fine if I switch from function references to view name strings in the URL conf and `reverse`. But I’m being a bit fussy, trying not to do that :) urls.py is the root URL conf. It’s a fresh Django project; I’ve only edited /urls.py and /myapp/views.py.
Paul D. Waite
Having said that, it seems I can leave my urlconf using function references, if I pass a fully qualified string to reverse, i.e. reverse('myproj.myapp.views.myview'). That’s probably good enough. Might see about opening a ticket though.
Paul D. Waite
Paul: check out the edited post. You can get it to work the original way using a full import in the shell session.
John Millikin
Ooh, spot on! Well done that man. I don’t quite understand why the imports were an issue, but as always, I need to firm up my Python knowledge. Awesome, much appreciated.
Paul D. Waite
It's the evils of implicit relative imports -- they're being removed in Py3, thankfully. I've lost more time than I care to think about worrying about import paths.
John Millikin
Right, okay. I’ll have to do some reading up on imports again. Like much of Python, it seems simple, but there are always subtleties. Do you have much Django experience? Any idea if this is worth a ticket?
Paul D. Waite
I don't think it is, this is more of a general Python thing and they're already quite aware of it. Just remember to use absolute imports *always* -- google search for "python absolute imports" for more info.
John Millikin
Cool cool. Thanks again for your help, this has been driving me mental all weekend.
Paul D. Waite
Upvoted for the "debuggin session log" - that's a great idea and I'll have to start doing that too!
viraptor
+1  A: 

If your two code pastes are complete, then it doesn't look like the second, which makes the actual call to reverse(), ever imports the urls module and therefor if the url mapping is ever actually achieved.

ironfroggy
I’ve tried the shell session again, importing the urls module first, and I still get the same result. I’m pretty sure the mapping is achieved, as `urls.urlpatterns[0].callback is views.myview` returns true. Updated the link in the question.
Paul D. Waite