23.1. Keeping Track of Data Values

In the past two chapters, you created Flask applications to collect user data and display it on a webpage. However, you may have noticed that when you reload the page, any data collected from the form disappears. The same thing happens if you close the page tab, restart the application, or quit the browser completely. Any data assigned to the Python variables is temporary.

This behavior also means we need to be very careful about sending data back and forth between our Python code and a Jinja2 template.

23.1.1. Problem With Guessing a Number

Let’s take a look at a function that asks a user to guess a secret number.

Example

The webpage:

Form asking the user to submit a number.

A simple guess the number form.

The Python code:

 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@app.route('/guess_the_number', methods=['GET', 'POST'])
def guess_the_number():
   if request.method == 'POST':
      # Collect the user's entry from the web form:
      user_guess = int(request.form['user_guess'])

      # Check if the user guessed correctly:
      if user_guess == magic_number:
         message = 'You guessed the number!'
      else:
         message = 'Nope! Try again.'
   else:
      magic_number = random.randint(1, 50)  # Create a random number from 1 - 50.
      message = ''

   return render_template('guess_the_number.html', message=message)
  1. Line 19 generates a random number and assigns the value to magic_number.

  2. Line 22 renders the guess_the_number template, which displays a form for the user.

Unfortunately, when we run the application and make a guess, the program crashes when we click Go!

The browser displays an UnboundLocalError after submitting the form.

Unbound local error: magic_number referenced before assignment.

Reading the error message tells us what went wrong. In line 14, magic_number hasn’t been assigned a value. If we think about the flow of the program, this might seem odd at first.

  1. The browser sends a GET request to the guess_the_number() function.

  2. This triggers the else clause on line 18, and magic_number is assigned a random number from 1 - 50.

  3. Line 22 renders the form in the browser, and the user submits their guess.

  4. The browser sends a POST request to the function, and line 11 collects the user’s guess.

Following these steps, it seems like magic_number should have a value. However, this is not the case. The return statement on line 22 renders the template, but it also ends the guess_the_number() function. When this happens, the value we assigned to magic_number is lost.

When the function runs after the POST request, Python treats magic_number as a brand new variable. The expression user_guess == magic_number throws an error because magic_number has not been assigned a value yet.

23.1.2. One Solution

With a little work, we can modify the guess_the_number() function to avoid the error. One way to do this is to send magic_number to the template, and then collect it again after the user submits the form. However, this isn’t ideal.

Assigning a value to magic_number, sending it to the template, and then pulling it back into the same function is inefficient. Also, sending magic_number to the template makes it part of the HTML page. Even if we hide it, users who know how to use the browser tools can find the data. If our goal is to have users guess a secret number, we don’t want to give them the answer!

23.1.3. A Better Solution

For many web applications, we want data to persist. This means saving the information even if the user refreshes the page, closes the tab, or quits their browser completely. Persistent data also stays around after a return statement ends a function.

In this chapter, we will learn two ways to handle short-term data storage. In the following chapters, we will use a database to save information over a longer period of time.