Python Django UnboundLocalError while calling a different function
Pregunta
Not long ago I started programming Python with Django. Sometimes I get strange errors and have no clue, why. So, let's beginn with one of these errors.
I have a View with two functions. Example:
def view_post(request, slug):
"""
Shows a single post
"""
posts = Post.objects(slug = slug).limit(1)
for items in posts:
post = items
cssClasses = css_class_converter({ _css_class_editable })
context = RequestContext(request)
return render_to_response("single.html", { 'post': post, 'class': cssClasses }, context)
def new_post(request):
'''
Opens a blank page for creating a new post
'''
post = Post()
cssClasses = css_class_converter({ _css_class_editable, _css_class_new })
context = RequestContext(request)
return render_to_response("single.html", {'post': post, 'new': True }, context)
Then calling them with my URLconf. Calling view_post function works as It should, without errors.
urlpatterns = patterns('blog.views',
# Examples:
url(r'^$', views.index),
url(r'^(?P<slug>[^\.]+)', 'view_post', name='view_blog_post'),
url(r'^new/$', 'new_post', name='new_blog_post'),
...
But calling the new_post function runs in a UnboundLocalError Exception on line 39 "local variable 'post' referenced before assignment". Line 39 is render_to_response of the view function, not the new function.
So, why the heck is my call of the new function throwing an error in my view function? Really, I have no clue. I came over from C# so I'm sure I didn't get some special Python rule which makes me coding things wrong.
Update: The indentation of the two functions isn't correct because of stackoverflow.com code panel. Don't care about it.
Solución
The problem is the indentation
def view(request):
...
def new(request):
...
Is different for python of:
def view(request):
...
def new(request):
...
You should be sure use spaces for indentation and python recommends 4 spaces instead of tab
Update:
The problem is in urls:
url(r'^$', views.index),
url(r'^(?P<slug>[^\.]+)', 'view_post', name='view_blog_post'),
url(r'^new/$', 'new_post', name='new_blog_post'),
Change it to:
url(r'^$', views.index),
url(r'^new/$', 'new_post', name='new_blog_post'),
url(r'^(?P<slug>[^\.]+)', 'view_post', name='view_blog_post'),
That is because the url /new/ match the regexp
r'^(?P<slug>[^\.]+)'
Otros consejos
The error definitely sounds like the view_post
view function is being called. Are you sure that your urlpatterns are the right way around? Or both URL regexes could be pointed at view_post
.
In view_post
if the query finds no items, then the variable post
that is only set in the for loop will not have been set, and the reference to it in render_to_response
will raise the UnboundLocalError
.
You can avoid that by setting post to None before the loop.
def view_post(request, slug):
"""
Shows a single post
"""
posts = Post.objects(slug = slug).limit(1)
post = None # Ensure post is bound even if there are no posts matching slug
for items in posts:
post = items
cssClasses = css_class_converter({ _css_class_editable })
context = RequestContext(request)
return render_to_response("single.html", { 'post': post, 'class': cssClasses }, context)
You can see why the UnboundLocalError is happening using this simpler function:
def first_element(items):
for item in items:
result = item
break
return result
(Obviously, you wouldn't really implement first_element
like this, but this illustrates what's happening.)
If you call first_element
with a non-empty list, it'll work as expected:
>>> first_element([2, 3, 4])
2
But if you call it with an empty list, result has never been bound, so you'll get the error:
>>> first_element([])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 5, in first_element
UnboundLocalError: local variable 'result' referenced before assignment