CS50 Video Player
    • 🧁

    • 🍧

    • 🍎

    • 🍿
    • 0:00:00Introduction
    • 0:00:49Web Servers
    • 0:04:26Flask
    • 0:10:33hello
    • 0:15:13Templates
    • 0:25:41Forms
    • 0:33:33GET and POST
    • 0:38:26Layouts
    • 0:48:20froshims
    • 0:49:59Error Checking
    • 1:04:45Loops
    • 1:11:58Storing Data
    • 1:21:21Databases
    • 1:37:01Sessions
    • 1:46:27login
    • 2:00:07store
    • 2:09:55shows
    • 2:25:47Ajax
    • 0:00:00[MUSIC PLAYING]
    • 0:00:49DAVID J. MALAN: All right, this is CS50, and this is week nine,
    • 0:00:53the week where we synthesize the past several weeks
    • 0:00:55and the past several languages, including HTML, CSS, and JavaScript
    • 0:00:59most recently, plus some SQL, plus some Python.
    • 0:01:02Indeed, the goal for this week is to synthesize all of those materials
    • 0:01:05into a topic that's broadly described as web programming.
    • 0:01:08Now to be fair, last week we did introduce a bit of JavaScript,
    • 0:01:11and via JavaScript can you indeed program,
    • 0:01:13but it was entirely client side as we used JavaScript last week.
    • 0:01:16This week we'll reintroduce a server side component
    • 0:01:19and indeed end by tying together the browser, the so-called client,
    • 0:01:23and the web server, the backend of any web application.
    • 0:01:27So to get there, let's consider where we came from.
    • 0:01:29Last week in order to serve up any of your static web pages,
    • 0:01:34static in the sense that once you write them, they don't change
    • 0:01:37fundamentally, they don't particularly take user input
    • 0:01:39unless you add a little bit of JavaScript,
    • 0:01:41we use just this simple program called http-server.
    • 0:01:44Now there are other equivalents of this command on Macs and Windows
    • 0:01:48and Linux and other platforms, but for our purposes,
    • 0:01:51http-server the program literally just runs a web server.
    • 0:01:55And a web server is just a piece of software
    • 0:01:57that's constantly listening on port 80 or port 443 in terms of TCP recall,
    • 0:02:03and it's just listening for connections.
    • 0:02:05And any time your browser connects it looks at the URL
    • 0:02:08that you visited and maybe some parameters that you
    • 0:02:11may have provided via that URL, or more generally via a form,
    • 0:02:15and it then serves up a web page, optionally with some kind of output.
    • 0:02:19But of course everything last week really was just static.
    • 0:02:22It was just HTML files, CSS files, and maybe
    • 0:02:25some JavaScript files or JavaScript within.
    • 0:02:27But within the requests that your browsers
    • 0:02:30were sending to that web server, were recall HTTP headers like this.
    • 0:02:35And so among the things that program http-server was doing,
    • 0:02:39and among the things any web server was doing,
    • 0:02:41was analyzing the contents of that virtual envelope, top to bottom,
    • 0:02:45left to right, trying to understand what is it your browser or your friend
    • 0:02:49or your family member if you shared your you
    • 0:02:51URL with them was trying to request.
    • 0:02:53And if the first line of those HTTP headers was just get slash,
    • 0:02:57that just means give me the default web page.
    • 0:02:59And by convention, recall that typically means specifically give me the file
    • 0:03:03called index dot HTML.
    • 0:03:05That might have been explicit if you literally typed index
    • 0:03:08dot HTML into the browser's URL bar, that would even more explicitly tell
    • 0:03:12the server to give you that file.
    • 0:03:14But recall we also played with google.com
    • 0:03:18and specifically its search functionality.
    • 0:03:20And recall that we manually last week constructed
    • 0:03:23a URL that looked like this, and then we replicated it
    • 0:03:26with an actual HTML form, the result of which when submitting it brought us
    • 0:03:30to a URL of the form HTTPS colon slash slash, www.google.com slash search,
    • 0:03:37question mark q equals cats.
    • 0:03:39And last week http-server is fairly simplistic.
    • 0:03:43It only served static content.
    • 0:03:44So it was pretty much ignoring things like these URL parameters.
    • 0:03:48But if you actually want to write a backend web server,
    • 0:03:51a web application like google.com itself, you
    • 0:03:54need to be able to analyze the contents of this virtual envelope,
    • 0:03:58figure out what path the user wants, or route, so to speak.
    • 0:04:02Slash search would be the route here in question.
    • 0:04:04And then you have to figure out what the exact HTTP parameters are.
    • 0:04:08Q, in this case, whose value is "cats."
    • 0:04:11So someone somewhere has to write code that parses that string, figuring out
    • 0:04:16that you want slash search and then, OK, let's ignore the question mark.
    • 0:04:19Let's declare a variable called q, and let's give it a value of "cats."
    • 0:04:22Like somewhere in the picture there needs to be code that does that.
    • 0:04:26And today we introduce you to that very code.
    • 0:04:28And we're going to introduce you to a library called Flask.
    • 0:04:31And technically speaking, Flask is also called a framework.
    • 0:04:35A framework is a way of doing things.
    • 0:04:37It's a way of organizing your code, it's a way
    • 0:04:40of writing your code that more specifically is
    • 0:04:42just how you should use this library.
    • 0:04:44Now why does Flask exist?
    • 0:04:45And there are alternatives.
    • 0:04:47Well, Flask exists just to honestly simplify a lot of tasks
    • 0:04:50that for you and me would just get pretty boring pretty quickly.
    • 0:04:54Imagine how annoying it would be if you wanted
    • 0:04:56to implement some new website or some new mobile application for that matter,
    • 0:05:00and you had to write code that analyzed text like this every time
    • 0:05:05just to figure out what the user's input is.
    • 0:05:07Wouldn't it be nice if you can just call a function, the equivalent
    • 0:05:10of get string or get int, and let some other function written
    • 0:05:14by someone else look at that text, figure out what the parameters are
    • 0:05:17and what their values are, and just hand them to you in nice easy to use
    • 0:05:22variables.
    • 0:05:23So that is one of the things that Flask does for you.
    • 0:05:25It analyzes the insides of these virtual envelopes,
    • 0:05:28figures out what the user input is, figures out
    • 0:05:31what the route is, like slash search or slash index dot HTML
    • 0:05:35that the user wants, and makes it more easily
    • 0:05:38available to you, the programmer.
    • 0:05:40And it's a framework now in the sense that typically when
    • 0:05:43you create, as we will today, a Flask based web application,
    • 0:05:48you're going to typically organize your files and folders in this way.
    • 0:05:51And this is what I mean by framework.
    • 0:05:52Frameworks are not just libraries where they have functions that you can call.
    • 0:05:56They typically have documentation that says, to use these functions,
    • 0:06:00you should additionally organize your files and folders in this way.
    • 0:06:04And perhaps the simplest Flask application would have these files:
    • 0:06:08application dot py, which is where really all of our effort
    • 0:06:13is going to go in writing code, in this case in Python,
    • 0:06:16requirements dot text, which is just a simple text file that enumerates,
    • 0:06:20top to bottom, one per line, what are the other libraries that you
    • 0:06:24want to use in your application, static, which
    • 0:06:27is going to be a folder that literally contains
    • 0:06:29static files like your gifs, your jpegs, your pings, your CSS
    • 0:06:33files, your JavaScript files, any of the files you wrote this past week,
    • 0:06:36now they're going to go into this static folder, and lastly templates.
    • 0:06:40And templates is going to be where more of your HTML goes.
    • 0:06:44And we'll see what the distinction is there versus last week.
    • 0:06:47So in short if you want to make a web application,
    • 0:06:50not a website, a web application that takes user input,
    • 0:06:53produces user output, maybe talks to a database,
    • 0:06:55maybe sends emails, maybe does any number of other things,
    • 0:06:58programmatically you have a web application
    • 0:07:01and will use this framework here called Flask.
    • 0:07:04And there are alternatives.
    • 0:07:05In the world of Python there is a framework called Django,
    • 0:07:09in the world of PHP there are frameworks like Symfony and Laravel and the like.
    • 0:07:13In Java, in C sharp, and other languages there are similar frameworks.
    • 0:07:17So this is just representative of the types of frameworks that are out there.
    • 0:07:21But it's perhaps helpful to know before we dive into some actual code
    • 0:07:25that these frameworks tend to implement certain design patterns.
    • 0:07:29A design pattern is, again, just a fancy way of describing ways
    • 0:07:32that humans write code.
    • 0:07:34Suffice it to say over the past many decades,
    • 0:07:36a lot of humans working initially independently
    • 0:07:39kept solving the same problems again and again,
    • 0:07:41and they realized, wow, I'm noticing patterns
    • 0:07:44in how I'm solving a problem for this project
    • 0:07:46and for this project and another.
    • 0:07:47And once human programmers noticed these patterns,
    • 0:07:50they might formalize them by writing a book,
    • 0:07:53by writing a blog post or the like, and then
    • 0:07:55give them a name, a design pattern that they recommend that other people adopt.
    • 0:07:58Why?
    • 0:07:59Just because it helps you organize your code instead
    • 0:08:02of putting all your code in one massive file,
    • 0:08:04well maybe if we put some code here, some code here,
    • 0:08:07we can collaborate more effectively with others
    • 0:08:09and just keep ourselves sane when maintaining bigger and bigger projects.
    • 0:08:12So Flask implements what's generally known
    • 0:08:15as an MVC design pattern or paradigm.
    • 0:08:19MVC just refers to an acronym representing model, view,
    • 0:08:23and controller.
    • 0:08:24And these are sort of technical terms of art that at the end of the day
    • 0:08:27we'll see are relatively simple.
    • 0:08:29Controller is going to be where you write most of your Python code.
    • 0:08:33It is the file or files that control your web application.
    • 0:08:37So in the past, any time we've written a C program or a Python program,
    • 0:08:41you have in effect been writing controller code.
    • 0:08:43We just never slapped that label on it.
    • 0:08:45There's also going to be today what we'll call a view, the V in MVC.
    • 0:08:50And view just refers to all of the stuff that a human views, the HTML, the CSS,
    • 0:08:55more generally the user interface.
    • 0:08:57Anything involving the user is going to be described
    • 0:09:00as part of your application's view.
    • 0:09:02But again, any of the code that you, the programmer, write,
    • 0:09:05that's part of your controller, so to speak.
    • 0:09:07And then lastly is the so-called model, the M in MVC.
    • 0:09:10And this generally refers to what technique, what service, what software
    • 0:09:15you're using for your data.
    • 0:09:17So maybe that's a SQL database, maybe that's a CSV file.
    • 0:09:20The model refers to the data that your application is using.
    • 0:09:24So again, none of these terms are introducing
    • 0:09:27things we couldn't have done last week or in weeks prior.
    • 0:09:30All we're doing by introducing MVC as an acronym or a design pattern
    • 0:09:34is just to slap a label on the approach that Flask,
    • 0:09:38and in turn many programmers, whether you're
    • 0:09:40using Python or other languages used when designing web based applications.
    • 0:09:44There are alternatives but this is among the most popular and perhaps
    • 0:09:48among the simplest.
    • 0:09:49So much like we've done with any language,
    • 0:09:51let's take a look at the simplest possible web
    • 0:09:53application you might write using this library or framework called Flask.
    • 0:09:57It turns out if you fill a file called application dot py
    • 0:10:01with these lines of code, you have just made your very first web application.
    • 0:10:06Now it's not going to do much of interest, as we'll see,
    • 0:10:09but this is the minimal amount of code pretty much that you need in order
    • 0:10:12to write a web application that is a program that once you start
    • 0:10:16it is going to just be constantly listening and listening and listening
    • 0:10:20for TCP requests on port 80 or 443 to come in from people's browsers,
    • 0:10:26your own included.
    • 0:10:27And then now you have the ability to write
    • 0:10:30code that responds to those users.
    • 0:10:33So what do I mean by this?
    • 0:10:34Well, let's go ahead and see this in action
    • 0:10:36and start writing some of our own very first programs.
    • 0:10:39I'm going to switch over to CS50 IDE, which we'll continue using here.
    • 0:10:43I'm not going to run http-server anymore.
    • 0:10:45Instead we're going to use Flask which will take the place of that server.
    • 0:10:48And I'm going to go ahead and let's create a very simple file first.
    • 0:10:52I'm going to go ahead and, actually let's do this.
    • 0:10:54I'm going to first create a directory.
    • 0:10:56So I'm going to create a directory called Hello,
    • 0:10:58and I'm going to cd into Hello.
    • 0:10:59And as always, if you open your file browser
    • 0:11:01you'll see the same thing at top left, but in general I'll
    • 0:11:04focus on the command line.
    • 0:11:05And now that I'm in the Hello directory, let me go ahead and create a file.
    • 0:11:09And I'll save this in Hello, whoops, I'll save this in Hello.
    • 0:11:15And let me go ahead and call this, let's say,
    • 0:11:20application dot py, and again, saving it in the Hello directory.
    • 0:11:24All right if I type ls, I'll see it there and it's currently empty.
    • 0:11:27So let me go ahead and pretty much copy some of that code from a moment ago.
    • 0:11:30Let me go ahead and from the Flask library import something
    • 0:11:34called Flask, capital F in this case, which is just their convention.
    • 0:11:37And let me go ahead and also import something called render.
    • 0:11:43Actually let's not even do that yet.
    • 0:11:45Let's keep this minimalist at first.
    • 0:11:46In order to turn this file into a full-fledged Flask application that
    • 0:11:51is my own server, I'm going to go ahead and define a variable called
    • 0:11:54app by convention.
    • 0:11:55I'm going to call this Flask function, and I'm
    • 0:11:58a little weirdly going to pass an underscore, underscore, name,
    • 0:12:01underscore, underscore.
    • 0:12:02Now we've only seen that special variable once in the past.
    • 0:12:05And that, recall, was when I said, well, you should either
    • 0:12:08call main at the bottom of your Python file to call your main function,
    • 0:12:12or you can do this annoying thing where you say, if name equals,
    • 0:12:15equals main then call main.
    • 0:12:17So no matter why we used that in the past, this special variable,
    • 0:12:22underscore, underscore, name, underscore, underscore essentially
    • 0:12:25refers to the name of the current file.
    • 0:12:27So this is a line of code that says, Flask, turn the current file
    • 0:12:32into an application, that is a web application that
    • 0:12:35will listen for browsers' requests.
    • 0:12:37Now in my file I have to tell Flask what are my routes.
    • 0:12:41A route is simply a URL.
    • 0:12:43So slash, or slash index dot HTML, or slash
    • 0:12:48search, or any number of other paths that you see in today's URLs
    • 0:12:52on websites.
    • 0:12:53And to do this in Flask I say app dot route, quote unquote slash.
    • 0:12:57Now, this is something we've not seen in Python before,
    • 0:13:00but this is actually a Python feature.
    • 0:13:02Any time you see an at sign at the beginning of a function like this,
    • 0:13:05it's what's called a Python decorator.
    • 0:13:07And for our purposes today, this is just a special way
    • 0:13:11of applying one function to another.
    • 0:13:14But let me leave it at that for now and just
    • 0:13:16say that the convention when using Flask is that at first you
    • 0:13:19define your route like your slash default route,
    • 0:13:22then you define a function.
    • 0:13:23And the function can be called whatever you want,
    • 0:13:25but by convention you should probably call
    • 0:13:27it something appropriate for the actual route in question.
    • 0:13:31And again, humans tend to call the default route your index.
    • 0:13:34So I'm going to go ahead and call this function index.
    • 0:13:36And below that, let me just go ahead and do something silly like Hello, world,
    • 0:13:40literally returning that string.
    • 0:13:43And let's go ahead and see what exactly happens here after doing that.
    • 0:13:46Let me go ahead now and in my Hello directory if I type ls,
    • 0:13:50notice that again, I only have one file, application dot py.
    • 0:13:53In order to start my server today, because it's a Flask application,
    • 0:13:57I do not want to use http-server, I instead
    • 0:14:00want to run the command Flask run.
    • 0:14:02So Flask is, yes, a library.
    • 0:14:04But it also comes with a program when you install it
    • 0:14:06on your Mac or PC or the IDE that lets you start a Flask application as well.
    • 0:14:11And you'll see some cryptic output here.
    • 0:14:13But just like http-server, you'll see the URL
    • 0:14:16of your currently running application.
    • 0:14:19And if I go ahead and click this URL and open it in another tab, voila,
    • 0:14:23I see my very first dynamic web application.
    • 0:14:27It's only printing Hello, world.
    • 0:14:28And in fact, if I go to Chrome's view page source feature,
    • 0:14:33notice that it's not even full-fledged HTML.
    • 0:14:35It's literally text, but that's because I didn't
    • 0:14:38bother returning any actual HTML yet.
    • 0:14:41So let's do that.
    • 0:14:42Let's actually do this more properly and not just return
    • 0:14:45some arbitrary string of text.
    • 0:14:47Let me stop the server and let me focus now on doing this.
    • 0:14:51By default the route called slash I've claimed
    • 0:14:54means that you should return a file called index dot HTML.
    • 0:14:58Well let's assume for the moment it exists.
    • 0:15:00How do I go ahead and return it?
    • 0:15:02Technically I'm going to go ahead and render
    • 0:15:04a template called index dot HTML.
    • 0:15:07And in order to use this function render template,
    • 0:15:09I need to import it from the Flask library as well.
    • 0:15:13So Flask again is a library, comes with a lot of functions.
    • 0:15:16The first one of which is called Flask itself.
    • 0:15:18That's what activates this as a web application.
    • 0:15:20Render template is another function whose purpose in life
    • 0:15:23is to go find a file called index dot HTML, grab its contents,
    • 0:15:28so that you can then return it.
    • 0:15:29So it's similar in spirit to using open and read in Python a few weeks ago.
    • 0:15:35But it's going to give us some other fancy features as well.
    • 0:15:38But let me go now into my command line, type
    • 0:15:40ls to remind us that we only have application dot py.
    • 0:15:43I don't want to create index dot HTML in the same directory.
    • 0:15:46Flask, recall, has certain organizational recommendations
    • 0:15:50like this here whereby I should actually put index dot
    • 0:15:54HTML and all of my HTML files today onward in my folder called templates.
    • 0:15:59So let's do that.
    • 0:16:00Let me go ahead and make a directory called templates with make dir enter.
    • 0:16:04If I type ls now you'll see that I have a templates directory.
    • 0:16:07Now let me go ahead and create a new actual file called index dot HTML.
    • 0:16:11Let me store that in my templates, directory and now let me go ahead
    • 0:16:15and something pretty familiar.
    • 0:16:17We've done this many times at this point whereby we just
    • 0:16:20whip up a quick HTML page.
    • 0:16:22I'll give my doc type of HTML up here.
    • 0:16:24Let me have my HTML tag.
    • 0:16:26My language will be English by default. Down here I'm
    • 0:16:29going to have the head of my page.
    • 0:16:31Inside the head I'll have a title.
    • 0:16:33I'm going to call this Hello.
    • 0:16:34Down here I'll have my body.
    • 0:16:36Inside the body I'm just going to say Hello, world instead.
    • 0:16:40So still no progress really from last week.
    • 0:16:43But let me now go back to application dot py,
    • 0:16:46remind us that we've just now returned the function call,
    • 0:16:50render template of index dot HTML, and again, this function, render template,
    • 0:16:54is going to go open that file, grab all of the bytes inside of it
    • 0:16:57and return them ultimately via this line.
    • 0:17:00So now let me go ahead and run Flask run again, let me go ahead
    • 0:17:03and click on my IDE's URL, voila, same thing.
    • 0:17:07But now if I open up Chrome's view source feature,
    • 0:17:11notice now that I've returned a full-fledged web page.
    • 0:17:15So again, not really fundamentally that interesting,
    • 0:17:18but I've taken this baby step now toward generating any HTML that I want.
    • 0:17:23So let's make this more interesting.
    • 0:17:24I've claimed all along that there is this way of course with web programming
    • 0:17:28to take user input via the URL.
    • 0:17:32So how can we go about doing that?
    • 0:17:33Well, me go ahead and propose this.
    • 0:17:35Let me go ahead here and write an additional line of code
    • 0:17:39that does something like this.
    • 0:17:41Let me declare a variable called, actually let's do this.
    • 0:17:45After render template, it turns out you can pass in 0 or more named arguments.
    • 0:17:51And the names of these arguments are entirely up to you,
    • 0:17:53and we haven't seen that thus far.
    • 0:17:55Thus far in the past, any time we've used someone else's function,
    • 0:17:58you had to check the documentation or the lecture notes
    • 0:18:00to figure out what were the available arguments you
    • 0:18:02could pass into this function.
    • 0:18:04But that's not true with render template.
    • 0:18:06It's a little more powerful.
    • 0:18:07So if I want to pass a user's name from my application dot py file,
    • 0:18:13which, recall we're going to start calling my controller,
    • 0:18:16I can say, give me a variable called name.
    • 0:18:18What do I want the value of this person's name to be?
    • 0:18:21Well really what I want it to be is the equivalent
    • 0:18:24of whatever is after that question mark, right?
    • 0:18:27The only way, fundamentally, to get user input from a URL, we've seen,
    • 0:18:31is whatever is after the question mark.
    • 0:18:32And I don't want q, and I don't want cats,
    • 0:18:35I want name equals David or name equals Brian.
    • 0:18:38So how can I do that?
    • 0:18:40Well, let me go ahead and say this: name equals
    • 0:18:43request dot args dot get, quote unquote name,
    • 0:18:48and let me Additionally add request to the top of this file.
    • 0:18:52So what am I doing?
    • 0:18:53When I import this other request variable from the Flask library,
    • 0:18:58this gives me access to the HTTP request and with it any parameters
    • 0:19:03that might have been in the URL.
    • 0:19:06And what Flask does for me is it parses that URL.
    • 0:19:09It figures out what is q, what is cats, what is name, what is David.
    • 0:19:12Whatever is after the question mark, Flask parses it for me
    • 0:19:16and hands it back to me as variables, and I can get access to those variables
    • 0:19:19by calling request dot args for arguments,
    • 0:19:22dot get, and then the name of the parameter
    • 0:19:26that I want to get from the URL.
    • 0:19:29So now if I save this, let me go back to index dot HTML,
    • 0:19:35and here is why this file today onward is now called a template.
    • 0:19:40A template, just like in the human world,
    • 0:19:43is kind of a framework into which you can plug other values.
    • 0:19:46It's a template that you can base your own work on.
    • 0:19:48It's like a blueprint that you can base a building on.
    • 0:19:51So templates typically have special syntax
    • 0:19:54via which you can plug in some values.
    • 0:19:56And I'm afraid the syntax is slightly new versus past things we've seen,
    • 0:19:59but if you use two curly braces, as though one weren't
    • 0:20:03bad enough in C and in other contexts, two curly braces, left and right,
    • 0:20:08you can tell Flask's render template function to plug
    • 0:20:13in right there the value of any variable that you
    • 0:20:16have passed into render template as one of these arguments in the function
    • 0:20:21call.
    • 0:20:22So let me go ahead now and run Flask run again to restart my web server.
    • 0:20:25Let me go ahead and open this URL, and it looks pretty stupid right now.
    • 0:20:29Hello, None.
    • 0:20:30But recall that None is a special value in Python.
    • 0:20:33It means something has no value.
    • 0:20:35So you know what I could do?
    • 0:20:36It turns out that, let me go back over here, if I go back to my URL,
    • 0:20:42and let me zoom out so you can see it, let me go ahead and add a slash,
    • 0:20:45question mark, and not q equals cats because that's irrelevant now,
    • 0:20:49but how about name equals David.
    • 0:20:51And let me zoom in on that.
    • 0:20:52All I've added is the slash question mark name equals David.
    • 0:20:56Let me go ahead and hit Enter, and voila, now I
    • 0:20:59have a web page that says Hello David.
    • 0:21:01And indeed if I view my page source, notice the HTML
    • 0:21:04has been dynamically generated.
    • 0:21:06There is no file on my IDE that says Hello comma David,
    • 0:21:09rather it's been dynamically plugged in.
    • 0:21:11And notice this can change.
    • 0:21:12If I go ahead and change the name to Brian, enter,
    • 0:21:15his page changes, and of course if I view the page source now
    • 0:21:18it's as though I had a file called index dot HTML
    • 0:21:21that literally had Brian's name in it.
    • 0:21:23But no, there's just that placeholder within my template instead.
    • 0:21:27And I can clean this up.
    • 0:21:28Notice it looked pretty stupid if there was no value for name,
    • 0:21:32but it turns out the request dot args, dot
    • 0:21:34get function takes a second optional argument.
    • 0:21:37If I don't know if there's going to be of value
    • 0:21:40and I want to give it a default argument,
    • 0:21:42my second argument to the get function can actually be the default value.
    • 0:21:46So if I go ahead now, and let me go ahead rerun Flask.
    • 0:21:50And let me go ahead now and reload with no name argument.
    • 0:21:53Now you see a default value of Hello comma world, but if I go back up there
    • 0:21:57and put my name back in, now it doesn't need the default.
    • 0:22:00I see what the human actually typed in.
    • 0:22:04So what's going on here, well, if you consider
    • 0:22:06what google.com is doing when you type in cats and hit Enter,
    • 0:22:10that word q equals cats is being passed to google.com in the URL,
    • 0:22:15Google is running some program, maybe it's Python with Flask,
    • 0:22:18maybe it's some other language that's analyzing the URL,
    • 0:22:21grabbing q equals cats, and then they're searching their database essentially
    • 0:22:25for a keyword of cats, and then they're dynamically
    • 0:22:28generating the HTML that shows you all of those pictures and search
    • 0:22:31results of cats.
    • 0:22:33There is no web page, there's no HTML file on Google servers
    • 0:22:37that constantly has a big list of cats.
    • 0:22:39Like there's certainly no human maintaining a really big HTML
    • 0:22:41file just filled with image tags and cats all day long.
    • 0:22:45Suffice it to say that's all dynamically generated.
    • 0:22:47And even with this trivial example, we see now
    • 0:22:50perhaps that we're scratching the surface of that very capability.
    • 0:22:54All right, let me pause here and see if there are any questions or confusion,
    • 0:23:01because it's a lot all at once given that with any framework
    • 0:23:04you typically have to learn the conventions first
    • 0:23:06and then you can start to be productive.
    • 0:23:08BRIAN: Yeah, so in your, when you called the render template function,
    • 0:23:11someone asked why don't you need to say it's in templates slash index dot HTML,
    • 0:23:16and why did you just say index dot HTML?
    • 0:23:18DAVID J. MALAN: Good question.
    • 0:23:18This is one of those "just because" answers.
    • 0:23:20The render template function has been implemented in such a way
    • 0:23:24that it assumes that your files are in the templates directory.
    • 0:23:27You can technically override that by reconfiguring the application,
    • 0:23:30but the default and indeed the convention
    • 0:23:32is to just put those files in templates.
    • 0:23:35Good question.
    • 0:23:36Santiago?
    • 0:23:37SANTIAGO: Why when you declare the app, app dot route,
    • 0:23:41you have to include the forward slash as an argument to that?
    • 0:23:45DAVID J. MALAN: Yeah, so why do you have to include the forward slash?
    • 0:23:48That is the way, in Flask, of telling Flask, use
    • 0:23:52the following function for this route.
    • 0:23:54And slash is perhaps the simplest, certainly most basic route
    • 0:23:59that you could define.
    • 0:24:00It's sort of the absence of any other words.
    • 0:24:02So you have to put slash there because otherwise the server won't
    • 0:24:05know what to do if you visit something dot com, slash, and that's it.
    • 0:24:11We can change this notice.
    • 0:24:12I can change this to anything I want.
    • 0:24:14Let me stop my server, and let me change this to slash secret, as though I'm
    • 0:24:18creating a secret URL on my website.
    • 0:24:20Let me go ahead and rerun Flask run.
    • 0:24:22Let me open up this URL, and notice what happens.
    • 0:24:25Nothing happens.
    • 0:24:26Not found, 404 when I visit slash.
    • 0:24:29And Chrome is just being annoying.
    • 0:24:31It's hiding the slash just for simplicity these days,
    • 0:24:34but the slash is there even though you're not seeing it as the human.
    • 0:24:37But if I change this and go to slash secret, then we see that page again.
    • 0:24:42So that app at app dot route function just
    • 0:24:46lets you define what route should be associated with the following function.
    • 0:24:52Other questions?
    • 0:24:53Yeah, over to Sophia.
    • 0:24:55SOPHIA: The model, is it a part of the HTML file now?
    • 0:24:58Like the name, I guess as the data, is it encoded with an HTML file?
    • 0:25:02DAVID J. MALAN: Yeah, at the moment I would
    • 0:25:04argue we don't really have a model yet.
    • 0:25:05Like there's no database, there's no CSV file,
    • 0:25:07so right now we're just playing with C, controller and V, view.
    • 0:25:11And that's totally fine.
    • 0:25:12It's only when we have a full-fledged application like we soon will today
    • 0:25:15that really the M comes into play.
    • 0:25:18But again, this is sort of, reasonable people might disagree.
    • 0:25:20They're just conventions, they're not hard, fast rules.
    • 0:25:23All right, so this is a little silly, that in order
    • 0:25:27to be greeted by my website you have to have the wherewithal
    • 0:25:31to know that you have to type your own name into the URL bar, right?
    • 0:25:35No one does that.
    • 0:25:36That's just not how the web typically works.
    • 0:25:38We instead find ourselves as humans filling out forms.
    • 0:25:41So let's take this one step further and actually improve things in such a way
    • 0:25:45that we actually have a form instead.
    • 0:25:47So let me go ahead and do this.
    • 0:25:49I'm going to go ahead and create another file.
    • 0:25:52Let me go ahead and do this.
    • 0:25:53Let me go into my templates directory where I currently
    • 0:25:56only have index dot HTML, and let me just copy this file,
    • 0:26:00index dot HTML, into another one right now called greet dot HTML.
    • 0:26:04And that is to say I want my greet file, ultimately, let me go ahead
    • 0:26:08and open this in my editor, greet ultimately
    • 0:26:12is going to do the job that index was a moment ago.
    • 0:26:14And I'm going to change the behavior of index
    • 0:26:16dot HTML to instead have an actual form.
    • 0:26:19So I'm going to go ahead and delete this and I'm
    • 0:26:21going to do a form action equals, a route
    • 0:26:24called slash greet, which doesn't exist yet, but I wager it soon will.
    • 0:26:27I'll use get initially, and then inside of this form
    • 0:26:31let me go ahead and give myself an input whose name is going to be literally
    • 0:26:35name, because I want the human's name.
    • 0:26:37So the red name is the tag's name.
    • 0:26:40Name in green at the moment is the name of the human in question,
    • 0:26:44although a little confusingly.
    • 0:26:46And then let me go ahead and say the type of this field will be text.
    • 0:26:50And then let me go ahead and give myself an input type, equals submit,
    • 0:26:53so I have a submit button, and the value of that
    • 0:26:57will be whatever the default is.
    • 0:26:59So now let me go back to application dot py,
    • 0:27:02let me go back to my Hello directory.
    • 0:27:04Make sure you don't run Flask run in your templates directory.
    • 0:27:07Make sure you only ever run it where the application dot py file is.
    • 0:27:11Let me run this file.
    • 0:27:13Let me go ahead and open it now, and voila.
    • 0:27:16My slash route, the default route notice,
    • 0:27:19has changed to be this HTML form.
    • 0:27:21And if I look at the view source of that in Chrome,
    • 0:27:25you'll see exactly what I just typed.
    • 0:27:26So there's nothing dynamic about the form.
    • 0:27:28That is indeed hard coded.
    • 0:27:29There's no placeholders there.
    • 0:27:31But notice that this form has been designed in advance by me to go
    • 0:27:34to the slash greet route using get.
    • 0:27:36Well how do I implement that?
    • 0:27:38Well let me actually go into my application
    • 0:27:40now, because you'll notice if I type in my name, David, and click Submit,
    • 0:27:44not found.
    • 0:27:45Because notice that slash greet does not exist.
    • 0:27:48That is not a defined route in my application.
    • 0:27:51So let me go back to my server, and let me go ahead down below,
    • 0:27:56and let's just do something similar.
    • 0:27:57App dot route, quote unquote, slash greet.
    • 0:28:01So let's give myself a second route.
    • 0:28:02Let me go ahead and define the function that
    • 0:28:04should be called when a user visits slash greet.
    • 0:28:07And again, you can call it anything you want, but let's keep ourselves,
    • 0:28:10let's keep things simple and just call it the same thing
    • 0:28:12as the route, though again, that's not a requirement.
    • 0:28:14And then in my greet function, well what do I want to do?
    • 0:28:17Let me go ahead and say, return "to do."
    • 0:28:20I haven't done it yet.
    • 0:28:21But again, as in C and in Python, take baby steps,
    • 0:28:24and just make sure the basics of your code are working so far.
    • 0:28:27Let me rerun Flask run.
    • 0:28:29Let me open my URL.
    • 0:28:31And now let me try filling out this same form again.
    • 0:28:33And actually I'm a little annoyed that the autocomplete is popping up,
    • 0:28:36but we can turn that off later.
    • 0:28:38Let me go ahead and click Submit, and voila.
    • 0:28:40Now the greet route does exist.
    • 0:28:43Notice that slash greet is fully functional.
    • 0:28:46It's not a not found anymore, but it's of course not doing anything useful,
    • 0:28:49hence the "to do."
    • 0:28:50Well that's OK.
    • 0:28:51Let me go back here and stop the server.
    • 0:28:55And let me instead have greet render a template called greet dot HTML.
    • 0:29:00But recall that greet dot HTML is where I started.
    • 0:29:04It is the file now that has this placeholder.
    • 0:29:06So I think what I can do is just move the code that I was using earlier
    • 0:29:12for index which no longer needs it, because index
    • 0:29:14dot HTML has only a hardcoded static form, let me go ahead
    • 0:29:18and just move the name parameter to my greet route instead.
    • 0:29:22Let me go ahead and restart my server.
    • 0:29:24Let me go ahead and open my URL.
    • 0:29:27Let me go ahead and type my name in and hit submit, and voila.
    • 0:29:31So now we have two fully functional routes, slash and slash greet,
    • 0:29:37the first of which just so happens to only display
    • 0:29:39the static form, the second of which actually happens to do something
    • 0:29:43more interesting and greet the user.
    • 0:29:46And I mentioned a moment ago, I was kind of annoyed by the user interface
    • 0:29:49here, the fact that it's remembering who typed their name in before,
    • 0:29:52I just, rubbing me the wrong way, leaks a little bit of privacy.
    • 0:29:54So let me actually go into my index and recall
    • 0:29:57that there's other attributes in HTML like autocomplete equals off.
    • 0:30:00I can also do autofocus to give that textfield the blinking cursor
    • 0:30:05by default. And now let me go ahead and restart
    • 0:30:08my server after making this change.
    • 0:30:11Let me go ahead and reload.
    • 0:30:12And you know what?
    • 0:30:13We can do a little better.
    • 0:30:14Let's also add one of those placeholder, quote unquote name,
    • 0:30:17just so that it's a little more clear to my visitors
    • 0:30:20that, oh, you want me to type my name, David, here.
    • 0:30:23Autocomplete is now off.
    • 0:30:24I click Submit, voila.
    • 0:30:26We now have a working, if simple, web application.
    • 0:30:31All right, so what were the additions there?
    • 0:30:33Same exact application as before, but we added a second route
    • 0:30:37and a second template so that one form could submit data to the other route
    • 0:30:44instead.
    • 0:30:45So let me pause here to see if there's any questions or confusion before we
    • 0:30:50continue to iterate on this.
    • 0:30:53Questions or confusion.
    • 0:30:55SPEAKER 1: I'm wondering how that came equals double quote name,
    • 0:31:00is that equivalent to the name that you later specified?
    • 0:31:05DAVID J. MALAN: It is.
    • 0:31:06And I'll demonstrate this by changing that now.
    • 0:31:09I called it name just because we're indeed talking about humans' names,
    • 0:31:12but I could call this anything I want.
    • 0:31:14So for instance in my form I'm collecting this as the person's name.
    • 0:31:19But I could change it to first underscore name, for instance,
    • 0:31:22if I only care about their first name.
    • 0:31:24Let me change the placeholder to make clear
    • 0:31:25that I only want their first name.
    • 0:31:27Let me now go to my application dot py.
    • 0:31:30Let me get the first underscore name HTTP parameter, and then
    • 0:31:36in my template, greet dot HTML, let me change this to first underscore name.
    • 0:31:42And actually I need to make one more change.
    • 0:31:44I need to change this to first underscore name as well.
    • 0:31:47So it's a little annoying that you have to repeat yourself all over the place,
    • 0:31:50but they mean different things.
    • 0:31:52In the context of application dot py, this is a parameter
    • 0:31:55that I'm passing into my template.
    • 0:31:57In the context of this get function, this is the HTTP parameter
    • 0:32:01that I'm grabbing from the URL.
    • 0:32:03And in the context of my template, this refers
    • 0:32:07to the first of those, which is the argument I'm
    • 0:32:10passing into render template.
    • 0:32:12And if I reload the server, and let me go ahead and reload the form here.
    • 0:32:17Let me type in only my first name.
    • 0:32:19Click submit.
    • 0:32:19A whole bunch of things changed, not in terms of output
    • 0:32:23but in terms of functionality.
    • 0:32:24And you'll see that now I'm using first underscore name instead of name itself.
    • 0:32:31Other questions or confusion about routes or parameters
    • 0:32:37in the URL, templates or otherwise?
    • 0:32:40No?
    • 0:32:41All right, well let me ask a question then of us.
    • 0:32:44Let me go ahead and scroll this down just a bit to make room.
    • 0:32:47Here, again, is my index dot HTML file.
    • 0:32:50It contains a form.
    • 0:32:51Here now is my greet dot HTML file, which contains just,
    • 0:32:55hello comma so and so.
    • 0:32:58What looks poorly designed about this?
    • 0:33:02I'm going to go ahead and revert just to my shorter names just so a little more
    • 0:33:05fits onto the screen at once.
    • 0:33:07But what looks poorly designed about this?
    • 0:33:10I claim that it's correct.
    • 0:33:12We seem to have a working web application.
    • 0:33:14But what is poorly designed arguably?
    • 0:33:17BRIAN: Let's hear from Peter.
    • 0:33:19SPEAKER 2: Well I guess you're getting it,
    • 0:33:22so the URL is exposing the personal data of the input, whatever
    • 0:33:27they might have put in, so I guess we'd want to use post instead to hide it.
    • 0:33:32DAVID J. MALAN: Good catch.
    • 0:33:33So all this time I have very deliberately but a little worrisomely
    • 0:33:37been leaking information in the URL in the sense
    • 0:33:40that now it's probably going to be saved by my browser, right?
    • 0:33:43Often when you're typing something to your URL bar,
    • 0:33:45you can see what you've searched for before,
    • 0:33:47what websites you've been to before, and that's a good user interface
    • 0:33:51feature in that it just helps you type your keystrokes because you can just
    • 0:33:54hit Tab and hit Enter to finish your thought quickly.
    • 0:33:56But it's a little invasive if you don't want people knowing where you went
    • 0:34:00or what you searched for or if it's a lab computer
    • 0:34:02or your siblings are using it too.
    • 0:34:04There's a lot of reasons why you don't want your typed input ending up
    • 0:34:07in that URL bar.
    • 0:34:08And I proposed last week that we can avoid this by using,
    • 0:34:12what you proposed is post.
    • 0:34:14And indeed let's do this.
    • 0:34:15Let me go back to my index dot HTML.
    • 0:34:17Let me make a simple change in my HTML file, changing the method to post.
    • 0:34:22Post is going to be almost the same as get,
    • 0:34:25but instead of putting my parameters in the URL
    • 0:34:29like q equals cats or name equals David in the URL,
    • 0:34:32it's going to instead metaphorically put it lower, deeper
    • 0:34:35inside the virtual envelope.
    • 0:34:37So it's still being sent from the browser to server,
    • 0:34:39but it's not going to get remembered by the browser in the URL bar.
    • 0:34:43But if I do this I need to change my controller.
    • 0:34:46I need my Python code to look in a somewhat different location
    • 0:34:49to get at that.
    • 0:34:50And it's a pretty simple change.
    • 0:34:52Instead I have to do this.
    • 0:34:53I have to tell Flask that the route, slash greet,
    • 0:34:59is actually going to support a different set of methods.
    • 0:35:02Rather than supporting get, which is the default,
    • 0:35:05I have to pass in the somewhat cryptic argument called methods.
    • 0:35:09And then I have to pass in literally a Python list of the methods
    • 0:35:12I want this list to support.
    • 0:35:13By default if you don't use this argument, all
    • 0:35:17of these routes, the first and the second,
    • 0:35:19have essentially this default value, methods equals,
    • 0:35:23quote unquote get in a list, so a list of size one.
    • 0:35:26It would be a little annoying if you had to type that all over the place,
    • 0:35:29so the default just allows you to just type nothing at all.
    • 0:35:32But if you want to support post, you do have
    • 0:35:34to override this and change the get default to post explicitly.
    • 0:35:39And you have to get the parameters from a different variable.
    • 0:35:43Instead of using request dot args which refers to the arguments in the URL,
    • 0:35:48you have to change it to request dot form.
    • 0:35:51These are horribly named.
    • 0:35:53In Flask, these global variables, request dot args and request dot form
    • 0:35:57refer to get and post respectively.
    • 0:36:00Better names might have been request dot get and request dot post,
    • 0:36:03but this is what we have.
    • 0:36:04So request dot args is for get requests in the URL.
    • 0:36:08Request dot form is for post requests where the same info is buried deeper
    • 0:36:13in the virtual envelope.
    • 0:36:14So after those changes, let me go ahead and make,
    • 0:36:17sorry not make, wrong language, Flask run.
    • 0:36:20All right let me go ahead and open my URL, and voila.
    • 0:36:25I'm going to type in David, click Submit, and now notice the magic.
    • 0:36:29Now the route is still slash greet, but no one
    • 0:36:33who uses my laptop later is going to know what my name was
    • 0:36:36or what my Google search was or what my credit card number was
    • 0:36:40or anything else that I might have typed into a form here like my name.
    • 0:36:44But the output is still fully functional.
    • 0:36:48So this indeed, to be clear, is post is what
    • 0:36:51you would use whenever you're collecting anything remotely personal
    • 0:36:53like people's email addresses, perhaps, or their credit
    • 0:36:56card, or their passwords, certainly, and other values,
    • 0:36:59but you can otherwise use get as we've been using thus far.
    • 0:37:04Well let me ask a follow up question, because I
    • 0:37:06think we can still do better than this.
    • 0:37:08There's something fundamentally about the design of index dot HTML
    • 0:37:12and greet dot HTML that feels a little suboptimal.
    • 0:37:15And I dare say several of you noticed this same problem in this past week
    • 0:37:21when you were just creating a few HTML files on your own.
    • 0:37:24What was a little tedious or annoying or messy or poorly designed
    • 0:37:28as best you could tell from creating these several HTML
    • 0:37:33files for your own personal home page?
    • 0:37:36BRIAN: The chat is suggesting you've repeated some HTML between the two HTML
    • 0:37:40files.
    • 0:37:40DAVID J. MALAN: Yeah, my god, I mean honestly by the second file,
    • 0:37:43third file, fourth file, you're probably just copying and pasting
    • 0:37:45your previous files and then making minor changes,
    • 0:37:48and that's all you could do.
    • 0:37:49With HTML and CSS alone and even JavaScript,
    • 0:37:51you can't share HTML across multiple pages.
    • 0:37:54Your only option this past week when serving static content
    • 0:37:57was to copy and paste that content redundantly.
    • 0:38:00And look at this.
    • 0:38:01In my index dot HTML file, everything I've just highlighted from line 1 to 7
    • 0:38:06happens to be exactly the same as everything from line 1 to 7
    • 0:38:10in greet dot HTML.
    • 0:38:12So here's another feature you get with Flask or any web programming framework.
    • 0:38:17These aren't specific to Flask, per se.
    • 0:38:19You get the ability to factor out common content.
    • 0:38:23And so how do we go about doing this?
    • 0:38:25Well, let me go ahead and do this.
    • 0:38:26Let me go ahead and create a third file that by convention in Flask
    • 0:38:30is called layout dot HTML.
    • 0:38:32And to be safe, to be sure, I'm going to put this in my templates directory
    • 0:38:35because again it's an HTML file.
    • 0:38:37I'm going to go ahead and copy paste all of that same boilerplate, if you will,
    • 0:38:40all of the commonalities, and delete what was specific to greet dot HTML.
    • 0:38:44And I think what we're looking at, lines 1 through 10,
    • 0:38:46is now like a template for, if you will, the layout for my other two web pages.
    • 0:38:52So you know what, let me go ahead and do this.
    • 0:38:54Let me use some special syntax and say block body, and then, a little weirdly,
    • 0:38:59end block here.
    • 0:39:01So this is Flask specific syntax or technically
    • 0:39:06Jinja specific syntax, which is a language that Flask
    • 0:39:08is using which someone else wrote, this is special syntax in Flask that
    • 0:39:12essentially says put a placeholder here.
    • 0:39:15Put a placeholder here so I can plug in other HTML in just a bit.
    • 0:39:19So how do I use this now?
    • 0:39:21Well let me go to my index dot HTML file,
    • 0:39:23and let me get rid of all of the redundancy,
    • 0:39:26boiling down index dot HTML really into its essence.
    • 0:39:29The only thing that's special about index dot HTML was that form.
    • 0:39:33So if I want to use the same layout that I just created, let me go ahead
    • 0:39:36and say up here using that special syntax, extends layout dot HTML,
    • 0:39:41and then down here let me say, here is my body block, if you will.
    • 0:39:46Let me un-indent that a bit.
    • 0:39:47And then let me say down here end block.
    • 0:39:51So again, syntax is weird, but the ideas are pretty similar.
    • 0:39:55This is almost similar in spirit to our header files
    • 0:39:58in C, where you could factor out some commonalities
    • 0:40:00and just reuse them in multiple places.
    • 0:40:02Here it's a little fancier because you can kind of
    • 0:40:04have this blueprint, this layout, literally
    • 0:40:07that you can then plug different content into.
    • 0:40:09So this first line says, hey Flask, the following file, index dot HTML,
    • 0:40:13essentially inherits from, it extends my default layout.
    • 0:40:17Well what's that layout?
    • 0:40:18It's this.
    • 0:40:18Notice that this layout defines this placeholder, arbitrarily called body.
    • 0:40:23I could have called it x or y or z, but I'm going to call it body.
    • 0:40:26Why?
    • 0:40:26Because really it's like 100% of the contents of my body.
    • 0:40:30So seems like a good name.
    • 0:40:32And then in index dot HTML, I just now need to tell Flask,
    • 0:40:35here comes some code that you should plug into that placeholder.
    • 0:40:40And you can plug one or more lines of HTML code in here.
    • 0:40:45So let me go ahead and save this.
    • 0:40:46Let me go ahead now and go to greet, similarly delete all that redundancy,
    • 0:40:51and now let me go up here and say again, extends layout dot HTML,
    • 0:40:56and then down here let me say here is my body block.
    • 0:40:59Let me just un-indent this slightly, and then let me say end block
    • 0:41:03and save that file.
    • 0:41:05So now these HTML files are quickly looking weird.
    • 0:41:08Like there's barely any HTML in here, it's
    • 0:41:10really just text and weird Flask syntax, same thing in index dot HTML,
    • 0:41:15but again, this is one of the values of a web application.
    • 0:41:19Where you have a programming language like Python,
    • 0:41:21you can factor out those commonalities and really
    • 0:41:23start generating pages dynamically just like the Googles and Facebooks
    • 0:41:26and others of the world do every day.
    • 0:41:29So let me go ahead now and rerun Flask run
    • 0:41:32and cross my fingers as always because I made a whole bunch of changes here.
    • 0:41:36Let me open my URL, and voila, seems to be working OK so far.
    • 0:41:40Let me go ahead and, before I submit this, let me show you the page source.
    • 0:41:44Notice that it's there it's not quite as pretty printed,
    • 0:41:48like the indentation's a little off, but that's OK.
    • 0:41:50Your templates should be well styled.
    • 0:41:52Everything should be beautifully indented in your templates.
    • 0:41:55But if your templates are then rendered by Flask
    • 0:41:58and the whitespace isn't quite as pretty,
    • 0:41:59that doesn't matter because again, the browser doesn't care.
    • 0:42:02But indeed, I have a full-fledged complete web page.
    • 0:42:05Let me submit this form.
    • 0:42:06It seems to still work.
    • 0:42:07That's my slash greet route.
    • 0:42:09I'm seeing Hello, David, and if I view page source here,
    • 0:42:12notice another confirm form submission.
    • 0:42:15This is actually a safety feature.
    • 0:42:16You don't want websites tricking you into like checking out
    • 0:42:19twice with your credit card via post, so I'm
    • 0:42:21going to go ahead and click reload manually
    • 0:42:23which confirms that I want to resubmit this form because there's
    • 0:42:26nothing dangerous about it.
    • 0:42:27There is now my greet route.
    • 0:42:30So this probably would have saved you all a lot of headache or tedium
    • 0:42:33or copy paste, you just didn't have this tool in the tool kit last week.
    • 0:42:37You cannot do it with HTML and CSS or even JavaScript alone in the browser.
    • 0:42:43So this is where you have templates really shining.
    • 0:42:47Not only can you have templates use two curly braces
    • 0:42:49to plug in values of variables, you can use this curly brace percent sign
    • 0:42:54syntax and plug in actual contents of other files.
    • 0:43:00Whew.
    • 0:43:01Questions or confusion?
    • 0:43:04BRIAN: There was a clarification question.
    • 0:43:05Could you tell us what is [? Jinja? ?]
    • 0:43:07DAVID J. MALAN: Yes, so I skirted over that because I
    • 0:43:09was getting annoyed at just like how many new terms there
    • 0:43:11are today and frankly in the web programming world
    • 0:43:13that we're sort of stuck with.
    • 0:43:14So this is a good example of code reuse.
    • 0:43:17There's a lot of smart people out there, a lot
    • 0:43:19of people solving different problems.
    • 0:43:21It would be a little annoying and a little arrogant if all of us
    • 0:43:23tried to solve all of the world's computing problems.
    • 0:43:25And so the authors of Flask decided that for their template feature
    • 0:43:31they would not reinvent the wheel and come up with their own syntax.
    • 0:43:34They would use someone else's existing crazy syntax, if you will,
    • 0:43:38that uses the double curly braces and the curly brace
    • 0:43:41and the percent signs so that they're just combining
    • 0:43:43one person's language with another.
    • 0:43:45And this is so common.
    • 0:43:46And this is a good thing because technically you
    • 0:43:48can plug in a different language if you really are anti-Jinja for some reason,
    • 0:43:52you just hate the syntax, you can actually use something else.
    • 0:43:55And so this is actually a demonstration in computing
    • 0:43:57of really a component based design where you
    • 0:44:01can plug different pieces of software together and have
    • 0:44:04them still interoperate.
    • 0:44:06And we'll add to the course's website links to Jinja's documentation,
    • 0:44:09and we'll see in a little bit what more you can do with Jinja in the template.
    • 0:44:13So again, Jinja just refers to really the double curly brace syntax
    • 0:44:17and the curly brace percent sign syntax that appears in our templates files.
    • 0:44:24All right, one last feature when it comes to saying hello.
    • 0:44:27Thus far in application dot py, I've been
    • 0:44:30keeping things a little conceptually simple if complicated syntactically
    • 0:44:34by having two separate routes, one route, slash, for my form,
    • 0:44:38and then another route for my actual greet route that
    • 0:44:44actually displays the information.
    • 0:44:46But strictly speaking I can be a little clever
    • 0:44:49and combine these routes, if only because if you're
    • 0:44:51building a pretty sophisticated website that's got lots of routes,
    • 0:44:54it's kind of annoying if like every route needs two routes if one form has
    • 0:44:58to submit to another.
    • 0:44:59So let me just propose that we tighten up our implementation
    • 0:45:03and actually reuse our one route.
    • 0:45:05So let me go ahead and delete my greet route,
    • 0:45:07just because it's bothering me that I'm getting a little redundancy there,
    • 0:45:11and let me go ahead and say that my default route, my index,
    • 0:45:15will actually support both methods, get and post.
    • 0:45:19So I'm going to use the same route, not only to show the user the form,
    • 0:45:23but also to say hello to the user as well.
    • 0:45:26So I need to be able to support both get and post on the same route.
    • 0:45:30So how am I going to do this?
    • 0:45:31Well, let me go ahead and do this.
    • 0:45:33What I can do is if request dot method equals equals get,
    • 0:45:40then I can actually go ahead and execute this line of code here.
    • 0:45:45Else if request dot method equals equals post, then what I'm going to do
    • 0:45:50is this, return, render template, greet dot HTML,
    • 0:45:55and I'm going to pass in a name of request dot form
    • 0:45:59dot get, name, and then a default value of world.
    • 0:46:03So nothing new.
    • 0:46:04That's just the code that I deleted a moment ago.
    • 0:46:07But notice now I'm reusing the same route,
    • 0:46:09and I'm just checking logically in my controller code,
    • 0:46:13if you will, well if the method came in as get,
    • 0:46:16go ahead and just display the form.
    • 0:46:17If the method came in as post, go ahead and greet the user.
    • 0:46:21Now why is this going to work?
    • 0:46:23Well it turns out, whenever you visit a URL on the internet,
    • 0:46:26like HTTP colon slash slash, www.harvard.edu or yale.edu
    • 0:46:31or google.com, you have always been making get requests.
    • 0:46:36In fact, recall last week when we looked at those sample HTTP requests,
    • 0:46:39and the headers, everything we did last week
    • 0:46:41had the keyword get by default in the envelope.
    • 0:46:44So get does not have anything fundamentally to do with forms,
    • 0:46:48it's actually the default HTTP verb that's
    • 0:46:51used whenever you just visit any URL.
    • 0:46:53Any time you visit a URL on the web you are using get by default.
    • 0:46:57When you submit a form, you are potentially using get as I did first,
    • 0:47:01or you are using post as I did second.
    • 0:47:05So what's the takeaway here?
    • 0:47:08Well let me go ahead and make one change to my form.
    • 0:47:12I do want to submit via post.
    • 0:47:15I'm going to submit though no longer to slash greet
    • 0:47:17but just to my default route.
    • 0:47:19And if I did everything right here, let me go ahead and run Flask run,
    • 0:47:23let me open up my URL as before, and I'm at my slash route.
    • 0:47:30Notice if I view my page source, the action is going to be slash also,
    • 0:47:35so I'm sort of using the same route for two different pieces of functionality.
    • 0:47:39Let me type in my name, click Submit, and voila, amazingly, it worked.
    • 0:47:43Hello, David, but my route has not changed.
    • 0:47:46Now, why is this why is this useful?
    • 0:47:48It may or may not be.
    • 0:47:49It's just the capability you now have because if you can express yourself
    • 0:47:52programmatically in Python, you can distinguish between the two verbs
    • 0:47:56get and post that are inside of your incoming HTTP request, and if anything
    • 0:48:00this just means you don't need twice as many routes just to have one thing pass
    • 0:48:04to another, and it turns out there's also
    • 0:48:06other features that derive from just being able to reuse the same routes.
    • 0:48:11But more on that down the road.
    • 0:48:13All right, any questions then on saying hello, Flask, or these routes?
    • 0:48:19No?
    • 0:48:19All right.
    • 0:48:20Well just as a teaser here, here is a screenshot
    • 0:48:23of pretty much the very first web application I made back in the day.
    • 0:48:26When I was an undergraduate, I got involved in intramural sports,
    • 0:48:29not in an athletic sense but in a computer science sense.
    • 0:48:32I volunteered to actually build the very first website for Harvard's freshman
    • 0:48:36intramural sports program, otherwise known as Frosh IMs.
    • 0:48:39At the time we were using paper to write your names down on and email
    • 0:48:43addresses down on, your choices of sports,
    • 0:48:45we would walk across Harvard Yard, the grassy area in the middle of Harvard,
    • 0:48:49slide it under the door of one of the proctors or resident advisors,
    • 0:48:52and voila, we were registered for sports.
    • 0:48:53But there was no website.
    • 0:48:55There was an internet but there was no website yet.
    • 0:48:57So this was late 90s.
    • 0:48:59So I thought it would be fun to implement
    • 0:49:01that same idea of a piece of paper for registering
    • 0:49:03for sports in a web browser.
    • 0:49:05And now unfortunately I only knew C and maybe some C++ at the time and a couple
    • 0:49:10of other languages from other CS courses.
    • 0:49:12I knew nothing about web programming.
    • 0:49:14So I taught myself how to web program with the help of lots
    • 0:49:16of questions and answers from friends.
    • 0:49:18I happened to use a language called Perl, and the result at the end
    • 0:49:22was this frightening design here with lots of menu options
    • 0:49:26up top that use JavaScript and some images here
    • 0:49:28and some CSV files essentially on the backend that
    • 0:49:32stored all of the registration data.
    • 0:49:33I knew nothing about databases at the time.
    • 0:49:36But it was my first foray into web programming.
    • 0:49:38And what we thought we'd do after our first break
    • 0:49:40here is reimplement the idea of a registration system using HTML, CSS,
    • 0:49:47and Python and ultimately SQL to reimplement the same idea,
    • 0:49:51worrying even less today about the aesthetics.
    • 0:49:53But let's go ahead and take our first five minute break here.
    • 0:49:56And when we come back Frosh IMs.
    • 0:49:57All right, we are back.
    • 0:49:59So the goal at hand is to implement the most primitive of features
    • 0:50:02that I first implemented via this Frosh IMs
    • 0:50:04website years ago, that of allowing students to register for sports.
    • 0:50:08And to register for a sport, let's collect everyone's name and maybe
    • 0:50:11the sport for which they want to register.
    • 0:50:13So how are we going to do this?
    • 0:50:14Well, let me go ahead and copy just the layout from our previous hello examples
    • 0:50:19just because it's pretty good boilerplate.
    • 0:50:20And I'm actually going to make one change.
    • 0:50:22It turns out that it's very easy to make your website pretty mobile friendly
    • 0:50:27just by adding a couple of lines of code.
    • 0:50:29In particular in my head here I'm going to go ahead and add a tag called meta.
    • 0:50:32It's going to have a name of viewport.
    • 0:50:34Viewport refers to the rectangular region
    • 0:50:36that defines most of your web browsers' user interface.
    • 0:50:40The content of this meta tag is going to be initial scale
    • 0:50:43equals 1 and width equals device width.
    • 0:50:47And this last keyword is kind of the magic.
    • 0:50:50This incantation here essentially tells the browser
    • 0:50:53whatever the user's, whatever the width of the user's device
    • 0:50:57is, assume that that's the maximum width for my web page,
    • 0:51:00and it's going to scale my font sizes automatically
    • 0:51:02up or down based on whether you're using a laptop or desktop or iPhone
    • 0:51:06or Android phone or something else altogether.
    • 0:51:09There's other things you need to do to make your websites responsive,
    • 0:51:11so to speak, and it's helpful to use libraries like Bootstrap for that,
    • 0:51:15But this minimally helps at least address issues of font size.
    • 0:51:18So I'll start including that here as well.
    • 0:51:20Now let me go into my application dot py, which unfortunately for Frosh IMs
    • 0:51:24is empty.
    • 0:51:25And indeed to be clear, over the break I created, application dot py is empty,
    • 0:51:29a templates directory, and inside my templates directory
    • 0:51:32is just that layout dot HTML.
    • 0:51:34So we've got a little bit of work to do here together.
    • 0:51:36So let's go ahead and start building out this application.
    • 0:51:39Let me go ahead and import from Flask the Flask function itself
    • 0:51:44plus render template which I bet we're going to need,
    • 0:51:47and then also request which we're probably going to need as well.
    • 0:51:49Let me initialize the application with this Flask function using
    • 0:51:54underscore underscore name.
    • 0:51:55So again, that's literally how we began earlier.
    • 0:51:57And let's just go ahead and start defining our first route.
    • 0:51:59App dot route, quote unquote slash.
    • 0:52:02Let me define a function called index, but again I could call it anything,
    • 0:52:05but that's good convention, and just to get
    • 0:52:06me started I could really do this quickly and say return "to do,"
    • 0:52:12but I'm kind of confident now in my ability
    • 0:52:15to at least use render template.
    • 0:52:18So let's at least render a template called index dot HTML
    • 0:52:21that admittedly does not yet exist.
    • 0:52:23So let's make it exist.
    • 0:52:25Let me go ahead and create a new file called index dot HTML.
    • 0:52:29And again I could call this whatever I want,
    • 0:52:31but if this is going to be my default route,
    • 0:52:33I might as well call my default page the most common default
    • 0:52:36name, which is index dot HTML.
    • 0:52:38Let me store it in my Frosh IMs folder and my templates folder
    • 0:52:41so that it's in the proper place.
    • 0:52:43In here, let me go ahead and say that this extends my layout dot HTML.
    • 0:52:49And then down here let me say my body block will be whatever's in here,
    • 0:52:54and then my end block will close that.
    • 0:52:56In here, now I'm feeling a little uncomfortable
    • 0:52:58with how much code I've written so let's just put a "to do" there,
    • 0:53:01and let's make sure this whole thing works.
    • 0:53:03So let me go back into my directory, do Flask run, enter.
    • 0:53:08All right, go ahead and open up this browser tab, and it just says, "to do."
    • 0:53:12So I think I'm in a good place, and indeed
    • 0:53:13if I view page source I see a full fledged web page,
    • 0:53:16including that meta tag we just added, but with just a big to do here.
    • 0:53:19Now what do I want to do?
    • 0:53:20The biggest thing I want to do is actually
    • 0:53:22have students be able to register for a sport.
    • 0:53:24So let's go ahead and define ourselves an HTML form via which to do this.
    • 0:53:28So inside of my body block in my index dot HTML template.
    • 0:53:33Let's go ahead and first just call this the registration page with an H1 tag,
    • 0:53:37and then let me go ahead and give myself a form.
    • 0:53:39The action will be, I mean, the sky's the limit, let's keep it simple
    • 0:53:43and just do slash register rather than complicate things
    • 0:53:46by using the same route again.
    • 0:53:48Let's keep things a little private, so let's do a method of post
    • 0:53:52so that my roommates don't know what sports I'm registering for or not
    • 0:53:55registering for.
    • 0:53:56And then in here let's go ahead and ask the user for their name.
    • 0:53:59So let's go ahead and do an input, name equals name,
    • 0:54:02where the name of this input is going to be name in the human sense.
    • 0:54:07The type of that box will be text, and let's add some of the fanciness
    • 0:54:12as before.
    • 0:54:12Autocomplete equals off, or oof.
    • 0:54:16Autofocus, whoops, autofocus, and then over here a placeholder
    • 0:54:22so that it's self-describing of name.
    • 0:54:25All right.
    • 0:54:25And then let me go ahead and give myself a submit button,
    • 0:54:28input type equals submit, and let's add a value of register,
    • 0:54:34just to make the website a little more user friendly.
    • 0:54:37Let me go ahead and stop my server and restart it
    • 0:54:39so I can reload all of these changes, and let
    • 0:54:41me go over to this URL again, and voila, we have the beginnings of a form.
    • 0:54:45It's only asking for name, so it's not quite there.
    • 0:54:48So I need to ask for some sports as well.
    • 0:54:50Now there's a bunch of ways we can do this,
    • 0:54:51and even though you might not have seen them all yet in CS50,
    • 0:54:54you've certainly used them on the actual internet when you visiting websites.
    • 0:54:57So let's do a drop down menu initially.
    • 0:54:59Let me do what's called the select menu because you select an option, the name
    • 0:55:03of which is going to be "sport."
    • 0:55:05Select menus if you read the documentation
    • 0:55:07or follow an online tutorial, you'll see that they
    • 0:55:09must have children called options.
    • 0:55:11And children must have values.
    • 0:55:15So for instance, let's use a value of how
    • 0:55:17about "dodgeball," which is a common intramural sport.
    • 0:55:20And then inside of the option tag, you actually
    • 0:55:24have to say, much like a link, what the human should see.
    • 0:55:27So I'm just going to redundantly say dodgeball here too.
    • 0:55:30And let me go ahead and make that my life a little easier here.
    • 0:55:33Let me, "to do," "to do," just to speed things up.
    • 0:55:37Let me do a little bit of copy paste here.
    • 0:55:39And how about we change this one to flag football, flag football.
    • 0:55:46Over here let's do, say, soccer and soccer.
    • 0:55:50And again just like links there might be this duality, the same values,
    • 0:55:53but they don't have to be the same.
    • 0:55:55But for now, we'll keep it simple.
    • 0:55:56This is what the computer will see.
    • 0:55:58This is what the human will see.
    • 0:56:00And then down here we'll call this ultimate Frisbee and here
    • 0:56:04the same thing, ultimate Frisbee.
    • 0:56:07OK, so now I have a working drop down menu.
    • 0:56:10So let me go ahead and prove as much by restarting my server,
    • 0:56:15visiting my same URL, and voila.
    • 0:56:18The user interface is kind of ugly, definitely very minimalist,
    • 0:56:21but if I now type in my name David, notice that by default dodgeball
    • 0:56:25is selected because one of those options should be, but I can actually fix that.
    • 0:56:28Let me, I don't like how I'm forcing everyone
    • 0:56:30to think they have to do dodgeball.
    • 0:56:32Let me do an option here with an empty value for instance.
    • 0:56:35And I can even say something up here, for instance,
    • 0:56:38like let's just say what it is, sports, so, with no value.
    • 0:56:42So it's not an actual valid option.
    • 0:56:45Let me go ahead and open my URL now.
    • 0:56:46OK, so now I see sport in the dropdown too.
    • 0:56:49So David, sport, OK, it's a little weird that you can register for sports.
    • 0:56:54That's not really a thing.
    • 0:56:55So let me fix that.
    • 0:56:57And it turns out if you read the documentation for a select menu,
    • 0:57:00you can actually disable the option and also, let's say, select it by default.
    • 0:57:07So these are just additional attributes that you
    • 0:57:09would discover in the documentation for the option tag that
    • 0:57:12now let me make the user interface a little more robust just so that users
    • 0:57:16aren't registering for "sport."
    • 0:57:18Let me go ahead and do David, and now notice, sport is selected by default,
    • 0:57:22but it's grayed out.
    • 0:57:23It's not a valid value anymore.
    • 0:57:24So minor user interface improvement.
    • 0:57:26Dodgeball sounds amazing.
    • 0:57:28That was not an intramural sport in my day, but it apparently is these days.
    • 0:57:31So let me go ahead and register for this.
    • 0:57:33OK, but, of course, not found.
    • 0:57:35Well why is that?
    • 0:57:36Well if we look at the URL I just ended up at,
    • 0:57:38I've not implemented my slash register route yet.
    • 0:57:41All right, so let's do that.
    • 0:57:42To do that, I need to go back into application dot py, and let's go ahead
    • 0:57:46and do some registration trickery.
    • 0:57:47App dot routes slash register, I'm going to define
    • 0:57:51a function that I might as well call register,
    • 0:57:53but I could, again, call it anything I want.
    • 0:57:55And in this function let's keep it simple initially.
    • 0:57:57Let's just go ahead and return a template called success
    • 0:58:01and just presume that the registration was successful.
    • 0:58:04And in fact, let me go ahead and create a new file called
    • 0:58:10success dot HTML in the Frosh IMs templates directory,
    • 0:58:15and let me make sure that also extends layout dot HTML,
    • 0:58:19and that it has a block body, and, for good measure, end block.
    • 0:58:24And then down here, I'm going to keep this so simple for now.
    • 0:58:27You are registered, exclamation point.
    • 0:58:29Not really registering anyone for anything,
    • 0:58:31but I'm going to claim that they were.
    • 0:58:33So now if I rerun my application, and I go back to my form here,
    • 0:58:39and I now re-register David for dodgeball and click Register, huh.
    • 0:58:46Now it's a bug.
    • 0:58:47And let me pause here just to see if the pieces are fitting together.
    • 0:58:51Method not allowed, even though I'm at slash register.
    • 0:58:55Any thoughts on how to fix this?
    • 0:58:57Method not allowed?
    • 0:58:59And notice, you can even see in my terminal window
    • 0:59:01in red an error message, status code 405 which means method not allowed,
    • 0:59:09which is a rare one but I indeed screwed up.
    • 0:59:12Yeah, over to Santiago?
    • 0:59:14SANTIAGO: Yeah, I think that when you defined
    • 0:59:16the app dot route for read, or register, [INAUDIBLE]
    • 0:59:21you didn't specify a methods is post.
    • 0:59:24DAVID J. MALAN: Yeah, so by default my methods are all get,
    • 0:59:26but if I want to use post for privacy's sake,
    • 0:59:28recall that I just have to tell Flask that the methods I want you to support
    • 0:59:32for this route are this list of verbs or methods,
    • 0:59:36and that list is going to be of size one with just the keyword post in it.
    • 0:59:39So again, when you encounter these issues, which you absolutely will
    • 0:59:42when doing all of this for the first time,
    • 0:59:43don't worry so much about the specifics of the error message, but the ideas.
    • 0:59:46Like what's gone wrong?
    • 0:59:47Method.
    • 0:59:48Method not allowed.
    • 0:59:48What could that mean?
    • 0:59:49The only methods we've talked about are get and post,
    • 0:59:52so maybe it's something related there.
    • 0:59:54Well the route in question was slash register,
    • 0:59:56so maybe it's something related to my register route,
    • 0:59:59and see if those kinds of clues can lead you to a solution like Santiago
    • 1:00:03just proposed.
    • 1:00:04Let me go ahead and restart my server.
    • 1:00:07Let me go ahead and reload my form, type in David again,
    • 1:00:10I'm going to select dodgeball, register, voila, you are registered.
    • 1:00:14All right.
    • 1:00:15I'm not actually registered for anything,
    • 1:00:16but I feel like there's still a missed opportunity for error checking now.
    • 1:00:20In fact, let me go back to my form, not type David
    • 1:00:23and not select dodgeball and click Register.
    • 1:00:26Well this is kind of stupid.
    • 1:00:27Like its claim, it's saying I'm registered even if I only type my name,
    • 1:00:30no sport, you are registered.
    • 1:00:32So there's a missed opportunity here for some error checking.
    • 1:00:34So let's at least go in and add this.
    • 1:00:36Let's go ahead and do something like this.
    • 1:00:38How about if not request dot form dot get
    • 1:00:42name or request dot form dot get sports, or not that,
    • 1:00:51then let me go ahead and return render template failure dot HTML,
    • 1:00:56otherwise I'll return success dot HTML.
    • 1:00:59So let me try this now.
    • 1:01:00Let me rerun the server, Flask run.
    • 1:01:02Let me go ahead and reload my form, no changes to the form,
    • 1:01:05but now if I don't cooperate and I click Register,
    • 1:01:07OK, now really bad things are happening.
    • 1:01:09This is one of those 500 errors.
    • 1:01:11Let's see where I screwed up.
    • 1:01:12Internal server error and the subsequent paragraph are not very descriptive.
    • 1:01:16But if I go to my terminal window, here's my exception
    • 1:01:20a Jinja 2 exception, that's the templating language,
    • 1:01:23failure dot HTML is not found.
    • 1:01:26All right, well that's just because I made a stupid mistake,
    • 1:01:28so let me stop the server.
    • 1:01:30Let me go ahead and copy success dot HTML for now.
    • 1:01:33Let me go ahead and paste it in here.
    • 1:01:35And I could probably factor this out, but we're
    • 1:01:37going to keep it simple for now and call this failure dot HTML,
    • 1:01:40put it in Frosh IMs templates, save it.
    • 1:01:44Now let me rerun my server.
    • 1:01:47Now let me go back.
    • 1:01:48Let me try not typing in anything and just registering, OK, voila.
    • 1:01:53Now it's actually checking.
    • 1:01:54And if I go back here, name, OK, I'll not type in a sport,
    • 1:01:58it's still not checking.
    • 1:02:00But there's still this kind of hack here.
    • 1:02:02You know, you recall from last week that if you right click or control click
    • 1:02:05on anything in any web page, you can inspect it
    • 1:02:08in Chrome or your preferred browser, and that
    • 1:02:10lets you start poking around the HTML.
    • 1:02:13And watch this, maybe I'm a little bit upset
    • 1:02:16that there's no, let's say, tennis.
    • 1:02:19Tennis is not offered by the intramural sports program, but darn it
    • 1:02:22I want to register for such.
    • 1:02:23Well, again, using a browser, there's nothing stopping me from, for instance,
    • 1:02:28I don't know how to do this, let's change ultimate Frisbee to tennis,
    • 1:02:32and literally change the HTML on the page, zoom back out,
    • 1:02:36close the inspector, ha, ha, now tennis is a sport at Harvard.
    • 1:02:41So now watch what I can do.
    • 1:02:42Register for tennis, type in my name David, voila, registered for tennis.
    • 1:02:46Right?
    • 1:02:47I've hacked the website.
    • 1:02:48All right, so in some sense I kind of sort of
    • 1:02:50have hacked the website this time.
    • 1:02:52Now obviously I'm not doing anything with this information
    • 1:02:54on the server at the moment.
    • 1:02:56I'm just blindly saying registered or not registered.
    • 1:02:58But notice what I've just done.
    • 1:03:00In fact, let's really notice what I've done.
    • 1:03:02Let me go ahead and inspect the page again, let me go to my network tab,
    • 1:03:06and let me resubmit this form, and watch what happens here.
    • 1:03:11Let me go back here, let me type in David, let me do this quick hack again,
    • 1:03:16let me go ahead and right click on sport,
    • 1:03:18inspect, let me get rid of ultimate Frisbee and change this to tennis
    • 1:03:22again, and let me change this to tennis again, enter.
    • 1:03:25Now let me go to my network tab, select tennis from the dropdown,
    • 1:03:30and then down here, let me go ahead here and click Register.
    • 1:03:37Notice what's going on down here.
    • 1:03:39Just like last week, you can see a list of all of the HTTP requests.
    • 1:03:42So let's look at slash register.
    • 1:03:44Let me now zoom in on all of this and scroll down to the form data
    • 1:03:52which we didn't see last week but is there for post requests.
    • 1:03:55I have legitimately sent from my browser David and tennis to the server.
    • 1:04:00The onus is now on you, the programmer, to make sure
    • 1:04:03that you're not actually going to let me register for tennis if it is not
    • 1:04:06a supported sport.
    • 1:04:07So now there's more error checking necessary.
    • 1:04:10It's not sufficient to just trust that the user typed in a name and a sport.
    • 1:04:14You can't just check for the absence of either of those.
    • 1:04:16Now, we should really be smarter and make sure
    • 1:04:18that whatever sport the human's browser sent actually exists at Harvard.
    • 1:04:23And you could imagine this going very wrong very quickly,
    • 1:04:25if it's a bank account, like how much money do you want to deposit?
    • 1:04:28Well I'll just hack the HTML and change the amount of money
    • 1:04:31I'm depositing or withdrawing, right?
    • 1:04:33You should not be able to change a server's data
    • 1:04:37or database by just changing some HTML.
    • 1:04:40You should never, ever trust your users, or myself in this case.
    • 1:04:45So how do we defend against this?
    • 1:04:46Well there's a solution to this problem that actually gives us
    • 1:04:49some other features as well.
    • 1:04:50Let me go ahead and get rid of this silliness
    • 1:04:54where I've literally hardcoded all of these sports
    • 1:04:56in the HTML, which literally I resorted to copying and pasting.
    • 1:05:00That should have been the first clue that we can probably do better.
    • 1:05:03Let me instead go into application dot py, and let me go up here,
    • 1:05:07and let me just define a global variable called Sports.
    • 1:05:09I'll capitalize it as is the convention when defining constants.
    • 1:05:13And let me go ahead and say, the sports are going to be this Python list.
    • 1:05:18So I'm going to keep it pretty and put it on multiple lines using
    • 1:05:20square brackets because it's a list.
    • 1:05:22Here's where I'm going to say dodgeball as one sport,
    • 1:05:25flag football was our next, comma, soccer is our third, volleyball fourth,
    • 1:05:33and then lastly we'll leave in ultimate Frisbee as legitimate.
    • 1:05:37So those are my sports.
    • 1:05:39How do I now use that list of sports?
    • 1:05:42Well, I can just pass them into my template.
    • 1:05:44I can say something like this: Sports, or anything, x, or y,
    • 1:05:48or z, but sports is more descriptive, equals sports.
    • 1:05:52So this is to say, I'm going to create in my template
    • 1:05:54a variable called sports, the value of which
    • 1:05:56is going to be that same global variable.
    • 1:05:59And to be clear, Flask requires that you choose a name.
    • 1:06:03You can't just pass in sports because Flask just
    • 1:06:06won't know what to do with a single argument like that.
    • 1:06:09You have to give these things names.
    • 1:06:10And again, it tends to lead to this redundancy
    • 1:06:13where you're reusing the same words, but one is your variable on the right,
    • 1:06:16the other is the name you're using in your template on the left.
    • 1:06:19So now let's do this in index dot HTML.
    • 1:06:22Here's where templating gets more powerful.
    • 1:06:24In templates like Jinja, you don't just have curly braces left and right,
    • 1:06:28and you don't just have the blocks with the curly braces and percent signs,
    • 1:06:32you even have some simple programming constructs.
    • 1:06:36I can actually do something like this.
    • 1:06:39It turns out I can actually do something like this.
    • 1:06:44For sport in sports, end for, so it's a little stupid,
    • 1:06:49the syntax, that you literally say end and then
    • 1:06:51the name of whatever the preposition was a moment ago,
    • 1:06:54but now let me go ahead and generate an option whose value is
    • 1:06:57quote unquote the current sport, close, that and then in here
    • 1:07:01let me say sport again.
    • 1:07:03Done.
    • 1:07:04Now I have used a for loop in my template, the syntax for which
    • 1:07:09looks almost identical to Python, that's deliberate, the authors of Jinja
    • 1:07:13essentially borrowed Python's own syntax so it's not too much new information
    • 1:07:18overload.
    • 1:07:18And notice what I'm doing.
    • 1:07:20I'm using this Jinja syntax to iterate over that Sports variable.
    • 1:07:25It's a for loop, so I'm saying iteratively set sport
    • 1:07:28equal to the first sport, then the second, then the third, then
    • 1:07:31the fourth, then the fifth.
    • 1:07:32And then inside of this block here, this for loop,
    • 1:07:36I'm using the double curly braces to just plug in the sport there,
    • 1:07:39plug in the sport there.
    • 1:07:41And what's amazing about this feature of Flask
    • 1:07:44and in turn web frameworks in general is that now when
    • 1:07:47I run Flask run, and I open up my URL and go to this form, voila, it's there.
    • 1:07:53It's dynamically generated for me.
    • 1:07:54And if I look at my page's source in my browser,
    • 1:07:57notice that all of those options are indeed there.
    • 1:08:00But I didn't have to type them out manually.
    • 1:08:02I now have it in an array and I'm dynamically generating even more HTML.
    • 1:08:07And what's more powerful about that savings
    • 1:08:09is that now I can go into my application dot py, and let's
    • 1:08:12not just check for this value.
    • 1:08:14So if not request dot form dot get name, that was saying if there is no name,
    • 1:08:19and then this part here was saying if there is no sport,
    • 1:08:23why don't I more specifically say, or the sport
    • 1:08:26that the user input into the form is not in sports.
    • 1:08:30So here's where Python gets pretty elegant.
    • 1:08:33Now I'm checking two Boolean expressions.
    • 1:08:36Is it the case that there is not a request dot form dot get name,
    • 1:08:40or is it the case that the sport that was in the user's form
    • 1:08:44is not in the global variable, sports, then it should be failure.
    • 1:08:49So you can send me tennis, you can send me water polo,
    • 1:08:51you can send me any sport you want.
    • 1:08:53I am now going to reject it because I am validating your submission
    • 1:08:58against server side data.
    • 1:09:00And again, this is thematic.
    • 1:09:01Unfortunately you can never, ever trust user input.
    • 1:09:05You should always assume that some annoying
    • 1:09:08hacker out there or friend or sibling is going
    • 1:09:11to be trying to hack into or crash your programs.
    • 1:09:15But you can defend against that by programming defensively in this way.
    • 1:09:19So case in point now, if I go ahead and David, dodgeball, register, that works.
    • 1:09:24But if I do this little hack again where I go into my HTML,
    • 1:09:28I go down here, I change ultimate Frisbee, for instance,
    • 1:09:32to tennis, and also change it here to tennis,
    • 1:09:36zoom out, close that, choose tennis, type in my name, David, voila.
    • 1:09:41Wait a minute.
    • 1:09:42Damn it.
    • 1:09:44Why did that work?
    • 1:09:46Oh, wait.
    • 1:09:47OK.
    • 1:09:48I didn't restart the server.
    • 1:09:49It was still running the old code.
    • 1:09:51Now I open it up.
    • 1:09:52Now we have to do this again.
    • 1:09:53OK.
    • 1:09:54Now I'm going to go ahead and hack the site.
    • 1:09:55Let me go ahead and inspect this, expand the sports, let's go ahead
    • 1:09:58and change ultimate Frisbee to tennis.
    • 1:10:00Let's go ahead and change that to tennis.
    • 1:10:02Close the window, change the dropdown to tennis, and now, there we go.
    • 1:10:07OK.
    • 1:10:08All right.
    • 1:10:08I was confused for a moment, but again, just restart the server,
    • 1:10:11and it will reload the changes we actually made.
    • 1:10:13And thank you so much, Brian, for diving in there to save me.
    • 1:10:15I figured it out.
    • 1:10:17All right, any questions or confusion beyond my own now on what it is we've
    • 1:10:21just done by one, dynamically generating a list of user select menu options
    • 1:10:27and also validating on the server against that same list of data?
    • 1:10:32Any questions?
    • 1:10:33Or anything on your end Brian?
    • 1:10:35BRIAN: Nothing here.
    • 1:10:36DAVID J. MALAN: All right.
    • 1:10:37So let me just demonstrate real quickly these just different user interface
    • 1:10:41mechanisms.
    • 1:10:41And suffice it to say, we're not too focused
    • 1:10:43for our purpose on user interface design.
    • 1:10:46Using a library like Bootstrap can you make these forms look much prettier
    • 1:10:50just by adding a few more HTML tags and a few more CSS classes,
    • 1:10:55but let me go ahead and make one tweak here
    • 1:10:58whereby we can change the UI from being the select menu
    • 1:11:00to a bunch of different things.
    • 1:11:02I index dot HTML I can go ahead and define, inside of my for loop,
    • 1:11:07instead of using the Select menu an input, name equals sport,
    • 1:11:12type equals radio for radio button, which
    • 1:11:15are the mutually exclusive circles that you might tick on a box, on a website,
    • 1:11:19and then the value of this will be this particular sport.
    • 1:11:22And then over here, let me go ahead outside of the radio button
    • 1:11:25and actually display to the human that sport name.
    • 1:11:28Let me restart the server.
    • 1:11:30Let me reload my page, and voila, we no longer have a select menu,
    • 1:11:33but we have radio buttons, which achieve the same result,
    • 1:11:36but they're just laid out in a slightly different way.
    • 1:11:38And they're indeed mutually exclusive like an old school
    • 1:11:41radio in a car where you can only select one of them at a time,
    • 1:11:44but otherwise the site works completely the same.
    • 1:11:47So which to use?
    • 1:11:48It depends.
    • 1:11:48If you've got 100 different sports you probably
    • 1:11:50don't want 100 different radio buttons if only
    • 1:11:53because they take up so much space.
    • 1:11:54But if instead you might want to use a drop down menu instead.
    • 1:11:58All right, so what's the key thing we're still not doing?
    • 1:12:01We've got a pretty dynamic user interface,
    • 1:12:03but I'm just throwing away the information
    • 1:12:05once the user clicks Register.
    • 1:12:06So let's actually start remembering, just as I did back
    • 1:12:10in the day, who has actually registered so that we actually have
    • 1:12:13a proper working registration system.
    • 1:12:16So how can we go about doing this?
    • 1:12:18Well we've seen in our look at Python in the past
    • 1:12:21that we can store data inside of a Python program using a list,
    • 1:12:26using a dictionary, using a set.
    • 1:12:28There's a lot of data structures that Python gives us.
    • 1:12:30So let's start simple.
    • 1:12:32And let's actually do that.
    • 1:12:33Let's go ahead and store our registrants maybe in a dictionary.
    • 1:12:36So in application dot py, which is thus far now unchanged,
    • 1:12:40let me give myself one other global variable, registrants,
    • 1:12:43and let me initialize it to an empty dictionary.
    • 1:12:45And recall that we did this to implement a phone book where
    • 1:12:48I added my name and phone number and Brian's name and phone number
    • 1:12:51a couple of weeks ago.
    • 1:12:52We used a Python dictionary to store those key value pairs, name and phone
    • 1:12:57number.
    • 1:12:58Here let's do something similar.
    • 1:12:59Let's store the user's name and the sport
    • 1:13:01for which they're registered so that we then
    • 1:13:03have in the computer's memory a list of all of the valid registrations.
    • 1:13:08So how are we going to do this and what needs to change?
    • 1:13:10Well let me go ahead and change this back to the Select menu
    • 1:13:14just because it fits on the screen a little better instead of the radio
    • 1:13:17buttons.
    • 1:13:18And from here we will have again, the user interface that we saw earlier.
    • 1:13:24So let me go ahead and do Flask run, let me go ahead
    • 1:13:28and open this up, and voila, we have that same interface
    • 1:13:31as before, the goal of which now is when I click Register,
    • 1:13:33I don't want to just naively see you are registered or you are not register,
    • 1:13:37I want to actually register the user.
    • 1:13:39So I think we're going to need to do a bit of work
    • 1:13:41in application dot py, the file that's actually
    • 1:13:44processing the user's registration.
    • 1:13:47So instead of just validating the form by checking
    • 1:13:50if they had a name or a sport that was in sports,
    • 1:13:53let's do something a little different.
    • 1:13:54So let me go ahead and sort of start anew here,
    • 1:13:57and let me do something like this: name equals request dot form dot get name,
    • 1:14:01just so I have it in a variable, it'll be a little easier to use.
    • 1:14:03And then let's just say if not name, then let's go ahead and return render
    • 1:14:08template failure dot HTML.
    • 1:14:12All right, let's then check sport.
    • 1:14:14Sport equals request dot form dot get sport, storing it in a variable
    • 1:14:18just so I can do this a little more cleanly.
    • 1:14:21And then if not sport, go ahead and return render template, template,
    • 1:14:27failure dot HTML.
    • 1:14:29And then let's do one more check, but I'm just
    • 1:14:31being a little more pedantic this time.
    • 1:14:33If sport not in sports, then let's go ahead
    • 1:14:38and return render template failure dot HTML.
    • 1:14:42But you know what, this seems a little silly.
    • 1:14:44If all I'm doing, if I know as the programmer what the error is,
    • 1:14:47but I'm just telling the user you are not registered,
    • 1:14:50that's not particularly good design.
    • 1:14:51I'm not telling the user any information about what they did wrong.
    • 1:14:56So let's do this instead.
    • 1:14:57Instead of failure dot HTML, let me go ahead and create a different file
    • 1:15:01called error dot HTML.
    • 1:15:03And this will be called error dot HTML in my Frosh IMs directory
    • 1:15:07in my templates directory, and in here, let's go ahead
    • 1:15:11and actually put an error message, for instance, something like this message.
    • 1:15:18So there's nothing really there other than a placeholder for message,
    • 1:15:22but let's see what this will do for us.
    • 1:15:24Let's now go back to application dot py.
    • 1:15:26And instead of error dot HTML, let's be a little more specific
    • 1:15:29and pass in an error message like missing name.
    • 1:15:32And down here, let's change this to error dot HTML
    • 1:15:35and say a message of missing sport.
    • 1:15:38And down here, let's change this to errors
    • 1:15:40dot HTML, whoops, error, error in the error, message of invalid sport.
    • 1:15:46So there's a sport there, but it's just not in the list of valid sports,
    • 1:15:49so we should tell the user accordingly.
    • 1:15:51So now let's just try this real quick.
    • 1:15:55Let's try this real quick and input this data in correctly so we can see these.
    • 1:15:59Let me rerun the server, reload the form, let's type in nothing
    • 1:16:03and just try to register.
    • 1:16:04OK.
    • 1:16:04Missing name.
    • 1:16:05It's more useful than just "you are not registered."
    • 1:16:07All right, fine, let me give you my name, David.
    • 1:16:09Register.
    • 1:16:11Missing sport.
    • 1:16:11OK.
    • 1:16:12Now let me go ahead and give you a sport.
    • 1:16:13And now if I registered, I'd probably get some other error
    • 1:16:16because I haven't handled the successful outcome yet.
    • 1:16:18So let's finish this up.
    • 1:16:20Let's go back to my application dot py in my register route
    • 1:16:26and then ask ourselves, well what does it mean to register the user?
    • 1:16:29I want to remember that the user is registered for a given
    • 1:16:33sport in the computer's memory.
    • 1:16:35And the computer's memory, recall, I'm using
    • 1:16:38by way of this global variable called registrants.
    • 1:16:40And it's all caps just to make clear that it's a global variable,
    • 1:16:43but that's not required.
    • 1:16:44And then down here, why don't I do this, registrants, name equals sport.
    • 1:16:50So this is very similar to what we did with names and phone numbers, keys
    • 1:16:55and values, where I'm using name to index into the Python dictionary,
    • 1:17:00and I'm setting the value equal to the sport.
    • 1:17:02And then let's go ahead for now and say return, render template, whoops,
    • 1:17:08render template, success dot HTML.
    • 1:17:11So let's test this out.
    • 1:17:13Let's go over to Frosh IMs, whoops, let's learn from my mistakes.
    • 1:17:16Stop the server and reload.
    • 1:17:19Let me go back to the browser.
    • 1:17:20Let me go ahead and type in David, who will register for dodgeball, register.
    • 1:17:25Claims it's registered.
    • 1:17:26Let me do Brian will register for flag football.
    • 1:17:29Registered.
    • 1:17:30He is registered.
    • 1:17:31But I don't know who's registered or not.
    • 1:17:35I don't know.
    • 1:17:35But you know what, let's do a little sanity check here.
    • 1:17:38Let me just print out registrants, right?
    • 1:17:39When in doubt, print or print f is your friend.
    • 1:17:42So let's just do a little poking around here and run Flask run again.
    • 1:17:46This time let's go back to the form and register David, sport, dodgeball,
    • 1:17:51register, same behavior.
    • 1:17:52But down here, notice we have this.
    • 1:17:54And I see a couple of you are registering as well.
    • 1:17:58So I see Lucas has registered for dodgeball as well,
    • 1:18:03Brian Y. has registered for volleyball.
    • 1:18:05So OK, now Harsh has registered for volleyball.
    • 1:18:08So this is the problem with having an internet when we do these live demos,
    • 1:18:11but apparently it does in fact work.
    • 1:18:13So let's go ahead and make this a little cleaner
    • 1:18:15here, and instead of just printing registrants, let's go ahead
    • 1:18:18and do something like this.
    • 1:18:20Why don't I go ahead, and you know what, let's do this.
    • 1:18:23Let's go ahead and render a template called registrants dot HTML.
    • 1:18:30And I think to make this happen, I'm going to need another route,
    • 1:18:33but this one will be kind of interesting in that we've never
    • 1:18:36dynamically generated quite this kind of success message.
    • 1:18:40So let me go ahead and whip something up real quick here.
    • 1:18:42Let me go to a new file, registrants dot HTML, Frosh IMs, templates,
    • 1:18:48and then in that folder, templates, and in here we're going to do
    • 1:18:53extends layout dot HTML, down here, block body, end block,
    • 1:19:01and then down here, in here, I'm going to do, this going to take me
    • 1:19:04a moment here, H1, registrants.
    • 1:19:06Then below that I'm going to give myself an HTML table.
    • 1:19:09Inside of the table, I'm going to define what's
    • 1:19:10called a table head, which is not strictly required
    • 1:19:12but it helps distinguish the head from the body from the optional footer.
    • 1:19:16Inside of here I'm going to create a table row.
    • 1:19:18Inside of here I'm going to create three columns,
    • 1:19:20using TH, table heading, which you might not have seen before.
    • 1:19:22Typically makes things bold by default to make clear it's the heading.
    • 1:19:26And I'm going to put the person's name and then another heading of their sport
    • 1:19:30down here.
    • 1:19:31And then below the table head I'm going to do tbody, which is the table body,
    • 1:19:35another tag you might not have seen before but just represents, indeed,
    • 1:19:38the body.
    • 1:19:39And I'm going to generate dynamically a whole bunch of TRs.
    • 1:19:42So for name in registrants, let's go ahead
    • 1:19:45in this Jinja for loop in my template and output dynamically a table row.
    • 1:19:51Inside of that table row, some table data, specifically the person's name,
    • 1:19:55and then specifically in the other column
    • 1:19:58the person's sport, which I can access, as we'll see by way of a variable
    • 1:20:04called registrants by indexing into it via the name.
    • 1:20:07So registrants, in a moment, is going to be a dictionary
    • 1:20:10that I pass into my template.
    • 1:20:12I think that's it.
    • 1:20:12It's a little tedious to type it out.
    • 1:20:14But notice there's no data.
    • 1:20:15It's only HTML and templating.
    • 1:20:17If I go back here now and pass in registrants equals registrants,
    • 1:20:23just like with the sports variable I'm providing my registrants template
    • 1:20:28with a variable called registrants, but again I could call it x or y or z,
    • 1:20:32but registrants feels a little more appropriate,
    • 1:20:34setting it equal to the value of that global variable,
    • 1:20:37thereby giving my template access to that dictionary.
    • 1:20:40If I now save the file, rerun Flask, go ahead back here,
    • 1:20:46whoops, let me go ahead and do this once more.
    • 1:20:48Let me go ahead and rerun Flask.
    • 1:20:50Let me go ahead and select my URL.
    • 1:20:53Let me go ahead and register David for dodgeball and click Register.
    • 1:20:57Voila.
    • 1:20:58Harsh, you beat me to it.
    • 1:20:59So Harsh is registered for flag football.
    • 1:21:01David has registered for dodgeball.
    • 1:21:03It's not the prettiest user interface, but if I view the page source,
    • 1:21:08you'll see that we indeed have the table head, followed by the table body,
    • 1:21:13followed by Harsh for flag football, followed by David
    • 1:21:15for dodgeball, and then after that any subsequent registrants
    • 1:21:19who are registering at the same time.
    • 1:21:21So this is pretty cool, I dare say, in that now we
    • 1:21:23have the ability to save all of these registrants to a global variable.
    • 1:21:28But what's a downside here?
    • 1:21:30We've made progress.
    • 1:21:33I'm now showing you users who have registered,
    • 1:21:36so that means the RA or proctor who's running the sport
    • 1:21:39knows who's registered.
    • 1:21:40But there's a catch.
    • 1:21:42What could go wrong here?
    • 1:21:45What's bad about using a dictionary?
    • 1:21:47Sophia?
    • 1:21:48SOPHIA: I'm assuming that every time you reload
    • 1:21:50the server you're just going to reset, and you're not going save the data?
    • 1:21:54DAVID J. MALAN: Exactly.
    • 1:21:55If I'm just using a global variable, it'll
    • 1:21:57work forever so long as the server runs forever and the IDE stays online
    • 1:22:01and we don't lose power or any other number of reasons.
    • 1:22:03But as soon as the web server, excuse me, stops or is turned off
    • 1:22:06or something goes wrong, we're going to lose all of that data.
    • 1:22:09So this is why a couple of weeks ago with Python
    • 1:22:11we actually used CSV files or ultimately a SQLite database.
    • 1:22:16But let me make one change too.
    • 1:22:17Because what I don't like right now is that at the moment
    • 1:22:20the only way to view my registrants is to actually register yourself.
    • 1:22:23And maybe that's a feature if you want to require people register before they
    • 1:22:27see who else is registered, but it feels nice to have a different route,
    • 1:22:30maybe slash registrants, that anyone can visit
    • 1:22:33to see the actual list of registrants.
    • 1:22:36So let me go ahead and make one tweak here.
    • 1:22:38Let me go ahead down here and define another route, app dot route, slash,
    • 1:22:44whoops, registrants.
    • 1:22:46And then define registrants as my function
    • 1:22:48but I could call it anything I want.
    • 1:22:50And let me actually move this rendering of template here.
    • 1:22:54And you know what, let me be a little clever.
    • 1:22:56Let me actually redirect the user to slash registrants.
    • 1:23:00So let me go ahead and import one more function up here,
    • 1:23:03redirect, from the Flask library.
    • 1:23:06And this is a function that will literally do one of those HTTP 301s
    • 1:23:10or some other status code that will send a location to the browser that says,
    • 1:23:14uh-uh, go here instead.
    • 1:23:16And the reason for this is that it's just nice now
    • 1:23:18that inside of my register route, once you're registered,
    • 1:23:22I don't need to literally copy and paste this line in two different places
    • 1:23:26and actually have redundant code.
    • 1:23:28I can just say, redirect the user to slash registrants
    • 1:23:31which already exists now so that they can see themselves
    • 1:23:34and who else has registered.
    • 1:23:36So if I do this again, let me restart the server.
    • 1:23:38Let me go back to the form, type in David for dodgeball, register.
    • 1:23:43OK, so Harsh, you didn't make the cut this year, this time, but we see Break
    • 1:23:47and Moash and David now registered in that split second,
    • 1:23:51amazingly, for these sports.
    • 1:23:52But notice the URL, which is the key point.
    • 1:23:55Now we're at slash registrants.
    • 1:23:56And if I keep reloading I'll keep seeing all of the changes since then.
    • 1:24:01All right, but Sophia makes a really good point in that the data is all
    • 1:24:04going to be lost.
    • 1:24:04So I think we should clean this up and maybe do one final version here,
    • 1:24:08where we actually save the data in, for instance, a SQLite database
    • 1:24:14or maybe even email it out so that we have a backup copy as well.
    • 1:24:18So let me go ahead and propose this.
    • 1:24:21Let me go ahead and propose that we make a few changes here, a few changes,
    • 1:24:27borrowing some inspiration from our look at both SQL and Python.
    • 1:24:31So I'm going to leave most of this alone for now.
    • 1:24:33I'm going to go ahead and get rid of this variable.
    • 1:24:36We'll have to store the data elsewhere.
    • 1:24:38And instead, let's go ahead and use a database, a SQLite database.
    • 1:24:42So let me go ahead and declare a database as SQL.
    • 1:24:47SQLite, colon slash slash slash, Frosh IMs dot db.
    • 1:24:51Let me go ahead and import this.
    • 1:24:53This, recall, is from CS50's library, this SQL library.
    • 1:24:56And let me go ahead in my window for just a moment
    • 1:24:58here and copy the database from an example I brought with me just so
    • 1:25:02that we don't have to create the database as well
    • 1:25:04and sort of rehash things from our week on SQL.
    • 1:25:08Let me go ahead and grab the database here, and indeed, voila,
    • 1:25:11the database is out of the oven.
    • 1:25:13And we now have a local file in my directory called Frosh IMs dot db.
    • 1:25:18So let's go ahead and first look at this database, SQLite of Frosh IMs dot db
    • 1:25:24dot schema, enter.
    • 1:25:26Here is the database table that I created in advance just so we
    • 1:25:29wouldn't have to reinvent the wheel.
    • 1:25:31I'm creating a table called registrants with an ID column, with a name column,
    • 1:25:36and a sport column.
    • 1:25:37The primary key is going to be that ID.
    • 1:25:40The textfield should not be null and the sport should not be null.
    • 1:25:43But we've seen all of this syntax actually in past weeks.
    • 1:25:46So it's just a simple table to give me an ID for every registrant, a name,
    • 1:25:51and a sport.
    • 1:25:52But I'm going to do this in Python now.
    • 1:25:53So let me go ahead and go back into my, let me go back into my application dot
    • 1:26:00py, and let's go ahead and do this.
    • 1:26:04When I want to actually register the user,
    • 1:26:07let's validate them as before, making sure
    • 1:26:10that they've given us their name and their sport,
    • 1:26:12and now let's just use a little SQL.
    • 1:26:14Instead of saving it into what was a global variable let's
    • 1:26:16do db dot execute, insert into registrants a name and a sport,
    • 1:26:22I don't need the ID, it will auto increment for me
    • 1:26:24as the primary key, and two values, question mark and question mark.
    • 1:26:29Again, beware SQL injection attacks, and let me pass in name and sport.
    • 1:26:34And I think that's all I need.
    • 1:26:36I'm now going to use a little bit of SQL in my Python
    • 1:26:39in order to insert that name and sport into the database,
    • 1:26:42and after that, I think I'm fine.
    • 1:26:44But I do need to change registrants because this variable down here no
    • 1:26:47longer exists.
    • 1:26:48But that's OK, because recall from our look at SQL in Python,
    • 1:26:51I can do something like this: registrants equals db dot
    • 1:26:55execute, select star from registrants.
    • 1:26:59Indeed that simple query would seem to give me access
    • 1:27:02to all of the registrants thus far, and now I can just pass that in like that.
    • 1:27:05Again, it looks a little weird that the name of your variable in your template
    • 1:27:09is the same as the name of this variable.
    • 1:27:12But again, that's because on the right is the variable, on the left
    • 1:27:15is the name you want to use in your template,
    • 1:27:17but you could call it anything you want.
    • 1:27:19All right, here, as always, let me cross my fingers.
    • 1:27:21Flask run, all right, let me go ahead and open up my URL.
    • 1:27:25Let me go ahead and type in David, dodgeball, register, OK, interesting.
    • 1:27:31So Harsh and Naveen got in there real fast,
    • 1:27:33again, you're just kind of waiting with the Submit button.
    • 1:27:36But this is a mess.
    • 1:27:37Something is clearly wrong now, I'm seeing like weird dictionary syntax.
    • 1:27:41But that's because I haven't adjusted my template.
    • 1:27:43So let me go into registrants dot HTML and recall
    • 1:27:48that when I select with select star from registrants,
    • 1:27:53this is like getting back rows.
    • 1:27:54I just renamed it to registrants, but I'm really getting back rows.
    • 1:27:58And rows are just dictionary, each row is a dictionary
    • 1:28:05of columns and the values they're in.
    • 1:28:07So I think my syntax just needs to change a little bit in my registrants
    • 1:28:11dot HTML.
    • 1:28:12I should really think of this as for registrant in registrants,
    • 1:28:15and let me go ahead and output the registrant's name.
    • 1:28:19And down here let me output the registrant's sport.
    • 1:28:23So again, this is more similar to the time we spent on SQL and Python
    • 1:28:27than it is to HTML and CSS right now.
    • 1:28:30Now I'm iterating over all of the rows.
    • 1:28:32And if you prefer, let's change even the semantics.
    • 1:28:35Let's go back over here.
    • 1:28:36Let's do it just like we did a couple of weeks ago.
    • 1:28:38Let's just get back a bunch of rows, pass in those rows to my template,
    • 1:28:43and now iterate over those rows.
    • 1:28:45And each row has a name field, and each row has a sport field.
    • 1:28:49So that too would be equivalent.
    • 1:28:51But once you understand the difference, let's
    • 1:28:53call it what they really are instead of this more generic row,
    • 1:28:56so let me revert that.
    • 1:28:57Now let me go ahead and rerun the code.
    • 1:29:00Let me try to beat everyone else to the punch, type in David for dodgeball,
    • 1:29:03and, Oh my god, OK, lot of people.
    • 1:29:07OK, new and better Harsh, all right, so you're working really quickly
    • 1:29:09it should seem.
    • 1:29:10So now we see all of the same results.
    • 1:29:12The database is really hopping now.
    • 1:29:14I'm a little nervous about scrolling any further,
    • 1:29:16so I'm going to leave it there.
    • 1:29:17But we now have a full fledged web application honestly
    • 1:29:20that is now much more representative of real world websites.
    • 1:29:23We have our own database using SQL, we have our own templates dynamically
    • 1:29:27generating this table even though it's ugly at the moment,
    • 1:29:29but I could certainly beautify it with some CSS or some Bootstrap
    • 1:29:32in particular.
    • 1:29:33But we now have a fully functioning website.
    • 1:29:36What would be the icing on the cake?
    • 1:29:38You know what?
    • 1:29:38It's pretty cool, even though we take it for granted
    • 1:29:41when a website emails you a confirmation.
    • 1:29:43What if we add one final feature just like I actually did years ago
    • 1:29:47whereby you email the registrant or yourself to report
    • 1:29:52that someone has actually registered?
    • 1:29:54So how can we go about doing this?
    • 1:29:56Well, let me go back into my code here.
    • 1:29:58And let me add one final flourish.
    • 1:30:00I'm going to scroll up to the top here, and I'm
    • 1:30:02going to import another library.
    • 1:30:04From Flask mail import mail and message.
    • 1:30:07And again, you would only know to do these things from a class,
    • 1:30:10from the documentation, from a tutorial or the like.
    • 1:30:12I looked it up in advance to see how to do this.
    • 1:30:14And then I have to, according to the documentation,
    • 1:30:17there's a bunch of initialization steps required
    • 1:30:19when you want to write Python code to generate emails programmatically.
    • 1:30:22You've got to know your username, your password,
    • 1:30:25and a bunch of other settings.
    • 1:30:26So let me go ahead and configure those now.
    • 1:30:28App dot config, quote unquote, mail default sender is one.
    • 1:30:35And I'm going to go ahead and set that equal to os
    • 1:30:37dot get env of mail default sender.
    • 1:30:42So default sender refers to a default email address.
    • 1:30:44So for instance, if I wanted all these emails to come from me,
    • 1:30:47I could do something like that.
    • 1:30:48But instead I'm going to store them in variables elsewhere because I
    • 1:30:51pre-configured my ID in advance so that no one could see my username
    • 1:30:54and password.
    • 1:30:55So I'm just going to grab some of those values from the so-called environment.
    • 1:30:59You won't use this feature too often but we'll
    • 1:31:01show it to you in the next problem set.
    • 1:31:03It's a way of getting access to a variable that's
    • 1:31:05been defined elsewhere in your IDE or in your Linux system
    • 1:31:09or Mac or PC more generally.
    • 1:31:11All right, I need to define a few more of these.
    • 1:31:13Let me go ahead and just do some boilerplate,
    • 1:31:15even though this looks like a little copy paste, which it is,
    • 1:31:19it is in fact necessary because there's a lot of settings for a mail server.
    • 1:31:22So let me do them one at a time.
    • 1:31:24Mail password, I don't want you to see that either,
    • 1:31:27so I'm going to go ahead and grab that from my environment, mail password.
    • 1:31:33Let's see, I need a mail port, mail port, so that's going to be 587.
    • 1:31:38It turns out I'm going to use Gmail, and Gmail
    • 1:31:40lets you send email on TCP port 587, so I looked that up in the documentation
    • 1:31:46too.
    • 1:31:46Mail server in fact will be SMTP dot gmail.com.
    • 1:31:49I found that on Google's documentation.
    • 1:31:53Mail use TLS, which is a type of encryption,
    • 1:31:57let me go ahead and enable that as true.
    • 1:31:59And then lastly mail username, I don't want
    • 1:32:02you to know what username I'm using, but I'm
    • 1:32:04going to go ahead and grab this from my environment, username.
    • 1:32:07And this is actually deliberate.
    • 1:32:09Though I'm sort of saying it tongue in cheek,
    • 1:32:11it is bad, bad practice to type in usernames, passwords, or anything
    • 1:32:15remotely private or secure into your code.
    • 1:32:19Because imagine if you did type your own Gmail username or password
    • 1:32:22into this program just to try it out literally
    • 1:32:24right now at home and then you can submit50 or check50, guess what?
    • 1:32:28You're sending your username and password to us, the staff,
    • 1:32:31not to mention GitHub and the whole internet,
    • 1:32:33by including it in your source code.
    • 1:32:35So while I'm sort of making fun at what I'm doing here,
    • 1:32:38this storing of private information in these environment variables
    • 1:32:42is actually best practice because it means it's not in my code,
    • 1:32:45I'm not going to accidentally copy and paste it elsewhere.
    • 1:32:49All right, I need to import one other thing which will explain all of these
    • 1:32:52red X's.
    • 1:32:53I need to also import OS.
    • 1:32:56So it's kind of a bold claim to import a whole operating system,
    • 1:32:59but there's indeed a library that comes with Python
    • 1:33:02called OS which gives you access to things like these environment variables
    • 1:33:06and others.
    • 1:33:08Whoo.
    • 1:33:08OK.
    • 1:33:08That was a lot, and I need one last line of code,
    • 1:33:10a mail variable equal to passing my Flask
    • 1:33:14application to a function called mail.
    • 1:33:17All right.
    • 1:33:18I think all of those errors will soon go away,
    • 1:33:20I think it's just being a little slow on me.
    • 1:33:22Let's go ahead now and actually send mail.
    • 1:33:25Indeed the red errors are gone.
    • 1:33:27Let me scroll down, and before I redirect the user to slash registrants
    • 1:33:31but after registering them, let me go ahead and do this.
    • 1:33:34Let me define a message variable equal to capital message, which
    • 1:33:37is the feature I imported from that library,
    • 1:33:41and let me create a subject line of You are registered!
    • 1:33:44And let me go ahead and, huh.
    • 1:33:46Oh, I don't actually know who to send this to, so that's OK.
    • 1:33:49Let's go ahead and change something here.
    • 1:33:51Let's go ahead and have the user ask not for their name,
    • 1:33:54let's ask the user for their email now.
    • 1:33:57So I'm going to change my actual form to ask for their email instead.
    • 1:34:02And now let me go back over here, and let me go back in here
    • 1:34:07and set a second argument of recipients equals email.
    • 1:34:14And let me change this here.
    • 1:34:15Let me go ahead and get the user's email from the form.
    • 1:34:18If there is no email let's yell at them and say missing email.
    • 1:34:22Sport is unchanged.
    • 1:34:23Let's go ahead and insert into registrants.
    • 1:34:26You know what, let's not bother inserting into the database actually.
    • 1:34:29Let's keep this super simple.
    • 1:34:30Let's get rid of the SQL stuff.
    • 1:34:32Let's just have it send a confirmation email.
    • 1:34:34So it's even simpler.
    • 1:34:35We'll keep the focus entirely on email here.
    • 1:34:38So I've created the message with a subject line and the
    • 1:34:41to line, so to speak, via the recipients.
    • 1:34:44Now I'm going to go ahead and send mail dot send message.
    • 1:34:48Save.
    • 1:34:49Here's where I'm going to cross my fingers as always.
    • 1:34:52I'm going to go ahead now and run this program.
    • 1:34:54I'm going to restart, reload my form here.
    • 1:34:56It's indeed asking me for email.
    • 1:34:58Let me go ahead and type in, let's say John Harvard's address here.
    • 1:35:03John Harvard, like me, will register for dodgeball,
    • 1:35:06and let me go and click Register.
    • 1:35:08Now the website is about to say internal server error, which comes as a surprise
    • 1:35:14to me, but let me see why.
    • 1:35:16Oh I accidentally am still using the database.
    • 1:35:19So let me fix this.
    • 1:35:23Let me go ahead and, since we're not using the database anymore,
    • 1:35:27let me go ahead and revert to our simpler return render template
    • 1:35:31success dot HTML.
    • 1:35:33All right.
    • 1:35:33User error.
    • 1:35:35Let me go ahead and restart the server.
    • 1:35:38OK, bad gateway, whole other problem, here we go. jharvard@cs50.
    • 1:35:41Let me go ahead now and register for dodgeball.
    • 1:35:45Register.
    • 1:35:46Crossing my fingers.
    • 1:35:47I am registered, and indeed, let's go over to my other browser
    • 1:35:51here where I have Gmail open.
    • 1:35:54I am currently masquerading as John Harvard in Gmail here.
    • 1:35:58Let me go ahead and refresh my inbox, and oh my god, a message from the CS50
    • 1:36:02bot that I am indeed registered.
    • 1:36:05So now we have a programmatic way of sending emails.
    • 1:36:08I hit it twice, so I got two emails this time.
    • 1:36:10But indeed I've now dynamically generated emails as well.
    • 1:36:17A lot.
    • 1:36:18But the ideas really are just based on templates
    • 1:36:22and the idea of sort of putting our controller logic and application dot py
    • 1:36:25and all of the user aesthetics and our so-called views,
    • 1:36:28and now that we've had a database or in some sense an email backend,
    • 1:36:32now we also have the notion of a model where the data is actually being stored
    • 1:36:36or in this case, sent.
    • 1:36:38Any questions or confusion or clarifications
    • 1:36:41here on the use of SQLites or email or any of these Frosh IMs features?
    • 1:36:50All right.
    • 1:36:50So why is there a shopping cart with cookies on the stage?
    • 1:36:54Well for that let's take our final five minute break.
    • 1:36:56We'll come back, talk about something called sessions,
    • 1:36:58and build even more sophisticated web apps.
    • 1:37:00Back in five.
    • 1:37:01All right, we're back, and there's one feature that we haven't really
    • 1:37:04fundamentally touched on at all when it comes to web programming,
    • 1:37:07and it's one that you and I take for granted every day,
    • 1:37:09any time we log into some website, buy something online, or generally interact
    • 1:37:13with dynamic websites.
    • 1:37:15And that's a feature that's generally known as sessions.
    • 1:37:18There is a mechanism built into web applications, whether they're
    • 1:37:22implemented in Flask or in other frameworks or languages that
    • 1:37:26allow the web servers to remember a little something about you.
    • 1:37:29And this is super important to how the web today works,
    • 1:37:31because without it you wouldn't be able to log in to Gmail or to any other site
    • 1:37:35and have the website remember that you are logged in.
    • 1:37:38Consider, after all, when you log in to Gmail,
    • 1:37:40you type your username, your password, maybe your two-factor authentication
    • 1:37:44code, and then you see your inbox.
    • 1:37:46And from there, you're not prompted to log in again the next time you open
    • 1:37:49an email and then the next time you open an email,
    • 1:37:52you stay logged in for some number of minutes or hours or even days
    • 1:37:55on your computer.
    • 1:37:56Even if you close the browser tab, even if you shut down your computer,
    • 1:38:00when you come back to Gmail quite often, you're still logged in,
    • 1:38:04and it remembers in some sense that you have been logged in.
    • 1:38:06Well, how does this actually work?
    • 1:38:08Well underneath the hood, anytime you visit gmail.com,
    • 1:38:11your browser is sending a request like this for slash, the default
    • 1:38:16route of gmail.com, and hopefully you're going to either see your inbox,
    • 1:38:19or if you're not yet logged in, you're going to see that login screen.
    • 1:38:22Indeed, the response is probably going to come back
    • 1:38:24along the lines of 200 OK, content type of text HTML,
    • 1:38:28and that's when you see the login form or your inbox.
    • 1:38:30But recall from last week there's other HTTP headers
    • 1:38:33that can be tucked inside of that virtual envelope that
    • 1:38:36provide hints to the browser and server as to how they should interoperate.
    • 1:38:40That's indeed what HTTP is all about.
    • 1:38:42It's a protocol set of conventions that govern how browsers and servers should
    • 1:38:46interoperate.
    • 1:38:47And it turns out that sometimes this server returns what are called cookies.
    • 1:38:52And odds are you've heard of cookies.
    • 1:38:54Odds are you have some sense that cookies
    • 1:38:55are bad or dangerous but also necessary, and indeed, all of that is true.
    • 1:39:01Cookies are essentially pieces of data or even files
    • 1:39:06that are planted on your computer or your phone
    • 1:39:08by a server that help them remember that you've been there before.
    • 1:39:13In some sense, they help remember who you are, but even websites
    • 1:39:16that you're not logging into very often put cookies onto your phone,
    • 1:39:20or your laptop or desktop which essentially might
    • 1:39:22be a big random number that only you are given so that even if they don't know
    • 1:39:26that it's Maggie who's visited the site, or Ryan, or Nicole, or someone else,
    • 1:39:30they know that unique identifier has been seen before.
    • 1:39:35And they can therefore track your behavior.
    • 1:39:37So if you've heard the term tracking cookies, that's all it is.
    • 1:39:40It's some piece of data, a big random number that's
    • 1:39:43been installed on your browser that your browser then remembers.
    • 1:39:47And the mechanism is as simple as this: one of the HTTP headers
    • 1:39:51that comes from server to browser literally
    • 1:39:53is set dash cookie, colon, then the name of a cookie, for instance session,
    • 1:39:58and then value, and the value might be that big random number.
    • 1:40:01And because of HTTP, again, it's a protocol
    • 1:40:04that browsers and servers speak together,
    • 1:40:06the browsers are designed to send that cookie back to every subsequent page
    • 1:40:12you visit on gmail.com or whatever server you're on.
    • 1:40:15In other words, when you visit gmail.com then an hour from now,
    • 1:40:18even after closing your tabs, or even tomorrow after going to sleep,
    • 1:40:22your browser is not only sending these headers, if you
    • 1:40:25have been given a cookie from gmail.com before, your browser sends
    • 1:40:30a similar header but without the word set, it says cookie, colon,
    • 1:40:34session equals value.
    • 1:40:36So unbeknownst to you, your browser is constantly reminding the server
    • 1:40:41that you've been there before.
    • 1:40:42And this is similar to the real world when you're perhaps in,
    • 1:40:46when you go into a club or a bar or an amusement park
    • 1:40:49and you go through the gates, you typically
    • 1:40:51are allowed back in by having your hand stamped.
    • 1:40:53And this way they don't have to check your tickets again
    • 1:40:56or your ID or the like, they just stamp your hand
    • 1:40:58which is a little reminder that you have been there before.
    • 1:41:02And so in fact, here, I've got a little stamp here.
    • 1:41:04If I visit a website, it's like my hand is being stamped here,
    • 1:41:08and my browser, by design of HTTP is supposed
    • 1:41:12to, every time I visit another page, click another link,
    • 1:41:14represent my hand just like you're going back
    • 1:41:17into the bar or the club or the amusement park
    • 1:41:20to remind the person at the door, the bouncer, that you've been there before
    • 1:41:24and to let you through rather than prompting you to log in again.
    • 1:41:28So that's what a cookie is.
    • 1:41:29It is like this virtual hand stamp that's put on your computer,
    • 1:41:32but your computer is designed to present it again and again.
    • 1:41:36So if you've ever wondered how sometimes you go shopping on one website
    • 1:41:39and then all of a sudden, creepily you're on Facebook or something else,
    • 1:41:42and you're seeing ads for the very thing you didn't buy on this other website,
    • 1:41:46that all derives from cookies.
    • 1:41:47Long story short, there's advertising companies
    • 1:41:50that Facebook and Google and others partner with that
    • 1:41:53put little images on the website or JavaScript files or other such files,
    • 1:41:58and because those ads are on different websites the cookies are shared
    • 1:42:02across multiple websites in some sense.
    • 1:42:04So it's like you are constantly presenting your hand
    • 1:42:06stamp to all these different third party companies,
    • 1:42:09and this is how they're advertising to you or even tracking you at worst.
    • 1:42:14As an aside, when you use incognito mode or private mode on your browser,
    • 1:42:18all of your cookies are temporarily thrown away.
    • 1:42:20You get an empty cookie jar, so to speak,
    • 1:42:22so that you do appear to be logged out.
    • 1:42:24And in fact, even in class, I've used incognito mode
    • 1:42:26a few times because I want to start fresh and not
    • 1:42:28have any of my prior clicks or our prior demos messing up future demos.
    • 1:42:32But the reason I was doing that was just to get rid of all cookies
    • 1:42:35and therefore all states.
    • 1:42:37So on the one hand, cookies are an incredibly important and powerful
    • 1:42:40mechanism, but suffice it to say, and more on this down the road,
    • 1:42:44they can be misused, certainly when it comes to one's privacy or security.
    • 1:42:49So with that said, let's focus on the good part of cookies
    • 1:42:52and consider how a typical website remembers that you're logged in.
    • 1:42:55Let's take that very simple idea and make it clear
    • 1:42:58that it's all within your power now with code to do this.
    • 1:43:00So let's start as follows.
    • 1:43:02I've created a new directory called login
    • 1:43:04just to demonstrate a program that logs people in.
    • 1:43:06I've got my same layout as before, and I've added one more tag.
    • 1:43:09It tends to be best practice to tell the browser that you're
    • 1:43:13using essentially Unicode.
    • 1:43:14This way I can have emojis and other characters in my HTML.
    • 1:43:17So I've added this line five which you'll see on Bootstrap's documentation
    • 1:43:21and other sites, so now I have sort of a good starting point for this page.
    • 1:43:24But there's really no content, there's just a block being defined here,
    • 1:43:27block body.
    • 1:43:28So that's just my same layout as before.
    • 1:43:30And indeed if I type ls you'll see an application
    • 1:43:32dot py and a templates folder.
    • 1:43:34In that templates folder for now is just layout dot HTML.
    • 1:43:39So let's go ahead and start writing a quick application whose purpose in life
    • 1:43:43is just to present the user with a form via which they can log in
    • 1:43:46and then remember that they are logged in.
    • 1:43:48So from Flask let me import Flask.
    • 1:43:51Let me also import the redirect function, the render template function,
    • 1:43:55the request variable, and now a session variable.
    • 1:43:59This is something new.
    • 1:44:00And then from Flask session, let me go ahead
    • 1:44:03and import session, and then down here let me do app gets Flask,
    • 1:44:08underscore, underscore, name.
    • 1:44:10And now I need a few more lines of code from documentation.
    • 1:44:13App dot config, open bracket, session permanent equals false,
    • 1:44:21and again, this is specific only to the library called Flask session
    • 1:44:25that I'm using, and that library is going
    • 1:44:27to enable hand stamps, essentially, for my application.
    • 1:44:30And I'm going to configure it to use literally the file system,
    • 1:44:34so session type equals file system, and file system
    • 1:44:38is just fancy speak for the hard drive or my own IDE account,
    • 1:44:41and this is an alternative to using a database or something
    • 1:44:44else like that, and then session app.
    • 1:44:46So this is just boilerplate, I literally copied
    • 1:44:48these three lines from the documentation for this library.
    • 1:44:52But to use this library, and this was also
    • 1:44:54true of the mail library a moment ago, there's
    • 1:44:56another file I should point out.
    • 1:44:59So when I am here, I should create one other file that I mentioned earlier
    • 1:45:05but we haven't created ourselves, this time called requirements dot text.
    • 1:45:09And I'm going to put this into my login directory,
    • 1:45:11and I'm going to require Flask for this application,
    • 1:45:14and I'm also going to require a library called Flask session.
    • 1:45:19So again, I claimed before on the slide that requirements
    • 1:45:21dot text is just a simple list of the requirements, the libraries you
    • 1:45:24want to use.
    • 1:45:25And there are commands you will use at the command line
    • 1:45:28to install those requirements.
    • 1:45:30You won't typically need to do this for the problem set,
    • 1:45:32but just so you've seen it, if you want to install
    • 1:45:34the requirements for a Python application,
    • 1:45:37you typically do a command like this.
    • 1:45:39Pip install dash r, requirements dot text.
    • 1:45:42And I'm not going to do because I did it in advance,
    • 1:45:45but just know that in order to install libraries, it's much easier in Python
    • 1:45:49than it was, for instance, in C. They tend
    • 1:45:51to be more easily packageable via this technology called
    • 1:45:55pip and things like it.
    • 1:45:56All right, let's go ahead and create a couple of routes.
    • 1:45:58App route slash def index, so just boilerplate as before,
    • 1:46:03and let's just go ahead and simply return render template of index dot
    • 1:46:08HTML by default, and then let's go ahead and create one other route,
    • 1:46:15app dot route slash login, and in my login route, which
    • 1:46:19will be governed by a login function, let me go ahead
    • 1:46:22and return render template login.
    • 1:46:25So let's keep it simple for the moment.
    • 1:46:27And now let me go ahead and implement this form.
    • 1:46:30So let me go ahead and create a file called
    • 1:46:31login dot HTML, whose purpose in life is just going to have my login form,
    • 1:46:36and what I want in that login form is just a space for the user's name.
    • 1:46:39In the real world, I would most certainly
    • 1:46:41do something like a username and a password,
    • 1:46:45but we're going to keep it simple, focus only on the essence of the idea,
    • 1:46:48and do something like just the user's name.
    • 1:46:50So, in login dot HTML, I'm going to extend layout dot HTML as before.
    • 1:46:57I'm going to have my block body, and I'm going to have my end block tag,
    • 1:47:01and then inside of here I'm going to have a form, the action for which
    • 1:47:05will be slash login, the method for which will be post.
    • 1:47:08I'll do best practices here, I don't want my username and password
    • 1:47:11in the real world being stored in people's autocomplete histories.
    • 1:47:16Let me do input, autocomplete equals off for good measure, autofocus
    • 1:47:20for convenience, name equals the user's name,
    • 1:47:23placeholder as before equals name, and then type equals text.
    • 1:47:28And then down here, input type equals submit.
    • 1:47:30So this is almost the same to our previous forms for registering
    • 1:47:33and so forth, but now I'm going to use it for login purposes.
    • 1:47:36So let's do a quick check.
    • 1:47:37Let me go back to my application, do Flask run,
    • 1:47:41I'm going to go ahead and open up my URL, and first error, let's see,
    • 1:47:46template not found.
    • 1:47:47OK, so that's actually expected.
    • 1:47:49I kind of knew I didn't create index dot HTML,
    • 1:47:51so I'm OK with that error for now.
    • 1:47:53Let me go ahead and zoom out though, because I deliberately
    • 1:47:55wanted to go to my slash login route because I wanted to test that first.
    • 1:47:59That seems to be working.
    • 1:48:00Now let's go ahead and create the file that doesn't exist,
    • 1:48:03index dot HTML, so index dot HTML, put this in my login folder in templates,
    • 1:48:08and I'm going to do this one pretty similarly.
    • 1:48:11I'm going to go ahead and copy paste just to save a bit of keystrokes,
    • 1:48:14but here I'm going to say, you are not logged in because I literally
    • 1:48:17haven't implemented that feature yet.
    • 1:48:18So that's pretty reasonable.
    • 1:48:20Let me go ahead and restart Flask.
    • 1:48:22Let me go to my index, and I should see, indeed, you are not logged in.
    • 1:48:26But if I go to login, I see my login form.
    • 1:48:29So let's make this a little more interactive.
    • 1:48:31Let me go into index dot HTML here and say,
    • 1:48:34you are not logged in, a href slash login, log in, period.
    • 1:48:40Let's create a better user interface.
    • 1:48:42So let me rerun Flask, let me go back to my web page, hit reload,
    • 1:48:47and now I have the beginnings of user interface,
    • 1:48:49super simple, but very similar to a website
    • 1:48:51where if you're presented with the web page, you're not logged in,
    • 1:48:53you're given a link or a button or something to log in.
    • 1:48:55And if I click this, it's just a simple get
    • 1:48:58request, which is the default for links, to slash login.
    • 1:49:02So we're almost there.
    • 1:49:04We almost have the ability to log in.
    • 1:49:05Let's do something with the information.
    • 1:49:07So let me go back to application dot py, and when the user logs in,
    • 1:49:11what is it I want to have happen exactly?
    • 1:49:14Well if they log in, first of all, I want to support multiple,
    • 1:49:18I want to support post here.
    • 1:49:19So let me do that for privacy's sake.
    • 1:49:22And then let me go ahead and do this.
    • 1:49:26If request dot method equals post, as before,
    • 1:49:31let's go ahead and remember that user logged in.
    • 1:49:36And then redirect user to slash.
    • 1:49:42Else let's go ahead and render the login form.
    • 1:49:44So I'm not done yet.
    • 1:49:46But this is the key idea.
    • 1:49:48I need to somehow remember that the user logged in.
    • 1:49:50So how can I do this?
    • 1:49:51Well, one, I can get the user's name from request dot form dot get name.
    • 1:49:58And I should assume that, yeah, that's OK, we won't validate that for now.
    • 1:50:04And what do I want to do with the user's name?
    • 1:50:07I want to store it in the session.
    • 1:50:09So what exactly is the session?
    • 1:50:10Well it turns out that as soon as you have this hand stamp, it's like,
    • 1:50:17it's kind of like a coat check too.
    • 1:50:18I don't mean to add too many metaphors here,
    • 1:50:20but in a fancy restaurant if you like hand your coat to someone,
    • 1:50:22they'll often put it in the closet and then
    • 1:50:24hand you back a number, which in some sense
    • 1:50:25is a little clue like this, a little hand stamp
    • 1:50:27so that they can line you up.
    • 1:50:29It probably shouldn't just be a smiley face because otherwise everyone
    • 1:50:31would get the same coat back, but they give you some kind of identifier,
    • 1:50:34and they put your coat into some hanger, a bucket, or some space in that closet.
    • 1:50:39So why is this relevant here?
    • 1:50:42Well the same is happening in the world of web browsers.
    • 1:50:44When you get a unique cookie, that's giving you a unique hanger.
    • 1:50:48I really should have thought this metaphor through before,
    • 1:50:50but you're given a unique hanger where all of your stuff can go,
    • 1:50:54including your coat, but in this case, including any variables
    • 1:50:56you want to remember.
    • 1:50:57So with that said, what I have access to thanks to Flask,
    • 1:51:02but really thanks to HTTP, is this special variable called session.
    • 1:51:08That is a special variable in that it's at the one hand,
    • 1:51:11on the one hand global, which means I can use it everywhere,
    • 1:51:15but fancy enough, it's tied to the current user.
    • 1:51:18So even if all of us on Zoom right now visit my URL,
    • 1:51:22as is kind of happening anyway, each of us
    • 1:51:24is getting our own copy of a variable called session.
    • 1:51:28And it's implemented underneath the hood by way of this cookie mechanism.
    • 1:51:31Each of us when you visit my application are getting a different cookie value.
    • 1:51:34That cookie value is like mapping to a different hanger in the closet
    • 1:51:38so that my data will go on this hanger, your data will go on that hanger,
    • 1:51:41and so forth.
    • 1:51:42And now I'm actually kind of proud of this metaphor that's working out OK.
    • 1:51:45The hanger is going to be where all of our individual data is stored.
    • 1:51:48What do we want to store?
    • 1:51:49This is simple, let's just store the user's name,
    • 1:51:53thereby remembering that I've logged in, or Brian's logged in,
    • 1:51:55or Sophia's logged in, or Ryan's logged in, or anyone else.
    • 1:51:59But the session global variable here that I've
    • 1:52:02highlighted in this very first line is going to be implemented by Flask for us
    • 1:52:07in such a way that each of us as users get our own version thereof.
    • 1:52:11So I'm going to quite simply use this as a dictionary in Python.
    • 1:52:15I'm going to go ahead and add to the session here the user's name as name.
    • 1:52:22And I don't even need this variable now.
    • 1:52:24Let me tighten this up just a little bit.
    • 1:52:26Let me go ahead and just store in the session, which
    • 1:52:28is like this hanger, the key of name and the value of whatever the user logged
    • 1:52:33in as.
    • 1:52:34And redirects I know how to do.
    • 1:52:35Let me go ahead and return redirect slash, let me get rid of my pseudocode,
    • 1:52:42and I think that's it.
    • 1:52:44That is sufficient, excuse me, to remember that a user has logged in.
    • 1:52:48You sort of put their name in the session,
    • 1:52:50but then it stands to reason that we need to check for this value.
    • 1:52:54So now let's improve index dot HTML, which at the moment
    • 1:52:56just blindly says always, you are not logged in.
    • 1:52:59Well let's actually make that decision, not just hard code that.
    • 1:53:02So let's do this.
    • 1:53:03If not session dot get name, this is the syntax
    • 1:53:09for saying if there's no name in the session,
    • 1:53:12then go ahead and return redirect slash login.
    • 1:53:16So if there's no name in the session, that
    • 1:53:18means you haven't given me your name yet.
    • 1:53:20Let me force you to by sending you to slash login.
    • 1:53:23Otherwise go ahead and render the template.
    • 1:53:26All right, well what more might I do now?
    • 1:53:28Well let me go into my index file here.
    • 1:53:31And instead of just naively hard coding this, watch what I can now do.
    • 1:53:34I can in here say, in Jinja syntax, not the curly braces, not the block syntax,
    • 1:53:41not the for loop, you can also use if conditions.
    • 1:53:44If session dot name exists, then I can say something like,
    • 1:53:48you are logged in as session dot name, period.
    • 1:53:53Then I might have something like a href equals slash logout.
    • 1:53:56Doesn't exist yet, but we can come back to that, log out.
    • 1:54:00Else if there is no session name, then let's assume
    • 1:54:04that the user is indeed not logged in and then say, you are not logged in,
    • 1:54:08log in.
    • 1:54:10So if I did this right, let me go ahead and stop the server here.
    • 1:54:13Let me go ahead and rerun Flask run, reload my form,
    • 1:54:16I still see, OK, wrong URL.
    • 1:54:19Let me go to index.
    • 1:54:22Oh no, this is right.
    • 1:54:23I'm on slash login, method's not allowed,
    • 1:54:25I think Santiago already pointed out what mistake I made earlier,
    • 1:54:28which I just repeated.
    • 1:54:29Let me go to application dot py.
    • 1:54:34I need to support multiple methods here.
    • 1:54:36I also want to support get so that I can actually render my template as needed.
    • 1:54:41So let me go ahead and rerun this server.
    • 1:54:43Here we go it's slash login.
    • 1:54:45And notice what happens.
    • 1:54:46Let me zoom in on this.
    • 1:54:47Let me get rid of the login and just go to slash,
    • 1:54:50notice that I'm immediately redirected.
    • 1:54:52Let's do what we did last week.
    • 1:54:53Let's open up the inspector.
    • 1:54:54Let's go to the Network tab.
    • 1:54:56Let's again visit slash and hit Enter, voila, notice what happens.
    • 1:55:00If I visit that request, zoom in on Chrome's network tab,
    • 1:55:04scroll down to response headers, notice that we get a 302, found,
    • 1:55:08which is a redirect code like 301, and the location is actually,
    • 1:55:12if you look over here, slash login.
    • 1:55:14So all the mechanics of HTTP are coming back into play.
    • 1:55:18All right let me go ahead and log in as David, click Submit, amazing.
    • 1:55:22You are logged in as David.
    • 1:55:23Notice what's going on.
    • 1:55:25I'm now at the slash route.
    • 1:55:28This is true, so this does not apply, so I'm actually seeing index dot HTML.
    • 1:55:34And notice I accidentally clicked log out, let's fix that.
    • 1:55:37Let's give us our logout route.
    • 1:55:39So here we go.
    • 1:55:40App dot route slash logout, def log out, and then in here I
    • 1:55:48need to just forget that the user has logged in.
    • 1:55:50And there's a few different ways to do this.
    • 1:55:52Let me do it the simplest for now.
    • 1:55:53Session name equals none.
    • 1:55:56Let's just change whatever your name is or my name is to none,
    • 1:55:59then let's return redirect back to slash.
    • 1:56:05So let's see what happens now.
    • 1:56:06Let me restart the server.
    • 1:56:08Let me go back to my index page.
    • 1:56:11Because I'm still logged in here, I'm going to go ahead and log out.
    • 1:56:15OK, let me go ahead and log in as Brian, for instance, submit.
    • 1:56:18You are logged in as Brian.
    • 1:56:19And notice I can reload, I can close the tab, I can reopen the tab.
    • 1:56:23It's all being remembered thanks to my browser representing the cookie.
    • 1:56:27And in fact, you can see that.
    • 1:56:29Let me go to inspect, let me go to the Network tab,
    • 1:56:32and let me reload this same page.
    • 1:56:34Reload.
    • 1:56:35Notice this response here.
    • 1:56:37In 200, notice that if I scroll down here to the request headers, notice
    • 1:56:43that my browser is requesting, oops, sorry,
    • 1:56:46notice that my browser is requesting slash, but notice if I scroll down,
    • 1:56:50down, down, nope, there we go, down, down, down, notice, unbeknownst to me
    • 1:56:55and unbeknownst to you perhaps all this time,
    • 1:56:57my browser is also presenting the hand stamp and saying my cookie value
    • 1:57:01is session equals 35456 dot, dot, dot.
    • 1:57:04Generally speaking it's bad to tell other people your sessions because now
    • 1:57:07you could all hijack my session by pretending to be me,
    • 1:57:10but of course this application doesn't do anything.
    • 1:57:12But don't do this with accounts that you care about.
    • 1:57:14No one should be seeing your cookie values.
    • 1:57:17So we actually see the cookie there, and it came from the server
    • 1:57:20the very first time I visited.
    • 1:57:24All right, any questions on that there?
    • 1:57:29BRIAN: Does the cookie just exist forever,
    • 1:57:31or is there a time when it disappears?
    • 1:57:33DAVID J. MALAN: Good question.
    • 1:57:34Does the cookie live forever or is there a time limit?
    • 1:57:37It depends on how you configure it.
    • 1:57:38So this cookie will have a default time limit
    • 1:57:41of some number of minutes or hours or days, after which it will expire
    • 1:57:45unless the server refreshes it for me.
    • 1:57:47You can, though, set cookies that do last forever,
    • 1:57:49which you probably don't want to do for the notion of logging in because odds
    • 1:57:53are all of us have accidentally walked away
    • 1:57:54from like a lab computer or a friend's computer, forgotten to log out.
    • 1:57:58It's at least nice to know that eventually I'll
    • 1:58:00be logged out automatically from this site,
    • 1:58:03and that's what's called a session cookie, one that eventually expires.
    • 1:58:06But it's also useful when you visit websites,
    • 1:58:08you notice that some websites have those buttons that say remember me,
    • 1:58:11or something like that.
    • 1:58:12Well you can actually set permanent cookies that don't just
    • 1:58:15store some random value on your computer,
    • 1:58:17they actually store some piece of information
    • 1:58:20that gets prefilled into forms subsequently.
    • 1:58:23And this is useful to remember your preferred settings for a website
    • 1:58:26if you've customized something, if you're
    • 1:58:27in night mode or day mode or something like that,
    • 1:58:30it can remember those kinds of settings.
    • 1:58:32And even CS50's website remembers what your selection was.
    • 1:58:36When you log in to some of CS50's websites,
    • 1:58:38you'll sometimes see a login prompt for Harvard or Yale.
    • 1:58:41And if you've never noticed, I regret this because we put a lot of effort
    • 1:58:45into this, it remembers if you used Harvard last time
    • 1:58:48or used Yale last time so you don't have to constantly interact
    • 1:58:51with the dropdown, it just remembers your implied preference.
    • 1:58:54We do that using cookies.
    • 1:58:55And so as an at home exercise if of interest, truly do start
    • 1:58:58opening the browser's network tab, start looking at those headers,
    • 1:59:02and you can begin to infer how all of today's websites work.
    • 1:59:06And in fact it's becoming increasingly a challenge
    • 1:59:09to implement things with cookies because long story short, a lot of the browser
    • 1:59:12manufacturers are finally starting to disable what are called third party
    • 1:59:15cookies, those sort of advertising style cookies that come from other servers,
    • 1:59:20not gmail.com, not Facebook.com, but other servers that are all too often
    • 1:59:24used to track people.
    • 1:59:27But recently, especially in the EU and now more recently in the US,
    • 1:59:30if you've ever had to click boxes saying I accept cookies,
    • 1:59:33I accept the necessary cookies, and so forth, honestly this
    • 1:59:36is sort of a nice idea, but it's also a little silly in
    • 1:59:39that now the world is just conditioning you and me to just constantly click
    • 1:59:43these boxes without really thinking of it.
    • 1:59:45But what you're really doing is granting permission
    • 1:59:48when you click those boxes to allow your hand to be stamped.
    • 1:59:53All right, any questions then on cookies,
    • 1:59:55or on how we've gone about implementing this login mechanism?
    • 2:00:01Seeing anything on your end Brian?
    • 2:00:03BRIAN: Nothing else here.
    • 2:00:04DAVID J. MALAN: All right, well let me go ahead and do this.
    • 2:00:07Let me go ahead and get ready an example that we
    • 2:00:09made in advance that will allow us to explore
    • 2:00:13a full-fledged e-commerce website, a store of some sort.
    • 2:00:16And we'll call this literally Store and this time
    • 2:00:19rather than write things from scratch, let me go ahead
    • 2:00:21and open things in my browser from a directory called store.
    • 2:00:24So this is available as always on the course's website.
    • 2:00:27And let me go ahead here and into store, let me go ahead
    • 2:00:31and open up application dot py, requirements
    • 2:00:34dot text, and my templates, including layout, books, and cart.
    • 2:00:40So this is a good exercise in honestly reading
    • 2:00:43someone else's sizable amount of code.
    • 2:00:45It's a few files.
    • 2:00:46How do you even begin when you've downloaded a program like this,
    • 2:00:49when you've downloaded distribution code for CS50 or any other course,
    • 2:00:52how do you begin to wrap your mind around it?
    • 2:00:54Well again, if you understand what the roles are
    • 2:00:56played by these different files, application dot py is your controller,
    • 2:00:59anything ending in dot HTML and templates is a view,
    • 2:01:02you can begin to sort of follow the breadcrumbs
    • 2:01:04and figure out what does what in these files.
    • 2:01:07So let's start with application dot py.
    • 2:01:09Notice I'm importing CS50's library, a bunch of Flask functionality,
    • 2:01:13including session support.
    • 2:01:14Below that I'm initializing the application as I've done before,
    • 2:01:17and below that I'm connecting to a database which
    • 2:01:20contains a whole bunch of products in a store, books in this case.
    • 2:01:24I then configure sessions, and we just did that,
    • 2:01:26so that code is the same as before.
    • 2:01:28And I think we can begin to infer what this website's going to do.
    • 2:01:31So my default slash route has an index function,
    • 2:01:34and it looks like there's a table in that database called books,
    • 2:01:38so I'm selecting star from books to get me all the books.
    • 2:01:41And then it looks like I'm rendering a template called books dot HTML,
    • 2:01:44and I'm passing in those books.
    • 2:01:45So let's follow the breadcrumbs here and go into books dot HTML.
    • 2:01:50It looks like books dot HTML extends layout dot HTML.
    • 2:01:54All right, let's look at layout dot HTML.
    • 2:01:56Nothing really interesting going on there, just the boilerplate placeholder
    • 2:01:59code that we've seen before.
    • 2:02:01So I'm going to close that and assume that it's not interesting.
    • 2:02:03Been there, done that.
    • 2:02:04But books dot HTML, if we continue further, is a little juicy.
    • 2:02:07We've got my block body between lines 3 and 14.
    • 2:02:12In there I've got a H1 tag just saying books at the top of the page.
    • 2:02:16And then I've got a for loop in my template,
    • 2:02:19again using Jinja syntax, that has below that an H2 with apparently
    • 2:02:24the title of a book, and then below that, a form,
    • 2:02:28and I haven't seen all of these features before,
    • 2:02:30like I'm not sure yet what hidden means, but notice that I do recognize a Submit
    • 2:02:34button that has a value of add to cart.
    • 2:02:37So it sounds like we're implementing a shopping catalog for like an amazon.com
    • 2:02:41or some kind of bookstore online.
    • 2:02:43All right, so now let's go back to application dot py,
    • 2:02:46and let's look in cart.
    • 2:02:47It looks like cart supports both get and post, all right,
    • 2:02:50so maybe I'm using the same route to handle two different things,
    • 2:02:53and we've seen that before.
    • 2:02:54The function is called cart, which makes sense.
    • 2:02:57This thing, let's come back to this, just to initialize our session,
    • 2:03:00we'll come back to that.
    • 2:03:01And it looks like there's not much more to this program.
    • 2:03:03If get is the verb, let's focus on the bottom
    • 2:03:07first, so let's ignore the if condition, what am I doing?
    • 2:03:10Selecting star from books where the ID of the book is in this list of books.
    • 2:03:17Well, where is this list of books coming from?
    • 2:03:19Session, quote unquote, cart.
    • 2:03:21So in the last example I used the session to store your name and my name
    • 2:03:26and everyone's name on a per user basis.
    • 2:03:29Now I'm using it to store what we'll call a shopping cart.
    • 2:03:31And indeed, this is the reason why there's a shopping cart,
    • 2:03:34thanks to our friends at the theater, on stage.
    • 2:03:36This is how shopping carts are implemented
    • 2:03:38on websites, via cookies underneath the hood
    • 2:03:41to give you the illusion of user specific variables, dictionaries
    • 2:03:46that you can store anything you want in there.
    • 2:03:48So it looks like my session cart key is going
    • 2:03:52to have a value equal to the IDs of a whole bunch of books.
    • 2:03:55I don't know how they're there yet, but let's assume
    • 2:03:57that's what I'm interpreting.
    • 2:03:59And then down here, I'm just rendering a template called cart dot HTML,
    • 2:04:02passing in the books from the database.
    • 2:04:06So how do they get into the session?
    • 2:04:07Well, if the user submits the form to the cart,
    • 2:04:10I think this code is now relevant.
    • 2:04:12If the user posts to slash cart, it looks
    • 2:04:16like I'm going to get the ID that they've typed in maybe?
    • 2:04:20Maybe?
    • 2:04:21And if there is in fact an ID, and it's not none,
    • 2:04:24it's not the default value of none, I'm going
    • 2:04:26to go ahead and append to the cart the ID of that book,
    • 2:04:31and then I'm going to redirect the user to cart.
    • 2:04:33So I think that's enough complexity that I think it's time
    • 2:04:36to look at what's actually going on.
    • 2:04:37So let me go ahead and do exactly that.
    • 2:04:40Let me scroll up and run Flask run.
    • 2:04:42Let me go ahead and open my URL.
    • 2:04:44OK, it's not the prettiest of website, but it
    • 2:04:47looks like my database has like all seven Harry Potter books in it,
    • 2:04:50and notice that each of them has a button, add to cart, add to cart,
    • 2:04:54add to cart.
    • 2:04:54Let's look at the HTML source to see what
    • 2:04:57my books dot HTML template generated.
    • 2:04:59Kind of interesting.
    • 2:05:00So there's that H1 books at the top.
    • 2:05:03Here's the first of my H2s with the title of the book.
    • 2:05:06Notice that there is this funky symbol here because it's like a curly quote,
    • 2:05:10so this is one of those HTML entities that lets you output symbols
    • 2:05:13that you couldn't type easily on your keyboard.
    • 2:05:15Here's my first form that I dynamically outputted.
    • 2:05:17And I claimed earlier that I didn't know what hidden was,
    • 2:05:20but it does what it says.
    • 2:05:21It allows you to submit a piece of data in a form that
    • 2:05:25is visually hidden from the user.
    • 2:05:26It's not a text box that they can type into but it is there.
    • 2:05:29And what's cool about this is that notice I gave it a value of 1.
    • 2:05:33But in this form I gave that hidden field
    • 2:05:35a value of 2, down here a value of 3.
    • 2:05:38Well, why is that?
    • 2:05:40Let me go into my IDE for just a moment, let me run SQLite on my store dot db,
    • 2:05:47let me show you with dot schema that inside of this database
    • 2:05:51is a books table, each of which has an ID, a title, and that's it.
    • 2:05:55Let me do select star from books using SQLite 3, and voila,
    • 2:06:00you see not only the titles of the books,
    • 2:06:02but a unique ID numbered aptly according to the order in which they came out,
    • 2:06:07as a coincidence, but intentional.
    • 2:06:09And so now down here, let me go ahead and move my terminal down a little bit
    • 2:06:15here.
    • 2:06:16Let me go ahead and rerun Flask run, and if I now submit one of these forms,
    • 2:06:21let's see what happens.
    • 2:06:22Add to cart the first book.
    • 2:06:25Interesting.
    • 2:06:25I've been redirected to slash cart, and notice I have an ordered list,
    • 2:06:29an OL tag, it would seem, with that first book.
    • 2:06:32Let me go back to the catalog.
    • 2:06:34Prisoner of Azkaban was pretty good.
    • 2:06:35Let's add that to cart.
    • 2:06:36Now I see this here.
    • 2:06:37And notice this, if I reload, reload, I don't
    • 2:06:40know why I'm holding up my hand because I'm clearly using my other hand,
    • 2:06:43but I'm reloading the page again and again,
    • 2:06:45and it's showing me exactly what is in my cart.
    • 2:06:48So where is this coming from?
    • 2:06:49Well, if we go back to our code, this logic here under the post condition
    • 2:06:55is adding the ID, book number one, book number three, to my session's cart key.
    • 2:07:02Now where did that cart key come from?
    • 2:07:04This is the only other piece I proposed we come back to.
    • 2:07:06I just need some way at the top of my code to initialize the cart.
    • 2:07:09And long story short, there's not going to be a key called cart in the session
    • 2:07:13unless you put it there, like Flask doesn't come with name or cart.
    • 2:07:17You have to put it there.
    • 2:07:18So notice if cart, quote unquote, is not yet in my session,
    • 2:07:22I'm just initializing it to an empty Python list.
    • 2:07:26And I could use a set, I could use a dictionary,
    • 2:07:28I could use anything I want.
    • 2:07:29I chose to keep it simple with a list, but that's
    • 2:07:31how I'm remembering what's in my cart.
    • 2:07:33And unlike all of our previous examples, because I see a bunch of you
    • 2:07:36are now trying to add things to your own cart, each of us
    • 2:07:39is seeing different shopping carts.
    • 2:07:41We all have different things in our shopping cart.
    • 2:07:43If I keep reloading, doesn't matter how many of you are trying to add books
    • 2:07:46to your cart, you're only adding them literally to your cart
    • 2:07:50and not mine because we all have different stamps on our hand.
    • 2:07:55All right, so here too I would encourage you when you, next time you
    • 2:07:58go on Amazon and add some things to your cart,
    • 2:08:00like literally, that is all that's happening underneath the hood.
    • 2:08:03And Amazon surely has other features, you
    • 2:08:05can change quantities, which is kind of nice, you can add things to wish lists,
    • 2:08:08which is nice, but all of that, if you start looking at the HTML,
    • 2:08:11and you look at the network request going back and forth, all of those
    • 2:08:14features that we just take for granted these days are just
    • 2:08:17built on very simple, well, relatively simple HTML
    • 2:08:21forms and these inputs and these buttons and on the server, these sessions.
    • 2:08:24And Amazon might be using a different language, a different framework,
    • 2:08:27but all of these ideas are the same.
    • 2:08:29And so indeed this week is not about teaching you Flask which in a few years
    • 2:08:32time will probably be irrelevant.
    • 2:08:34It's about teaching these ideas and principles of these HTTP requests
    • 2:08:37and responses, these cookies, these sessions, and the features
    • 2:08:40that we can therefore build on top of them.
    • 2:08:43All right, I think we have time for one final example that
    • 2:08:47will now bring back our old friend JavaScript from last week.
    • 2:08:50Up until now, we have been using Python on the server
    • 2:08:55to generate dynamically HTML, and even though my designs today
    • 2:08:59have been hideous, you could certainly imagine adding CSS to them
    • 2:09:02some, classes, some Bootstrap, just a pretty things up like
    • 2:09:04the forms especially, but that's not anything new versus last week.
    • 2:09:09But last week we did have JavaScript as a programming language,
    • 2:09:12but it was a client side programming language,
    • 2:09:14at least in our usage thereof.
    • 2:09:16It was a way of getting the user's browsers
    • 2:09:18to run code, conditions, and functions, and loops, and so forth on the browser
    • 2:09:22not on the server.
    • 2:09:23Today, we've introduced Python again, so we have now the ability
    • 2:09:26to do something on the server, and where the web gets
    • 2:09:29really interesting these days is when we marry
    • 2:09:32the so-called front end that the user sees
    • 2:09:34and the backend which is the server.
    • 2:09:37The front end is the user interface you and I interact
    • 2:09:39with, the backend is the Python code, the SQL code
    • 2:09:42that the humans never see but you, the programmer, see.
    • 2:09:45So what can we do to combine these two components, ultimately,
    • 2:09:52front end and backend?
    • 2:09:53Well let's do something like this.
    • 2:09:55Let's bring back our old database of shows, and let me go ahead
    • 2:10:00and show you quickly what's in this folder here.
    • 2:10:03In shows we have SQLite 3 shows dot db, a smaller version
    • 2:10:08of what we played with a few weeks back.
    • 2:10:10I've whittled this down just for size's sake
    • 2:10:12to have only the names, only the IDs of TV shows and the titles of TV shows
    • 2:10:17and that's it.
    • 2:10:17I threw away the ratings and the writers and the directors and all of that
    • 2:10:20just to keep the focus only on the titles
    • 2:10:22because now we're going to start sending a lot of data back and forth
    • 2:10:25over the internet using Python and JavaScript.
    • 2:10:28Well let me go ahead now and open up this example as made in advance.
    • 2:10:32And this is in shows.
    • 2:10:34And we'll see a few files here, application
    • 2:10:35dot py, requirements dot text, and my templates,
    • 2:10:39which are index and layout and search.
    • 2:10:42All right, so a bunch but not more than we've really seen before.
    • 2:10:46And let's go ahead and, I kind of cheated earlier.
    • 2:10:48I didn't look at requirements.
    • 2:10:49But I might as well look at requirements for good measure.
    • 2:10:51Here's just an example of what else might be in requirements dot text:
    • 2:10:54CS50 library and Flask.
    • 2:10:56And again, those are preinstalled in the IDE,
    • 2:10:58but we do this so that if you get adventurous and try
    • 2:11:00running these things on your own Mac or PC, you have all of the configuration
    • 2:11:03that you need.
    • 2:11:04Well let's start as before with applications dot py,
    • 2:11:07and let me go ahead and do this first.
    • 2:11:09Let me run this thing first so we can actually see the app in motion
    • 2:11:13and then understand how it works.
    • 2:11:16So if I visit this, let me go ahead and type in office.
    • 2:11:19All right, nothing happens yet.
    • 2:11:20Let me click Search.
    • 2:11:22OK.
    • 2:11:23I then see a big bulleted list of all shows that mention office, so not
    • 2:11:27The Office per se, but an office.
    • 2:11:30And let me go ahead and zoom out here and look at the URLs.
    • 2:11:32Let me go back here.
    • 2:11:33Let me point out that we're at the slash route.
    • 2:11:35Again, the slash is there, it's just Chrome
    • 2:11:37is hiding it by default these days.
    • 2:11:39And when I search for something like office and hit Enter,
    • 2:11:42notice that I end up at slash search, question mark, q equals office.
    • 2:11:46So I borrowed some inspiration from Google, right?
    • 2:11:48I kind of cheated last week where I really only
    • 2:11:51implemented the front end to Google, the HTML form,
    • 2:11:53and then I let Google find me some cats and some dogs and so forth.
    • 2:11:57But now as, with Python, you now have the ability
    • 2:12:01to implement both sides of that application.
    • 2:12:04So if I look at the source code here, notice
    • 2:12:06that I seem to be returning a really big unordered list of list items that
    • 2:12:10represent, again, all the titles of office related shows.
    • 2:12:14All right, this is fine and good, but this
    • 2:12:16is of like old school web programming where you fill out
    • 2:12:19a form, you hit Enter, and boom.
    • 2:12:21But these days recall most any time you visit a website,
    • 2:12:23lots is happening at once.
    • 2:12:25You've got some autocomplete going on here,
    • 2:12:26you've got chat messages popping up here,
    • 2:12:29you've got a map over here that's automatically moving
    • 2:12:31and a little car for Uber is moving around at the same time.
    • 2:12:34Like the web of today, web 2.0 if you will,
    • 2:12:37which is not a technical term as much as it is a buzzword,
    • 2:12:40refers to just increasingly dynamic web interactions
    • 2:12:44between users and computers.
    • 2:12:46And with JavaScript and with your own backend,
    • 2:12:48be it Python or something else, you can now
    • 2:12:50begin to build these more dynamic interfaces as well.
    • 2:12:53Wouldn't it be cool if when I start typing O F F I C E,
    • 2:12:58we actually immediately saw the results.
    • 2:13:00And we've seen this already.
    • 2:13:01Last week, remember that I started searching
    • 2:13:03for all words that start with A, and I used a really large dictionary
    • 2:13:07from p set 5, but that was 140,000 words but just words.
    • 2:13:11This database of titles is several megabytes large.
    • 2:13:15And I don't necessarily want to send the entire database
    • 2:13:18of movies to the browser, one, because of performance and size,
    • 2:13:21and maybe two, intellectual property.
    • 2:13:23I don't really want to send every user my entire database.
    • 2:13:26You can imagine wanting to keep a little more control over the data you're
    • 2:13:29providing, requiring users to log in or pay for your service or the like.
    • 2:13:33So there's reasons where you might want the browser to talk to the server
    • 2:13:36and only get the data the user is searching for, not the whole thing.
    • 2:13:40So let's do that.
    • 2:13:41Let's enhance this show's application in such a way
    • 2:13:45that it actually does things more dynamically,
    • 2:13:47but let's first understand how it works.
    • 2:13:49So I have a couple of libraries here, CS50's and Flask.
    • 2:13:52I'm configuring Flask here.
    • 2:13:54I'm then configuring my database here.
    • 2:13:57Down here, I just have a very simple route
    • 2:13:59that renders index dot HTML, which apparently, let's take a look,
    • 2:14:03has that form, very simple, very similar to last week,
    • 2:14:07very similar to today, all of the forms we've done thus far.
    • 2:14:10Here's that name equals q, and everything else is pretty boilerplate.
    • 2:14:14So let's get rid of that file now.
    • 2:14:15The layout, also pretty boilerplate, it looks like things in the past.
    • 2:14:19So let's close that one as well and go back to index, application dot py.
    • 2:14:23So the juicy part seems to be the search route.
    • 2:14:25And this was the piece we were missing last week when we used Google instead
    • 2:14:29to find us some cats and some dogs.
    • 2:14:32Here I have a route called slash search, a function
    • 2:14:35called search that will be called for that route,
    • 2:14:37and here is kind of a nice amalgam of SQL and Python.
    • 2:14:41I'm going to give myself a variable called
    • 2:14:43shows, which is going to be the result of all of the rows
    • 2:14:45that come back when I execute this.
    • 2:14:47Select star from shows where title is like this placeholder question mark.
    • 2:14:54Now what am I going to plug-in?
    • 2:14:55Notice it's a little cryptic at first glance,
    • 2:14:58but I seem to be taking the user's input, q, from request
    • 2:15:02dot args dot get, so that's using get apparently because of the word args,
    • 2:15:06and it's not form now, it's request dot args.
    • 2:15:08So I'm getting the user's q value, which was office in my case, and just
    • 2:15:12as a quick check, what's with the percent
    • 2:15:14signs on the left and the right?
    • 2:15:17Because we've seen a lot of percent signs today, but these are different.
    • 2:15:21Perhaps in the chat?
    • 2:15:22What do these percent signs represent in this context?
    • 2:15:26Brian, you want to echo the consensus?
    • 2:15:28BRIAN: Yeah, people are saying they're SQL placeholders.
    • 2:15:30DAVID J. MALAN: Yeah, so SQL wildcard placeholders.
    • 2:15:33That means zero or more characters to the left, zero or more to the right.
    • 2:15:36This is why I was getting matches with office at the beginning of the word,
    • 2:15:40office at the end, and office in the middle.
    • 2:15:42If I really wanted to be anal I could get rid of the percent signs
    • 2:15:45and require only a perfect match, but that's not my goal
    • 2:15:48with this particular implementation.
    • 2:15:50Once I get back all of, whoops, once I get back all of these rows
    • 2:15:54I have here a line, return render template, search dot HTML,
    • 2:15:58passing in all of those shows.
    • 2:16:00So the last thing to look at is search dot HTML.
    • 2:16:02It's pretty simple, and surely like an hour ago
    • 2:16:04this would have looked super cryptic.
    • 2:16:06It frankly might very well still look cryptic.
    • 2:16:08But once you start using this syntax yourself
    • 2:16:10you'll see that, oh, this is just a template that
    • 2:16:13is allowing me to dynamically output an unordered list of LI tags, each
    • 2:16:19of which represents the title of a show that's been passed into this template.
    • 2:16:26All right.
    • 2:16:27So now then the final flourish.
    • 2:16:29Let's go about enhancing this application so that there is no form
    • 2:16:34to submit in the same way, but instead all of the logic
    • 2:16:37actually happens between JavaScript and the backend.
    • 2:16:41I'm going to go ahead and do this first.
    • 2:16:43I'm going to go back to application dot py here, and instead of this here,
    • 2:16:47I'm going to go ahead and do the following: I'm going to go ahead
    • 2:16:50and instead of rendering a template, I'm going
    • 2:16:53to use a new function called jsonify.
    • 2:16:55It's kind of a funny name, and I have to import it up here.
    • 2:16:59So let me import jsonify.
    • 2:17:01It turns out that jsonify, or json, means JavaScript Object Notation.
    • 2:17:06And we'll see what it looks like in a moment.
    • 2:17:08But the world years ago standardized on a format
    • 2:17:11for transmitting data between servers and browsers
    • 2:17:14using a very lightweight text format, Jason, or json, JavaScript Object
    • 2:17:19Notation.
    • 2:17:20Jsonify is a Flask function that takes a Python list or a Python dictionary
    • 2:17:26and converts it into json format.
    • 2:17:29So we'll see this in just a moment.
    • 2:17:30So notice that I'm now returning the jsonification of the show's
    • 2:17:35variable, which recall is just the rows that came back from db dot execute.
    • 2:17:41So let's see this in action.
    • 2:17:42I'm actually going to go to my, let me go ahead
    • 2:17:44and restart the server because I've made some changes.
    • 2:17:47Let me go ahead and now go to the server slash search, question mark,
    • 2:17:52q equals office.
    • 2:17:53So I'm going to manually visit this URL and hit Enter.
    • 2:17:56Notice what comes back.
    • 2:17:58It's kind of cryptic at first glance here.
    • 2:18:00But notice this is indeed what's called JavaScript Object Notation.
    • 2:18:04There's a square bracket over here at top left.
    • 2:18:07This means, hey browser, here comes a list.
    • 2:18:10There's a curly brace, which means, hey browser
    • 2:18:12here comes a dictionary, otherwise known as an object in JavaScript.
    • 2:18:16Quote unquote ID is the key in this dictionary.
    • 2:18:20108878 is the value of that key.
    • 2:18:24Here comes the second key, quote unquote title, the value of which
    • 2:18:27is nice day at the office, quote unquote.
    • 2:18:30Then there's a closed curly brace, which is
    • 2:18:31end of the dictionary or end of the object, a comma,
    • 2:18:35and then there's a whole bunch of these other dictionaries.
    • 2:18:38So in short, all we're looking at here is a very raw text
    • 2:18:42based format of a Python dictionary converted to again,
    • 2:18:46something called json, JavaScript Object Notation,
    • 2:18:48which literally, this is about it.
    • 2:18:50Json uses double quotes, colons, curly braces, and square brackets,
    • 2:18:55and that's about it, to represent information.
    • 2:18:59So this is great.
    • 2:19:00But this is a horrible, horrible user interface if what the user sees
    • 2:19:03is this mess.
    • 2:19:04But that's not the point.
    • 2:19:06The point now is to have our backend return a json array of search results
    • 2:19:11and let the browser convert it into HTML.
    • 2:19:15Recall from last week I claimed that with JavaScript you
    • 2:19:17can mutate or change your dom, the document
    • 2:19:20object model, the tree in memory.
    • 2:19:22And what you can do in particular with JavaScript
    • 2:19:25is add more nodes to that tree.
    • 2:19:27You can add more things to that family tree like structure.
    • 2:19:31So yes, hello.
    • 2:19:32We just got, Hi, David, Hi, David, Hi, David via get.
    • 2:19:34Thank you very much.
    • 2:19:35OK.
    • 2:19:36So let me minimize that, and let me open up now, not
    • 2:19:41search dot HTML which we're not going to bother using anymore,
    • 2:19:44but instead index dot HTML.
    • 2:19:46And index dot HTML, you know what, I'm going
    • 2:19:48to go ahead and borrow some code here.
    • 2:19:50I'm going to go ahead and open up index dot HTML,
    • 2:19:52and I'm going to simplify this.
    • 2:19:54Let me stop the server.
    • 2:19:55I'm going to go ahead and get rid of my layout template,
    • 2:19:58and I'm going to get rid of my search template
    • 2:20:00because I just don't need them anymore.
    • 2:20:02So I'm going to simplify this program and focus entirely now on index dot
    • 2:20:10HTML.
    • 2:20:11So what do I want to do in here?
    • 2:20:12I need to involve some JavaScript now.
    • 2:20:15So how am I going to go about doing this?
    • 2:20:17Well first I need to go ahead and use a library called jQuery,
    • 2:20:22and jQuery is something that's a little decreasing in popularity,
    • 2:20:27but it's still used by Bootstrap, and so we'll
    • 2:20:29continue using it because you're already including it in fact,
    • 2:20:32for some of your other examples, but we need a different version of it.
    • 2:20:35So I'm going to quickly Google jQuery CDN, I'm going to go to this website,
    • 2:20:39and long story short, there's a lot of libraries
    • 2:20:42out there that you can use by just grabbing their script tag
    • 2:20:45and using it inside of your own.
    • 2:20:47So I'm going to go ahead here, and let me just clean up the tag
    • 2:20:50so it's all in one long, if ugly, line, but via copying and pasting
    • 2:20:53that line, which we'll typically do for you in the problem sets in distribution
    • 2:20:57code, I'm going to go ahead and copy that library into my file
    • 2:21:00so that it's among the libraries I can use.
    • 2:21:02And now I'm going to go ahead, just as we did last week,
    • 2:21:05and I'm going to add a script tag inside of my own pages head,
    • 2:21:09and then down here, let's see, where do I want to do this?
    • 2:21:12Actually, you know what?
    • 2:21:13Let me go ahead and do this a little differently just for consistency
    • 2:21:16with what you'll see online.
    • 2:21:18I'm going to go ahead and do this all in my body
    • 2:21:20so we don't have to worry about things being out of order.
    • 2:21:24We'll indeed keep it all inside of the page's body.
    • 2:21:26And what I'm going to do is give myself this:
    • 2:21:28an input, autocomplete equals quote unquote off,
    • 2:21:32autofocus, and then I'm going to give it a placeholder of query
    • 2:21:38and then a type of text.
    • 2:21:39So I'm going to have a very simple search box there
    • 2:21:42and I'm going to have an unordered list with nothing in it.
    • 2:21:44And that's deliberate for now.
    • 2:21:46Then I'm going to import this, the jQuery library.
    • 2:21:50And then down here, inside of my own script tags,
    • 2:21:53this is where I'm going to write the actual code.
    • 2:21:56I'm going to do let input equals document dot,
    • 2:22:00let's do query selector just like last week, and let me go ahead
    • 2:22:03and select my input tag.
    • 2:22:05This works.
    • 2:22:06I don't need an ID or any class because at the moment I only have one input,
    • 2:22:09so query selector of quote unquote input will give me that tag specifically.
    • 2:22:14Let me go ahead now and add to that input
    • 2:22:16an event listener that listens for the key up event.
    • 2:22:20Recall from last week our discussion of events, and key up,
    • 2:22:23like the user touching and letting go of the key
    • 2:22:25is one of the events you can listen for.
    • 2:22:27And any time the user does that, I want to call the following function,
    • 2:22:31and it's an anonymous function which we saw last week too.
    • 2:22:33And what I'm going to do inside of this anonymous function is the following.
    • 2:22:37I'm going to call a new function, the only new JavaScript function today,
    • 2:22:41called dollar sign underscore get.
    • 2:22:43This is shorthand notation for jQuery dot get,
    • 2:22:46but, sorry, dollar sign dot get is shorthand for jQuery dot get.
    • 2:22:52And I'm going to go ahead and get the following.
    • 2:22:54I'm going to get a slash search URL with a question mark followed by q equals,
    • 2:23:00and then I'm going to go ahead and put input dot value.
    • 2:23:04So notice with this line of code, this JavaScript string,
    • 2:23:08I'm kind of dynamically constructing in JavaScript code
    • 2:23:11the URL that I want to request shows from, slash search question
    • 2:23:17mark q equals, whatever the value of the input box
    • 2:23:20was that the user typed something into.
    • 2:23:23And the way this dollar sign dot get function works
    • 2:23:26is it supports what's called a callback.
    • 2:23:28A callback is a function that will eventually be
    • 2:23:31called when it has an answer for you.
    • 2:23:33And this is important on the web.
    • 2:23:35The internet can sometimes be slow, and you
    • 2:23:37might want to write code that contacts another server
    • 2:23:39like I'm about to do now.
    • 2:23:40And that server might be slow or the internet connection might be slow,
    • 2:23:43and it would be annoying if your entire browser froze
    • 2:23:46while you waited and waited and waited for the server's response to come back.
    • 2:23:50It's a lot nicer if you can say, when you have the data,
    • 2:23:54call me back, so to speak, sort of metaphorically on the phone,
    • 2:23:57call me back by calling this function.
    • 2:24:00So what function do I want it to call?
    • 2:24:02I want it to call this anonymous function that's
    • 2:24:04going to take as input an argument called shows,
    • 2:24:07but I could call that anything I want, and I'm going to do this.
    • 2:24:11I'm going to create an HTML string that's
    • 2:24:13initially nothing, quote unquote.
    • 2:24:15I'm then going to do this, a for loop in JavaScript
    • 2:24:18letting a variable called ID equal to whatever ID is in the shows that just
    • 2:24:24came back to me, and now inside of this for loop,
    • 2:24:28let me go ahead and inside of this for loop
    • 2:24:31create a title that's equal to shows, which is again,
    • 2:24:35the data that came back on the, came back via that callback so
    • 2:24:40to speak, shows dot ID dot title, and semicolon,
    • 2:24:46then I'm going to do HTML plus equals quote unquote list item plus title
    • 2:24:54plus quote unquote closed list item.
    • 2:24:57Then down here, lastly, I'm going to do document dot query selector, quote
    • 2:25:03unquote ul, dot inner HTML gets HTML.
    • 2:25:07All right, this is definitely the craziest thing
    • 2:25:09we've done thus far today, and it's tying together
    • 2:25:11so many different ideas, not to mention new syntax, so of all the examples,
    • 2:25:14don't worry too much if this is just a lot at once.
    • 2:25:17The goal really now is not to introduce you
    • 2:25:19to this syntax so that you can repeat it tomorrow, but rather
    • 2:25:22the idea of what we're doing.
    • 2:25:24So what are we doing?
    • 2:25:25We have a simple web page with a text input.
    • 2:25:27We've got a simple web page with an unordered list that's currently empty.
    • 2:25:31Below that, we're including this third party JavaScript library called jQuery,
    • 2:25:35which Bootstrap also happens to use for some of its graphical components.
    • 2:25:39Then I have my own JavaScript code that first
    • 2:25:41declares a variable that gives me access to the input tag on my own webpage
    • 2:25:46a few lines above.
    • 2:25:47I'm then using this fancy function dollar sign
    • 2:25:49get to do what's called an AJAX call.
    • 2:25:52AJAX refers to the ability for a web page
    • 2:25:55to make additional HTTP requests programmatically to a server.
    • 2:26:01It is allowing, this is how all websites today
    • 2:26:03get your chat messages if a friend just messaged you on some platform.
    • 2:26:07It's how when you're typing in your address,
    • 2:26:10it can autocomplete your address based on the entire globe's
    • 2:26:13worth of addresses.
    • 2:26:14It's how Google search autocomplete works as well.
    • 2:26:16Dollar sign dot get is a function built into jQuery that's
    • 2:26:20using some standard JavaScript functionality that's
    • 2:26:22going to allow me to visit the URL that ends with slash search
    • 2:26:25question mark q equals whatever the human typed in,
    • 2:26:28and when the response is ready, this anonymous function
    • 2:26:32is going to get called back, and it's going to be handed an input,
    • 2:26:34an argument called shows that is going to equal this stuff.
    • 2:26:39This is what my JavaScript function is going to receive as its shows argument.
    • 2:26:44And notice, it's a list or an array, inside
    • 2:26:48of which is a list of dictionaries or objects with two keys, ID, and title.
    • 2:26:53This, let me stipulate, happens to be the syntax in JavaScript
    • 2:26:56for iterating over such a data structure,
    • 2:26:58and this happens to be the syntax in JavaScript
    • 2:27:01for allowing me to create one line after another, another LI, another LI,
    • 2:27:06appending it using concatenation to this variable called HTML.
    • 2:27:10And this very last line of code says to the browser,
    • 2:27:13go select me that UL and plug into its inner HTML
    • 2:27:17the contents of it, the value of my variable.
    • 2:27:20And now crossing my fingers I did not screw up syntactically, let me go ahead
    • 2:27:25and do Flask run, I still need to run Flask because I do have a backend here.
    • 2:27:29Let me go back to my browser and reload the slash route.
    • 2:27:33Let me go ahead now and type in O, F, dammit.
    • 2:27:38Oh wait, it was just slow.
    • 2:27:39So notice, everything I've typed thus far or everything I'm seeing thus far
    • 2:27:42matches the word off.
    • 2:27:44And if I continue this, office, and let it search
    • 2:27:47and let the internet do its thing, now indeed,
    • 2:27:49it's been whittled down to a list of office matches.
    • 2:27:51And notice again if I go to the inspect tab,
    • 2:27:53open the network tab under inspect, let me go ahead and type in office,
    • 2:27:58notice here that every keystroke I typed triggered, O F F I C E to be executed,
    • 2:28:05triggered an AJAX call, that is an HTTP call to the server.
    • 2:28:08And notice that if I click on the last one there, scroll down,
    • 2:28:12I will see not only my request and my response,
    • 2:28:14but if I click on the response, I'll indeed see this big json
    • 2:28:18array of all of the data.
    • 2:28:20And it's actually rather fortuitous that it was slow,
    • 2:28:22because we were going to end with one demo.
    • 2:28:24For instance, if I was curious to know, being indoors
    • 2:28:26all day, what the weather is like, I might maybe
    • 2:28:28call Brian up on this here telephone.
    • 2:28:33[PHONE RINGING]
    • 2:28:38BRIAN: Hello?
    • 2:28:39DAVID J. MALAN: Hi, Brian, do you know what the weather is like outside today?
    • 2:28:42BRIAN: I haven't been outside in a little bit, but I can check,
    • 2:28:44and I will call you back.
    • 2:28:45DAVID J. MALAN: OK.
    • 2:28:46And notice it would be annoying if I had to wait on the phone for him
    • 2:28:48to go outside.
    • 2:28:49So I'm going to hang up, and I'm just going to wait for him to call me back,
    • 2:28:52and that's exactly what's happening in my code.
    • 2:28:54When I pass this anonymous function to this get function,
    • 2:28:58and they get here means HTTP get, this is telling the browser,
    • 2:29:01call this function once you have the answer,
    • 2:29:04much like hopefully Brian's about to have the, and indeed, the call back.
    • 2:29:11Hello.
    • 2:29:12BRIAN: Hi.
    • 2:29:13I looked up the weather.
    • 2:29:14It looks like it's going to be a brisk fall day today.
    • 2:29:16DAVID J. MALAN: Wonderful, well thank you so much.
    • 2:29:18And that is it for CS50.
    • 2:29:20We will see you all next time.
    • 2:29:22[MUSIC PLAYING]
  • CS50.ai
Shortcuts
Before using a shortcut, click at least once on the video itself (to give it "focus") after closing this window.
Play/Pause spacebar or k
Rewind 10 seconds left arrow or j
Fast forward 10 seconds right arrow or l
Previous frame (while paused) ,
Next frame (while paused) .
Decrease playback rate <
Increase playback rate >
Toggle captions on/off c
Toggle mute m
Toggle full screen f or double-click video