0

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.

enter image description here

diegueus9
  • 29,351
  • 16
  • 62
  • 74
René Stalder
  • 2,536
  • 5
  • 31
  • 50

2 Answers2

3

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>[^\.]+)'
diegueus9
  • 29,351
  • 16
  • 62
  • 74
  • No, that isn't it. Thats just because I copied my code really ugly. It's ok in original. And wouldn't it throw an indention error instead of a reference error? – René Stalder Nov 16 '11 at 21:37
  • Not really, in python you can define funcs in funcs – diegueus9 Nov 16 '11 at 21:40
  • Yes, I know that. But as I said, it's correct in my file. I don't have indentation in my editor. It's just because of the code panel here. I'm going to add a screenshot to clarify that. – René Stalder Nov 16 '11 at 21:42
  • I updated my question and added a screenshot. I'm sure it's not an indentation problem. Is it? – René Stalder Nov 16 '11 at 21:45
  • 1
    Oh, damn, you're so right with the URLconf. It would never have occurred to me. Thanks dude, that could be the solution for many of my problems :D – René Stalder Nov 16 '11 at 21:54
  • My pleasure, btw, please use 4 spaces for indentation XD, And i fixed the indentation in the question – diegueus9 Nov 16 '11 at 21:56
1

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
babbageclunk
  • 8,523
  • 1
  • 33
  • 37
  • +1 for the tip. It wasn't the problem but it could also be one in future. Because I use MongoDB instead of the really well supported database mappers I didn't take a look to this details during my first steps. But you're right! – René Stalder Nov 16 '11 at 21:57