Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Flask Basics Character Builder Cookies

Manish Giri
Manish Giri
16,266 Points

Understanding the flow of 'setting the cookie'

I think I got lost halfway through the video. Things were fine until the get_saved_data() method was defined. After that, I just couldn't understand the flow of the whole process. It also doesn't help that the instructor doesn't explain the things he uses in the video BEFORE using them. It's of course very apparent to him because he knows this stuff, but for beginners to Flask, all this need not be so evident.

For instance - why was get_saved_data() called in the save() method? I mean I know it's so that you have the latest data, but why is that needed for this example? Then the update() method is called on data. What's the purpose of that? Again, I know that update() changes the value of a dict to update the values of the passed in dict, but why is that needed in this example? You're anyway always displaying the latest 'cookie' value right? You can't have multiple cookies show up in the form under the bear thing.

Also, there's the saves variable that pops out of nowhere. What's the need for that?

I tried to trace through the code with comments and put some ? marks on places I didn't grasp fully (including the above questions).

from flask import Flask
from flask import render_template, redirect, url_for, json, make_response, request

app = Flask(__name__)


def get_saved_data():
  try:
    #json.loads takes a JSON string and turns it into "Python object ???"
    data = json.loads(request.cookies.get('character'))
  except TypeError:
    data = {}
  return data


# route for homepage
@app.route('/')
def index():
  # this view is called when the request comes in at homepage /
  # get_saved_data() is called to look for any saved data, which in this case, is the cookie
  data = get_saved_data()
  # if nothing is saved, an empty dict is returned and stored in the saves variable
  # in index.html, the value for the key 'name' (of cookie) is displayed - blank if nothing is set
  return render_template("index.html", saves=data)

@app.route('/save', methods=['POST'])
def save():
  """
  this method is called as the handler when user submits the form
  method saves the user's input data to set a cookie
  """
  response = make_response(redirect(url_for('index')))
  # load existing saved data
  data = get_saved_data()
  # updates current dict with new values ???
  data.update(dict(request.form.items()))
  # sets value of cookie named 'character' to be value entered by user in form
  response.set_cookie('character', json.dumps(dict(request.form.items())))
  # return response to be displayed to user
  return response


app.run(debug=True, host='0.0.0.0', port=8000)

Looking for some help!

1 Answer

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,441 Points

Let's start with review of some concepts.

When a browser requests a webpage, flask creates a request object with:

  • data from the URL query data (items after the "?" in the URL): stored in request.args
  • data from a submitted form: stored in request.form
  • data from previously set cookies: stored in request.cookie

All cookie data received from the browser is found in request.cookie. All cookie data sent to the browser is added as an attribute to the response

At the beginning there is no cookie data and request.cookie is empty. Any cookie data, if present, is received from the browser on every request.

As a precaution, since we don't know ahead of time whether there are current cookies set in the browser (from some other page view, or other future added view code), it is best to merge any data for new cookies with current cookies received from the browser. Without this merge, we might accidentally clearing cookies we are unaware of by not sending them back to the browser.

A helper function get_saved_data() is created to extract data from the cookies out of the current request. (A better name might be get_received_cookie_data()). Cookie data is received as JSON string. The function json.loads translates this string into a python dict.

In the index() view, get_saved_data() is used to help populate the index.html template with any cookie data if present.

In the save() view, the form data received from the browser will be sent back to the browser as a cookie. The first step is to use get_saved_data() to get the current cookie data received from the browser and place it in the dict data.

The data.update() is a fancy way to update one dict with the contents of another. Otherwise, we would need to loop through the form data and add each key/value pair to the data dict. At this point, data has the merged values from current cookies and form data. The last task is getting the merged cookie data back to the browser.

Continuing in the save() view, the response sent from the server will be a redirect (HTTP status code 302) which will say to the browser: "Ask again for this page, but use URL 'index' instead". We piggy-back any cookie data we want to send on this response. The cookie data is prepared by converting data into a string using json.dumps() and added as an attribute using response.set_cookie.

When the browser gets the "302" response with the piggy-backed cookie data, it saves the cookies, then resubmits a new request to '/index' including the latest (just saved) cookies.

Back in the index() view, this new request received with the latest cookies is used to populate the 'index.html' template. The saves=data part of the render_template function, assigns the data dict to the context variable 'saves'. This context variable is accessed in the 'index.html' template using {{ saves.get(....) }} to extract values from the dictionary and display them in the template.

That's a lot of info. Let me know if you have more questions (or if I got something wrong).

Kourosh Raeen
Kourosh Raeen
23,733 Points

This is a great explanation! Thank!

Jay Reyes
seal-mask
.a{fill-rule:evenodd;}techdegree
Jay Reyes
Python Web Development Techdegree Student 15,937 Points

A fundamental explanation, Chris.

One note here: " Continuing in the save() view, the response sent to the server will be a redirect (HTTP status code 302) which will say to the browser:"

Should that instead say, "Continuing in the save() view, the response sent to the browser"?

Chris Freeman
Chris Freeman
Treehouse Moderator 68,441 Points

@Jay Reyes, Could catch! I've updated the text to read "Continuing in the save() view, the response sent from the server will be a redirect...."