WEBVTT X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000 00:00:00.000 --> 00:00:03.472 [MUSIC PLAYING] 00:01:18.510 --> 00:01:19.410 DAVID: All right. 00:01:19.410 --> 00:01:23.460 So this is CS50 and this is week nine, and this is it 00:01:23.460 --> 00:01:25.710 in terms of programming fundamentals. 00:01:25.710 --> 00:01:28.488 Today, we come rather full circle with so many of the languages 00:01:28.488 --> 00:01:30.780 that we've been looking at over the past several weeks. 00:01:30.780 --> 00:01:33.750 And with HTML and CSS and JavaScript last week, 00:01:33.750 --> 00:01:37.020 we're going to add back into the mix, Python and SQL. 00:01:37.020 --> 00:01:40.420 And with that, do we have the ability to program for the web. 00:01:40.420 --> 00:01:42.840 And even though this isn't the only user interface out 00:01:42.840 --> 00:01:45.930 there, increasingly-- or people certainly using laptops and desktops 00:01:45.930 --> 00:01:48.960 and a browser to access applications that people have written, 00:01:48.960 --> 00:01:53.550 but it's also, increasingly, the way that mobile apps are written as well. 00:01:53.550 --> 00:01:55.770 There are languages called Swift for iOS, 00:01:55.770 --> 00:01:57.930 there are languages called Java for Android, 00:01:57.930 --> 00:02:00.750 but coding applications in both of those language 00:02:00.750 --> 00:02:04.530 means knowing twice as many language, building twice as many applications, 00:02:04.530 --> 00:02:05.190 potentially. 00:02:05.190 --> 00:02:07.740 So we're increasingly seeing, for better or for worse, 00:02:07.740 --> 00:02:09.840 that the world is starting to really standardize, 00:02:09.840 --> 00:02:13.740 at least for the next some number of years, on HTML, CSS, and JavaScript 00:02:13.740 --> 00:02:17.730 coupled with other languages like Python and SQL on the so-called backend. 00:02:17.730 --> 00:02:20.168 And so today, we'll tie all of those together 00:02:20.168 --> 00:02:22.710 and give you the last of the tools in your toolkit with which 00:02:22.710 --> 00:02:25.290 to tackle final projects to go off into the real world, 00:02:25.290 --> 00:02:28.200 ultimately, and somehow solve problems with programming. 00:02:28.200 --> 00:02:33.310 But we need an additional tool today, and we've sort of outgrown HTTP server. 00:02:33.310 --> 00:02:36.477 This is just a program that comes on certain computers 00:02:36.477 --> 00:02:38.310 that you can install for free, happens to be 00:02:38.310 --> 00:02:40.980 written in a language called JavaScript, but it's a program 00:02:40.980 --> 00:02:44.340 that we've been using to run a web server in VSCO. 00:02:44.340 --> 00:02:47.170 But you can run it on your own Mac or PC or anywhere else. 00:02:47.170 --> 00:02:50.940 But all this particular HTTP server does is 00:02:50.940 --> 00:02:54.750 serve up static content like HTML files, CSS files, 00:02:54.750 --> 00:02:59.220 JavaScript files, maybe images, maybe video files, but just static content. 00:02:59.220 --> 00:03:03.600 It has no ability to really interact with the user beyond simple clicks. 00:03:03.600 --> 00:03:08.800 You can create a web form and serve it visually using HTTP server, 00:03:08.800 --> 00:03:12.630 but if the human types in input into a form and click Submit, unless you 00:03:12.630 --> 00:03:16.080 submit it elsewhere to something like google.com like we did last time, 00:03:16.080 --> 00:03:19.200 it's not actually going to go anywhere because this server can't actually 00:03:19.200 --> 00:03:21.460 process the requests that are coming in. 00:03:21.460 --> 00:03:24.390 So today, we're going to introduce another type of server that 00:03:24.390 --> 00:03:28.350 comes with Python that allows us to not only serve web pages 00:03:28.350 --> 00:03:29.970 but also process user input. 00:03:29.970 --> 00:03:33.720 And recall that all that input is going to come ultimately from the URL, 00:03:33.720 --> 00:03:36.300 or more deeply inside of those virtual envelopes. 00:03:36.300 --> 00:03:40.140 So here's the canonical URL we talked about last week for random website 00:03:40.140 --> 00:03:42.280 like www.example.com. 00:03:42.280 --> 00:03:46.020 And I've highlighted the slash to connote the root of the web server, 00:03:46.020 --> 00:03:48.540 like the default folder where, presumably, there's 00:03:48.540 --> 00:03:52.150 a file called index.html or something else in there. 00:03:52.150 --> 00:03:54.360 Otherwise, you might have a more explicit mention 00:03:54.360 --> 00:03:56.790 of the actual file named file.html. 00:03:56.790 --> 00:03:59.873 You can have folders, as you probably gleaned from the most recent problem 00:03:59.873 --> 00:04:00.373 set. 00:04:00.373 --> 00:04:02.460 You can have files in folders like this, and these 00:04:02.460 --> 00:04:06.330 are all examples of what a programmer would typically call a path. 00:04:06.330 --> 00:04:08.160 So it might not just be a single word, it 00:04:08.160 --> 00:04:12.070 might have multiple slashes and multiple folders and some folders and files. 00:04:12.070 --> 00:04:14.070 But this is just more generally known as a path. 00:04:14.070 --> 00:04:16.709 But there's another term of our, that's essentially equivalent, 00:04:16.709 --> 00:04:17.890 that we'll introduce today. 00:04:17.890 --> 00:04:21.300 This is also synonymously called a route, which is maybe 00:04:21.300 --> 00:04:25.260 a better generic description of what these things are because it turns out 00:04:25.260 --> 00:04:27.750 they don't have to map to, that is, refer 00:04:27.750 --> 00:04:30.810 to a specific folder or a specific file, you 00:04:30.810 --> 00:04:33.333 can come up with your own routes in a website. 00:04:33.333 --> 00:04:36.000 And just make sure that when the user visits that, you give them 00:04:36.000 --> 00:04:36.900 a certain website page. 00:04:36.900 --> 00:04:39.608 If they visit something else, you give them a different web page. 00:04:39.608 --> 00:04:42.480 It doesn't have to map to a very specific file, as we'll soon see. 00:04:42.480 --> 00:04:45.660 And if you want to get input from the user, just like Google does, 00:04:45.660 --> 00:04:50.460 like q=cats, you can add a question mark at the end of this route. 00:04:50.460 --> 00:04:54.300 The key, or the HTTP parameter name that you want to define for yourself, 00:04:54.300 --> 00:04:57.263 and then equal sum value that, presumably, the human typed in. 00:04:57.263 --> 00:04:59.430 If you have more of these, you can put an ampersand, 00:04:59.430 --> 00:05:04.140 and then more key equals value pairs ampersand, repeat, repeat, repeat. 00:05:04.140 --> 00:05:08.770 The catch, though, is that using the tools that we had last week alone, 00:05:08.770 --> 00:05:13.920 we don't really have the ability to parse, that is, to analyze and extract 00:05:13.920 --> 00:05:15.840 things like q equals cats. 00:05:15.840 --> 00:05:18.900 You could have appended question mark q equals cats or anything else 00:05:18.900 --> 00:05:22.800 to any of URLs in your home page for problem set eight, 00:05:22.800 --> 00:05:26.460 but it doesn't actually do anything useful, necessarily, 00:05:26.460 --> 00:05:28.350 unless you use some fancy JavaScript. 00:05:28.350 --> 00:05:31.480 The server is not going to bother even looking in that for you. 00:05:31.480 --> 00:05:34.380 But today, we're going to introduce using a bit of Python. 00:05:34.380 --> 00:05:38.220 And in fact, we're going to use a web server implemented in Python, instead 00:05:38.220 --> 00:05:41.550 of using HTTP server alone, to automatically, for you, 00:05:41.550 --> 00:05:44.430 look for any key value pairs after the question mark 00:05:44.430 --> 00:05:48.120 and then hand them to you in the form of a Python dictionary. 00:05:48.120 --> 00:05:52.020 Recall that a dictionary in Python, a dict object, is just key value pairs. 00:05:52.020 --> 00:05:56.155 That seems like a perfect fit for these kinds of parameters. 00:05:56.155 --> 00:05:58.530 And you're not going to have to write that code yourself. 00:05:58.530 --> 00:06:02.140 It's going to be handed to you by way of what's called a framework. 00:06:02.140 --> 00:06:04.020 So this will be the second of two frameworks, 00:06:04.020 --> 00:06:05.562 really, that we look at in the class. 00:06:05.562 --> 00:06:08.250 And a framework is essentially a bunch of libraries 00:06:08.250 --> 00:06:10.740 that someone else wrote and a set of conventions, 00:06:10.740 --> 00:06:12.190 therefore, for doing things. 00:06:12.190 --> 00:06:15.000 So those of you who really started dabbling with Bootstrap 00:06:15.000 --> 00:06:18.680 this past week to make your home pages prettier and nicely laid out, 00:06:18.680 --> 00:06:19.950 you are using a framework. 00:06:19.950 --> 00:06:20.450 Why? 00:06:20.450 --> 00:06:24.110 Well, you're using libraries, code that someone else wrote, like all the CSS, 00:06:24.110 --> 00:06:27.200 maybe some of the JavaScript that the Bootstrap people wrote for you. 00:06:27.200 --> 00:06:30.980 But it's also a framework in the sense that you have to go all in. 00:06:30.980 --> 00:06:33.500 You have to use Bootstraps classes, and you 00:06:33.500 --> 00:06:37.820 have to lay out your divs or your spans or your table tags 00:06:37.820 --> 00:06:39.663 in a sort of Bootstrap-friendly way. 00:06:39.663 --> 00:06:42.080 And it's not too onerous, but you're following conventions 00:06:42.080 --> 00:06:43.920 that a bunch of humans standardized on. 00:06:43.920 --> 00:06:47.660 So similarly, in the world of Python, is there another framework 00:06:47.660 --> 00:06:49.040 we're going to start using today. 00:06:49.040 --> 00:06:52.430 And whereas Bootstrap is used for CSS and JavaScript, 00:06:52.430 --> 00:06:54.470 Flask is going to be used for Python. 00:06:54.470 --> 00:06:57.170 And it just solves a lot of common problems for us. 00:06:57.170 --> 00:07:00.380 It's going to make it easier for us to analyze the URLs 00:07:00.380 --> 00:07:03.020 and get key value pairs, it's going to make it easier 00:07:03.020 --> 00:07:06.110 for us to find files or images that the human wants 00:07:06.110 --> 00:07:07.550 to see when visiting our website. 00:07:07.550 --> 00:07:10.442 It's even going to make it easier to send emails automatically, 00:07:10.442 --> 00:07:11.900 like when someone fills out a form. 00:07:11.900 --> 00:07:15.210 You can dynamically, using code, send them an email as well. 00:07:15.210 --> 00:07:17.510 So Flask, and with it some related libraries, 00:07:17.510 --> 00:07:20.750 it's just going to make stuff like that easier for us. 00:07:20.750 --> 00:07:25.340 And to do this, all we have to do is adhere to some pretty minimalist 00:07:25.340 --> 00:07:27.450 requirements of this framework. 00:07:27.450 --> 00:07:30.650 We're going to have to create a file for ourselves called app.py, 00:07:30.650 --> 00:07:33.650 this is where our web app or application is going to live. 00:07:33.650 --> 00:07:38.120 If we have any libraries that we want to use, the convention in the Python world 00:07:38.120 --> 00:07:41.750 is to have a very simple text file called requirements.txt 00:07:41.750 --> 00:07:44.090 where you list the names of those libraries, 00:07:44.090 --> 00:07:48.890 top to bottom, in that text file, similar in spirit to the include 00:07:48.890 --> 00:07:52.650 or the import statements that we saw in C and Python, respectively. 00:07:52.650 --> 00:07:55.700 We're going to have a static folder or static directory, which 00:07:55.700 --> 00:07:58.850 means any files you create that are not ever going to change, 00:07:58.850 --> 00:08:01.442 like images, CSS files, JavaScript files, 00:08:01.442 --> 00:08:02.900 they're going to go in this folder. 00:08:02.900 --> 00:08:05.750 And then lastly, any HTML that you write, 00:08:05.750 --> 00:08:08.150 web pages you want the human to see, are going 00:08:08.150 --> 00:08:09.990 to go in a folder called templates. 00:08:09.990 --> 00:08:12.950 So this is, again, evidence of what we mean by a framework. 00:08:12.950 --> 00:08:14.750 Do you have to make a web app like this? 00:08:14.750 --> 00:08:16.940 No, but if you're using this particular framework, 00:08:16.940 --> 00:08:20.990 this is what people decided would be the human conventions. 00:08:20.990 --> 00:08:25.200 If you've heard of other frameworks like Django or asp.net or bunches of others, 00:08:25.200 --> 00:08:28.370 there are just different conventions out there for creating applications. 00:08:28.370 --> 00:08:32.390 Flask is a very nice microframework in that that's it. 00:08:32.390 --> 00:08:35.870 All you have to do is adhere to these pretty minimalist requirements 00:08:35.870 --> 00:08:38.840 to get some code up and running. 00:08:38.840 --> 00:08:41.620 All right, so let's go ahead and make a web app. 00:08:41.620 --> 00:08:43.370 Let me go ahead and switch over to VS Code 00:08:43.370 --> 00:08:45.412 here, and let me practice what I'm preaching here 00:08:45.412 --> 00:08:47.720 by first creating app.py. 00:08:47.720 --> 00:08:51.410 And let's go ahead and create an application that very simply, 00:08:51.410 --> 00:08:53.700 maybe, says hello to the user. 00:08:53.700 --> 00:08:58.160 So something that, initially, is not all that dynamic, pretty static, in fact. 00:08:58.160 --> 00:09:00.120 But we'll build on that as we've always done. 00:09:00.120 --> 00:09:04.100 So in app.py, what I'm going to do first is exactly the line of code 00:09:04.100 --> 00:09:05.420 I had on the screen earlier. 00:09:05.420 --> 00:09:11.840 From Flask, import Flask, with a capital F second and a lowercase f first. 00:09:11.840 --> 00:09:14.390 And I'm also going to preemptively import 00:09:14.390 --> 00:09:18.440 a couple of functions, render template, and request. 00:09:18.440 --> 00:09:20.360 More on those in just a bit. 00:09:20.360 --> 00:09:23.000 And then below that, I'm going to say, go ahead and do this. 00:09:23.000 --> 00:09:25.730 Give me a web-- a variable called app that's 00:09:25.730 --> 00:09:29.930 going to be the result of calling the Flask function and passing in it 00:09:29.930 --> 00:09:32.400 this weird incantation here, name. 00:09:32.400 --> 00:09:35.810 So we've seen this a few weeks back when we played around with Python 00:09:35.810 --> 00:09:38.540 and we had that if main thing at the bottom of the screen. 00:09:38.540 --> 00:09:44.090 For now, just know that __name__ refers to the name of the current file. 00:09:44.090 --> 00:09:48.560 And so this line here, simple as it is, tells Python, hey, Python, 00:09:48.560 --> 00:09:52.310 turn this file into a Flask application. 00:09:52.310 --> 00:09:56.030 Flask is a function that just figures out, then, how to do the rest. 00:09:56.030 --> 00:09:59.630 The last thing I'm going to do for this very simple web application is this. 00:09:59.630 --> 00:10:03.290 I'm going to say that I'm going to have a function called 00:10:03.290 --> 00:10:05.210 index that takes no arguments. 00:10:05.210 --> 00:10:07.130 And whenever this function is called, I want 00:10:07.130 --> 00:10:12.560 to return the results of rendering a template called index.html. 00:10:12.560 --> 00:10:13.515 And that's it. 00:10:13.515 --> 00:10:15.890 So let's assume there's a file somewhere, haven't created 00:10:15.890 --> 00:10:17.720 it yet, called index.html. 00:10:17.720 --> 00:10:19.940 But render template means render this file 00:10:19.940 --> 00:10:23.400 that is printed to the user's screen, so to speak. 00:10:23.400 --> 00:10:26.210 The last thing I'm going to do is I have to tell Flask 00:10:26.210 --> 00:10:28.500 when to call this index function. 00:10:28.500 --> 00:10:34.166 And so I'm going to tell it to define a route for, quote unquote, "slash." 00:10:34.166 --> 00:10:35.268 And that's it. 00:10:35.268 --> 00:10:37.310 So let's take a look at what I just created here. 00:10:37.310 --> 00:10:40.280 This is slightly new syntax, and it's really the only weirdness 00:10:40.280 --> 00:10:42.050 that we'll have today in Python. 00:10:42.050 --> 00:10:44.750 This is what's known in Python is what's called a decorator. 00:10:44.750 --> 00:10:46.760 A decorator is a special type of function 00:10:46.760 --> 00:10:49.310 that modifies, essentially, another function. 00:10:49.310 --> 00:10:52.350 For our purposes, just know that on line six this says, 00:10:52.350 --> 00:10:55.700 hey Python, define a route for slash, the default 00:10:55.700 --> 00:10:57.500 page on my website application. 00:10:57.500 --> 00:10:59.900 The next two lines, seven and eight, say, hey Python, 00:10:59.900 --> 00:11:02.630 define a function called index, takes no arguments. 00:11:02.630 --> 00:11:07.520 And the only thing you should ever do is return render template of quote unquote 00:11:07.520 --> 00:11:09.540 "index.html." 00:11:09.540 --> 00:11:10.650 All right, so that's it. 00:11:10.650 --> 00:11:13.700 So really, the next question, naturally, should be all right, well, 00:11:13.700 --> 00:11:17.490 what is in index.html? 00:11:17.490 --> 00:11:19.230 Well, let me go ahead and do that next. 00:11:19.230 --> 00:11:22.500 Let me create a directory called templates, practicing, again, 00:11:22.500 --> 00:11:23.760 what I preached earlier. 00:11:23.760 --> 00:11:26.520 So I'm going to create a new empty directory called templates, 00:11:26.520 --> 00:11:30.240 I'm going to go and CD into that directory 00:11:30.240 --> 00:11:33.900 and then do code of index.html. 00:11:33.900 --> 00:11:35.520 So here is going to be my index page. 00:11:35.520 --> 00:11:38.465 And I'm going to do a very simple web page, doc type HTML. 00:11:38.465 --> 00:11:40.590 I'm just going to borrow some stuff from last week. 00:11:40.590 --> 00:11:42.840 HTML language equals English. 00:11:42.840 --> 00:11:44.010 I'll close that tag. 00:11:44.010 --> 00:11:47.730 I'll then do a head tag, I'll do a meta tag, the name of which is viewport. 00:11:47.730 --> 00:11:50.250 This makes my site recall responsive. 00:11:50.250 --> 00:11:52.980 That is, it just grows and shrink to fit the size of the device. 00:11:52.980 --> 00:11:56.460 The initial scale for which is going to be one, and the width of which 00:11:56.460 --> 00:11:58.500 is going to be device width. 00:11:58.500 --> 00:12:00.540 So I'm typing this out, I have it printed here. 00:12:00.540 --> 00:12:02.490 This is stuff I typically copy paste. 00:12:02.490 --> 00:12:05.190 But then lastly, I'm going to add in my title, which will just 00:12:05.190 --> 00:12:06.750 be hello for the name of this app. 00:12:06.750 --> 00:12:07.750 And then the body-- 00:12:07.750 --> 00:12:08.640 whoops, Bobby. 00:12:08.640 --> 00:12:12.510 The body of this tag will be-- 00:12:12.510 --> 00:12:13.470 there we go. 00:12:13.470 --> 00:12:17.140 The body of this page, rather, will just be hello comma world. 00:12:17.140 --> 00:12:20.680 So very uninteresting and really a regression to where we began last week. 00:12:20.680 --> 00:12:24.030 But let's go now and experiment with these two files. 00:12:24.030 --> 00:12:25.890 I'm not going to bother with a static folder 00:12:25.890 --> 00:12:28.890 right now, because I don't have any other files that I want to serve up. 00:12:28.890 --> 00:12:30.990 No images, no CSS, nothing like that. 00:12:30.990 --> 00:12:34.000 And honestly, requirements.txt is going to be pretty simple. 00:12:34.000 --> 00:12:37.800 I'm going to go requirements.txt and just say make sure the system has 00:12:37.800 --> 00:12:41.190 access to the Flask library itself. 00:12:41.190 --> 00:12:44.190 All right, but that's the only thing we can add in there for now. 00:12:44.190 --> 00:12:49.470 All right, so now I have two files, app.py, and I have index.html. 00:12:49.470 --> 00:12:52.950 But index.html thank you is inside of my templates directory 00:12:52.950 --> 00:12:55.450 so how do I actually start a web server last week, 00:12:55.450 --> 00:12:56.730 I would have said HTTP server. 00:12:56.730 --> 00:12:59.070 But HTTP server is not a Python thing. 00:12:59.070 --> 00:13:03.390 It has no idea about Flask or Python or anything I just wrote. 00:13:03.390 --> 00:13:06.220 HTTP server will just spit out static files. 00:13:06.220 --> 00:13:09.525 So if I ran HTTP server, and then I clicked on app.py, 00:13:09.525 --> 00:13:11.490 I would literally see my Python code. 00:13:11.490 --> 00:13:15.660 It would not get executed because HTTP server is just for static content. 00:13:15.660 --> 00:13:19.740 But today, I'm going to run a different command called Flask run. 00:13:19.740 --> 00:13:23.490 So this framework Flask that I actually preinstalled in advance, 00:13:23.490 --> 00:13:27.180 so it wasn't strictly necessary that I create that requirements.txt file just 00:13:27.180 --> 00:13:31.140 yet, comes with a program called Flask, takes command line arguments like 00:13:31.140 --> 00:13:34.710 the word run, and when I do that, you'll see somewhat similar output to last 00:13:34.710 --> 00:13:36.900 week whereby you'll see the name-- 00:13:36.900 --> 00:13:39.867 your URL for your unique preview of that. 00:13:39.867 --> 00:13:42.450 You might see a pop up saying that your application is running 00:13:42.450 --> 00:13:44.400 on TCP port, something or other. 00:13:44.400 --> 00:13:46.830 By default, last week, we used port 8080. 00:13:46.830 --> 00:13:49.860 Flask, just because, prefers port 5,000. 00:13:49.860 --> 00:13:50.940 So that's fine too. 00:13:50.940 --> 00:13:53.670 I'm going to go ahead and open up this URL now. 00:13:53.670 --> 00:13:55.890 And once it authenticates and redirects me, 00:13:55.890 --> 00:13:59.460 just to make sure I'm allowed to access that particular port, let me zoom in. 00:13:59.460 --> 00:14:02.460 Voila, there's the extent of this application. 00:14:02.460 --> 00:14:06.150 If I view source by right-clicking or control clicking, 00:14:06.150 --> 00:14:08.130 there's my HTML that's been spit out. 00:14:08.130 --> 00:14:11.070 So really, I've just reinvented the wheel from last week 00:14:11.070 --> 00:14:13.510 because there's no dynamism now, nothing at all. 00:14:13.510 --> 00:14:15.207 But what if I do this? 00:14:15.207 --> 00:14:17.040 Let me close the source and let me zoom out. 00:14:17.040 --> 00:14:18.660 So you can see my URL bar. 00:14:18.660 --> 00:14:21.840 Let me zoom in now, and I have a very unique cryptic URL. 00:14:21.840 --> 00:14:23.820 But the point is that it ends with nothing. 00:14:23.820 --> 00:14:26.040 Or implicitly, it ends with slash. 00:14:26.040 --> 00:14:27.930 This is just Chrome being a little helpful. 00:14:27.930 --> 00:14:31.000 It doesn't bother showing you a slash, even though it's implicitly there. 00:14:31.000 --> 00:14:37.110 But let me do something explicit like my name equals, quote unquote, "David." 00:14:37.110 --> 00:14:39.480 So there's a key value pair that I've manually 00:14:39.480 --> 00:14:42.900 typed into my URL bar and hit Enter. 00:14:42.900 --> 00:14:44.400 Nothing happens, nothing changes. 00:14:44.400 --> 00:14:45.900 It still says hello, world. 00:14:45.900 --> 00:14:50.220 But the opportunity today is to now, dynamically, get at the input 00:14:50.220 --> 00:14:53.410 from that URL and start displaying it to the user. 00:14:53.410 --> 00:14:57.610 So let me go back over here to my terminal window and code. 00:14:57.610 --> 00:15:00.060 Let me move that down to the bottom there. 00:15:00.060 --> 00:15:03.280 And what if I want to say, huh, hello, name. 00:15:03.280 --> 00:15:05.010 I ideally want to say something like-- 00:15:05.010 --> 00:15:06.630 I don't want to hard code David because then it's never 00:15:06.630 --> 00:15:08.520 going to say hello to anyone else. 00:15:08.520 --> 00:15:14.010 I want to put like a variable name here, like name should go here. 00:15:14.010 --> 00:15:17.550 But it's not an HTML tag, so I need some kind of placeholder. 00:15:17.550 --> 00:15:19.680 Well, here's what I can do. 00:15:19.680 --> 00:15:24.810 If I go back to my Python code, I can now define a variable called name. 00:15:24.810 --> 00:15:28.800 And I can ask Flask to go into the current request, 00:15:28.800 --> 00:15:32.670 into its arguments, that is in the URL, as they're called, 00:15:32.670 --> 00:15:36.960 and get whatever the value of the parameter called name is. 00:15:36.960 --> 00:15:39.030 That puts that into a variable for me. 00:15:39.030 --> 00:15:41.940 And then, in render template-- this is one of those functions 00:15:41.940 --> 00:15:43.800 that can take more than one argument. 00:15:43.800 --> 00:15:45.780 If it takes another argument, you can pass 00:15:45.780 --> 00:15:47.740 in the name of any variable you want. 00:15:47.740 --> 00:15:52.210 So if I want to pass in my name, I can literally say name equals name. 00:15:52.210 --> 00:15:56.970 So this is the name of a variable I want to give to the template. 00:15:56.970 --> 00:16:01.920 This is the actual variable that I want to get the value from. 00:16:01.920 --> 00:16:07.920 And now lastly, in my index.html, the syntax as of today in Flask, 00:16:07.920 --> 00:16:12.030 is to do two curly braces and then put the name of the variable 00:16:12.030 --> 00:16:13.592 that you want to plug in. 00:16:13.592 --> 00:16:15.860 So here's what we mean by a template. 00:16:15.860 --> 00:16:18.950 A template is like a blueprint in the real world, where 00:16:18.950 --> 00:16:21.080 it's plans to make something. 00:16:21.080 --> 00:16:24.950 This is the plan to make a web page that has all of this code literally, 00:16:24.950 --> 00:16:28.460 but there's this placeholder with two curly braces here and here 00:16:28.460 --> 00:16:32.880 that says go ahead and plug in the value of the name variable right there. 00:16:32.880 --> 00:16:35.450 So in this sense, it's similar in spirit to our f strings 00:16:35.450 --> 00:16:36.980 or format strings in Python. 00:16:36.980 --> 00:16:39.897 The syntax is a little different just because reasonable people 00:16:39.897 --> 00:16:42.230 disagree, different people, different frameworks come up 00:16:42.230 --> 00:16:43.355 with different conventions. 00:16:43.355 --> 00:16:46.220 The convention in Flask, in their templates, 00:16:46.220 --> 00:16:48.680 is to use two curly braces here. 00:16:48.680 --> 00:16:50.960 The hope is that you, the programmer, will never 00:16:50.960 --> 00:16:54.480 want to display two curly braces in your actual web page. 00:16:54.480 --> 00:16:56.670 But even if you do, there's a workaround. 00:16:56.670 --> 00:16:57.810 We can escape that. 00:16:57.810 --> 00:17:01.250 So now let me go ahead and go back to my browser tab here. 00:17:01.250 --> 00:17:04.160 Previously, even though I added name equals David 00:17:04.160 --> 00:17:07.190 to the end of the URL with a question mark, it still said hello, world. 00:17:07.190 --> 00:17:09.920 But now, hopefully, if I made these changes, 00:17:09.920 --> 00:17:13.589 let me go ahead and open up my terminal window. 00:17:13.589 --> 00:17:17.670 Let me restart Flask so it loads my changes by default. 00:17:17.670 --> 00:17:21.619 Let me go back to my hello tab and click reload so it grabs the page anew 00:17:21.619 --> 00:17:23.240 from the server. 00:17:23.240 --> 00:17:24.710 And there we go, hello, David. 00:17:24.710 --> 00:17:27.740 I can play around now and I can change the URL appear to, for instance, 00:17:27.740 --> 00:17:28.280 Carter. 00:17:28.280 --> 00:17:30.080 Zoom out, hit Enter. 00:17:30.080 --> 00:17:32.790 And now we have something more dynamic. 00:17:32.790 --> 00:17:36.980 So the new pieces here are, in Python, we have some code here 00:17:36.980 --> 00:17:40.790 that allows us to access, programmatically, everything 00:17:40.790 --> 00:17:42.950 that's after the question mark in the URL. 00:17:42.950 --> 00:17:48.860 And the only thing we have to do that is call this function request.args.get. 00:17:48.860 --> 00:17:50.690 You and I don't have to bother figuring out 00:17:50.690 --> 00:17:53.107 where is the question mark, where is the equal sign, where 00:17:53.107 --> 00:17:54.680 are the ampersands, potentially. 00:17:54.680 --> 00:17:58.460 The framework, Flask, does all of that for us. 00:17:58.460 --> 00:18:05.490 OK, any questions then on these principles thus far? 00:18:05.490 --> 00:18:06.795 Yeah, in back. 00:18:06.795 --> 00:18:12.380 AUDIENCE: Why do you say the question mark in the URL? 00:18:12.380 --> 00:18:14.810 DAVID: Why do you need a question mark in the URL? 00:18:14.810 --> 00:18:21.390 The short answer is just because that is where key value pairs must go. 00:18:21.390 --> 00:18:24.990 If you're making a GET request from a browser to a server, 00:18:24.990 --> 00:18:29.690 the convention, standardized by the HTTP protocol, is to put them in the URL 00:18:29.690 --> 00:18:33.390 after the so-called route or path, then a question mark. 00:18:33.390 --> 00:18:35.840 And it delineates what's part of the root or the path, 00:18:35.840 --> 00:18:39.990 and what's part of the human input to the right. 00:18:39.990 --> 00:18:40.800 Other questions? 00:18:40.800 --> 00:18:41.465 Yeah. 00:18:41.465 --> 00:18:45.110 AUDIENCE: Can you go over again why the left and right in the [INAUDIBLE]?? 00:18:45.110 --> 00:18:45.610 DAVID: Sure. 00:18:45.610 --> 00:18:47.740 This is this annoying thing about Python. 00:18:47.740 --> 00:18:51.790 When you pass in parameters, two functions that have names, 00:18:51.790 --> 00:18:54.710 you typically say something equals something else. 00:18:54.710 --> 00:18:57.280 So let me make a slight tweak here. 00:18:57.280 --> 00:19:01.610 How about I say name of person here. 00:19:01.610 --> 00:19:06.460 This allows me to invent my own variable for my template 00:19:06.460 --> 00:19:08.450 and assign it the value of name. 00:19:08.450 --> 00:19:14.915 I now, though, have to go into my index file and say name of person-- 00:19:14.915 --> 00:19:15.790 did I get that right? 00:19:15.790 --> 00:19:17.290 Name of person, yeah. 00:19:17.290 --> 00:19:19.310 So these two have to match. 00:19:19.310 --> 00:19:22.690 And so this is just stupid because it's unnecessarily verbose. 00:19:22.690 --> 00:19:26.680 So what typically people do is they just use the same name as the variable 00:19:26.680 --> 00:19:30.600 itself, even though it looks admittedly stupid, but it has two different roles. 00:19:30.600 --> 00:19:32.350 The thing to the left of the equal sign is 00:19:32.350 --> 00:19:36.460 the name of the variable you plan to use in the template, the thing on the right 00:19:36.460 --> 00:19:38.470 is the actual value you're assigning it. 00:19:38.470 --> 00:19:40.330 And this is because its general purpose. 00:19:40.330 --> 00:19:43.570 I could override this and I could say something like name always 00:19:43.570 --> 00:19:46.120 equals Emma, no matter what that variable is. 00:19:46.120 --> 00:19:48.430 And now if I go back to my browser and reload, 00:19:48.430 --> 00:19:52.360 no matter what's in the URL, David or Carter, It's always-- 00:19:52.360 --> 00:19:55.210 OK, Emma broke the server. 00:19:55.210 --> 00:19:56.380 What did I do? 00:19:56.380 --> 00:19:58.840 Oh, I didn't change my template back. 00:19:58.840 --> 00:19:59.380 There we go. 00:19:59.380 --> 00:20:02.650 Let me change that back to be name, so that it's name there 00:20:02.650 --> 00:20:03.580 and it's name here. 00:20:03.580 --> 00:20:06.130 But I've hardcoded Emma's name, so now we're 00:20:06.130 --> 00:20:09.730 only ever going to see Emma no matter whose name is in the URL. 00:20:09.730 --> 00:20:10.760 That's all. 00:20:10.760 --> 00:20:14.020 All right, so this is bad user interface. 00:20:14.020 --> 00:20:16.930 If, in order to get a greeting for the day, you, the user, 00:20:16.930 --> 00:20:19.420 have to manually change the URL, which none of us ever do. 00:20:19.420 --> 00:20:21.220 This is not how web pages work. 00:20:21.220 --> 00:20:25.210 What is the more normal mechanism for getting input from the user 00:20:25.210 --> 00:20:28.900 and putting it in that URL automatically? 00:20:28.900 --> 00:20:31.780 How did we do that last week? 00:20:31.780 --> 00:20:33.500 With Google, if you recall. 00:20:33.500 --> 00:20:38.190 AUDIENCE: We have the search bar and we [INAUDIBLE] you have 00:20:38.190 --> 00:20:44.340 to make something in there [INAUDIBLE]. 00:20:44.340 --> 00:20:47.970 DAVID: OK, so we did make something in order to get the input from the user. 00:20:47.970 --> 00:20:51.840 And specifically, what was the tag or the terminology we used last week? 00:20:51.840 --> 00:20:53.070 AUDIENCE: [INAUDIBLE]. 00:20:53.070 --> 00:20:55.000 DAVID: Sorry, a little louder? 00:20:55.000 --> 00:20:57.010 Oh, no. 00:20:57.010 --> 00:20:57.510 But yeah. 00:20:57.510 --> 00:20:58.680 AUDIENCE: Is it input? 00:20:58.680 --> 00:21:01.030 DAVID: So the input tag, inside of the form tag. 00:21:01.030 --> 00:21:03.775 So in short, forms, or of course, how the web works 00:21:03.775 --> 00:21:05.650 and how we typically get input from the user, 00:21:05.650 --> 00:21:08.978 whether it's a button or a text box or a dropdown menu or something else. 00:21:08.978 --> 00:21:11.020 So let's go ahead and add that into the mix here. 00:21:11.020 --> 00:21:14.940 So let's enhance this hello app to do a little something more by, 00:21:14.940 --> 00:21:16.240 this time, just doing this. 00:21:16.240 --> 00:21:20.340 Let me get rid of this name stuff and let me just 00:21:20.340 --> 00:21:26.130 have a very simple index.html file that, by default, is going to simply ask 00:21:26.130 --> 00:21:28.480 the user for some input as follows. 00:21:28.480 --> 00:21:33.090 I'm going to go back into my index.html, and instead of printing out 00:21:33.090 --> 00:21:36.090 the user's name, this is the page I'm going to use to actually get input 00:21:36.090 --> 00:21:36.880 from the user. 00:21:36.880 --> 00:21:39.210 So I'm going to create a form tag. 00:21:39.210 --> 00:21:42.870 The method I'm going to use for now is going to be, quote unquote, "get." 00:21:42.870 --> 00:21:45.330 Then, inside of that form, I'm going to have an input tag. 00:21:45.330 --> 00:21:47.872 And I'm going to turn off autocomplete like we did last week. 00:21:47.872 --> 00:21:51.780 I'm going to turn on auto focus, so it puts the cursor in the text box for me. 00:21:51.780 --> 00:21:55.502 I'm going to give the name of this input the name, name. 00:21:55.502 --> 00:21:58.210 Not to be too confusing, but I'm asking the human for their name. 00:21:58.210 --> 00:22:01.770 So it makes sense that the name of the input should be, quote unquote, "name." 00:22:01.770 --> 00:22:04.470 The placeholder I want the human to see in light gray text 00:22:04.470 --> 00:22:07.500 will be Name with a capital N, just so it's a little grammatical. 00:22:07.500 --> 00:22:09.750 And then type of this text fiel-- 00:22:09.750 --> 00:22:11.610 type of this input is going to be text. 00:22:11.610 --> 00:22:14.550 Then I'm just going to give myself, like last week, a submit button. 00:22:14.550 --> 00:22:16.467 And I don't care what it says, it's just going 00:22:16.467 --> 00:22:18.820 to say the default submit terminology. 00:22:18.820 --> 00:22:23.310 Let me go ahead, now, and open up my terminal window again. 00:22:23.310 --> 00:22:28.470 Let me go to that same URL so that I can see-- whoops. 00:22:33.320 --> 00:22:33.990 There we go. 00:22:33.990 --> 00:22:35.540 So that was just cached from earlier. 00:22:35.540 --> 00:22:38.840 Let me go back to that same URL, my GitHub preview.dev URL, 00:22:38.840 --> 00:22:40.260 and here I have the form. 00:22:40.260 --> 00:22:42.170 And now, I can type in anything I want. 00:22:42.170 --> 00:22:45.840 The catch, though, is when I click Submit, where is it going to go? 00:22:45.840 --> 00:22:46.880 Well, let's be explicit. 00:22:46.880 --> 00:22:50.240 It does have a default value, but let me go into my index.html 00:22:50.240 --> 00:22:53.000 and let me add, just like we did last week for it, Google. 00:22:53.000 --> 00:22:58.427 Whereas previously, I said something like www.google.com/search, but today, 00:22:58.427 --> 00:23:00.260 we're not going to rely on some third party. 00:23:00.260 --> 00:23:02.720 I'm going to implement the so-called backend, 00:23:02.720 --> 00:23:06.990 and I'm going to have the user submit this form to a second route, 00:23:06.990 --> 00:23:09.620 not just slash, how about /greet. 00:23:09.620 --> 00:23:11.180 I can make it up, whatever I want. 00:23:11.180 --> 00:23:15.650 Greet feels like a nice operative word, so /greet is where the user will be 00:23:15.650 --> 00:23:18.650 sent when they click Submit on this form. 00:23:18.650 --> 00:23:22.130 All right, so let's go ahead now and go back to my browser tab. 00:23:22.130 --> 00:23:24.830 Let me go ahead, actually, and let me reload Flask 00:23:24.830 --> 00:23:27.320 here so that it reloads all of my changes. 00:23:27.320 --> 00:23:31.400 Let me reload this tab so that I get the very latest HTML and, indeed, 00:23:31.400 --> 00:23:32.510 quick safety check. 00:23:32.510 --> 00:23:35.630 If I view page source, we indeed see that my browser 00:23:35.630 --> 00:23:37.490 has downloaded the latest HTML. 00:23:37.490 --> 00:23:39.155 So it definitely has changed. 00:23:39.155 --> 00:23:40.530 Let's go ahead and type in David. 00:23:40.530 --> 00:23:44.300 And when I click Submit here, what's going to happen? 00:23:44.300 --> 00:23:44.980 Hypotheses. 00:23:47.500 --> 00:23:50.620 What's going to happen visually, functionally, however you 00:23:50.620 --> 00:23:54.540 want to interpret when I click Submit. 00:23:54.540 --> 00:23:55.040 Yeah? 00:23:55.040 --> 00:23:56.585 AUDIENCE: [INAUDIBLE] an empty page. 00:23:56.585 --> 00:23:58.710 DAVID: OK, the user's going to go to an empty page. 00:23:58.710 --> 00:24:00.002 Pretty good instinct, because-- 00:24:00.002 --> 00:24:02.880 no where else, if I mentioned /greet, it doesn't seem to exist. 00:24:02.880 --> 00:24:07.110 How's the URL going to change, just to be clear? 00:24:07.110 --> 00:24:09.360 What's going to appear, suddenly, in the URL? 00:24:12.050 --> 00:24:12.925 Yeah? 00:24:12.925 --> 00:24:14.200 AUDIENCE: 404? 00:24:14.200 --> 00:24:15.010 DAVID: 404? 00:24:15.010 --> 00:24:15.802 No, not in the URL. 00:24:15.802 --> 00:24:18.677 Specifically in the URL, something's going to get added automatically 00:24:18.677 --> 00:24:19.232 when I click. 00:24:19.232 --> 00:24:20.440 AUDIENCE: The key value pair? 00:24:20.440 --> 00:24:21.815 DAVID: The key value pair, right. 00:24:21.815 --> 00:24:22.900 That's how forms work. 00:24:22.900 --> 00:24:25.180 That's why our Google trick last week worked. 00:24:25.180 --> 00:24:27.843 I sort of recreated a form on my own website. 00:24:27.843 --> 00:24:30.760 And even though I didn't get around to implementing google.com itself, 00:24:30.760 --> 00:24:34.420 I can still send the information to Google just relying on browsers, 00:24:34.420 --> 00:24:35.590 standardizing-- 00:24:35.590 --> 00:24:38.470 to your question earlier, that whenever you submit a form, 00:24:38.470 --> 00:24:41.740 it automatically ends up after a question mark in the URL 00:24:41.740 --> 00:24:42.670 if you're using GET. 00:24:42.670 --> 00:24:45.460 So this both of you are right, this is going to break. 00:24:45.460 --> 00:24:49.000 And all three of you are right, in effect, 404 not found. 00:24:49.000 --> 00:24:50.450 You can see it in the tab here. 00:24:50.450 --> 00:24:51.950 That's the error that has come back. 00:24:51.950 --> 00:24:55.720 But what's interesting, and most important, the URL did change. 00:24:55.720 --> 00:24:59.890 And it went to /greet?name=david. 00:24:59.890 --> 00:25:02.260 So I just, now, need to add some logic that actually 00:25:02.260 --> 00:25:04.000 looks for that so-called route. 00:25:04.000 --> 00:25:06.520 So let me go back to my app.py. 00:25:06.520 --> 00:25:11.680 Let me define another route for, quote unquote, "slash greet." 00:25:11.680 --> 00:25:15.200 And then, inside of-- under this, let me define another function. 00:25:15.200 --> 00:25:18.280 I'll call it greet, but I could call it anything I want. 00:25:18.280 --> 00:25:20.950 No arguments, for now, for this, and then 00:25:20.950 --> 00:25:24.430 let me go ahead and do this in my app.py. 00:25:24.430 --> 00:25:27.290 This time around, I do want to get the human's name. 00:25:27.290 --> 00:25:31.300 So let me say requeste.args get quote unquote "name", 00:25:31.300 --> 00:25:33.340 and let me store that in a variable called name. 00:25:33.340 --> 00:25:37.030 Then let me return a template, and you know 00:25:37.030 --> 00:25:39.610 what, I'm going to give myself a new template, greet.html. 00:25:39.610 --> 00:25:41.860 Because this has a different purpose, it's not a form. 00:25:41.860 --> 00:25:44.800 I want to say hello to the user in this HTML file, 00:25:44.800 --> 00:25:49.960 and I want to pass, into it, the name that the human just typed in. 00:25:49.960 --> 00:25:56.320 All right, so now if I go up and reload the page, what might happen now? 00:25:56.320 --> 00:25:58.480 Other logical check here. 00:25:58.480 --> 00:26:01.930 If I go ahead and hit reload or resubmit the form, what might happen now? 00:26:05.200 --> 00:26:08.480 Any instincts? 00:26:08.480 --> 00:26:10.890 Let me try, so let's try this. 00:26:10.890 --> 00:26:12.470 Let's go ahead and reload the page. 00:26:12.470 --> 00:26:13.980 Previously, it was not found. 00:26:13.980 --> 00:26:17.900 Now it's worse, and this is the 500 error, internal server 00:26:17.900 --> 00:26:22.130 error that I promised next week we will all encounter accidentally, ultimately. 00:26:22.130 --> 00:26:24.020 But here we have an internal server error. 00:26:24.020 --> 00:26:27.750 Because it's an internal error, this means something's wrong with your code. 00:26:27.750 --> 00:26:31.280 So the route was actually found because it's not a 404 this time. 00:26:31.280 --> 00:26:36.740 But if we go into VS Code here and we look at the console, the terminal 00:26:36.740 --> 00:26:39.860 window, you'll see that-- 00:26:39.860 --> 00:26:43.280 this is actually a bit misleading. 00:26:43.280 --> 00:26:44.370 Do I want to do this? 00:26:44.370 --> 00:26:46.350 Let me reload this. 00:26:46.350 --> 00:26:47.370 Let me reload here. 00:26:47.370 --> 00:26:49.730 Oh, standby. 00:26:49.730 --> 00:26:51.120 Come on. 00:26:51.120 --> 00:26:53.100 There we go. 00:26:53.100 --> 00:26:54.440 Come on. 00:26:54.440 --> 00:26:57.110 OK, here we have this error here, and this 00:26:57.110 --> 00:26:59.480 is where your terminal window is going to be helpful. 00:26:59.480 --> 00:27:02.390 In your terminal window, by default, is typically 00:27:02.390 --> 00:27:05.510 going to go helpful stuff like a log, L-O-G, 00:27:05.510 --> 00:27:08.847 of what it is the server is seeing from the browser. 00:27:08.847 --> 00:27:11.180 For instance, here's what the server just saw in purple. 00:27:11.180 --> 00:27:16.310 Get /greet?name=david using HTTP version 1.0. 00:27:16.310 --> 00:27:19.790 Here, though, is the status code that the server returned, 500. 00:27:19.790 --> 00:27:20.840 Why, what's the error? 00:27:20.840 --> 00:27:24.260 Well, here's where we get these annoying pretty cryptic Python messages 00:27:24.260 --> 00:27:26.090 that help50 might ultimately help you with, 00:27:26.090 --> 00:27:29.403 or here, we might just have a clue at the bottom. 00:27:29.403 --> 00:27:31.820 And this is actually pretty clear, even though we've never 00:27:31.820 --> 00:27:32.810 seen this error before. 00:27:32.810 --> 00:27:34.130 What did I screw up here? 00:27:34.130 --> 00:27:36.710 I just didn't create greet.html, right? 00:27:36.710 --> 00:27:37.737 Template not found. 00:27:37.737 --> 00:27:40.070 All right, so that must be the last piece of the puzzle. 00:27:40.070 --> 00:27:43.460 And again, representative of how you might diagnose problems like these, 00:27:43.460 --> 00:27:46.550 let me go into my terminal window. 00:27:46.550 --> 00:27:51.050 After hitting Control C, which cancels or interrupts a process, 00:27:51.050 --> 00:27:53.030 let me go into my templates directory. 00:27:53.030 --> 00:27:55.910 If I type ls, I only have index.html. 00:27:55.910 --> 00:27:58.460 So let's code up greet.html. 00:27:58.460 --> 00:28:02.360 And in this file let's quickly do doc type. 00:28:02.360 --> 00:28:07.160 Doc type HTML, open bracket HTML, language equals English. 00:28:07.160 --> 00:28:11.090 Inside of this, I'll have the head tag, inside of here, I'll have the meta. 00:28:11.090 --> 00:28:15.740 The name is viewport, the content of which is-- 00:28:15.740 --> 00:28:18.020 I always forget this to. 00:28:18.020 --> 00:28:25.220 The content of which is initial scale equals one, width equals device width. 00:28:25.220 --> 00:28:28.130 Quote unquote, title is still going to be, 00:28:28.130 --> 00:28:30.530 I'll call this greet because this is my template. 00:28:30.530 --> 00:28:35.880 And then here, in the body, I'm going to have hello comma name. 00:28:35.880 --> 00:28:40.100 So I could have kept around the old version of this, but I just recreated, 00:28:40.100 --> 00:28:41.570 essentially, my second template. 00:28:41.570 --> 00:28:45.200 So index.html now is almost the same, but the title is different 00:28:45.200 --> 00:28:46.580 and it has a form. 00:28:46.580 --> 00:28:49.790 greet.html is almost the same, but it does not have a form. 00:28:49.790 --> 00:28:52.020 It just has the hello comma name. 00:28:52.020 --> 00:28:56.510 So let me now go ahead and rerun in the correct directory. 00:28:56.510 --> 00:29:01.080 You have to run Flask wherever app.py is, not in your templates directory. 00:29:01.080 --> 00:29:04.040 So let me do Flask run to get back to where I was. 00:29:04.040 --> 00:29:05.900 Let me go into my other tab. 00:29:05.900 --> 00:29:09.080 Cross my fingers this time that, when I go back to slash 00:29:09.080 --> 00:29:14.030 and I get index.html's form, now I type in David and click Submit, 00:29:14.030 --> 00:29:16.730 now we get hello, David. 00:29:16.730 --> 00:29:20.240 And now we have a full-fledged web app that has two different routes, 00:29:20.240 --> 00:29:25.430 slash and /greet, the latter of which takes input like this and then, 00:29:25.430 --> 00:29:27.470 using a template, spits it out. 00:29:27.470 --> 00:29:30.990 But something could go wrong, and let's see what happens here. 00:29:30.990 --> 00:29:33.620 Suppose I don't type anything in. 00:29:33.620 --> 00:29:35.870 Let me go here and just click Submit. 00:29:35.870 --> 00:29:38.662 Now, I mean, it looks stupid. 00:29:38.662 --> 00:29:40.620 So there's bunches of ways we could solve this. 00:29:40.620 --> 00:29:44.060 I could require that the user have input on the previous page, 00:29:44.060 --> 00:29:46.193 I could have some kind of error check for this. 00:29:46.193 --> 00:29:48.860 But there's another mechanism I can use that I'll just show you. 00:29:48.860 --> 00:29:53.150 It turns out this GET function, in the context of HTTP 00:29:53.150 --> 00:29:55.400 and also in general with Python dictionaries, 00:29:55.400 --> 00:29:57.810 you can actually supply a default value. 00:29:57.810 --> 00:30:02.180 So if there is no name parameter or no value for a name parameter, 00:30:02.180 --> 00:30:04.830 you can actually give it a default value like this. 00:30:04.830 --> 00:30:07.010 So I'll say world, for instance. 00:30:07.010 --> 00:30:08.450 Now, let me go back here. 00:30:08.450 --> 00:30:10.580 Let me type in nothing again and click Submit. 00:30:10.580 --> 00:30:14.270 And hopefully this time, I'll do-- oops, sorry. 00:30:14.270 --> 00:30:17.030 Let me restart Flask to reload the template. 00:30:17.030 --> 00:30:20.090 Let me go ahead and type nothing this time, clicking Submit. 00:30:20.090 --> 00:30:23.300 And hopefully, we now-- 00:30:23.300 --> 00:30:26.210 Oh, interesting. 00:30:26.210 --> 00:30:27.860 I should have faked this. 00:30:27.860 --> 00:30:31.700 Suppose that the reason this-- 00:30:31.700 --> 00:30:33.260 Oh. 00:30:33.260 --> 00:30:37.430 Suppose I just get rid of name altogether like this and hit Enter. 00:30:37.430 --> 00:30:40.070 Now I see hello, world, and this is a subtlety 00:30:40.070 --> 00:30:42.200 that I didn't intend to get into here. 00:30:42.200 --> 00:30:45.620 When you have question mark name equals nothing, 00:30:45.620 --> 00:30:47.660 you're passing in what's called-- whoops. 00:30:47.660 --> 00:30:51.350 When you have greet question mark name equals something, 00:30:51.350 --> 00:30:54.080 you actually are giving a value to name. 00:30:54.080 --> 00:30:56.720 It is quote unquote with nothing in between. 00:30:56.720 --> 00:30:59.190 That is different from having no value at all. 00:30:59.190 --> 00:31:03.203 So allow me to just propose that the error here, we 00:31:03.203 --> 00:31:05.120 would want to require this in a different way. 00:31:05.120 --> 00:31:07.610 And probably the most robust way to do this 00:31:07.610 --> 00:31:13.260 would be to go in here, in my HTML, and say that the name field is required. 00:31:13.260 --> 00:31:17.990 Now, if I go back to my form after restarting Flask here, 00:31:17.990 --> 00:31:21.950 and I go ahead and click reload on my form and type in nothing 00:31:21.950 --> 00:31:25.580 and click Submit, now the browser is going to yell at me. 00:31:25.580 --> 00:31:27.740 But just as a teaser for something we'll be 00:31:27.740 --> 00:31:30.470 doing in the next problem set in terms of error checking, 00:31:30.470 --> 00:31:37.010 you should never, ever, ever rely on client side safety checks like this. 00:31:37.010 --> 00:31:41.540 Because we know, from last week, that a curious programmer can go to inspect, 00:31:41.540 --> 00:31:43.920 and let me poke around the HTML here. 00:31:43.920 --> 00:31:46.280 Let me go into the body, the form. 00:31:46.280 --> 00:31:48.900 OK, you say required, I say not required. 00:31:48.900 --> 00:31:51.960 You can just delete what's in the dom, in the browser, 00:31:51.960 --> 00:31:54.920 and now I can go ahead and submit this form. 00:31:54.920 --> 00:31:56.510 And it appears to be broken. 00:31:56.510 --> 00:31:59.700 Not a big deal with a silly little greeting application like this. 00:31:59.700 --> 00:32:02.750 But if you're trying to require that humans actually 00:32:02.750 --> 00:32:06.140 provide input that is necessary for the correct operation of the site, 00:32:06.140 --> 00:32:11.690 you don't want to trust that the HTML is not altered by some adversary. 00:32:11.690 --> 00:32:14.570 All right, any questions, then, on this particular app 00:32:14.570 --> 00:32:18.450 before we add another feature here? 00:32:18.450 --> 00:32:21.203 Any questions here? 00:32:21.203 --> 00:32:22.185 Yeah. 00:32:22.185 --> 00:32:24.149 AUDIENCE: Do you guys [INAUDIBLE]. 00:32:26.493 --> 00:32:27.660 DAVID: Sorry, little louder. 00:32:27.660 --> 00:32:29.163 In the index function-- 00:32:29.163 --> 00:32:30.612 AUDIENCE: Oh, sorry. 00:32:30.612 --> 00:32:33.030 [INAUDIBLE] 00:32:33.030 --> 00:32:33.807 DAVID: Sorry? 00:32:33.807 --> 00:32:36.508 AUDIENCE: [INAUDIBLE] 00:32:36.508 --> 00:32:38.050 DAVID: Would it be a problem if what? 00:32:38.050 --> 00:32:39.523 AUDIENCE: You have to [INAUDIBLE]. 00:32:42.390 --> 00:32:42.890 DAVID: No. 00:32:42.890 --> 00:32:44.360 I mean no, this is OK. 00:32:44.360 --> 00:32:46.760 What you should really do is something we're going to do with another example 00:32:46.760 --> 00:32:48.330 where I'm going to start error checking things. 00:32:48.330 --> 00:32:50.030 So let me wave my hands at that and propose that we'll 00:32:50.030 --> 00:32:51.498 solve this better in just a bit. 00:32:51.498 --> 00:32:53.540 But it's not bad to do what I just did here, it's 00:32:53.540 --> 00:32:56.480 only going to handle one of the scenarios that I was worried about. 00:32:56.480 --> 00:32:57.990 Not all of them. 00:32:57.990 --> 00:33:00.290 All right, so even though this is new to most of us 00:33:00.290 --> 00:33:05.630 here, consider index.html, my first template, and consider greet.html, 00:33:05.630 --> 00:33:07.790 my second template. 00:33:07.790 --> 00:33:11.022 What might be arguably badly designed? 00:33:11.022 --> 00:33:12.980 Even though this might be the first time you've 00:33:12.980 --> 00:33:16.700 ever touched web programming like this. 00:33:16.700 --> 00:33:24.140 What's bad or dumb about this design of these two templates alone? 00:33:24.140 --> 00:33:28.070 And there's a reason, too, that I bored us by typing it out that second time. 00:33:28.070 --> 00:33:28.880 Yeah? 00:33:28.880 --> 00:33:33.653 AUDIENCE: [INAUDIBLE] you said, stuff like Notepad and [INAUDIBLE].. 00:33:33.653 --> 00:33:35.320 DAVID: Yeah, there's so much repetition. 00:33:35.320 --> 00:33:38.560 I mean, it was deliberately tedious that I was retyping everything. 00:33:38.560 --> 00:33:41.590 The doc type, the HTML tag, the head tag, the title tag. 00:33:41.590 --> 00:33:44.230 And little things did change along the way, like the title 00:33:44.230 --> 00:33:46.270 and certainly, the content of the body. 00:33:46.270 --> 00:33:49.540 But so much of this, I mean, almost all of the page 00:33:49.540 --> 00:33:52.837 is a copy of itself in multiple files. 00:33:52.837 --> 00:33:56.170 And God forbid we have a third template, a fourth template, a hundredth template 00:33:56.170 --> 00:33:57.310 for a really big website. 00:33:57.310 --> 00:33:59.680 This is going to get very tedious very quickly. 00:33:59.680 --> 00:34:02.208 And suppose you want to change something in one place, 00:34:02.208 --> 00:34:05.500 you're going to have to change it now in two, three, a hundred different places 00:34:05.500 --> 00:34:06.200 instead. 00:34:06.200 --> 00:34:08.980 So just like in programming more generally, 00:34:08.980 --> 00:34:11.469 we have this ability to factor out commonalities. 00:34:11.469 --> 00:34:13.719 So do you in the context of web programming, 00:34:13.719 --> 00:34:16.510 and specifically templating, have the ability 00:34:16.510 --> 00:34:18.617 to factor out all of those commonalities. 00:34:18.617 --> 00:34:20.409 The syntax is going to be a little curious, 00:34:20.409 --> 00:34:23.330 but it functionally is pretty straightforward. 00:34:23.330 --> 00:34:24.530 Let me go ahead and do this. 00:34:24.530 --> 00:34:28.540 Let me go ahead and copy the contents of index.html. 00:34:28.540 --> 00:34:31.420 Let me go into my templates directory and code a file that, 00:34:31.420 --> 00:34:34.370 by default, is called layout.html. 00:34:34.370 --> 00:34:37.989 And let me go ahead, and per your answer, copy all of those 00:34:37.989 --> 00:34:40.520 commonalities into this file now instead. 00:34:40.520 --> 00:34:43.389 So here I have a file called layout.html. 00:34:43.389 --> 00:34:48.550 I don't want to give every page the same title, maybe, but for now that's OK. 00:34:48.550 --> 00:34:50.020 I'm going to call everything hello. 00:34:50.020 --> 00:34:52.900 But in the body of the page, what I'm going to do here is just 00:34:52.900 --> 00:34:57.500 have a placeholder for actual contents that do change. 00:34:57.500 --> 00:35:00.370 So in this layout, I'm going to go ahead in here 00:35:00.370 --> 00:35:05.060 and just put in the body of my page, how about this syntax? 00:35:05.060 --> 00:35:06.610 And this is admittedly new. 00:35:06.610 --> 00:35:11.020 Block body, and then percent sign close curly brace. 00:35:11.020 --> 00:35:13.210 And then I'm going to do end block. 00:35:13.210 --> 00:35:18.130 So a curious syntax here, but this is more template syntax. 00:35:18.130 --> 00:35:21.460 The other template syntax we saw before was the two curly braces. 00:35:21.460 --> 00:35:23.290 That's for just plugging in values. 00:35:23.290 --> 00:35:27.550 There's this other syntax with Flask that allows you to, say, a single curly 00:35:27.550 --> 00:35:32.350 brace, a percent sign, and then some functionality like this defining 00:35:32.350 --> 00:35:33.130 a block. 00:35:33.130 --> 00:35:35.620 And this one's a little weird because there's literally 00:35:35.620 --> 00:35:38.960 nothing between the close curly and the open curly brace here. 00:35:38.960 --> 00:35:41.440 But let's see what this can do for us. 00:35:41.440 --> 00:35:47.920 Let me now go into my index.html, which is where I borrowed most of that code 00:35:47.920 --> 00:35:51.190 from, and let me focus on what is minimally different. 00:35:51.190 --> 00:35:56.120 The only thing that's really different in this page, title aside, is the form. 00:35:56.120 --> 00:35:59.920 So let me go ahead and just cut that form out to my clipboard. 00:35:59.920 --> 00:36:02.680 Let me change the first line of index.html 00:36:02.680 --> 00:36:08.980 to say this file is going to extend layout.html, 00:36:08.980 --> 00:36:11.170 and notice I'm using the curly braces again. 00:36:11.170 --> 00:36:14.620 And this file is going to have its own body block 00:36:14.620 --> 00:36:19.540 inside of which is just the HTML that I actually 00:36:19.540 --> 00:36:22.030 want to make specific to this page. 00:36:22.030 --> 00:36:24.290 And I'll keep my indentation nice and neat here. 00:36:24.290 --> 00:36:25.780 And let's consider what I've done. 00:36:25.780 --> 00:36:28.210 This is starting to look weird fast, and this is now 00:36:28.210 --> 00:36:32.110 a mix of HTML with templating code. 00:36:32.110 --> 00:36:38.200 Index.html, first line now says, hey, Flask, this file extends layout.html, 00:36:38.200 --> 00:36:39.310 whatever that is. 00:36:39.310 --> 00:36:42.640 This next line, three through 10, says, hey, Flask, here 00:36:42.640 --> 00:36:45.970 is what I consider my body block to be. 00:36:45.970 --> 00:36:49.600 Plug this into the layout placeholder. 00:36:49.600 --> 00:36:55.850 Therefore, so if I now go back to layout.html, and layout.html, 00:36:55.850 --> 00:36:57.910 it's almost all HTML by contrast. 00:36:57.910 --> 00:37:00.995 But there is this placeholder, and if I want to put a default value, 00:37:00.995 --> 00:37:01.870 I could say-- whoops. 00:37:01.870 --> 00:37:03.640 If I want to put a default value, I could 00:37:03.640 --> 00:37:07.240 put a default value there just in case some page does not have a body block. 00:37:07.240 --> 00:37:09.410 But in general, that's not going to be relevant. 00:37:09.410 --> 00:37:13.120 So this is just a placeholder, albeit a little verbose, that says 00:37:13.120 --> 00:37:16.730 plug in the page-specific content right here. 00:37:16.730 --> 00:37:20.500 So if I go now into greet.html, this one's even easier. 00:37:20.500 --> 00:37:23.500 I'm going to cut this content and get rid of everything else. 00:37:23.500 --> 00:37:29.080 Greet.html 2 is going to extend layouts, dot HTML extends plural, 00:37:29.080 --> 00:37:35.750 and then I'm going to have my body block here simply be this one line of code. 00:37:35.750 --> 00:37:38.200 And then I'm going to go ahead and end that block here. 00:37:38.200 --> 00:37:41.050 These are not HTML tags, this is not HTML syntax. 00:37:41.050 --> 00:37:44.980 Technically, the syntax we keep seeing with the curly braces, 00:37:44.980 --> 00:37:51.220 and these now curly braces with percent signs, is an example of Jinja syntax, 00:37:51.220 --> 00:37:56.050 J-I-N-J-A, which is a language, that some humans invented, 00:37:56.050 --> 00:37:57.970 for this purpose of templating. 00:37:57.970 --> 00:38:00.190 And the people who invented Flask decided, 00:38:00.190 --> 00:38:02.290 we're not going to come up with our own syntax, 00:38:02.290 --> 00:38:06.305 we're going to use these other people's syntax called Jinja syntax. 00:38:06.305 --> 00:38:08.680 So again, there starts to be at this point in the course, 00:38:08.680 --> 00:38:12.610 and really in computing, a lot of sharing, now, of ideas and sharing 00:38:12.610 --> 00:38:13.190 of code. 00:38:13.190 --> 00:38:17.080 So Flask is using this syntax, but other libraries and other languages 00:38:17.080 --> 00:38:19.270 might also too. 00:38:19.270 --> 00:38:24.070 All right, so now index.html is half HTML, 00:38:24.070 --> 00:38:26.500 half templating code, Jinja syntax. 00:38:26.500 --> 00:38:30.250 Greet.html is almost all Jinja syntax, no tags even, 00:38:30.250 --> 00:38:33.820 but because they both extend layout.html, 00:38:33.820 --> 00:38:37.150 now I think I've improved the design of this thing. 00:38:37.150 --> 00:38:41.180 If I go back to app.py, none of this really needs to change. 00:38:41.180 --> 00:38:44.350 I don't change my templates to mention layout.html, 00:38:44.350 --> 00:38:47.860 that's already implicit in the fact that we have the extends keyword. 00:38:47.860 --> 00:38:50.980 So now if I go ahead and open my terminal window, 00:38:50.980 --> 00:38:55.060 go back to the same folder as app.py and do Flask run, 00:38:55.060 --> 00:38:57.970 all right, my application is running on port 5000. 00:38:57.970 --> 00:39:01.630 Let me now go back to the /route in my browser and hit Enter, 00:39:01.630 --> 00:39:02.920 I have this form again. 00:39:02.920 --> 00:39:06.490 And just as a little check, let me view the source of the page 00:39:06.490 --> 00:39:08.170 that my browser is seeing. 00:39:08.170 --> 00:39:10.600 And there's all of the code. 00:39:10.600 --> 00:39:13.660 No mention of Jinja, no curly braces, no percent signs. 00:39:13.660 --> 00:39:14.335 It's just HTML. 00:39:14.335 --> 00:39:16.960 It's not quite pretty printed in the same way, but that's fine. 00:39:16.960 --> 00:39:19.502 Because now, we're starting to dynamically generate websites. 00:39:19.502 --> 00:39:22.750 And by that, I mean this isn't quite indented nicely or perfectly. 00:39:22.750 --> 00:39:23.350 That's fine. 00:39:23.350 --> 00:39:26.110 If it's indented in the source code version, 00:39:26.110 --> 00:39:28.150 doesn't matter what the browser really sees. 00:39:28.150 --> 00:39:30.640 Let me now go ahead and type in my name, click Submit. 00:39:30.640 --> 00:39:32.290 I should see, yep, hello, David. 00:39:32.290 --> 00:39:34.390 Let me go ahead and view the source of this page. 00:39:34.390 --> 00:39:38.450 And we'll see almost the same thing with what's plugged in there. 00:39:38.450 --> 00:39:41.980 So this is, now, web programming in the literal sense. 00:39:41.980 --> 00:39:45.280 I did not hard code a page that says hello comma David, hello comma Carter, 00:39:45.280 --> 00:39:46.270 hello comma Emma. 00:39:46.270 --> 00:39:49.990 I hardcoded a page that has a template with a placeholder, 00:39:49.990 --> 00:39:53.710 and now I'm using actual logic, some code in app.py, 00:39:53.710 --> 00:39:59.990 to actually tell the server what to send to the browser. 00:39:59.990 --> 00:40:05.140 All right, any questions, then, on where we're at here? 00:40:05.140 --> 00:40:07.510 This is now a web application. 00:40:07.510 --> 00:40:11.290 Simple though it is, it's no longer just a web site. 00:40:11.290 --> 00:40:12.161 Yeah? 00:40:12.161 --> 00:40:16.760 AUDIENCE: Is what we did just better for design or for memory [INAUDIBLE]?? 00:40:16.760 --> 00:40:19.470 DAVID: It better for design or for memory? 00:40:19.470 --> 00:40:19.970 Both. 00:40:19.970 --> 00:40:22.160 It's definitely better for design because, truly, 00:40:22.160 --> 00:40:24.350 if we had a third page, fourth page, I would really 00:40:24.350 --> 00:40:26.150 start just resorting to copy paste. 00:40:26.150 --> 00:40:29.270 And as you saw with home page, often, in the head of your page, 00:40:29.270 --> 00:40:32.870 you might want to include some CSS files like Bootstrap or something else. 00:40:32.870 --> 00:40:35.360 You might want to have other information up there. 00:40:35.360 --> 00:40:38.992 If you had to upgrade the version of Bootstrap or you change libraries, 00:40:38.992 --> 00:40:40.700 so you want to change one of those lines, 00:40:40.700 --> 00:40:44.090 you would literally have to go into three, four, a hundred different files 00:40:44.090 --> 00:40:45.870 to make one simple change. 00:40:45.870 --> 00:40:47.240 So that's bad design. 00:40:47.240 --> 00:40:48.740 And in terms of memory, yes. 00:40:48.740 --> 00:40:52.970 Theoretically, the server, because it knows there's this common layout, 00:40:52.970 --> 00:40:55.632 it can theoretically do some optimizations underneath the hood. 00:40:55.632 --> 00:40:58.340 Flask is probably doing that, but not in the mode we're using it. 00:40:58.340 --> 00:41:00.260 We're using it in development mode, which 00:41:00.260 --> 00:41:03.830 means it's typically reloading things each time. 00:41:03.830 --> 00:41:07.040 Other questions on this application? 00:41:10.210 --> 00:41:11.080 Anything at all? 00:41:11.080 --> 00:41:16.700 All right, so let me ask a question, not just in terms of the code design. 00:41:16.700 --> 00:41:18.880 What about the implications for privacy? 00:41:18.880 --> 00:41:24.430 Why is this maybe not the best design for users, how I've implemented this? 00:41:24.430 --> 00:41:26.470 I've used a web form, but-- 00:41:26.470 --> 00:41:27.305 Yeah? 00:41:27.305 --> 00:41:28.730 AUDIENCE: For some reason, you wanted your name. 00:41:28.730 --> 00:41:30.920 So these private people could just look at the URL. 00:41:30.920 --> 00:41:31.420 DAVID: Yeah. 00:41:31.420 --> 00:41:33.280 I mean, if you have a nosy sibling or roommate 00:41:33.280 --> 00:41:35.290 and they have access to your laptop and they just 00:41:35.290 --> 00:41:37.540 go trolling through your autocomplete or your history, 00:41:37.540 --> 00:41:40.570 like, literally what you typed into a website is going to be visible. 00:41:40.570 --> 00:41:43.570 Not a big deal if it's your name, but if it's your password, your credit 00:41:43.570 --> 00:41:45.790 card or anything else that's mildly sensitive, 00:41:45.790 --> 00:41:49.180 you probably don't want it ending up in the URL at all 00:41:49.180 --> 00:41:51.640 even if you're in incognito mode or whatnot. 00:41:51.640 --> 00:41:56.390 You just don't want to expose yourself or your users to that kind of risk. 00:41:56.390 --> 00:41:58.180 So perhaps, we can do better than that. 00:41:58.180 --> 00:42:00.550 And fortunately, this one is actually an easy change. 00:42:00.550 --> 00:42:05.740 Let me go into my index.html where my form is. 00:42:05.740 --> 00:42:10.510 And in my form, I can just change the method from GET to POST. 00:42:10.510 --> 00:42:13.150 It's still going to send key value pairs to the server, 00:42:13.150 --> 00:42:15.190 but it's not going to put them in the URL. 00:42:15.190 --> 00:42:18.495 The upside of which is that we can assuage this privacy concern, 00:42:18.495 --> 00:42:20.620 but I'm going to have to make one other change too. 00:42:20.620 --> 00:42:25.090 Because now, if I go ahead and run Flask again after making that change, 00:42:25.090 --> 00:42:29.570 and I now reload the form to make sure I have the latest version. 00:42:29.570 --> 00:42:32.410 You should be in the habit of going to View, Developer, 00:42:32.410 --> 00:42:35.022 View Source, or Developer Tools just to make sure 00:42:35.022 --> 00:42:37.480 that what you're seeing in your browser is what you intend. 00:42:37.480 --> 00:42:40.300 And yes, I do see what I wanted. 00:42:40.300 --> 00:42:41.890 Method equals POST now. 00:42:41.890 --> 00:42:44.620 Let me go ahead and type in David and click Submit. 00:42:44.620 --> 00:42:47.110 Now I get a different error. 00:42:47.110 --> 00:42:51.140 This one is HTTP 405, method not allowed. 00:42:51.140 --> 00:42:52.240 Why is that? 00:42:52.240 --> 00:42:56.320 Well, in my Flask application, I've only defined a couple of routes so far. 00:42:56.320 --> 00:42:59.840 One of which is for slash, then that worked fine. 00:42:59.840 --> 00:43:02.990 One of which is for /greet, and that used to work fine. 00:43:02.990 --> 00:43:08.540 But apparently, what Flask is doing is it only supports GET by default. 00:43:08.540 --> 00:43:13.750 So if I want to change this route to support different methods, I can say, 00:43:13.750 --> 00:43:18.920 quote unquote "POST" inside of this parameter here. 00:43:18.920 --> 00:43:23.830 So that now, I can actually support POST, not just GET. 00:43:23.830 --> 00:43:30.880 And if I now restart Flask, so Flask run, Enter, and I go back to this URL. 00:43:30.880 --> 00:43:33.520 Let me go back one screen to the form, reload 00:43:33.520 --> 00:43:35.440 the page just to make sure I have the latest 00:43:35.440 --> 00:43:37.023 even though nothing there has changed. 00:43:37.023 --> 00:43:40.630 Type David and click Submit now, now I should see hello, world. 00:43:40.630 --> 00:43:47.380 Notice that I'm at the greet route, but there's no mention of name 00:43:47.380 --> 00:43:50.260 equals anything in the URL. 00:43:50.260 --> 00:43:52.570 All right, so that's an interesting takeaway. 00:43:52.570 --> 00:43:57.380 It's a simple change, but whereas GET puts things in the URL, POST does not. 00:43:57.380 --> 00:43:59.620 But it still works so long as you tweak the backend 00:43:59.620 --> 00:44:04.210 to look as a POST request, which means look deeper in the envelope. 00:44:04.210 --> 00:44:06.760 It's not going to be as simple as looking at the URL itself. 00:44:06.760 --> 00:44:08.560 Why shouldn't we just always use POST? 00:44:11.730 --> 00:44:15.990 Why not use POST everywhere? 00:44:15.990 --> 00:44:17.580 Any thoughts? 00:44:17.580 --> 00:44:21.060 Right, because it's obnoxious to be putting any information in URLs 00:44:21.060 --> 00:44:24.322 if you're leaving these little breadcrumbs in your history and people 00:44:24.322 --> 00:44:26.280 can poke around and see what you've been doing. 00:44:29.940 --> 00:44:31.377 Yeah, what do you think? 00:44:31.377 --> 00:44:33.762 AUDIENCE: You're supposed to duplicate [INAUDIBLE].. 00:44:37.270 --> 00:44:37.770 DAVID: Yeah. 00:44:37.770 --> 00:44:41.030 I mean, if you get rid of GET requests and put nothing in the URL, 00:44:41.030 --> 00:44:44.432 your history, your autocomplete, gets pretty less useful. 00:44:44.432 --> 00:44:47.390 Because none of the information is there for storage, so you can't just 00:44:47.390 --> 00:44:48.807 go through the menu and hit Enter. 00:44:48.807 --> 00:44:50.420 You'd have to re-fill out the form. 00:44:50.420 --> 00:44:52.628 And there's this other symptom that you can see here. 00:44:52.628 --> 00:44:55.253 Let me zoom out and let me just reload this page. 00:44:55.253 --> 00:44:57.170 Notice that you'll get this warning, and it'll 00:44:57.170 --> 00:45:01.790 look different in Safari and Firefox and Edge and Chrome here, confirm form. 00:45:01.790 --> 00:45:02.580 args 00:45:02.580 --> 00:45:06.170 So your browser might remember what your inputs were and that's great, 00:45:06.170 --> 00:45:07.950 but just while you're on the page. 00:45:07.950 --> 00:45:12.800 And this is in contrast to GET, where the state is information. 00:45:12.800 --> 00:45:16.195 Like, key value pairs is embedded in the URL itself. 00:45:16.195 --> 00:45:18.320 And if you looked at an email I sent earlier today, 00:45:18.320 --> 00:45:21.515 I deliberately linked to https://www.google.c 00:45:21.515 --> 00:45:23.120 om/search?q=what+time+is+it. 00:45:29.210 --> 00:45:33.710 This is, by definition, a GET request when you click on it. 00:45:33.710 --> 00:45:37.100 Because it's going to grab the information, the key value pair, 00:45:37.100 --> 00:45:40.410 from the URL, send it to Google server, and it's just going to work. 00:45:40.410 --> 00:45:42.830 And the reason I sent this via email earlier was I 00:45:42.830 --> 00:45:46.050 wanted people to very quickly be able to check what is the current time. 00:45:46.050 --> 00:45:49.970 And so I can sort automate the process of creating a Google search for you, 00:45:49.970 --> 00:45:52.190 but that you induce when you click that link. 00:45:52.190 --> 00:45:57.620 If Google did not support GET, they only supported this, the best I could do 00:45:57.620 --> 00:46:00.410 is send you all to this URL which, unfortunately, 00:46:00.410 --> 00:46:02.000 has no useful information. 00:46:02.000 --> 00:46:05.210 I would have had to add to my email, by the way, type in the words 00:46:05.210 --> 00:46:07.050 what time is it. 00:46:07.050 --> 00:46:08.760 So it's just bad for usability. 00:46:08.760 --> 00:46:11.960 So there, too, we might have design when it comes to the low level code, 00:46:11.960 --> 00:46:15.500 but also the design when it comes to the user experience, or UX, 00:46:15.500 --> 00:46:17.180 as a computer scientist would call it. 00:46:17.180 --> 00:46:20.450 Just in terms of what you want to optimize for, ultimately. 00:46:20.450 --> 00:46:22.320 So GET and POST both have their roles. 00:46:22.320 --> 00:46:24.820 It depends on what kind of functionality you want to provide 00:46:24.820 --> 00:46:29.150 and what kind of sensitivity there might be around it. 00:46:29.150 --> 00:46:32.360 All right, any questions, then, on this, our first web application? 00:46:32.360 --> 00:46:35.960 Super simple, just gets someone's name and prints it back out. 00:46:35.960 --> 00:46:38.270 But we now have all the plumbing with which 00:46:38.270 --> 00:46:41.270 to create really most anything we want. 00:46:44.058 --> 00:46:46.350 All right, let's go ahead and take a five minute break. 00:46:46.350 --> 00:46:50.190 And when we come back, we'll add to this some first year intramural sports. 00:46:50.190 --> 00:46:52.440 All right, so we are back. 00:46:52.440 --> 00:46:54.420 And recall that the last thing we just changed 00:46:54.420 --> 00:46:57.180 was the route to use POST instead of GET. 00:46:57.180 --> 00:47:00.090 So gone is my name and any value in the URL. 00:47:00.090 --> 00:47:06.270 But there was a subtle bug or change here that we didn't call out earlier. 00:47:06.270 --> 00:47:09.300 I did type David into the form and I did click Submit, 00:47:09.300 --> 00:47:12.690 and yet here it is saying hello comma world. 00:47:12.690 --> 00:47:15.730 So that seems to be broken all of a sudden, 00:47:15.730 --> 00:47:18.810 even though we added support for POST. 00:47:18.810 --> 00:47:21.030 But something must be wrong. 00:47:21.030 --> 00:47:23.580 Logically, it must be the case here. 00:47:23.580 --> 00:47:27.700 Intuitively, that if I'm seeing hello, world, that's the default value 00:47:27.700 --> 00:47:29.400 I gave the name variable. 00:47:29.400 --> 00:47:32.340 It must be that it's not seeing a key called 00:47:32.340 --> 00:47:36.720 name in request.args, which is this. 00:47:36.720 --> 00:47:39.090 Gives you access to everything after the URL. 00:47:39.090 --> 00:47:41.710 That's because there's this other thing we should know about, 00:47:41.710 --> 00:47:43.980 which is not just request.args but request.form. 00:47:43.980 --> 00:47:48.450 These are horribly named, but request.args is for GET requests, 00:47:48.450 --> 00:47:51.042 request.form is for POST requests. 00:47:51.042 --> 00:47:53.250 Otherwise, they're pretty much functionally the same. 00:47:53.250 --> 00:47:55.830 But the onus is on you, the user or the programmer, 00:47:55.830 --> 00:47:58.070 to make sure you're using the right one. 00:47:58.070 --> 00:48:00.910 So I think if we want to get rid of the world 00:48:00.910 --> 00:48:03.240 and actually see what I, the human, typed in, 00:48:03.240 --> 00:48:07.440 I think I can just change request.args to request.form. 00:48:07.440 --> 00:48:10.020 Still dot get, still quote unquote "name," 00:48:10.020 --> 00:48:14.490 and now, if I go ahead and rerun Flask in my terminal window, 00:48:14.490 --> 00:48:17.015 go back to my browser, go back to-- and actually, 00:48:17.015 --> 00:48:18.390 I won't even go back to the form. 00:48:18.390 --> 00:48:21.630 I will literally just reload, Command R or Control R, 00:48:21.630 --> 00:48:24.810 and what this warning is saying is it's going to submit 00:48:24.810 --> 00:48:27.240 the same information to the website. 00:48:27.240 --> 00:48:31.140 When I click Continue, now I should see hello comma David. 00:48:31.140 --> 00:48:33.420 So again, you, too, are going to encounter, probably, 00:48:33.420 --> 00:48:35.040 all these little subtleties. 00:48:35.040 --> 00:48:38.040 But if you focus on, really, the first principles of last week, 00:48:38.040 --> 00:48:40.920 like what it HTTP, how does it get request work, 00:48:40.920 --> 00:48:43.025 how does a POST request work now, you should 00:48:43.025 --> 00:48:45.150 have a lot of the mental building blocks with which 00:48:45.150 --> 00:48:47.340 to solve problems like these. 00:48:47.340 --> 00:48:50.580 And let me give you one other mental model, now, for what it is we're doing. 00:48:50.580 --> 00:48:54.900 This framework called Flask is just an example of many different frameworks 00:48:54.900 --> 00:48:58.500 that all implement the same paradigm, the same way of thinking 00:48:58.500 --> 00:49:00.720 and the same way of programming applications. 00:49:00.720 --> 00:49:04.170 And that's known as MVC, model view controller. 00:49:04.170 --> 00:49:08.670 And here's a very simple diagram that represents the process that you 00:49:08.670 --> 00:49:10.540 and I have been implementing thus far. 00:49:10.540 --> 00:49:13.530 And actually, this is more than we've been implementing thus far. 00:49:13.530 --> 00:49:17.362 In app.py is what a programmer would typically call the controller. 00:49:17.362 --> 00:49:19.320 That's the code you're writing, this are called 00:49:19.320 --> 00:49:23.370 business logic that makes all of the decisions, decides what to render, 00:49:23.370 --> 00:49:25.710 what values to show, and so forth. 00:49:25.710 --> 00:49:32.100 In layout.html, index.html, greet.html is the so-called view templates 00:49:32.100 --> 00:49:34.890 that is the visualizations that the human actually 00:49:34.890 --> 00:49:36.510 sees, the user interface. 00:49:36.510 --> 00:49:40.800 Those things are dumb, they pretty much just say plop some values here. 00:49:40.800 --> 00:49:43.230 All of the hard work is done in app.py. 00:49:43.230 --> 00:49:48.240 So controller, AKA app.py, is where your Python code generally is. 00:49:48.240 --> 00:49:53.730 And in your view is where your HTML and your Jinja code, your Jinja templating, 00:49:53.730 --> 00:49:57.750 the curly braces, the curly braces with percent signs, usually is. 00:49:57.750 --> 00:50:01.650 We haven't added an M to MVC yet model, that's 00:50:01.650 --> 00:50:04.560 going to refer to things like CSV files or databases. 00:50:04.560 --> 00:50:08.355 The model, where do you keep actual data, typically long term. 00:50:08.355 --> 00:50:10.230 So we'll come back to that, but this picture, 00:50:10.230 --> 00:50:14.550 where you have one of these-- each of these components communicating with one 00:50:14.550 --> 00:50:17.460 another is representative of how a lot of frameworks work. 00:50:17.460 --> 00:50:21.030 What we're teaching today, this week, is not really specific to Python. 00:50:21.030 --> 00:50:23.820 It's not really specific to Flask, even though we're using Flask. 00:50:23.820 --> 00:50:25.710 It really is a very common paradigm that you 00:50:25.710 --> 00:50:30.450 could implement in Java, C sharp, or bunches of other languages as well. 00:50:30.450 --> 00:50:34.833 All right, so let's now pivot back to VS Code here. 00:50:34.833 --> 00:50:36.750 Let me stop running Flask, and let me go ahead 00:50:36.750 --> 00:50:42.160 and create a new folder altogether after closing these files here. 00:50:42.160 --> 00:50:46.830 And let me go ahead and create a folder called FroshIMS, 00:50:46.830 --> 00:50:50.100 representing freshman intramural sports or first year intramural sports 00:50:50.100 --> 00:50:51.660 that I can now CD into. 00:50:51.660 --> 00:50:54.840 And now I'm going to code an app.py. 00:50:54.840 --> 00:50:58.590 And in anticipation, I'm going to create another templates directory. 00:50:58.590 --> 00:51:00.490 This one in the FroshIMS folder. 00:51:00.490 --> 00:51:04.590 And then in my templates directory, I'm going to create a layout.html. 00:51:04.590 --> 00:51:06.720 and I'm just going to get myself started here. 00:51:06.720 --> 00:51:08.460 FroshIMS will go here. 00:51:08.460 --> 00:51:10.980 I'm just copying my layout from earlier because most 00:51:10.980 --> 00:51:15.270 of my interesting work, this time, is now going to be, initially, in app.py. 00:51:15.270 --> 00:51:16.750 So what is it we're creating? 00:51:16.750 --> 00:51:20.520 So literally, the very first thing I wrote as a web application 00:51:20.520 --> 00:51:24.280 20 years ago, was a site that literally looked like this. 00:51:24.280 --> 00:51:26.370 So I was like a sophomore or junior at the time. 00:51:26.370 --> 00:51:29.220 I'd taken CS50 and a follow-on class only. 00:51:29.220 --> 00:51:31.127 I had no idea how to do web programming. 00:51:31.127 --> 00:51:33.960 Neither of those two courses taught web programming back in the day. 00:51:33.960 --> 00:51:36.630 So I taught myself, at the time, a language called Perl. 00:51:36.630 --> 00:51:40.238 And I learned a little something about CSV files, and I sort of read enough-- 00:51:40.238 --> 00:51:42.780 can't even say googled enough, because Google didn't come out 00:51:42.780 --> 00:51:44.380 for a couple of years later. 00:51:44.380 --> 00:51:48.670 Read enough online to figure out how to make a web application so that students 00:51:48.670 --> 00:51:51.520 on campus, first years, could actually register 00:51:51.520 --> 00:51:54.400 via a website for intramural sports. 00:51:54.400 --> 00:51:57.400 Back in my day, you would literally fill out a piece of paper 00:51:57.400 --> 00:52:00.730 and then walk it across the yard to Wigglesworth Hall, one of the dorms, 00:52:00.730 --> 00:52:03.100 slide it under the dorm of the Proctor or RA, 00:52:03.100 --> 00:52:05.620 and thus you were registered for sports so. 00:52:05.620 --> 00:52:07.240 1996, 1997. 00:52:07.240 --> 00:52:08.770 We could do better by then. 00:52:08.770 --> 00:52:10.750 There was an internet, just wasn't really being 00:52:10.750 --> 00:52:13.040 used much on campus or more generally. 00:52:13.040 --> 00:52:16.540 So background images that repeat infinitely 00:52:16.540 --> 00:52:18.850 was in vogue, apparently, at the time. 00:52:18.850 --> 00:52:21.190 All of this was like images that I had to hand make 00:52:21.190 --> 00:52:26.120 because we did not have the features that JavaScript and CSS nowadays have. 00:52:26.120 --> 00:52:30.040 So it was really just HTML, and it was really just controller code written, 00:52:30.040 --> 00:52:31.750 not in Python, but in Perl. 00:52:31.750 --> 00:52:34.540 And it was really just the same building blocks 00:52:34.540 --> 00:52:37.370 that we hear already today now have. 00:52:37.370 --> 00:52:39.940 So we'll get rid of all of the imagery and focus more 00:52:39.940 --> 00:52:42.190 on the functionality and the aesthetics, but let's see 00:52:42.190 --> 00:52:45.490 if we can whip up a web application via which someone could 00:52:45.490 --> 00:52:48.520 register for one such intramural sport. 00:52:48.520 --> 00:52:52.210 So in app.py, me go ahead and import some familiar things now. 00:52:52.210 --> 00:52:55.720 From Flask, let's import capital Flask, which 00:52:55.720 --> 00:52:58.720 is that function we need to kick everything kick start everything. 00:52:58.720 --> 00:53:01.900 Render templates, so we have the ability to render, that is print out, 00:53:01.900 --> 00:53:04.360 those templates, and request so that we have the ability 00:53:04.360 --> 00:53:07.088 to get at input from the human. 00:53:07.088 --> 00:53:09.130 Let me go ahead and create the application itself 00:53:09.130 --> 00:53:11.540 using this magical incantation here. 00:53:11.540 --> 00:53:18.380 And then let's go ahead and define a route for slash for instance first. 00:53:18.380 --> 00:53:20.260 I'm going to define a function called index. 00:53:20.260 --> 00:53:22.900 But just to be clear, this function could be anything. 00:53:22.900 --> 00:53:25.460 Foo, bar, baz, anything else. 00:53:25.460 --> 00:53:27.520 But I tend to name them in a manner that's 00:53:27.520 --> 00:53:29.260 consistent with what the route is called. 00:53:29.260 --> 00:53:31.300 But you could call it anything you want, it's 00:53:31.300 --> 00:53:34.840 just the function that will get called for this particular route. 00:53:34.840 --> 00:53:37.090 Now, let me go ahead here and just get things started. 00:53:37.090 --> 00:53:40.533 Return, render template of index.html. 00:53:40.533 --> 00:53:41.950 Just keep it simple, nothing more. 00:53:41.950 --> 00:53:44.860 So there's nothing really FroshIM specific about this here, 00:53:44.860 --> 00:53:47.410 I just want to make sure I'm doing everything correctly. 00:53:47.410 --> 00:53:49.390 Meanwhile, I've got my layout. 00:53:49.390 --> 00:53:53.800 OK, let me go ahead, and in my templates directory, code a file 00:53:53.800 --> 00:53:55.990 called index.html. 00:53:55.990 --> 00:54:02.290 And let's just do extends layout.html at the top 00:54:02.290 --> 00:54:04.390 just so that we get benefit from that template. 00:54:04.390 --> 00:54:06.250 And down here, I'm just going to say to do. 00:54:06.250 --> 00:54:09.250 Just so that I have something going on visually to make sure 00:54:09.250 --> 00:54:10.690 I've not screwed up yet. 00:54:10.690 --> 00:54:14.020 In my FroshIMS directory, let me do Flask run. 00:54:14.020 --> 00:54:17.200 Let me now go back to my previous URL, which used to be my hello example. 00:54:17.200 --> 00:54:21.850 But now, I'm serving up the FroshIM site. 00:54:21.850 --> 00:54:23.590 Oh, and I'm seeing nothing. 00:54:23.590 --> 00:54:26.860 That's because I screwed up accidentally. 00:54:26.860 --> 00:54:30.090 What did I do wrong in index.html? 00:54:34.580 --> 00:54:35.600 What am I doing wrong? 00:54:35.600 --> 00:54:38.468 This file extends layout.html, but-- 00:54:38.468 --> 00:54:40.010 AUDIENCE: You left out the block tag? 00:54:40.010 --> 00:54:40.510 DAVID: Yeah. 00:54:40.510 --> 00:54:44.900 I forgot to tell Flask what to plug into that layout. 00:54:44.900 --> 00:54:49.040 So I just need to say block body, and then in here, I can just say to do 00:54:49.040 --> 00:54:51.080 or whatever I want to eventually get around to. 00:54:51.080 --> 00:54:52.310 Then end the block. 00:54:52.310 --> 00:54:53.990 Let me end this tag here. 00:54:53.990 --> 00:54:56.330 OK, so now it looks ugly, more cryptic. 00:54:56.330 --> 00:54:59.210 But this is, again, the essence of doing templating. 00:54:59.210 --> 00:55:03.340 Let me now restart Flask up here, let me go back to the page. 00:55:03.340 --> 00:55:03.972 Let me reload. 00:55:03.972 --> 00:55:05.930 Crossing my fingers this time, and there we go. 00:55:05.930 --> 00:55:06.320 To do. 00:55:06.320 --> 00:55:08.480 So it's not the application I want, but at least I 00:55:08.480 --> 00:55:10.868 know I have some of the plumbing there by default. 00:55:10.868 --> 00:55:13.160 All right, so if I want the user to be able to register 00:55:13.160 --> 00:55:15.350 for one of these sports, let's enhance, now, 00:55:15.350 --> 00:55:18.290 index.html to actually have a form that's 00:55:18.290 --> 00:55:22.050 maybe got a dropdown menu for all of the sports for which you can register. 00:55:22.050 --> 00:55:24.230 So let me go into this template here. 00:55:24.230 --> 00:55:27.410 And instead of to do, let's go ahead and give myself, 00:55:27.410 --> 00:55:31.160 how about an H1 tag that just says register so the user knows what it is 00:55:31.160 --> 00:55:31.970 they're looking at. 00:55:31.970 --> 00:55:35.270 How about a form tag that's going to use POST, 00:55:35.270 --> 00:55:38.450 just because it's not really necessary to put this kind of information 00:55:38.450 --> 00:55:39.530 in the URL. 00:55:39.530 --> 00:55:42.260 The action for that, how about we plan to create 00:55:42.260 --> 00:55:47.210 a register route so that we're sending information from to a register route. 00:55:47.210 --> 00:55:48.770 So we'll have to come back to that. 00:55:48.770 --> 00:55:54.650 In here, let me go ahead and create, how about an input with autocomplete 00:55:54.650 --> 00:55:58.020 equals off, auto focus on. 00:55:58.020 --> 00:56:00.062 How about a name equals name, because I'm 00:56:00.062 --> 00:56:03.020 going to ask the student for their name using placeholder text of quote 00:56:03.020 --> 00:56:03.867 unquote "name." 00:56:03.867 --> 00:56:05.450 And the type of this box will be text. 00:56:05.450 --> 00:56:07.610 So this is pretty much identical to before. 00:56:07.610 --> 00:56:11.060 But if you've not seen this yet, let's create a select menu, 00:56:11.060 --> 00:56:13.280 a so-called dropdown menu in HTML. 00:56:13.280 --> 00:56:16.790 And maybe the first option I want to be in there 00:56:16.790 --> 00:56:20.570 is going to be, oh, how about the current three 00:56:20.570 --> 00:56:27.560 sports for the fall, which are basketball, and another option 00:56:27.560 --> 00:56:30.140 is going to be soccer, and a third option is 00:56:30.140 --> 00:56:35.700 going to be ultimate frisbee for first year intramurals right now. 00:56:35.700 --> 00:56:37.160 So I've got those three options. 00:56:37.160 --> 00:56:38.300 I've got my form. 00:56:38.300 --> 00:56:42.650 I haven't implemented my route yet, but this feels like a good time 00:56:42.650 --> 00:56:45.920 to go back now and check if my form has reloaded. 00:56:45.920 --> 00:56:48.658 So let me go ahead and stop and start Flask. 00:56:48.658 --> 00:56:51.200 You'll see there's ways to automate the process of restarting 00:56:51.200 --> 00:56:53.360 the server that we'll do for you for problem set nine, 00:56:53.360 --> 00:56:55.070 so you don't have to keep stopping Flask. 00:56:55.070 --> 00:56:59.060 Let me reload my index route and OK, it's not that pretty. 00:56:59.060 --> 00:57:01.220 It's not though, maybe-- 00:57:01.220 --> 00:57:02.000 nor was this. 00:57:02.000 --> 00:57:04.070 But it now has at least some functionality 00:57:04.070 --> 00:57:07.160 where I can type in my name and then type in the sport. 00:57:07.160 --> 00:57:09.740 Now, I might be biasing people toward basketball. 00:57:09.740 --> 00:57:14.210 Like UX wise, user experience wise, it's obnoxious to precheck 00:57:14.210 --> 00:57:15.810 basketball but not the others. 00:57:15.810 --> 00:57:17.810 So there's some little tweaks we can make there. 00:57:17.810 --> 00:57:20.060 Let me go back into index.html. 00:57:20.060 --> 00:57:26.348 Let me create an empty option up here that, technically, this option is not 00:57:26.348 --> 00:57:27.890 going to have the name of any sports. 00:57:27.890 --> 00:57:30.348 But it's just going to have a word I want the human to see, 00:57:30.348 --> 00:57:34.860 so I'm actually going to disable this option and make it selected by default. 00:57:34.860 --> 00:57:37.250 But I'm going to say sport up here. 00:57:37.250 --> 00:57:40.730 And there's different ways to do this, this is just one way of creating, 00:57:40.730 --> 00:57:42.470 essentially, a-- 00:57:42.470 --> 00:57:43.430 whoops, option. 00:57:43.430 --> 00:57:44.660 Yep, that looks right. 00:57:44.660 --> 00:57:47.000 Creating a placeholder sports so that the user 00:57:47.000 --> 00:57:49.100 sees something in the dropdown. 00:57:49.100 --> 00:57:52.170 Let me go ahead and restart Flask, reload the page, 00:57:52.170 --> 00:57:54.170 and now it's just going to be marginally better. 00:57:54.170 --> 00:57:56.360 Now you see sport that's checked by default, 00:57:56.360 --> 00:57:59.330 but you have to check one of these other ones ultimately. 00:57:59.330 --> 00:58:00.720 All right, so that's pretty good. 00:58:00.720 --> 00:58:02.930 So let me now type in David. 00:58:02.930 --> 00:58:05.360 I'll register for ultimate frisbee. 00:58:05.360 --> 00:58:08.750 OK, I definitely forgot something. 00:58:08.750 --> 00:58:09.540 Submit button. 00:58:09.540 --> 00:58:10.940 So let's add that. 00:58:10.940 --> 00:58:15.020 All right, so input type equals submit. 00:58:15.020 --> 00:58:16.340 All right, let's put that in. 00:58:16.340 --> 00:58:19.010 Restart Flask, reload. 00:58:19.010 --> 00:58:19.783 Getting better. 00:58:19.783 --> 00:58:21.200 Submit could be a little prettier. 00:58:21.200 --> 00:58:25.730 Recall that we can change some of these HTTP-- these HTML attributes. 00:58:25.730 --> 00:58:27.890 The value of this button should be register, maybe, 00:58:27.890 --> 00:58:29.480 just to make things a little prettier. 00:58:29.480 --> 00:58:32.695 Let me now reload the page and register. 00:58:32.695 --> 00:58:35.570 All right, so now we really have the beginnings of the user interface 00:58:35.570 --> 00:58:39.980 that I created some years ago to let people actually register for the sport. 00:58:39.980 --> 00:58:43.880 So let's go, now, and create maybe the other route that we might need. 00:58:43.880 --> 00:58:44.930 Let me go into app.py. 00:58:44.930 --> 00:58:47.925 And in here, if we want to allow the user to register, 00:58:47.925 --> 00:58:51.050 let's do a little bit of error checking which I promised we'd come back to. 00:58:51.050 --> 00:58:52.850 What could the user do wrong? 00:58:52.850 --> 00:58:55.040 Because assume that they will. 00:58:55.040 --> 00:58:56.870 One, they might not type their name. 00:58:56.870 --> 00:58:58.743 Two, they might not choose a sport. 00:58:58.743 --> 00:59:00.410 So they might just submit an empty form. 00:59:00.410 --> 00:59:02.540 So that's two things we could check for, just 00:59:02.540 --> 00:59:05.960 so that we're not scoring bogus entries in our database, ultimately. 00:59:05.960 --> 00:59:09.590 So let's create another route called greet, /greet. 00:59:09.590 --> 00:59:12.740 And then in this route, let's create a function called greet 00:59:12.740 --> 00:59:14.900 but can be called anything we want. 00:59:14.900 --> 00:59:18.293 And then let's go ahead, and in the greet function, let's go ahead 00:59:18.293 --> 00:59:19.460 and validate the submission. 00:59:19.460 --> 00:59:21.590 So a little comment to myself here. 00:59:21.590 --> 00:59:30.270 How about if there is not a request.form GET name value, 00:59:30.270 --> 00:59:32.450 so that is if that function returns nothing, 00:59:32.450 --> 00:59:36.300 like quote unquote, or the special word none in Python. 00:59:36.300 --> 00:59:47.280 Or request.form.get"sport" not in quote unquote, what were they? 00:59:47.280 --> 00:59:54.360 Basketball, the other one was soccer, and the last was ultimate frisbee. 00:59:54.360 --> 00:59:58.200 Getting a little long, but notice what I'm-- the question I'm asking. 00:59:58.200 --> 01:00:01.290 If the user did not give us a name, that is, 01:00:01.290 --> 01:00:03.910 if this function returns the equivalent of false, 01:00:03.910 --> 01:00:07.830 which is, quote unquote, or literally none if there's no such parameter. 01:00:07.830 --> 01:00:14.490 Or if the sport the user provided is not some value in basketball, soccer, 01:00:14.490 --> 01:00:18.300 or ultimate frisbee, which I've defined as a Python list, then let's go ahead 01:00:18.300 --> 01:00:19.890 and just yell at the user in some way. 01:00:19.890 --> 01:00:25.200 Let's return render template of failure.html. 01:00:25.200 --> 01:00:28.450 And that's just going to be some error message inside of that file. 01:00:28.450 --> 01:00:30.990 Otherwise, if they get this far, let's go ahead 01:00:30.990 --> 01:00:34.350 and confirm registration by just returning-- whoops, 01:00:34.350 --> 01:00:40.420 returning render template quote unquote "success" dot HTML. 01:00:40.420 --> 01:00:42.700 All right, so a couple quick things to do. 01:00:42.700 --> 01:00:47.350 Let me first go in and in my templates directory, 01:00:47.350 --> 01:00:50.530 let's create this failure.html file. 01:00:50.530 --> 01:00:53.430 And this is just meant to be a message to the user 01:00:53.430 --> 01:00:56.500 that they fail to provide the information correctly. 01:00:56.500 --> 01:00:59.280 So let me go ahead and in failure.html. 01:00:59.280 --> 01:01:02.250 not repeat my past mistake. 01:01:02.250 --> 01:01:07.140 So let me extend layout.html and in the block body, you are not registered. 01:01:07.140 --> 01:01:10.140 I'll just yell at them like that so that they know something went wrong. 01:01:10.140 --> 01:01:14.760 And then let me create one other file called success.html, that 01:01:14.760 --> 01:01:17.143 similarly is mostly just Jinja syntax. 01:01:17.143 --> 01:01:19.560 And I'm just going to say for now, even though they're not 01:01:19.560 --> 01:01:22.140 technically registered in any database, you are registered. 01:01:22.140 --> 01:01:24.270 That's what we mean by success. 01:01:24.270 --> 01:01:27.450 All right, so let me go ahead, and back in my FroshIMS, 01:01:27.450 --> 01:01:29.370 directory run Flask run. 01:01:29.370 --> 01:01:31.560 Let me go back to the form and reload. 01:01:31.560 --> 01:01:33.150 Should look the same. 01:01:33.150 --> 01:01:36.120 All right, so now let me not cooperate and just 01:01:36.120 --> 01:01:39.300 immediately click Register impatiently. 01:01:39.300 --> 01:01:42.960 OK, what did I do wrong. 01:01:42.960 --> 01:01:47.008 Register-- oh, I'm confusing our two examples. 01:01:47.008 --> 01:01:48.300 All right, I spotted the error. 01:01:48.300 --> 01:01:49.133 What did I do wrong? 01:01:51.600 --> 01:01:54.480 Unintentional. 01:01:54.480 --> 01:01:58.050 There's where I am, what did I actually invent over here? 01:02:01.910 --> 01:02:05.140 Where did I screw up? 01:02:05.140 --> 01:02:06.800 Anyone? 01:02:06.800 --> 01:02:08.050 AUDIENCE: Register, not greet. 01:02:08.050 --> 01:02:08.758 DAVID: Thank you. 01:02:08.758 --> 01:02:09.850 So register, not greet. 01:02:09.850 --> 01:02:12.970 I had last example on my mind, so the route should be register. 01:02:12.970 --> 01:02:16.360 Ironically, the function could be greet, because that actually doesn't matter. 01:02:16.360 --> 01:02:20.030 But to keep ourselves sane, let's use the one and the same words there. 01:02:20.030 --> 01:02:22.457 Let me go ahead now and start Flask as intended. 01:02:22.457 --> 01:02:24.790 Let me reload the form just to make sure all is working. 01:02:24.790 --> 01:02:29.140 Now, let me not cooperate and be a bad user, clicking register-- 01:02:29.140 --> 01:02:30.220 oh my God. 01:02:30.220 --> 01:02:32.890 OK, other unintended mistake. 01:02:32.890 --> 01:02:35.120 But this one we've seen before. 01:02:35.120 --> 01:02:37.960 Notice that by default, route only support GET. 01:02:37.960 --> 01:02:40.960 So if I want to specifically support POST, 01:02:40.960 --> 01:02:47.710 I have to pass in, by a methods parameter, a list of allowed route 01:02:47.710 --> 01:02:50.590 methods that could be GET comma POST, but if I 01:02:50.590 --> 01:02:54.460 don't have no need for a GET in this context, I can just do POST. 01:02:54.460 --> 01:02:56.980 All right, now let's do this one last time. 01:02:56.980 --> 01:02:59.680 Reload the form to make sure everything's OK, click Register, 01:02:59.680 --> 01:03:01.433 and you are not registered. 01:03:01.433 --> 01:03:02.350 So it's catching that. 01:03:02.350 --> 01:03:04.767 All right, let me go ahead and at least give them my name. 01:03:04.767 --> 01:03:05.500 Register. 01:03:05.500 --> 01:03:06.550 You are not registered. 01:03:06.550 --> 01:03:11.900 Fine, I'm going to go ahead and be David with ultimate frisbee register. 01:03:11.900 --> 01:03:14.260 Huh. 01:03:14.260 --> 01:03:15.640 OK. 01:03:15.640 --> 01:03:20.780 What should I-- what did I mean to do here? 01:03:20.780 --> 01:03:22.810 All right, so let's figure this out. 01:03:22.810 --> 01:03:26.950 How to debug something like this, which is my third and final unintended, 01:03:26.950 --> 01:03:29.320 unforced error? 01:03:29.320 --> 01:03:32.590 How can we go about troubleshooting this? 01:03:32.590 --> 01:03:35.440 Turn this into the teachable moment. 01:03:35.440 --> 01:03:38.020 All right, well first, some safety checks. 01:03:38.020 --> 01:03:39.610 What did I actually submit? 01:03:39.610 --> 01:03:42.760 Let me go ahead and view page source, a good rule of thumb. 01:03:42.760 --> 01:03:45.320 Look at the HTML that you actually sent to the user. 01:03:45.320 --> 01:03:49.490 So here, I have an input with a name name. 01:03:49.490 --> 01:03:51.950 So that's what I intended, that looks OK. 01:03:51.950 --> 01:03:54.790 Ah, I see it already, even though you, if you've never 01:03:54.790 --> 01:03:58.030 used a select menu, you might not know what, apparently, 01:03:58.030 --> 01:04:04.150 is missing from here that I did have for my text input. 01:04:04.150 --> 01:04:07.705 Just intuitively, logically. 01:04:07.705 --> 01:04:09.580 What's going through my head, embarrassingly, 01:04:09.580 --> 01:04:13.930 is, all right, if my form thinks that it's missing a name or a sport, 01:04:13.930 --> 01:04:17.710 how did I create a situation in which name is blank or sport is blank? 01:04:17.710 --> 01:04:19.720 Well, name, I don't think it's going to be blank 01:04:19.720 --> 01:04:23.500 because I explicitly gave this text field a name name 01:04:23.500 --> 01:04:25.210 and that did work last time. 01:04:25.210 --> 01:04:28.660 I've now given a second input in the form of the select menu. 01:04:28.660 --> 01:04:35.350 But what seems to be missing here that I'm assuming exists here? 01:04:35.350 --> 01:04:38.620 It's just a dumb mistake I made. 01:04:38.620 --> 01:04:41.686 What might be missing here? 01:04:41.686 --> 01:04:45.790 If request.form gives you all of the inputs that the user might 01:04:45.790 --> 01:04:48.790 have typed in, let me go into my actual code 01:04:48.790 --> 01:04:52.690 here in my form and name equals sport. 01:04:52.690 --> 01:04:54.710 I just didn't give a name to that input. 01:04:54.710 --> 01:04:56.890 So it exists, and the browser doesn't care. 01:04:56.890 --> 01:04:58.723 It's still going to display the form to you, 01:04:58.723 --> 01:05:02.450 it just hasn't given it a unique name to actually transmit to the server. 01:05:02.450 --> 01:05:04.790 So now, if I'm not going to put my foot in my mouth, 01:05:04.790 --> 01:05:06.980 I think that's what I did wrong. 01:05:06.980 --> 01:05:08.950 And again, my process for figuring that out 01:05:08.950 --> 01:05:11.282 was looking at my code, thinking through logically, 01:05:11.282 --> 01:05:12.490 is this right, is this right? 01:05:12.490 --> 01:05:14.660 No, I was missing the name there. 01:05:14.660 --> 01:05:17.620 So let's run Flask, let's reload the form 01:05:17.620 --> 01:05:22.180 just to make sure it's all defaults again, type in my name and type 01:05:22.180 --> 01:05:26.680 in ultimate frisbee, crossing my fingers extra hard this time. 01:05:26.680 --> 01:05:27.470 And there. 01:05:27.470 --> 01:05:28.337 You are registered. 01:05:28.337 --> 01:05:29.170 So I can emphasize-- 01:05:29.170 --> 01:05:30.903 I did not intend to screw up in that way, 01:05:30.903 --> 01:05:33.070 but that's exactly the right kind of thought process 01:05:33.070 --> 01:05:34.390 to diagnose issues like this. 01:05:34.390 --> 01:05:38.260 Go back to the basics, go back to what HTTP and what HTML forms are all about, 01:05:38.260 --> 01:05:40.670 and just rule things in and out. 01:05:40.670 --> 01:05:43.420 There's only a finite number of ways I could have screwed that up. 01:05:43.420 --> 01:05:44.292 Yeah? 01:05:44.292 --> 01:05:45.648 AUDIENCE: Are you [INAUDIBLE]. 01:05:47.820 --> 01:05:49.320 DAVID: Excuse-- say a little louder? 01:05:49.320 --> 01:05:53.220 AUDIENCE: I don't understand why name equals sport [INAUDIBLE].. 01:05:53.220 --> 01:05:55.870 DAVID: Why did name equal sport address the problem? 01:05:55.870 --> 01:05:58.110 Well, let's first go back to the HTML. 01:05:58.110 --> 01:06:05.410 Previously, it was just the reality that I had this user input dropdown menu, 01:06:05.410 --> 01:06:06.840 but I never gave it a name. 01:06:06.840 --> 01:06:10.170 But names, or more generally, key value pairs, 01:06:10.170 --> 01:06:13.450 is how information is sent from a form to the server. 01:06:13.450 --> 01:06:18.660 So if there's no name, there's no key to send, even if the human types a value. 01:06:18.660 --> 01:06:22.320 It would be like nothing equals ultimate frisbee, and that just doesn't work. 01:06:22.320 --> 01:06:24.670 The browser is just not going to send it. 01:06:24.670 --> 01:06:30.570 However, in app.py, I was naively assuming that in my requests form, 01:06:30.570 --> 01:06:33.300 there would be a name called quote unquote "sport." 01:06:33.300 --> 01:06:35.940 It could have been anything, but I was assuming it was sport. 01:06:35.940 --> 01:06:37.770 But I never told the form that. 01:06:37.770 --> 01:06:41.260 And if I really wanted to dig in, we could do a little something more. 01:06:41.260 --> 01:06:44.250 Let me go back to the way it was a moment ago. 01:06:44.250 --> 01:06:48.040 Let me get rid of the name of the sport dropdown menu. 01:06:48.040 --> 01:06:53.220 Let me rerun Flask down here and reload the form itself 01:06:53.220 --> 01:06:55.650 after it finishes being served. 01:06:55.650 --> 01:06:56.820 And now, let me do this. 01:06:56.820 --> 01:07:01.515 View Developer Tools, and then let me watch the Network tab, which recall, 01:07:01.515 --> 01:07:03.390 we played around with a little bit last week. 01:07:03.390 --> 01:07:06.572 And we also played around with Curl, which let us see the HTTP requests. 01:07:06.572 --> 01:07:08.280 Here's another-- here's what I would have 01:07:08.280 --> 01:07:11.650 done if I still wasn't seeing the error and was really embarrassed on stage. 01:07:11.650 --> 01:07:15.750 I would have typed in my name as before, I would have chosen ultimate frisbee. 01:07:15.750 --> 01:07:17.490 I would have clicked register. 01:07:17.490 --> 01:07:21.480 And now, I would have looked at the HTTP request. 01:07:21.480 --> 01:07:23.580 And I would click on Register here. 01:07:23.580 --> 01:07:27.180 And just like we did last week, I would go down to the request down here. 01:07:27.180 --> 01:07:29.910 And there's a whole lot of stuff that we can typically ignore. 01:07:29.910 --> 01:07:33.030 But here, let me zoom in, way at the bottom, 01:07:33.030 --> 01:07:35.370 what Chrome's developer tools are doing for me, 01:07:35.370 --> 01:07:38.380 it's showing me all of the form data that was submitted. 01:07:38.380 --> 01:07:40.950 So this really would have been my telltale clue. 01:07:40.950 --> 01:07:44.220 I'm just not sending the sport, even if the human typed it in. 01:07:44.220 --> 01:07:46.230 And logically, because I've done this before, 01:07:46.230 --> 01:07:49.170 that must mean I didn't give the thing a name. 01:07:49.170 --> 01:07:50.340 But another good tool. 01:07:50.340 --> 01:07:53.610 Like good programmers, web developers are using these kinds of tools 01:07:53.610 --> 01:07:54.660 all the time. 01:07:54.660 --> 01:07:56.398 They're not writing bug-free code. 01:07:56.398 --> 01:07:57.690 That's not the point to get to. 01:07:57.690 --> 01:08:00.780 The point to get to is being a good diagnostician, 01:08:00.780 --> 01:08:02.790 I would say, in these cases. 01:08:02.790 --> 01:08:07.790 OK, other questions on this? 01:08:07.790 --> 01:08:09.050 Yeah. 01:08:09.050 --> 01:08:14.390 AUDIENCE: What if you want to edit one HTML in CSS, [INAUDIBLE].. 01:08:14.390 --> 01:08:16.682 DAVID: I'm sorry, a little bit louder? 01:08:16.682 --> 01:08:19.250 AUDIENCE: If you want to edit in CSS or anything, 01:08:19.250 --> 01:08:23.660 in HTML, once you have to fix the template, how do you that? 01:08:23.660 --> 01:08:27.770 DAVID: So how would you edit CSS if you have these templates? 01:08:27.770 --> 01:08:30.080 That process we'll actually see before long. 01:08:30.080 --> 01:08:31.705 It's almost going to be the exact same. 01:08:31.705 --> 01:08:34.788 Just to give you a teaser for this, and you'll do this in the problem set, 01:08:34.788 --> 01:08:37.640 but we'll give you some distribution code to automate this process. 01:08:37.640 --> 01:08:40.100 You can absolutely still do something like this. 01:08:40.100 --> 01:08:44.899 Link href equals quote unquote "styles" dot 01:08:44.899 --> 01:08:49.609 CSS rel equals style sheet, that's one of the techniques we showed last week. 01:08:49.609 --> 01:08:53.660 The only difference today, using Flask, is that all of your static files, 01:08:53.660 --> 01:08:56.279 by convention, should go in your static folder. 01:08:56.279 --> 01:08:58.310 So the change you would make in your layout 01:08:58.310 --> 01:09:02.240 would be to say that styles dot CSS is in your static folder. 01:09:02.240 --> 01:09:06.229 And then, if I go into my FroshIMS directory, 01:09:06.229 --> 01:09:08.569 I can create a static folder. 01:09:08.569 --> 01:09:11.060 I can CD into it, nothing's there by default. 01:09:11.060 --> 01:09:14.000 But if I now code a file called styles.css, 01:09:14.000 --> 01:09:17.060 I could now do something like this body. 01:09:17.060 --> 01:09:28.220 And in here, I could say background color, say FF0000 to make it red. 01:09:28.220 --> 01:09:32.450 Let me go ahead now and restart Flask in the FroshIMS directory. 01:09:32.450 --> 01:09:35.060 Cross my fingers because I'm doing this on the fly. 01:09:35.060 --> 01:09:38.399 Go back to my form and reload. 01:09:38.399 --> 01:09:41.990 Voila, now we've tied together last week's stuff as well. 01:09:41.990 --> 01:09:45.092 If I answered the right question? 01:09:45.092 --> 01:09:49.440 AUDIENCE: [INAUDIBLE] change one page and not the other. 01:09:49.440 --> 01:09:52.440 DAVID: If you want to change one page and not the other in terms of CSS? 01:09:52.440 --> 01:09:53.250 AUDIENCE: Yes. 01:09:53.250 --> 01:09:54.360 DAVID: That depends. 01:09:54.360 --> 01:09:59.190 In that case, you might want to have different CSS files for each page 01:09:59.190 --> 01:10:00.390 if they're different. 01:10:00.390 --> 01:10:04.620 You could use different classes in one template than you did in the other. 01:10:04.620 --> 01:10:06.040 There's different ways to do that. 01:10:06.040 --> 01:10:09.990 You could even have a placeholder in your layout 01:10:09.990 --> 01:10:14.460 that allows you to plug in the URL of a specific style 01:10:14.460 --> 01:10:15.900 sheet in your individual files. 01:10:15.900 --> 01:10:18.670 But that starts to get more complicated quickly. 01:10:18.670 --> 01:10:20.410 So in short, you can absolutely do it. 01:10:20.410 --> 01:10:24.180 But typically, I would say most websites try not 01:10:24.180 --> 01:10:25.890 to use different style Sheets per page. 01:10:25.890 --> 01:10:28.560 They reuse the styles as much as they can. 01:10:28.560 --> 01:10:30.810 All right, let me go ahead and revert this real quick. 01:10:30.810 --> 01:10:33.750 And let's start to add a little bit more functionality here. 01:10:33.750 --> 01:10:36.630 I'm going to go ahead and just remove the static folder just so as 01:10:36.630 --> 01:10:38.400 to not complicate things just yet. 01:10:38.400 --> 01:10:41.490 And let's go ahead and just play around with a different user interface 01:10:41.490 --> 01:10:42.090 mechanism. 01:10:42.090 --> 01:10:45.503 In my form here, the dropdown menu is perfectly fine. 01:10:45.503 --> 01:10:46.420 Nothing wrong with it. 01:10:46.420 --> 01:10:49.650 But suppose that I wanted to change it to checkboxes instead. 01:10:49.650 --> 01:10:53.670 Maybe I want students to be able to register for multiple sports instead. 01:10:53.670 --> 01:10:57.060 Well, it might make sense to clean this up in a couple of ways. 01:10:57.060 --> 01:10:57.810 And let's do this. 01:10:57.810 --> 01:11:03.000 Before we even get into the checkboxes, there's one subtle bad design here. 01:11:03.000 --> 01:11:07.450 Notice that I've hardcoded basketball, soccer, and ultimate frisbee here. 01:11:07.450 --> 01:11:11.830 And if you recall, in app.py, I also enumerated all three of those here. 01:11:11.830 --> 01:11:15.170 And any time you see copy paste or the equivalent thereof, 01:11:15.170 --> 01:11:16.840 feels like we could do better. 01:11:16.840 --> 01:11:18.640 So what if I instead do this. 01:11:18.640 --> 01:11:22.950 What if I instead give myself a global variable of Sports, 01:11:22.950 --> 01:11:25.350 I'll capitalize the word just to connote that it's 01:11:25.350 --> 01:11:29.400 meant to be constant even though Python does not have constants, per se. 01:11:29.400 --> 01:11:31.980 The first sport will be basketball. 01:11:31.980 --> 01:11:33.960 The second will be soccer. 01:11:33.960 --> 01:11:38.160 The third will be ultimate frisbee. 01:11:38.160 --> 01:11:42.180 Now I have one convenient place to store all of my sports 01:11:42.180 --> 01:11:44.700 if it changes next semester or next year or whatnot. 01:11:44.700 --> 01:11:46.710 But notice what I could do to. 01:11:46.710 --> 01:11:48.340 I could now do something like this. 01:11:48.340 --> 01:11:52.350 Let me pass into my index template a variable 01:11:52.350 --> 01:11:56.700 called sports that's equal to that global variable sports. 01:11:56.700 --> 01:12:00.030 Let me go into my index now, and this is really, now, 01:12:00.030 --> 01:12:04.020 going to hint at the power of templating and Jinja, in this case here. 01:12:04.020 --> 01:12:07.690 Let me go ahead and get rid of all three of these hard coded options 01:12:07.690 --> 01:12:12.570 and let me show you some slightly different syntax for sport, in sports. 01:12:12.570 --> 01:12:15.090 Then end for. 01:12:15.090 --> 01:12:17.110 We've not seen this end for syntax. 01:12:17.110 --> 01:12:19.930 There's like end block syntax, but it's as simple as that. 01:12:19.930 --> 01:12:23.190 So you have a start and an end to your block without indentation mattering. 01:12:23.190 --> 01:12:24.630 Watch what I can do here. 01:12:24.630 --> 01:12:30.720 Option curly brace sport close curly brace. 01:12:30.720 --> 01:12:32.070 Let me save that. 01:12:32.070 --> 01:12:35.220 Let me go back into my terminal window, do Flask run. 01:12:35.220 --> 01:12:38.400 And if I didn't mess up here, let me go back to this. 01:12:38.400 --> 01:12:41.040 The red's going to go away because I deleted my CSS. 01:12:41.040 --> 01:12:44.070 And now I still have a sport dropdown and all of those sports 01:12:44.070 --> 01:12:45.010 are still there. 01:12:45.010 --> 01:12:46.590 I can make one more improvement now. 01:12:46.590 --> 01:12:49.540 I don't need to mention these same sports manually in app.py. 01:12:49.540 --> 01:12:53.910 I can now just say if the user's inputed sport is not 01:12:53.910 --> 01:12:57.308 in my global variable, sports, and ask the same question. 01:12:57.308 --> 01:12:59.100 And this is really handy because if there's 01:12:59.100 --> 01:13:03.240 another sport, for instance, that gets added, like say football, 01:13:03.240 --> 01:13:06.160 all I have to do is change my global variable. 01:13:06.160 --> 01:13:09.780 And if I reload the form now and look in the dropdown, boom, 01:13:09.780 --> 01:13:11.970 now I have support for a fourth sport. 01:13:11.970 --> 01:13:13.660 And I can keep adding and adding there. 01:13:13.660 --> 01:13:17.250 So here's where templating starts to get really powerful in that 01:13:17.250 --> 01:13:22.710 now, in this template, I'm using Jinja's for loop syntax, which 01:13:22.710 --> 01:13:25.200 is almost identical to Python here, except you 01:13:25.200 --> 01:13:28.500 need the curly brace and the percent sign and you need the weird ending 01:13:28.500 --> 01:13:29.370 and for. 01:13:29.370 --> 01:13:31.050 But it's the same idea as in Python. 01:13:31.050 --> 01:13:35.310 Iterating over something with a for loop lets you generate more and more HTML. 01:13:35.310 --> 01:13:37.230 And this is like every website out there. 01:13:37.230 --> 01:13:38.100 For instance, Gmail. 01:13:38.100 --> 01:13:42.090 When you visit your inbox and you see all of this big table of emails, 01:13:42.090 --> 01:13:44.730 Google has not hardcoded your emails manually. 01:13:44.730 --> 01:13:46.560 They have grabbed them from a database. 01:13:46.560 --> 01:13:48.310 They have some kind of for loop like this, 01:13:48.310 --> 01:13:54.295 and are just outputting table row after table row or div after div dynamically. 01:13:54.295 --> 01:13:56.670 All right, so now, let's go ahead and change this, maybe, 01:13:56.670 --> 01:14:02.470 to, oh, how about little checkboxes or radio buttons. 01:14:02.470 --> 01:14:03.810 So let me go ahead and do this. 01:14:03.810 --> 01:14:08.670 Instead of a select menu, I'm going to go ahead and do something like this. 01:14:08.670 --> 01:14:14.590 For each of these sports let me go ahead and output, not an option, 01:14:14.590 --> 01:14:17.520 but let me go ahead and output an input tag, 01:14:17.520 --> 01:14:21.540 the name for which is quote unquote "sport," the type of which 01:14:21.540 --> 01:14:27.240 is checkbox, the value of which is going to be the current "sport," 01:14:27.240 --> 01:14:31.642 quote unquote, and then afterward I need to redundantly, seemingly, 01:14:31.642 --> 01:14:32.350 output the sport. 01:14:32.350 --> 01:14:34.150 So you see a word next to the checkbox. 01:14:34.150 --> 01:14:36.400 And we'll look at the result of this in just a moment. 01:14:36.400 --> 01:14:39.960 So it's actually a little simpler than a select menu, a dropdown menu, 01:14:39.960 --> 01:14:43.200 because now watch what happens if I reload my form. 01:14:43.200 --> 01:14:46.560 Different user interface, and it's not as pretty, 01:14:46.560 --> 01:14:49.840 but it's going to allow users to sign up for multiple sports at once now, 01:14:49.840 --> 01:14:50.590 it would seem. 01:14:50.590 --> 01:14:53.920 Now I can click on basketball and football and soccer 01:14:53.920 --> 01:14:56.260 or some other combination thereof. 01:14:56.260 --> 01:15:00.040 If I view the page's source, this is, again, the power of templating. 01:15:00.040 --> 01:15:04.220 I didn't have to type out four inputs, I got them now automatically. 01:15:04.220 --> 01:15:07.540 And these things all have the same name, but that's OK. 01:15:07.540 --> 01:15:11.170 It turns out with Flask, if it sees multiple values for the same name, 01:15:11.170 --> 01:15:15.160 it's going to hand them back to you as a list if you use the right function. 01:15:15.160 --> 01:15:18.430 All right, but suppose we don't want users registering for multiple sports. 01:15:18.430 --> 01:15:19.810 Maybe capacity is an issue. 01:15:19.810 --> 01:15:23.440 Let me go ahead and change this checkbox to radio button, which 01:15:23.440 --> 01:15:25.400 a radio button is mutually exclusive. 01:15:25.400 --> 01:15:27.130 So you can only sign up for one. 01:15:27.130 --> 01:15:31.780 So now, once I reload the page, there we go. 01:15:31.780 --> 01:15:34.240 It now looks like this. 01:15:34.240 --> 01:15:39.010 And because I've given each of these inputs the same name, quote unquote, 01:15:39.010 --> 01:15:42.410 "sport," that's what makes them mutually exclusive. 01:15:42.410 --> 01:15:45.730 The browser knows all four of these things are types of sports, 01:15:45.730 --> 01:15:48.970 therefore I'm only going to let you select one of these things. 01:15:48.970 --> 01:15:51.310 And that's simply because they all have the same name. 01:15:51.310 --> 01:15:54.730 Again, if I view page source, notice all of them, name equal sport, 01:15:54.730 --> 01:15:58.780 name equals sport, name equals sport, but what differs is the value 01:15:58.780 --> 01:16:01.942 that each one is going to have. 01:16:01.942 --> 01:16:07.490 All right, any questions, then, on this approach? 01:16:07.490 --> 01:16:07.990 All right. 01:16:07.990 --> 01:16:09.990 Well, let me go ahead and open a version of this 01:16:09.990 --> 01:16:13.690 that I made in advance that's going to now start saving the information. 01:16:13.690 --> 01:16:15.580 So thus far, we're not quite at the point 01:16:15.580 --> 01:16:18.910 of where this website was, which actually allowed the proctors to see, 01:16:18.910 --> 01:16:21.430 like in a database, everyone who had registered for sports. 01:16:21.430 --> 01:16:24.130 Now, we're literally telling students you are registered 01:16:24.130 --> 01:16:26.050 or you are not registered, but we're literally 01:16:26.050 --> 01:16:28.130 doing nothing with this information. 01:16:28.130 --> 01:16:30.770 So how might we go about implementing this? 01:16:30.770 --> 01:16:32.740 Well, let me go ahead and close these tabs, 01:16:32.740 --> 01:16:38.140 and let me go into what I call version three of this in the code for today. 01:16:38.140 --> 01:16:41.950 And let me go into my source nine directory, FroshIMS3, 01:16:41.950 --> 01:16:44.890 and let me go ahead and open up app.py. 01:16:44.890 --> 01:16:46.450 So this is a premade version. 01:16:46.450 --> 01:16:48.380 I've gotten rid of football, in this case. 01:16:48.380 --> 01:16:51.100 But I've added one thing at the very top. 01:16:51.100 --> 01:16:56.110 What's, in English, does this represent on line seven? 01:16:56.110 --> 01:16:58.150 What would you describe what that thing is? 01:17:01.230 --> 01:17:02.700 What are we looking at? 01:17:02.700 --> 01:17:03.690 What do you think? 01:17:03.690 --> 01:17:04.410 AUDIENCE: It's an empty dictionary. 01:17:04.410 --> 01:17:05.610 DAVID: Yeah, it's an empty dictionary, right? 01:17:05.610 --> 01:17:07.860 Registrants is apparently a variable on the left. 01:17:07.860 --> 01:17:10.168 It's being assigned an empty dictionary on the right. 01:17:10.168 --> 01:17:12.210 And a dictionary, again, is just key value pairs. 01:17:12.210 --> 01:17:15.690 Here, again, is where dictionaries are just such a useful data structure. 01:17:15.690 --> 01:17:16.198 Why? 01:17:16.198 --> 01:17:18.990 Because this is going to allow me to remember that David registered 01:17:18.990 --> 01:17:21.510 for ultimate frisbee, Carter registered for soccer, 01:17:21.510 --> 01:17:23.070 Emma registered for something else. 01:17:23.070 --> 01:17:26.550 You can associate keys with values, names with sports, 01:17:26.550 --> 01:17:29.770 assuming a model where you can only register for one sport for now. 01:17:29.770 --> 01:17:35.040 And so let's see what the logic is that handles this. 01:17:35.040 --> 01:17:38.520 Here in my register route in the code I've premade, 01:17:38.520 --> 01:17:40.440 notice that I'm validating the user's name. 01:17:40.440 --> 01:17:42.630 Slightly differently from before but same idea. 01:17:42.630 --> 01:17:45.720 I'm using request.form.get to get the human's name. 01:17:45.720 --> 01:17:48.750 If not name, so if the human did not type a name, 01:17:48.750 --> 01:17:51.480 I'm going to output error.html. 01:17:51.480 --> 01:17:55.890 But notice I've started to make the user interface more expressive. 01:17:55.890 --> 01:17:59.790 I'm telling the user, apparently, with a message what they did wrong. 01:17:59.790 --> 01:18:00.870 Well how? 01:18:00.870 --> 01:18:03.420 I'm apparently passing to my error template, 01:18:03.420 --> 01:18:06.773 instead of just failure.html, a specific message. 01:18:06.773 --> 01:18:08.190 So let's go down this rabbit hole. 01:18:08.190 --> 01:18:14.290 Let me actually go into templates/error.hml, and sure enough, 01:18:14.290 --> 01:18:18.000 here's a new file I created here, that adorably is apparently going to have 01:18:18.000 --> 01:18:21.570 a grumpy cat as part of the error message, but notice what I've done. 01:18:21.570 --> 01:18:26.820 In my block body I've got an H1 tag that just says error, big and bold. 01:18:26.820 --> 01:18:29.370 I then have a paragraph tag that plugs in whatever 01:18:29.370 --> 01:18:33.120 the error message is that the controller, app.py, is passing in. 01:18:33.120 --> 01:18:36.450 And then just for fun, I have a picture of a grumpy cat connoting 01:18:36.450 --> 01:18:37.990 that there was, in fact, an error. 01:18:37.990 --> 01:18:39.000 Let's keep looking. 01:18:39.000 --> 01:18:40.660 How do I validate sport? 01:18:40.660 --> 01:18:45.030 I do similarly request.form.get of sport, 01:18:45.030 --> 01:18:46.800 and I store it in a variable called sport. 01:18:46.800 --> 01:18:50.590 If there's no such sport, that is the human did not check any of the boxes, 01:18:50.590 --> 01:18:53.010 then I'm going to render error.html two, but I'm 01:18:53.010 --> 01:18:55.600 going to give a different message, missing sport. 01:18:55.600 --> 01:19:00.690 Else, if the sport they did type in is not in my sports global variable, 01:19:00.690 --> 01:19:04.230 I'm going to render error.html, but complain differently, 01:19:04.230 --> 01:19:07.020 you gave me an invalid sport somehow. 01:19:07.020 --> 01:19:09.270 As if a hacker went into the HTML of the page, 01:19:09.270 --> 01:19:11.617 changed it to add their own sport like volleyball. 01:19:11.617 --> 01:19:13.950 Even though it's not offered, they submitted volleyball. 01:19:13.950 --> 01:19:17.400 But that's OK, I'm rejecting it, even though they might have maliciously 01:19:17.400 --> 01:19:20.910 tried to send it to me by changing the dom locally. 01:19:20.910 --> 01:19:23.070 And then really, the magic is just this. 01:19:23.070 --> 01:19:25.680 I remember that this person has registered 01:19:25.680 --> 01:19:28.860 by indexing into the registrant dictionary 01:19:28.860 --> 01:19:33.870 using the name the human typed in as the key and assigning it a value of sport. 01:19:33.870 --> 01:19:35.140 Why is this useful? 01:19:35.140 --> 01:19:37.470 Well, I added one final route here. 01:19:37.470 --> 01:19:41.910 I have a /registrants route with a registrants function that renders 01:19:41.910 --> 01:19:43.860 a template called registrants.html. 01:19:43.860 --> 01:19:48.820 But it takes as input that global variable just like before. 01:19:48.820 --> 01:19:55.200 So let's go down this rabbit hole let me go into templates registrants dot HTML. 01:19:55.200 --> 01:19:56.640 Here's this template. 01:19:56.640 --> 01:20:00.420 It looks a little crazy big, but it extends the layout. 01:20:00.420 --> 01:20:01.620 Here comes the body. 01:20:01.620 --> 01:20:04.560 I've got an H1 tag that says registrants, big and bold. 01:20:04.560 --> 01:20:06.930 Then I've got a table that we saw last week. 01:20:06.930 --> 01:20:10.800 This has a table head that just says name sport for two columns. 01:20:10.800 --> 01:20:16.200 Then it has a table body where in, using this for loop in Jinja syntax, 01:20:16.200 --> 01:20:19.410 I'm saying, for each name in the registrants variable, 01:20:19.410 --> 01:20:23.400 output a table row, start tag, and end tag, inside of which, 01:20:23.400 --> 01:20:26.580 two table datas, two cells, table data for name, 01:20:26.580 --> 01:20:30.790 table data for registrants bracket name. 01:20:30.790 --> 01:20:33.210 So it's very similar to Python syntax. 01:20:33.210 --> 01:20:37.100 It essentially is Python syntax, albeit with these curly braces and the percent 01:20:37.100 --> 01:20:37.600 sign. 01:20:37.600 --> 01:20:39.750 So the net effect here is what? 01:20:39.750 --> 01:20:43.050 Let me open up my terminal window, run Flask run. 01:20:43.050 --> 01:20:47.110 Let me now go into the form that I premade here. 01:20:47.110 --> 01:20:48.270 So gone is football. 01:20:48.270 --> 01:20:50.190 Let me go ahead and type in David. 01:20:50.190 --> 01:20:52.560 Let me choose, oh, no sport. 01:20:52.560 --> 01:20:53.880 Register. 01:20:53.880 --> 01:20:55.470 Error, missing sport. 01:20:55.470 --> 01:20:57.120 And there is the grumpy cat. 01:20:57.120 --> 01:21:00.120 So missing sport, though, specifically was outputed. 01:21:00.120 --> 01:21:00.910 All right, fine. 01:21:00.910 --> 01:21:03.660 Let me go ahead and say no name. 01:21:03.660 --> 01:21:04.920 But I'll choose basketball. 01:21:04.920 --> 01:21:06.210 Register. 01:21:06.210 --> 01:21:06.990 Missing name. 01:21:06.990 --> 01:21:09.480 All right, and let me maliciously, now, do this. 01:21:09.480 --> 01:21:10.530 Now I'm hacking. 01:21:10.530 --> 01:21:11.820 Let me go into this. 01:21:11.820 --> 01:21:15.930 I'll type my name, sure, but let me go into the body tag down here. 01:21:15.930 --> 01:21:20.130 Let me maliciously go down in ultimate frisbee, heck with that, let's 01:21:20.130 --> 01:21:21.780 volleyball. 01:21:21.780 --> 01:21:26.580 Change that and change this to volleyball. 01:21:26.580 --> 01:21:27.360 Enter. 01:21:27.360 --> 01:21:31.230 So now, I can register for any sport I want to create. 01:21:31.230 --> 01:21:34.200 Let me click register, but invalid sports. 01:21:34.200 --> 01:21:36.420 So again, that speaks to the power and the need 01:21:36.420 --> 01:21:39.600 for checking things on backend and not trusting users. 01:21:39.600 --> 01:21:43.830 It is that easy to hack websites otherwise if you're not validating data 01:21:43.830 --> 01:21:44.530 server side. 01:21:44.530 --> 01:21:46.530 All right, finally, let's just do this for real. 01:21:46.530 --> 01:21:48.530 David is going to register for ultimate frisbee. 01:21:48.530 --> 01:21:49.530 Clicking register. 01:21:49.530 --> 01:21:52.800 And now, the output is not very pretty, but notice 01:21:52.800 --> 01:21:54.790 I'm at the registrants route. 01:21:54.790 --> 01:21:56.880 And if I zoom out, I have an HTML table. 01:21:56.880 --> 01:22:00.370 Two columns, name and sport, David and ultimate frisbee. 01:22:00.370 --> 01:22:04.120 Let me go back to the form, letting me pretend Carter walked up to my laptop 01:22:04.120 --> 01:22:05.560 and registered for basketball. 01:22:05.560 --> 01:22:06.460 Register. 01:22:06.460 --> 01:22:11.350 Now we see two rows in this table, David, ultimate frisbee, Carter, 01:22:11.350 --> 01:22:11.860 basketball. 01:22:11.860 --> 01:22:13.652 And if we do this one more time, maybe Emma 01:22:13.652 --> 01:22:16.000 comes along and registers for soccer register. 01:22:16.000 --> 01:22:20.905 All of this information is being stored in this dictionary, now. 01:22:20.905 --> 01:22:22.030 All right, so that's great. 01:22:22.030 --> 01:22:26.230 Now we have a database, albeit in the form of a Python dictionary. 01:22:26.230 --> 01:22:31.277 But why is this, maybe, not the best implementation? 01:22:31.277 --> 01:22:32.110 Why is it not great? 01:22:32.110 --> 01:22:32.954 Yeah. 01:22:32.954 --> 01:22:35.858 AUDIENCE: You are storing [INAUDIBLE]. 01:22:40.590 --> 01:22:41.090 DAVID: Yeah. 01:22:41.090 --> 01:22:43.715 So we're only storing this dictionary in the computer's memory, 01:22:43.715 --> 01:22:46.970 and that's great until I hit Control C and kill Flask, 01:22:46.970 --> 01:22:48.410 stopping the web server. 01:22:48.410 --> 01:22:51.650 Or the server reboots, or maybe I close my laptop or whatever. 01:22:51.650 --> 01:22:54.950 If the server stops running, memory is going to be lost. 01:22:54.950 --> 01:22:56.420 RAM is volatile. 01:22:56.420 --> 01:22:59.550 It's thrown away when you lose power or stop the program. 01:22:59.550 --> 01:23:01.340 So maybe this isn't the best approach. 01:23:01.340 --> 01:23:03.600 Maybe it would be better to use a CSV file. 01:23:03.600 --> 01:23:06.230 And in fact, some 20 years ago, that's literally what I did. 01:23:06.230 --> 01:23:08.130 I stored everything in a CSV file. 01:23:08.130 --> 01:23:10.640 But let's skip that step, because we already saw last week, 01:23:10.640 --> 01:23:13.790 or a couple of weeks ago now, how we can use SQLite. 01:23:13.790 --> 01:23:16.370 Let's see if we can't marry in some SQL here 01:23:16.370 --> 01:23:20.370 to store an actual database for the program. 01:23:20.370 --> 01:23:22.310 Let me go back here and let me open up, say, 01:23:22.310 --> 01:23:25.850 version four of this, which is almost the same but it 01:23:25.850 --> 01:23:27.530 adds a bit more functionality. 01:23:27.530 --> 01:23:32.780 Let me close these tabs and let me open up app.py now in version four. 01:23:32.780 --> 01:23:35.990 So notice it's almost the same, but at the top, 01:23:35.990 --> 01:23:40.490 I'm creating a database connection to a database called FroshIMS.db. 01:23:40.490 --> 01:23:42.283 So that's a database I created in advance. 01:23:42.283 --> 01:23:43.700 So let's go down that rabbit hole. 01:23:43.700 --> 01:23:44.720 What does it look like? 01:23:44.720 --> 01:23:46.860 Let me make my terminal window bigger. 01:23:46.860 --> 01:23:50.600 Let me run SQLite 3 of FroshIMS.db. 01:23:50.600 --> 01:23:51.290 OK, I'm in. 01:23:51.290 --> 01:23:52.580 Let's do .schema. 01:23:52.580 --> 01:23:55.670 and let's just infer what I designed this to be. 01:23:55.670 --> 01:23:59.960 I have a table called registrants, which has one, two, three columns. 01:23:59.960 --> 01:24:04.340 An ID column that's an integer, a name column that's text but cannot be null, 01:24:04.340 --> 01:24:06.950 and a sport column that's also text, cannot be null, 01:24:06.950 --> 01:24:08.600 and the primary key is just ID. 01:24:08.600 --> 01:24:11.630 So that I have a unique ID for every registration. 01:24:11.630 --> 01:24:14.150 Let's see if there's anyone in there yet. 01:24:14.150 --> 01:24:17.843 Select star from registrants. 01:24:17.843 --> 01:24:19.010 OK, there's no one in there. 01:24:19.010 --> 01:24:20.600 No one is yet registered for sports. 01:24:20.600 --> 01:24:23.180 So let's go back to the code and continue on. 01:24:23.180 --> 01:24:26.030 In my code now, I've got the same global variable 01:24:26.030 --> 01:24:29.270 for validation and generation of my HTML. 01:24:29.270 --> 01:24:31.760 Looks like my index route is the same. 01:24:31.760 --> 01:24:35.480 It's dynamically generating the menu of sports. 01:24:35.480 --> 01:24:37.137 Interestingly, we'll come back to this. 01:24:37.137 --> 01:24:39.470 There's a deregister route that's going to allow someone 01:24:39.470 --> 01:24:44.330 to deregister themselves if they want to exit the sport 01:24:44.330 --> 01:24:45.530 or undo their registration. 01:24:45.530 --> 01:24:46.940 But this is the juicy part. 01:24:46.940 --> 01:24:49.580 Here's my new and improved register route. 01:24:49.580 --> 01:24:52.940 Still works on POST, so some mild privacy there. 01:24:52.940 --> 01:24:55.770 I'm validating the submission as follows. 01:24:55.770 --> 01:24:59.180 I'm getting the user's inputted name, the user's inputted sport, 01:24:59.180 --> 01:25:03.380 and if it is not a name or the sport is not in sports, 01:25:03.380 --> 01:25:05.197 I'm going to render failure.html. 01:25:05.197 --> 01:25:06.030 So I kept it simple. 01:25:06.030 --> 01:25:07.380 There's no cat in this version. 01:25:07.380 --> 01:25:08.780 It just says failure. 01:25:08.780 --> 01:25:12.500 Otherwise, recall how we co-mingled SQL and Python before. 01:25:12.500 --> 01:25:15.800 We're using CS50's SQL library, but that just 01:25:15.800 --> 01:25:19.130 makes it a little easier to execute SQL queries and we're executing this. 01:25:19.130 --> 01:25:22.760 Insert into registrants name comma sport. 01:25:22.760 --> 01:25:27.390 What two values, the name and the sport, that came from that HTML form. 01:25:27.390 --> 01:25:30.260 And then lastly, and this is a new function that we're calling out 01:25:30.260 --> 01:25:33.020 explicitly now, Flask also gives you access 01:25:33.020 --> 01:25:39.645 to a redirect function, which is how safetyschool.org, Harvardsucks.org, 01:25:39.645 --> 01:25:42.020 and all these other sites we played around with last week 01:25:42.020 --> 01:25:45.500 we're all implemented redirecting the user from one place to another. 01:25:45.500 --> 01:25:48.980 This Flask function redirect comes from my just 01:25:48.980 --> 01:25:52.460 having imported it at the very top of this file. 01:25:52.460 --> 01:25:57.380 It handles the HTTP 301 or 302 or 307 code, whatever the appropriate one is. 01:25:57.380 --> 01:25:59.450 It does that for me. 01:25:59.450 --> 01:26:04.520 All right, so that's it for registering via this route. 01:26:04.520 --> 01:26:08.150 Let's look at what the registrant's route is. 01:26:08.150 --> 01:26:11.270 Here, we have a new route for /registrants. 01:26:11.270 --> 01:26:14.280 And instead of just iterating over a dictionary like before, 01:26:14.280 --> 01:26:18.710 we're getting back, let's see, db.execute of select star 01:26:18.710 --> 01:26:19.550 from registrants. 01:26:19.550 --> 01:26:22.640 So that's literally the programmatic version of what I just did manually. 01:26:22.640 --> 01:26:24.650 That gives me back a list of dictionaries, 01:26:24.650 --> 01:26:27.890 each of which represents one row in the table. 01:26:27.890 --> 01:26:31.070 Then, I'm going to render register and start HTML, 01:26:31.070 --> 01:26:34.520 passing in literally that list of dictionaries 01:26:34.520 --> 01:26:37.800 just like using CS50's library in the past. 01:26:37.800 --> 01:26:40.760 So let's go and look at these-- that form. 01:26:40.760 --> 01:26:46.610 If I go into templates and open up registrants.html, 01:26:46.610 --> 01:26:49.610 oh, OK, it's just a table like before. 01:26:49.610 --> 01:26:52.910 And actually, let me change this syntactically for consistency. 01:26:52.910 --> 01:26:58.550 We have a Jinja for loop that iterates over each registrant 01:26:58.550 --> 01:27:01.910 and for each of them, outputs a table row. 01:27:01.910 --> 01:27:03.140 Oh, but this is interesting. 01:27:03.140 --> 01:27:06.860 Instead of just having two columns with the person's name and sport, 01:27:06.860 --> 01:27:09.892 notice that I'm also outputting a full-fledged form. 01:27:09.892 --> 01:27:11.600 All right, this is starting to get juicy. 01:27:11.600 --> 01:27:14.900 So let's actually go back to my terminal window, 01:27:14.900 --> 01:27:18.770 run Flask, and actually see what this example looks like now. 01:27:18.770 --> 01:27:20.550 Let me reload the page. 01:27:20.550 --> 01:27:21.050 All right. 01:27:21.050 --> 01:27:22.830 In the home page, it looks exactly the same. 01:27:22.830 --> 01:27:24.413 But let me now register for something. 01:27:24.413 --> 01:27:27.030 David for ultimate frisbee, register. 01:27:27.030 --> 01:27:27.680 Oh, damn it. 01:27:30.195 --> 01:27:31.070 Let's try this again. 01:27:31.070 --> 01:27:34.800 David registering for ultimate frisbee, register. 01:27:34.800 --> 01:27:35.300 OK. 01:27:35.300 --> 01:27:37.370 So good thing I have deregister. 01:27:37.370 --> 01:27:39.170 So this is what it should now look like. 01:27:39.170 --> 01:27:44.358 I have a page at the route called /registrants that has a table with two 01:27:44.358 --> 01:27:46.400 columns, name and sport, David, ultimate frisbee. 01:27:46.400 --> 01:27:47.760 But oh, wait, a third column. 01:27:47.760 --> 01:27:48.260 Why? 01:27:48.260 --> 01:27:52.700 Because if I view the page source, notice that it's not the prettiest UI. 01:27:52.700 --> 01:27:56.810 For every row in this table, I'm also going to be outputting a form just 01:27:56.810 --> 01:27:58.770 to deregister that user. 01:27:58.770 --> 01:28:02.330 But before we see how that works, let me go ahead and register Carter, 01:28:02.330 --> 01:28:02.920 for instance. 01:28:02.920 --> 01:28:04.520 So Carter will give you basketball. 01:28:04.520 --> 01:28:05.780 Again, register. 01:28:05.780 --> 01:28:07.200 The table grows. 01:28:07.200 --> 01:28:10.310 Now, let me go back and let's register Emma for soccer. 01:28:10.310 --> 01:28:12.290 And the table should grow. 01:28:12.290 --> 01:28:17.720 Before we look at that HTML, let's go back to my terminal window. 01:28:17.720 --> 01:28:21.980 Let's go into SQLite FroshIMS. 01:28:21.980 --> 01:28:32.390 Let me go into FroshIMS, and let me open up with SQLite 3 FroshIMS.db. 01:28:32.390 --> 01:28:34.700 And now do select star from registrants. 01:28:34.700 --> 01:28:38.630 And whereas, previously, when I executed this there were zero people, now 01:28:38.630 --> 01:28:39.710 there's indeed three. 01:28:39.710 --> 01:28:43.350 So now we see exactly what's going on underneath the hood. 01:28:43.350 --> 01:28:46.700 So let's look at this form now-- this page now. 01:28:46.700 --> 01:28:51.380 If I want to unregister, deregister one of these people specifically, 01:28:51.380 --> 01:28:53.180 how do we do this? 01:28:53.180 --> 01:28:55.280 Clicking one of those buttons will indeed 01:28:55.280 --> 01:28:58.260 delete the row from the database. 01:28:58.260 --> 01:29:03.650 But how do we go about linking a web page with Python code with a database? 01:29:03.650 --> 01:29:05.690 This is the last piece of the puzzle. 01:29:05.690 --> 01:29:09.470 Up until now, everything's been with forms and also with URLs. 01:29:09.470 --> 01:29:11.600 But what if the user is not typing anything in, 01:29:11.600 --> 01:29:13.790 they're just clicking a button? 01:29:13.790 --> 01:29:15.660 Well, watch this. 01:29:15.660 --> 01:29:18.135 Let me go ahead and sniff the traffic, which 01:29:18.135 --> 01:29:19.760 you could be in the habit of doing now. 01:29:19.760 --> 01:29:23.570 Any time you're curious how a website works, let me go to the Network tab. 01:29:23.570 --> 01:29:28.010 And Carter, shall we deregister you from basketball? 01:29:28.010 --> 01:29:31.550 Let's deregister Carter and let's see what just happened. 01:29:31.550 --> 01:29:35.630 If I look at the deregister request, notice that it's a POST. 01:29:35.630 --> 01:29:38.450 The status code that eventually came back as 302, 01:29:38.450 --> 01:29:40.880 but let's look at the request itself. 01:29:40.880 --> 01:29:43.610 All the headers there we'll ignore. 01:29:43.610 --> 01:29:48.110 The only thing that button submits, cleverly, 01:29:48.110 --> 01:29:52.130 is an ID parameter, a key equaling two. 01:29:52.130 --> 01:29:56.390 What does two presumably represent or map to? 01:29:56.390 --> 01:29:59.300 Where did this two come from? 01:29:59.300 --> 01:30:03.020 It doesn't say Carter, it doesn't say basketball? 01:30:03.020 --> 01:30:03.568 What is it? 01:30:03.568 --> 01:30:05.360 AUDIENCE: The second person who registered. 01:30:05.360 --> 01:30:06.770 DAVID: The second person that registered. 01:30:06.770 --> 01:30:09.920 So those primary keys that we started talking about a couple of weeks 01:30:09.920 --> 01:30:13.220 ago, why it's useful to be able to uniquely identify a row in a table, 01:30:13.220 --> 01:30:15.470 here is just one of the reasons why. 01:30:15.470 --> 01:30:20.510 If it suffices for me just to send the ID number of the person 01:30:20.510 --> 01:30:25.380 I want to delete from the database, because I can then have code like this. 01:30:25.380 --> 01:30:31.560 If I go into app.py and I look at my deregister route now, the last of them, 01:30:31.560 --> 01:30:33.410 notice that I got this. 01:30:33.410 --> 01:30:37.880 I first go into the form, and I get the ID that was submitted, hopefully. 01:30:37.880 --> 01:30:42.110 If there was, in fact, an ID, and the form wasn't somehow empty, 01:30:42.110 --> 01:30:43.880 I execute this line of code. 01:30:43.880 --> 01:30:46.880 Delete from registrants where ID equals question mark, 01:30:46.880 --> 01:30:51.440 and then I plug-in that number, deleting Carter and only Carter. 01:30:51.440 --> 01:30:54.710 And I'm not using his name, because what if we have two people named Carter, 01:30:54.710 --> 01:30:56.240 two people named Emma or David? 01:30:56.240 --> 01:30:57.840 You don't want to delete both of them. 01:30:57.840 --> 01:31:01.970 That's why these unique IDs are so, so important. 01:31:01.970 --> 01:31:03.860 And here's another reason why. 01:31:03.860 --> 01:31:07.250 You don't want to store some things in URLs. 01:31:07.250 --> 01:31:11.990 Suppose we went to this URL, deregister?ID=3. 01:31:15.500 --> 01:31:20.370 Suppose I, maliciously, emailed this URL to Emma. 01:31:20.370 --> 01:31:22.370 It doesn't matter so much what the beginning is, 01:31:22.370 --> 01:31:28.490 but supposed I emailed her this URL, /deregister?ID=3, and I said, hey, 01:31:28.490 --> 01:31:29.930 Emma, click this. 01:31:29.930 --> 01:31:32.570 And it uses GET instead of POST. 01:31:32.570 --> 01:31:34.565 What did I just trick her into doing? 01:31:37.608 --> 01:31:39.400 What's going to happen if Emma clicks this? 01:31:39.400 --> 01:31:39.900 Yeah? 01:31:39.900 --> 01:31:41.260 AUDIENCE: Deregistering? 01:31:41.260 --> 01:31:43.760 DAVID: You would trick her into deregistering herself. 01:31:43.760 --> 01:31:44.260 Why? 01:31:44.260 --> 01:31:47.050 Because if she's logged into this FroshIMS website, 01:31:47.050 --> 01:31:51.100 and the URL contains her ID just because I'm being malicious, 01:31:51.100 --> 01:31:54.160 and she clicked on it and the website is using GET, 01:31:54.160 --> 01:31:56.950 unfortunately, GET URLs are, again, stateful. 01:31:56.950 --> 01:31:59.020 They have state information in the URLs. 01:31:59.020 --> 01:32:01.770 And in this case, it's enough to delete the user and boom, 01:32:01.770 --> 01:32:04.382 she would have accidentally deregistered herself. 01:32:04.382 --> 01:32:05.590 And this is pretty innocuous. 01:32:05.590 --> 01:32:08.110 Suppose that this was her bank account trying 01:32:08.110 --> 01:32:09.910 to make a withdrawal or a deposit. 01:32:09.910 --> 01:32:13.060 Suppose that this were some other website, a Facebook URL, 01:32:13.060 --> 01:32:15.577 trying to trick her into posting something automatically. 01:32:15.577 --> 01:32:17.410 Here, too, is another consideration when you 01:32:17.410 --> 01:32:21.640 should use POST versus GET, because GET requests can 01:32:21.640 --> 01:32:26.570 be plugged into emails sent via Slack messages, text messages, or the like. 01:32:26.570 --> 01:32:28.840 And unless there's a prompt saying, are you sure 01:32:28.840 --> 01:32:31.990 you want to deregister yourself, you might blindly 01:32:31.990 --> 01:32:34.090 trick the user into being vulnerable to what's 01:32:34.090 --> 01:32:36.460 called a cross-site request forgery. 01:32:36.460 --> 01:32:39.640 A fancy way of saying you trick them into clicking a link 01:32:39.640 --> 01:32:43.480 that they shouldn't have, because the website was using GET alone. 01:32:43.480 --> 01:32:47.350 All right, any question, then, on these building blocks? 01:32:47.350 --> 01:32:49.168 Yeah. 01:32:49.168 --> 01:32:54.514 AUDIENCE: What do the first thing in the instance of the SQL 01:32:54.514 --> 01:32:56.470 [INAUDIBLE] where they have three slashes? 01:32:56.470 --> 01:32:57.740 What does that mean? 01:32:57.740 --> 01:32:59.695 DAVID: When three columns, you mean? 01:32:59.695 --> 01:33:04.550 AUDIENCE: No, three forward slashes. 01:33:04.550 --> 01:33:07.120 DAVID: The three forward slashes. 01:33:07.120 --> 01:33:08.645 I'm not sure I follow. 01:33:08.645 --> 01:33:11.615 AUDIENCE: Yeah, so I think it's in [INAUDIBLE].. 01:33:15.090 --> 01:33:18.270 DAVID: Sorry, it's in where? 01:33:18.270 --> 01:33:19.473 Which file? 01:33:19.473 --> 01:33:24.303 AUDIENCE: It's in [INAUDIBLE] scroll up. 01:33:24.303 --> 01:33:25.269 [INAUDIBLE] 01:33:28.543 --> 01:33:29.960 DAVID: Sorry, the other direction? 01:33:29.960 --> 01:33:30.780 AUDIENCE: Yeah. 01:33:30.780 --> 01:33:32.216 DAVID: OK. 01:33:32.216 --> 01:33:35.084 AUDIENCE: [INAUDIBLE]. 01:33:35.084 --> 01:33:38.645 So please scroll a little bit more. 01:33:38.645 --> 01:33:39.770 DAVID: Keep scrolling more? 01:33:39.770 --> 01:33:40.790 Oh, this thing. 01:33:40.790 --> 01:33:42.320 OK, sorry. 01:33:42.320 --> 01:33:50.000 This is a URI, it's typical syntax that's referring to the SQLite 01:33:50.000 --> 01:33:54.590 protocol, so to speak, which means use SQLite to talk to a file locally. 01:33:54.590 --> 01:33:57.560 :// is just like you and I see in URLs. 01:33:57.560 --> 01:34:00.200 The third slash, essentially, means current folder. 01:34:00.200 --> 01:34:00.860 That's all. 01:34:00.860 --> 01:34:03.700 So it's a weird curiosity, but it's typical 01:34:03.700 --> 01:34:06.200 whenever you're referring to a local file and not one that's 01:34:06.200 --> 01:34:07.490 elsewhere on the internet. 01:34:07.490 --> 01:34:10.550 That's a bit of an oversimplification, but that's indeed a convention. 01:34:10.550 --> 01:34:12.710 Sorry for not clicking earlier. 01:34:12.710 --> 01:34:15.530 All right, let's do one other iteration of FroshIMS 01:34:15.530 --> 01:34:18.860 here just to show what I was actually doing too, back in the day, 01:34:18.860 --> 01:34:21.800 was not only storing these things in CSV files, as I recall. 01:34:21.800 --> 01:34:24.285 I was also automatically generating an email 01:34:24.285 --> 01:34:26.660 to the proctor in charge of the intramural sports program 01:34:26.660 --> 01:34:29.750 so that they would have sort of a running history of people registering 01:34:29.750 --> 01:34:31.890 and they could easily reply to them as well. 01:34:31.890 --> 01:34:35.520 Let me go into FroshIMS version five, which I precreated here, 01:34:35.520 --> 01:34:40.880 and let me go ahead and open up, say, app.py this time. 01:34:40.880 --> 01:34:43.370 And this is some code that I wrote in advance. 01:34:43.370 --> 01:34:47.160 And it looks a little scary at first glance, but I've done the following. 01:34:47.160 --> 01:34:52.550 I have now added the Flask mail library to the picture 01:34:52.550 --> 01:34:55.700 by adding Flask mail to requirements.txt and running a command 01:34:55.700 --> 01:34:59.340 to automatically install email support for Flask as well. 01:34:59.340 --> 01:35:02.090 And this is a little bit cryptic, but it's honestly mostly 01:35:02.090 --> 01:35:03.860 copy paste from the documentation. 01:35:03.860 --> 01:35:06.590 What I'm doing here is I'm configuring my Flask 01:35:06.590 --> 01:35:09.960 application with a few configuration variables, if you will. 01:35:09.960 --> 01:35:13.460 This is the syntax for that. app.config is a special dictionary that 01:35:13.460 --> 01:35:17.840 comes with Flask that is automatically created when you create the app appear 01:35:17.840 --> 01:35:21.200 on line nine, and I just had to fill in a whole bunch of configuration 01:35:21.200 --> 01:35:23.720 values for the default sender address that I 01:35:23.720 --> 01:35:27.710 want to send email as, the default password I want to use to send email, 01:35:27.710 --> 01:35:30.890 the port number, the TCP port, that we talked about last week. 01:35:30.890 --> 01:35:34.730 The mail server, I'm going to use Gmail's smtp.gmail.com server. 01:35:34.730 --> 01:35:36.770 Use TLS, this means use encryption. 01:35:36.770 --> 01:35:37.910 So I set that to true. 01:35:37.910 --> 01:35:40.800 Mail username, this is going to grab it from my environment. 01:35:40.800 --> 01:35:44.210 So for security purposes, I didn't want to hard code my own Gmail username 01:35:44.210 --> 01:35:46.262 and password into the code. 01:35:46.262 --> 01:35:49.220 So I'm actually storing those in what are called environment variables. 01:35:49.220 --> 01:35:51.350 You'll see more of these in problem set nine, 01:35:51.350 --> 01:35:53.360 and it's a very common convention on a server 01:35:53.360 --> 01:35:59.010 in the real world to store sensitive information in the computer's memory 01:35:59.010 --> 01:36:01.880 so that it can be accessed when your website is running, 01:36:01.880 --> 01:36:03.440 but not in your source code. 01:36:03.440 --> 01:36:06.470 It's way too easy if you put credentials, 01:36:06.470 --> 01:36:09.770 sensitive stuff in your source code, to post it to GitHub 01:36:09.770 --> 01:36:13.070 or to screenshot it accidentally, or for information to leak out. 01:36:13.070 --> 01:36:18.200 So for today's purposes, know that the OS.environ dictionary refers to what 01:36:18.200 --> 01:36:19.820 are called environment variables. 01:36:19.820 --> 01:36:23.330 And this is like an out-of-band, a special way of defining key value 01:36:23.330 --> 01:36:26.360 pairs in the computer's memory by running a certain command 01:36:26.360 --> 01:36:28.730 but that never show up in your actual code. 01:36:28.730 --> 01:36:31.370 Otherwise, there would be so many usernames and passwords 01:36:31.370 --> 01:36:34.160 accidentally visible on the internet. 01:36:34.160 --> 01:36:36.560 So I've installed this in advance. 01:36:36.560 --> 01:36:38.390 Let me see if I can do this correctly. 01:36:38.390 --> 01:36:41.820 Let me go over to another tab in just a moment. 01:36:41.820 --> 01:36:45.680 And here, I have on my second screen here, John Harvards inbox. 01:36:45.680 --> 01:36:48.560 It's currently empty, and I'm going to go ahead and register 01:36:48.560 --> 01:36:50.780 for some sport as John Harvard here, hopefully. 01:36:50.780 --> 01:36:55.070 So let me go ahead and run Flask run on this version five. 01:36:55.070 --> 01:36:58.550 Let me go ahead and reload the main screen. 01:36:58.550 --> 01:36:59.240 Not that one. 01:36:59.240 --> 01:37:00.920 Let me reload the main screen here. 01:37:00.920 --> 01:37:03.650 This time, clearly, I'm asking for name and email. 01:37:03.650 --> 01:37:05.735 So name will be John Harvard. 01:37:05.735 --> 01:37:08.010 jharvard@cs50.harvard.edu. 01:37:08.010 --> 01:37:13.580 He'll register for, how about soccer. 01:37:13.580 --> 01:37:15.140 Register. 01:37:15.140 --> 01:37:19.850 And if I did this correctly, not only is John Harvard, on his screen, 01:37:19.850 --> 01:37:28.940 seeing you are registered, but when he checks his email on this other screen, 01:37:28.940 --> 01:37:35.240 crossing his fingers that this actually works as a demonstration, 01:37:35.240 --> 01:37:37.325 and I promise it did right before class. 01:37:43.370 --> 01:37:45.090 Horrifying. 01:37:45.090 --> 01:37:47.480 I don't think there's a mistake this time. 01:37:50.640 --> 01:37:54.060 Let me try something over here real quick, 01:37:54.060 --> 01:37:57.900 but I don't think this is broken. 01:37:57.900 --> 01:38:01.410 It wouldn't have said success if it were. 01:38:01.410 --> 01:38:07.230 I just tried submitting again, so I just did another you are registered. 01:38:07.230 --> 01:38:09.395 Oh, I'm really sad right now. 01:38:12.110 --> 01:38:13.590 AUDIENCE: [INAUDIBLE] 01:38:13.590 --> 01:38:14.547 DAVID: What's that? 01:38:14.547 --> 01:38:15.920 AUDIENCE: Check spam. 01:38:15.920 --> 01:38:19.760 DAVID: I could check spam, but then it's-- 01:38:19.760 --> 01:38:24.230 not sure we want to show spam here on the internet that every one of us gets. 01:38:24.230 --> 01:38:27.110 Oh, maybe. 01:38:27.110 --> 01:38:27.900 Oh! 01:38:27.900 --> 01:38:29.990 [LAUGHTER AND APPLAUDING] 01:38:29.990 --> 01:38:30.740 Thank you. 01:38:35.300 --> 01:38:35.990 OK. 01:38:35.990 --> 01:38:37.760 Wow, that was a risky click I worried. 01:38:37.760 --> 01:38:41.488 All right, so you are registered is the email that I sent out, 01:38:41.488 --> 01:38:43.530 and it doesn't have any actual information in it. 01:38:43.530 --> 01:38:45.500 But back in the day it would have, because I 01:38:45.500 --> 01:38:47.330 included the student's name and their dorm 01:38:47.330 --> 01:38:49.872 and all of the other fields of information that we asked for. 01:38:49.872 --> 01:38:52.550 So let's just take a quick look at how that code might work. 01:38:52.550 --> 01:38:55.760 I did have to configure Gmail in a certain way to allow, 01:38:55.760 --> 01:38:58.430 what they call, less secure apps using SMTP, 01:38:58.430 --> 01:39:00.830 which is the protocol used for outbound email. 01:39:00.830 --> 01:39:05.198 But besides setting these things, let's look at the register route down here. 01:39:05.198 --> 01:39:06.740 It's actually pretty straightforward. 01:39:06.740 --> 01:39:09.870 In my register route, I validated the submission just like before. 01:39:09.870 --> 01:39:11.100 Nothing new there. 01:39:11.100 --> 01:39:15.000 I then confirmed the registration down here, nothing new there. 01:39:15.000 --> 01:39:17.720 All I did was use two new lines of code. 01:39:17.720 --> 01:39:20.222 And it's this easy to automate the sending of emails. 01:39:20.222 --> 01:39:21.930 I apparently have done it too many times, 01:39:21.930 --> 01:39:23.840 which is why it ended up in spam. 01:39:23.840 --> 01:39:25.820 I created a variable called message. 01:39:25.820 --> 01:39:29.220 I used a message function that I must have imported higher up, 01:39:29.220 --> 01:39:30.330 so we'll go back to that. 01:39:30.330 --> 01:39:33.110 Here's, apparently, the subject line as the first argument. 01:39:33.110 --> 01:39:37.430 And the second argument is the named parameter recipients, 01:39:37.430 --> 01:39:40.920 which takes a list of emails that should get the confirmation email. 01:39:40.920 --> 01:39:43.280 So in brackets, I just put the one user's email 01:39:43.280 --> 01:39:46.320 and then mail.send that message. 01:39:46.320 --> 01:39:51.260 So let's scroll back up to see what message and what mail actually is. 01:39:51.260 --> 01:39:52.800 Mail, I think, we saw. 01:39:52.800 --> 01:39:56.030 Yep, mail is this, which I have as a variable 01:39:56.030 --> 01:39:58.460 because I followed the documentation for this library. 01:39:58.460 --> 01:40:04.048 You simply configure your current app with Mail support, capital M here. 01:40:04.048 --> 01:40:05.840 And if you look up here now, on line seven, 01:40:05.840 --> 01:40:08.990 here's the new library from Flask mail I imported. 01:40:08.990 --> 01:40:13.190 Capital Mail, capital Message, so that I had the ability to create a message 01:40:13.190 --> 01:40:14.690 and send a mail. 01:40:14.690 --> 01:40:17.540 So such a simple thing whether you want to confirm things for users, 01:40:17.540 --> 01:40:19.070 you want to do password resets. 01:40:19.070 --> 01:40:22.940 It can be this easy to actually generate emails 01:40:22.940 --> 01:40:25.992 provided you have the requisite access and software installed. 01:40:25.992 --> 01:40:28.200 And just to make clear that I did add something here, 01:40:28.200 --> 01:40:31.340 let me open up my requirements.txt file, and indeed, I 01:40:31.340 --> 01:40:36.050 have both Flask and Flask-mail ready to go. 01:40:36.050 --> 01:40:39.050 But I ran the command in advance to actually do that. 01:40:39.050 --> 01:40:44.320 All right, any questions, then, on these examples here? 01:40:44.320 --> 01:40:45.180 No? 01:40:45.180 --> 01:40:45.680 All right. 01:40:45.680 --> 01:40:51.810 So what other pieces might actually remain for us let me flip over here. 01:40:51.810 --> 01:40:54.913 It turns out that a key component of most any web 01:40:54.913 --> 01:40:57.080 application nowadays that we haven't touched on yet, 01:40:57.080 --> 01:41:00.635 but it'll be one of our final flourishes today, is the notion of a session. 01:41:00.635 --> 01:41:03.440 And a session is actually a feature that derives 01:41:03.440 --> 01:41:06.080 from all of the basics we talked about today and last week, 01:41:06.080 --> 01:41:09.590 and a session is the technical term for what you and I know as a shopping cart. 01:41:09.590 --> 01:41:13.160 When you go to amazon.com and you start adding things to your shopping cart, 01:41:13.160 --> 01:41:15.843 they follow you from page to page to page. 01:41:15.843 --> 01:41:18.260 Heck if you close your browser, come back to the next day, 01:41:18.260 --> 01:41:21.907 they're typically still your shopping cart, which is great for Amazon 01:41:21.907 --> 01:41:23.240 because they want your business. 01:41:23.240 --> 01:41:25.950 They don't want you to have to start from scratch the next day. 01:41:25.950 --> 01:41:29.720 Similarly, when you log into any website these days, 01:41:29.720 --> 01:41:33.530 even if it's not an e-commerce thing but it has usernames and passwords, 01:41:33.530 --> 01:41:35.240 you and I are not in the habit of logging 01:41:35.240 --> 01:41:37.430 into every darn page we visit on a website. 01:41:37.430 --> 01:41:41.420 Typically, you log in once, and then for the next hour, day, week, year, 01:41:41.420 --> 01:41:43.400 you stay logged into that website. 01:41:43.400 --> 01:41:47.540 So somehow, the website is remembering that you have logged in. 01:41:47.540 --> 01:41:50.128 And that is being implemented by way of this thing called 01:41:50.128 --> 01:41:51.920 a session, and perhaps a more familiar term 01:41:51.920 --> 01:41:55.027 that you might know as, and worry about, called cookies. 01:41:55.027 --> 01:41:57.360 Let's go ahead and take one more five minute break here. 01:41:57.360 --> 01:41:59.652 And when we come back, we'll look at cookies, sessions, 01:41:59.652 --> 01:42:01.910 and these final features. 01:42:01.910 --> 01:42:03.190 All right. 01:42:03.190 --> 01:42:06.200 So the promise now is that we're going to implement 01:42:06.200 --> 01:42:09.620 this notion of a session, which is going to allow us to log users in and keep 01:42:09.620 --> 01:42:12.500 them logged in and even implement things like a shopping cart. 01:42:12.500 --> 01:42:16.880 And the overarching goal here is to build an application that 01:42:16.880 --> 01:42:18.380 is, quote unquote, "stateful." 01:42:18.380 --> 01:42:21.560 Again, state refers to information, and something that's stateful 01:42:21.560 --> 01:42:23.240 remembers information. 01:42:23.240 --> 01:42:27.950 And in this context, the curiosity is that HTTP is technically 01:42:27.950 --> 01:42:29.510 a stateless protocol. 01:42:29.510 --> 01:42:33.620 Once you visit a URL, http://something, hit Enter, 01:42:33.620 --> 01:42:36.710 web page is downloaded to your browser, like that's it. 01:42:36.710 --> 01:42:39.710 You can unplug from the internet, you can turn off your Wi-Fi, 01:42:39.710 --> 01:42:42.230 but you still have the web page locally. 01:42:42.230 --> 01:42:45.410 And yet we somehow want to make sure that the next time you 01:42:45.410 --> 01:42:48.060 click on a link on that website, it doesn't forget who you are. 01:42:48.060 --> 01:42:50.060 Or the next thing you add to your shopping cart, 01:42:50.060 --> 01:42:51.870 it doesn't forget what was already there. 01:42:51.870 --> 01:42:55.100 So we somehow want to make HTTP stateful, 01:42:55.100 --> 01:42:58.430 and we can actually do this using the building blocks we've seen thus far. 01:42:58.430 --> 01:43:03.140 So concretely, here's a form you might see occasionally, but pretty rarely, 01:43:03.140 --> 01:43:04.730 when you log into Gmail. 01:43:04.730 --> 01:43:08.660 And I say rarely because most of you don't log into Gmail frequently, 01:43:08.660 --> 01:43:11.690 you just stay logged in, pretty much endlessly, in your browser. 01:43:11.690 --> 01:43:14.390 And that's because Google has made the conscious choice 01:43:14.390 --> 01:43:18.100 to give you a very long session time, maybe a day, a week, 01:43:18.100 --> 01:43:19.850 a month, a year, because they don't really 01:43:19.850 --> 01:43:23.510 want to add friction to using their tool and making you log in every darn day. 01:43:23.510 --> 01:43:26.430 By contrast, there's other applications on campus, 01:43:26.430 --> 01:43:29.270 including some of the CS50 zone, that makes you log in every time. 01:43:29.270 --> 01:43:31.340 Because we want to make sure that it's indeed you 01:43:31.340 --> 01:43:35.280 accessing the site, and not a roommate or friend or someone maliciously. 01:43:35.280 --> 01:43:39.060 So once you do fill out this form, how does Google subsequently 01:43:39.060 --> 01:43:42.390 know that you are you, and when you reload the page even 01:43:42.390 --> 01:43:45.150 or open a second tab for your same Gmail account, 01:43:45.150 --> 01:43:48.907 how do they know that you're still David or Carter or Emma or someone else? 01:43:48.907 --> 01:43:51.240 Well, let's look underneath the hood of what's going on. 01:43:51.240 --> 01:43:54.690 When you log into Gmail, essentially, you initially 01:43:54.690 --> 01:43:57.420 see a form like this using a GET request. 01:43:57.420 --> 01:44:00.090 And the website responds like we saw last week 01:44:00.090 --> 01:44:01.510 with some kind of HTTP response. 01:44:01.510 --> 01:44:03.630 Hopefully 200 OK with the form. 01:44:03.630 --> 01:44:08.460 Meanwhile, the website might also respond with an HTTP header 01:44:08.460 --> 01:44:11.670 that, last week we didn't care about, this week, we now do. 01:44:11.670 --> 01:44:15.240 Whenever you visit a website, it is very commonly the case 01:44:15.240 --> 01:44:18.310 that the website is putting a cookie on your computer. 01:44:18.310 --> 01:44:20.910 And you may generally know that cookies can be bad 01:44:20.910 --> 01:44:25.650 and they track you in some way, and that's both a blessing and a curse. 01:44:25.650 --> 01:44:31.050 Without cookies, you could not implement things like shopping carts and log-ins 01:44:31.050 --> 01:44:32.580 as we know them today. 01:44:32.580 --> 01:44:34.980 Unfortunately, they can also be used for ill purposes 01:44:34.980 --> 01:44:38.280 like tracking you on every website and serving you ads more effectively and so 01:44:38.280 --> 01:44:38.780 forth. 01:44:38.780 --> 01:44:40.710 So with good comes some bad. 01:44:40.710 --> 01:44:43.890 But the basic primitive for us, the computer scientist, 01:44:43.890 --> 01:44:46.860 boils down to just HTTP headers. 01:44:46.860 --> 01:44:51.750 A cookie is typically a big number, a big, seemingly random value, 01:44:51.750 --> 01:44:55.980 that a server tells your browser to store in memory, or even 01:44:55.980 --> 01:44:57.640 longer term, store on disk. 01:44:57.640 --> 01:45:01.180 So you can think of it like a file that a server is planting on your computer. 01:45:01.180 --> 01:45:05.250 And the promise that HTTP makes is that if a server sets 01:45:05.250 --> 01:45:08.220 a cookie on your computer, you will represent 01:45:08.220 --> 01:45:12.220 that same cookie or that same value on every subsequent request. 01:45:12.220 --> 01:45:14.100 So when you visit the website like Gmail, 01:45:14.100 --> 01:45:17.850 they plop a cookie on your computer like this with some session 01:45:17.850 --> 01:45:19.980 equals value, some long random value. 01:45:19.980 --> 01:45:22.310 One, two, three, A, B, C, something like that. 01:45:22.310 --> 01:45:26.730 And when you then visit another page on gmail.com or any other website, 01:45:26.730 --> 01:45:31.590 you send the opposite header, not set cookie, but just cookie colon, 01:45:31.590 --> 01:45:33.610 and you send the exact same value. 01:45:33.610 --> 01:45:36.017 It's similar to going to a club or an amusement park 01:45:36.017 --> 01:45:38.100 where you pay once, you go through the gates once, 01:45:38.100 --> 01:45:40.020 you get checked by security once, and then 01:45:40.020 --> 01:45:44.910 they very often take like a little stamp and say, OK, now you can come and go. 01:45:44.910 --> 01:45:47.743 And then for you, efficiency-wise, if you come back later in the day 01:45:47.743 --> 01:45:50.077 or later in the evening, you can just present your hand. 01:45:50.077 --> 01:45:51.690 You've been stamped, presumably. 01:45:51.690 --> 01:45:54.360 They've already-- you've already paid, you've 01:45:54.360 --> 01:45:55.900 already been searched or whatnot. 01:45:55.900 --> 01:45:57.870 And so it's this sort of fast track ticket 01:45:57.870 --> 01:45:59.700 back into the club, back into the park. 01:45:59.700 --> 01:46:03.480 That's essentially what a cookie is doing for you, whereby 01:46:03.480 --> 01:46:06.060 it's a way of reminding the website we've already done this, 01:46:06.060 --> 01:46:08.290 you already asked me for my username and password. 01:46:08.290 --> 01:46:10.770 This is my path to now come and go. 01:46:10.770 --> 01:46:14.760 Now, unlike this hand stamp, which can be easily copied or transferred 01:46:14.760 --> 01:46:17.370 or duplicated or kept on over multiple days, 01:46:17.370 --> 01:46:22.180 these cookies are really big, seemingly random values, letters and numbers. 01:46:22.180 --> 01:46:24.900 So statistically, there's no way someone else 01:46:24.900 --> 01:46:28.110 is just going to guess your cookie value and pretend to be you It's just 01:46:28.110 --> 01:46:31.110 very low probability, statistically. 01:46:31.110 --> 01:46:34.950 But this is all it boils down to is this agreement between browser and server 01:46:34.950 --> 01:46:39.010 to send these values back and forth in this way. 01:46:39.010 --> 01:46:41.370 So when we actually translate this, now, to code, 01:46:41.370 --> 01:46:43.470 let's do something like a simple login app. 01:46:43.470 --> 01:46:46.590 Let me go into a folder I made in advance today called login. 01:46:46.590 --> 01:46:51.160 And let me code up app.py and let's take a look in here. 01:46:51.160 --> 01:46:52.710 So what's going on? 01:46:52.710 --> 01:46:54.150 A couple of new things up top. 01:46:54.150 --> 01:46:59.040 If I want to have the ability to stamp my users hands, virtually, 01:46:59.040 --> 01:47:02.850 and implement sessions, I'm going to have to import from Flask support 01:47:02.850 --> 01:47:03.790 for sessions. 01:47:03.790 --> 01:47:06.810 So this is another feature you get for free by using a framework 01:47:06.810 --> 01:47:09.040 and not having to implement all this yourself. 01:47:09.040 --> 01:47:11.190 And from the Flask session library, I'm going 01:47:11.190 --> 01:47:13.740 to import Session, capital S. Why? 01:47:13.740 --> 01:47:15.990 I'm going to configure the session as follows. 01:47:15.990 --> 01:47:18.990 Long story short, there's different ways to implement sessions. 01:47:18.990 --> 01:47:22.950 The server can store these cookies in a database, in a file, 01:47:22.950 --> 01:47:25.390 in memory, in RAM, in other places too. 01:47:25.390 --> 01:47:30.220 We are telling it to store these cookies on the server's hard drive. 01:47:30.220 --> 01:47:33.690 So in fact, whenever you use sessions as you will for problem set nine, 01:47:33.690 --> 01:47:37.530 you'll actually see a folder suddenly appear called Flask_session, 01:47:37.530 --> 01:47:40.080 inside of which are the cookies, essentially, 01:47:40.080 --> 01:47:42.450 for any users or friends or yourself who've been 01:47:42.450 --> 01:47:45.040 visiting your particular application. 01:47:45.040 --> 01:47:46.800 So I'm setting it to use the file system, 01:47:46.800 --> 01:47:49.008 and I don't want them to be permanent because I want, 01:47:49.008 --> 01:47:51.720 when you close your browser, the session to go away. 01:47:51.720 --> 01:47:54.270 They could be made to be permanent and last much longer. 01:47:54.270 --> 01:47:56.460 Then I tell my app to support sessions. 01:47:56.460 --> 01:47:58.140 And that's it for now. 01:47:58.140 --> 01:48:01.810 Let's see what this application actually does before we dissect the code. 01:48:01.810 --> 01:48:07.140 Let me go over to my terminal window, run Flask run, and then let me go ahead 01:48:07.140 --> 01:48:11.440 and reload my preview URL. 01:48:11.440 --> 01:48:13.740 Give it a second to kick back in. 01:48:13.740 --> 01:48:15.600 Let me go ahead and open my URL. 01:48:15.600 --> 01:48:17.550 Come on. 01:48:17.550 --> 01:48:20.557 Oops, let me go ahead. 01:48:20.557 --> 01:48:21.390 Too long of a break. 01:48:21.390 --> 01:48:22.030 There we go. 01:48:22.030 --> 01:48:24.465 So this website simply has a login form. 01:48:24.465 --> 01:48:26.340 There's no password, though I could certainly 01:48:26.340 --> 01:48:28.140 add that and check for that too. 01:48:28.140 --> 01:48:29.650 It just asks for your name. 01:48:29.650 --> 01:48:32.310 So I'm going to log in as myself, David, and click Login. 01:48:32.310 --> 01:48:35.130 And now notice I'm currently at the /login route. 01:48:35.130 --> 01:48:36.120 But notice this. 01:48:36.120 --> 01:48:38.445 If I try to go to the default route, just, 01:48:38.445 --> 01:48:41.070 slash, which is where most websites live by default, 01:48:41.070 --> 01:48:44.010 notice that I magically get redirected to log in. 01:48:44.010 --> 01:48:47.160 So somehow, my code knows, hey, if you're not logged in, you're going 01:48:47.160 --> 01:48:48.930 to /login instead. 01:48:48.930 --> 01:48:51.540 Let me type in my name, David, and click Login. 01:48:51.540 --> 01:48:54.345 And now notice I am back at slash. 01:48:54.345 --> 01:48:57.570 Chrome is sort of annoyingly hiding it, but this is the same thing as just 01:48:57.570 --> 01:48:58.720 a single slash. 01:48:58.720 --> 01:49:01.470 And now notice it says you are logged in as David. 01:49:01.470 --> 01:49:02.130 Log out. 01:49:02.130 --> 01:49:05.400 What's cool is notice if I reload the page, it still knows that. 01:49:05.400 --> 01:49:08.820 If I create a second tab and go to the same URL, it still knows that. 01:49:08.820 --> 01:49:10.532 I could even-- 01:49:10.532 --> 01:49:12.240 I could keep doing this in multiple tabs, 01:49:12.240 --> 01:49:15.960 it's still going to remember me on both of them as being logged in as David. 01:49:15.960 --> 01:49:17.290 So how does that work? 01:49:17.290 --> 01:49:22.380 Especially when I click Log Out, then I get forgotten altogether. 01:49:22.380 --> 01:49:24.030 All right, so let's see how this works. 01:49:24.030 --> 01:49:25.980 And it's some basic building blocks. 01:49:25.980 --> 01:49:29.790 Under my /route, notice I have this. 01:49:29.790 --> 01:49:34.660 If there is no name in the session, redirect the user to /login. 01:49:34.660 --> 01:49:37.230 So these two lines together are what implement 01:49:37.230 --> 01:49:41.850 that automatic redirection using HTTP 301 or 302 automatically. 01:49:41.850 --> 01:49:43.930 It's handled for me with these two lines. 01:49:43.930 --> 01:49:45.590 Otherwise, show index.html. 01:49:45.590 --> 01:49:47.340 All right, let's go down that rabbit hole. 01:49:47.340 --> 01:49:48.990 What's in index.html? 01:49:48.990 --> 01:49:52.230 Well, if I look in my-- 01:49:52.230 --> 01:49:58.890 let me look in my templates folder for my login demo and look 01:49:58.890 --> 01:50:02.280 at templates/index.html. 01:50:02.280 --> 01:50:03.870 All right, so what's going on here? 01:50:03.870 --> 01:50:08.460 I extend layout.html, I have a block body, 01:50:08.460 --> 01:50:10.200 and then I've got some other syntax. 01:50:10.200 --> 01:50:12.742 So we haven't seen this yet, but it's more Jinja stuff, which 01:50:12.742 --> 01:50:14.460 again, is almost identical to Python. 01:50:14.460 --> 01:50:17.550 If there's a name in the session variable, 01:50:17.550 --> 01:50:22.320 then literally say you are logged in as curly braces session bracket name. 01:50:22.320 --> 01:50:26.880 And then notice this, I've got a simple HTML link to log out via /logout. 01:50:26.880 --> 01:50:29.550 Else, if there is no name in the session, 01:50:29.550 --> 01:50:33.360 then it apparently says you are not logged in and it leads me to an HTML 01:50:33.360 --> 01:50:35.850 link to /login and then end diff. 01:50:35.850 --> 01:50:38.460 So again, Jinja does not rely on indentation. 01:50:38.460 --> 01:50:41.460 Recall the HTML and CSS don't really care about indentation, 01:50:41.460 --> 01:50:42.690 only the human does. 01:50:42.690 --> 01:50:46.260 But in code with Jinja, you need these end tags, 01:50:46.260 --> 01:50:49.110 end block, end for, end if, to make super obvious 01:50:49.110 --> 01:50:51.330 that you're done with that thought. 01:50:51.330 --> 01:50:54.840 So session is just this magic variable that we now 01:50:54.840 --> 01:50:59.130 have access to because we've included these two lines of code 01:50:59.130 --> 01:51:04.140 and these that handle that whole process of stamping every user's hand 01:51:04.140 --> 01:51:06.090 with a different, unique identifier. 01:51:06.090 --> 01:51:08.760 If I made my code space public and I let all of you 01:51:08.760 --> 01:51:12.270 visit the exact same URL, all of you would be logged out by default. 01:51:12.270 --> 01:51:14.340 You could all type your own names individually, 01:51:14.340 --> 01:51:18.330 all log in at the same URL using different sessions. 01:51:18.330 --> 01:51:21.570 And in fact, I would then see, if I go into my terminal window 01:51:21.570 --> 01:51:26.230 here and my login directory, notice the Flask session directory I mentioned. 01:51:26.230 --> 01:51:30.120 And if I CD into that and type ls, notice that I had two tabs open, 01:51:30.120 --> 01:51:32.790 or actually, I think I started the server twice. 01:51:32.790 --> 01:51:34.180 I have two files in there. 01:51:34.180 --> 01:51:36.930 I would ultimately have one file for every one of you. 01:51:36.930 --> 01:51:38.940 And that's what's beautiful about sessions 01:51:38.940 --> 01:51:43.530 is it creates the illusion of per user storage. 01:51:43.530 --> 01:51:47.860 Inside of my session is my name, inside of your session, so to speak, 01:51:47.860 --> 01:51:48.720 is your name. 01:51:48.720 --> 01:51:52.750 And the same is going to apply to shopping carts, ultimately, as well. 01:51:52.750 --> 01:51:54.450 Let's see how login works here. 01:51:54.450 --> 01:51:58.860 My login route supports both GET and POST, so I could play around if I want. 01:51:58.860 --> 01:52:03.430 And notice this, this login route is kind of interesting as follows. 01:52:03.430 --> 01:52:07.633 If the user got to this route via POST, my inference 01:52:07.633 --> 01:52:09.300 is that they must have submitted a form. 01:52:09.300 --> 01:52:09.800 Why? 01:52:09.800 --> 01:52:13.230 Because that's how I'm going to design the HTML form in a second. 01:52:13.230 --> 01:52:15.870 And if they did submit the form via POST, 01:52:15.870 --> 01:52:18.930 I'm going to store, in the session, at the name 01:52:18.930 --> 01:52:21.330 key, whatever the human's name is. 01:52:21.330 --> 01:52:23.505 And then, I'm going to redirect them back to slash. 01:52:23.505 --> 01:52:26.590 Otherwise, I'm going to show them the login form. 01:52:26.590 --> 01:52:27.870 So this is what's cool. 01:52:27.870 --> 01:52:31.200 If I go to this login form, which lives at, 01:52:31.200 --> 01:52:35.100 literally, slash login, by default, when you visit a URL like that, 01:52:35.100 --> 01:52:36.960 you're visiting via GET. 01:52:36.960 --> 01:52:39.120 And so that's why I see the form. 01:52:39.120 --> 01:52:40.650 However, notice this. 01:52:40.650 --> 01:52:44.550 The form, very cleverly, submits to itself, 01:52:44.550 --> 01:52:49.230 like the one route/login submits to its same self, /login, 01:52:49.230 --> 01:52:51.820 but it uses POST when you submit the form. 01:52:51.820 --> 01:52:55.950 And this is a nice way of having one route but for two different types 01:52:55.950 --> 01:52:58.380 of operations or views. 01:52:58.380 --> 01:53:02.890 When I'm just there visiting /login via URL, it shows me the form. 01:53:02.890 --> 01:53:07.980 But if I submit the form, then this logic, these three lines, kick in, 01:53:07.980 --> 01:53:11.970 and this just avoids my having to have both an index route and a greet route, 01:53:11.970 --> 01:53:12.540 for instance. 01:53:12.540 --> 01:53:17.640 I can just have one route that handles both GET and POST. 01:53:17.640 --> 01:53:18.450 How about logout? 01:53:18.450 --> 01:53:19.420 What does this do? 01:53:19.420 --> 01:53:21.030 Well, it's as simple as this. 01:53:21.030 --> 01:53:23.460 Change whatever name is in the session to be 01:53:23.460 --> 01:53:27.690 none, which is Python's version of null, essentially, and then redirect the user 01:53:27.690 --> 01:53:28.320 back to slash. 01:53:28.320 --> 01:53:34.390 Because now, in index.html, I will not notice a name there anymore. 01:53:34.390 --> 01:53:35.710 This will be false. 01:53:35.710 --> 01:53:39.160 And so I'll tell the user instead, you are not logged in. 01:53:39.160 --> 01:53:40.613 So like it's-- 01:53:40.613 --> 01:53:42.780 I want to say as simple as this is, though I realize 01:53:42.780 --> 01:53:44.640 this is a bunch of steps involved. 01:53:44.640 --> 01:53:47.820 This is the essence of every website on the internet that 01:53:47.820 --> 01:53:49.190 has usernames and passwords. 01:53:49.190 --> 01:53:52.440 And we skip the password name step for that, more on that in problem set nine, 01:53:52.440 --> 01:53:56.850 but this is how every website out there remembers that you're logged in. 01:53:56.850 --> 01:53:59.460 And how this works, ultimately, is that as soon 01:53:59.460 --> 01:54:03.240 as you use in Python lines like this and lines like this, 01:54:03.240 --> 01:54:07.380 Flask takes care of stamping the virtual hand of all of your users 01:54:07.380 --> 01:54:12.630 and whenever Flask sees the same cookie coming back from a user, 01:54:12.630 --> 01:54:15.870 it grabs the appropriate file from that folder, 01:54:15.870 --> 01:54:18.280 loads it into the session global variable 01:54:18.280 --> 01:54:23.250 so that your code is now unique to that user and their name. 01:54:23.250 --> 01:54:26.160 Let's do one other example with sessions here 01:54:26.160 --> 01:54:28.890 that'll show how we might use these, now, for shopping carts. 01:54:28.890 --> 01:54:31.380 Let me go into the store example here. 01:54:31.380 --> 01:54:33.210 Let me go ahead and run this thing first. 01:54:33.210 --> 01:54:39.090 If I run store in my same tab and go back over here, 01:54:39.090 --> 01:54:42.120 we'll see a very ugly e-commerce site that 01:54:42.120 --> 01:54:44.010 just sells seven different books here. 01:54:44.010 --> 01:54:48.497 But each of these books has a button via which I can add it to my cart. 01:54:48.497 --> 01:54:50.580 All right, well where are these books coming from? 01:54:50.580 --> 01:54:51.600 Well, let's poke around. 01:54:51.600 --> 01:54:54.990 Let me go into my terminal window again. 01:54:54.990 --> 01:54:58.230 Let me go into this example, which is called store, 01:54:58.230 --> 01:55:03.150 and let me open up about index dot ht-- whoops. 01:55:03.150 --> 01:55:10.620 Let's open up index, how about, books.html 01:55:10.620 --> 01:55:12.910 is the default one, not index this time. 01:55:12.910 --> 01:55:17.700 So if I look here, notice that that route that we just saw 01:55:17.700 --> 01:55:22.410 uses a for loop in Jinja to iterate over a whole bunch of books, apparently, 01:55:22.410 --> 01:55:25.770 and it outputs, in an H2 tag, the title of the book, 01:55:25.770 --> 01:55:27.960 and then another one of these forms. 01:55:27.960 --> 01:55:29.320 So that's interesting. 01:55:29.320 --> 01:55:30.310 Let's go back one step. 01:55:30.310 --> 01:55:34.230 Let's go ahead and open up app.py, because that must be-- excuse me, 01:55:34.230 --> 01:55:35.670 what's ticking all of this off. 01:55:35.670 --> 01:55:39.090 Notice that this file is importing session support. 01:55:39.090 --> 01:55:42.090 It's configuring sessions down here, but it's also 01:55:42.090 --> 01:55:44.530 connecting to a store.db file. 01:55:44.530 --> 01:55:46.080 So it's adding some SQLite. 01:55:46.080 --> 01:55:51.600 And notice this, in my /route, I'm selecting star from books, 01:55:51.600 --> 01:55:53.880 which is going to give me a list of dictionaries, 01:55:53.880 --> 01:55:55.890 each of which represents a row of books. 01:55:55.890 --> 01:56:00.120 And I'm going to pass that list of books into my books.html template, which 01:56:00.120 --> 01:56:03.840 is why this for loop works the way it does. 01:56:03.840 --> 01:56:05.520 Let's look at this actual database. 01:56:05.520 --> 01:56:10.710 Let me increase my terminal window and do SQLite of store.db.schema 01:56:10.710 --> 01:56:11.760 will show me everything. 01:56:11.760 --> 01:56:13.120 There's not much there. 01:56:13.120 --> 01:56:17.770 It's a book-- it's a table called books with two columns, ID and title. 01:56:17.770 --> 01:56:20.790 Let's do select star from books semicolon. 01:56:20.790 --> 01:56:23.550 There are the seven books, each of which has a unique ID. 01:56:23.550 --> 01:56:25.480 And you might see where this is going. 01:56:25.480 --> 01:56:29.040 If I go to the UI and I look at each of these buttons 01:56:29.040 --> 01:56:34.200 for add to cart, just like Amazon might have, notice that each of these buttons 01:56:34.200 --> 01:56:35.550 is just a form. 01:56:35.550 --> 01:56:37.503 And what's magical here, just like deregister, 01:56:37.503 --> 01:56:39.420 even though I didn't highlight it at the time, 01:56:39.420 --> 01:56:41.580 there's another type of input that allows 01:56:41.580 --> 01:56:45.480 you to specify a value without the human being able easily to change it. 01:56:45.480 --> 01:56:48.270 Instead of type equals text or type equals submit, 01:56:48.270 --> 01:56:53.230 type equals hidden will put the value in the form but not reveal it to the user. 01:56:53.230 --> 01:56:56.340 So that's how I'm saying that the idea of this book is one, 01:56:56.340 --> 01:57:00.550 the idea of this book is two, the idea of this book is three, and so forth. 01:57:00.550 --> 01:57:03.120 And each of these forms, then, will submit, apparently, 01:57:03.120 --> 01:57:08.080 to /cart using POST and that would seem to be what adds things to cart. 01:57:08.080 --> 01:57:08.830 So let's try this. 01:57:08.830 --> 01:57:10.860 Let me click on one or two of these. 01:57:10.860 --> 01:57:13.830 Let's add the first book, add to cart. 01:57:13.830 --> 01:57:14.880 Here's my cart. 01:57:14.880 --> 01:57:17.160 Notice my route change to /cart. 01:57:17.160 --> 01:57:21.120 All right, let's go back and let's add the book number two. 01:57:21.120 --> 01:57:22.110 There we have that one. 01:57:22.110 --> 01:57:25.110 And let's skip ahead to the seventh book, Deathly Hallows, 01:57:25.110 --> 01:57:27.850 and now we have all three books here. 01:57:27.850 --> 01:57:31.530 So what does the cart route do at /cart? 01:57:31.530 --> 01:57:32.280 Well, let's look. 01:57:32.280 --> 01:57:37.590 If I go back to my terminal window, look at app.py and look at /cart, OK, 01:57:37.590 --> 01:57:39.960 there's a lot going on here, but let's see. 01:57:39.960 --> 01:57:43.530 So the /cart route supports both GET or POST, 01:57:43.530 --> 01:57:47.130 which is a nice way to consolidate things into one URL. 01:57:47.130 --> 01:57:49.030 All right, this is interesting. 01:57:49.030 --> 01:57:53.183 If there is not a, quote unquote, "cart" key in session, 01:57:53.183 --> 01:57:54.850 we haven't technically seen this syntax. 01:57:54.850 --> 01:57:59.010 But long story short, these lines here do ensure that the cart exists. 01:57:59.010 --> 01:58:00.220 What do I mean by that? 01:58:00.220 --> 01:58:05.400 It makes sure that there's a cart key in the session, global variable, 01:58:05.400 --> 01:58:07.980 and it's by default going to be an empty list. 01:58:07.980 --> 01:58:08.520 Why? 01:58:08.520 --> 01:58:10.520 That just means you have an empty shopping cart. 01:58:10.520 --> 01:58:17.850 But if the user visits this route via POST and the user did provide an ID, 01:58:17.850 --> 01:58:21.480 they didn't muck with the form in any way and try to hack into the website, 01:58:21.480 --> 01:58:24.930 they gave me a valid ID, then I'm going to use this syntax. 01:58:24.930 --> 01:58:27.780 If session bracket cart is a list-- 01:58:27.780 --> 01:58:30.085 recall from a couple of weeks ago that dot append just 01:58:30.085 --> 01:58:31.210 adds something to the list. 01:58:31.210 --> 01:58:35.440 So I'm going to add the ID to the list and return the user to cart. 01:58:35.440 --> 01:58:40.850 Otherwise, if the user is at /cart via GET, implicitly, we just do this. 01:58:40.850 --> 01:58:44.330 Select star from books where ID is in. 01:58:44.330 --> 01:58:47.170 And this might be syntax you recall from Pset six. 01:58:47.170 --> 01:58:49.780 It lets you look for multiple IDs all at once, 01:58:49.780 --> 01:58:52.000 because if I have a list of session-- 01:58:52.000 --> 01:58:56.470 list of IDs in my cart, I can get all of those books at once. 01:58:56.470 --> 01:58:59.170 So long story short, what has happened here? 01:58:59.170 --> 01:59:05.170 I am storing, in the cart, the books that I myself have added to my cart. 01:59:05.170 --> 01:59:08.030 My browser is sending the same hand stamp again and again, 01:59:08.030 --> 01:59:11.150 which is how this website knows that it's me adding these books to my cart 01:59:11.150 --> 01:59:12.940 and not you or not Carter or not Emma. 01:59:12.940 --> 01:59:16.240 Indeed, if all of us visited the same long URL and I made it public 01:59:16.240 --> 01:59:19.030 and allowed that, then we would all have our own illusions 01:59:19.030 --> 01:59:20.650 of our own separate carts. 01:59:20.650 --> 01:59:23.140 And each of those carts, in practice, would just 01:59:23.140 --> 01:59:26.920 be stored in this Flask session directory on the server 01:59:26.920 --> 01:59:28.720 so that the server can keep track of each 01:59:28.720 --> 01:59:31.690 of us using, again, these cookie values that are being 01:59:31.690 --> 01:59:35.420 sent back and forth via these headers. 01:59:35.420 --> 01:59:35.920 All right. 01:59:35.920 --> 01:59:38.230 I know that's a lot, but again, it's just 01:59:38.230 --> 01:59:41.710 the new Python way of just leveraging those HTTP 01:59:41.710 --> 01:59:44.360 headers from last week in a clever way. 01:59:44.360 --> 01:59:48.340 Any questions before we look at one final set of examples? 01:59:48.340 --> 01:59:49.648 Yeah. 01:59:49.648 --> 01:59:53.560 AUDIENCE: [INAUDIBLE] understand how a log in has to do with [INAUDIBLE]?? 01:59:53.560 --> 01:59:56.983 How does it use [INAUDIBLE],, how do you change [INAUDIBLE]?? 01:59:56.983 --> 02:00:01.384 Because in order to use a GET request dot [INAUDIBLE] equals, 02:00:01.384 --> 02:00:05.320 there has to be an exchange in [INAUDIBLE].. 02:00:05.320 --> 02:00:08.530 DAVID: So I think you're asking about using the GET 02:00:08.530 --> 02:00:10.700 and POST in the same function. 02:00:10.700 --> 02:00:15.220 So this is just a nice aesthetic, if you will. 02:00:15.220 --> 02:00:18.670 If I had to have separate routes for GET and POST, I mean, 02:00:18.670 --> 02:00:21.430 it literally might mean I need twice as many routes in my file. 02:00:21.430 --> 02:00:23.680 And it just starts to get a little annoying. 02:00:23.680 --> 02:00:26.420 And these days, too, in terms of user experience, 02:00:26.420 --> 02:00:30.400 this is maybe only appeals to the geek in us, but having clean URLs 02:00:30.400 --> 02:00:31.690 is actually a thing. 02:00:31.690 --> 02:00:33.700 You don't want to have lots of words in the URL, 02:00:33.700 --> 02:00:37.160 it's nice if the URLs are nice and succinct and canonical, if you will. 02:00:37.160 --> 02:00:43.270 So it's nice if I can centralize all of my shopping cart functionality in /cart 02:00:43.270 --> 02:00:47.890 only, and not in multiple routes, one for GET, one for POST. 02:00:47.890 --> 02:00:52.250 It's a little nitpicky of me, but this is commonly done here. 02:00:52.250 --> 02:00:56.860 So what this code here means is that this route, this function, 02:00:56.860 --> 02:01:00.460 henceforth will support both GET requests and POST requests. 02:01:00.460 --> 02:01:04.450 But then I need to distinguish between whether it's GET or POST coming in. 02:01:04.450 --> 02:01:07.120 Because if it's a GET request, I want to show the cart. 02:01:07.120 --> 02:01:09.580 If it's a POST request, I want to update the cart. 02:01:09.580 --> 02:01:13.780 And the simplest way to do that is just to check this value here. 02:01:13.780 --> 02:01:17.230 In the request variable that we imported from Flask up above, 02:01:17.230 --> 02:01:20.080 you can check what is the current type of request. 02:01:20.080 --> 02:01:23.170 Is it a GET, is it a POST, or is it something else altogether? 02:01:23.170 --> 02:01:24.820 There are other verbs. 02:01:24.820 --> 02:01:29.890 If it's a POST, that must mean, because I created the web form that uses POST, 02:01:29.890 --> 02:01:33.160 that the user clicked the Add to Cart button. 02:01:33.160 --> 02:01:38.350 Otherwise, if it's not POST, it's implicitly going to be logically GET. 02:01:38.350 --> 02:01:42.610 Then, I just want to show the user the contents of the cart 02:01:42.610 --> 02:01:44.120 and I use these lines instead. 02:01:44.120 --> 02:01:47.920 So it's just one way of avoiding having two routes for two different HTTP 02:01:47.920 --> 02:01:48.430 verbs. 02:01:48.430 --> 02:01:51.340 You can combine them so long as you have a check like this. 02:01:51.340 --> 02:01:57.670 If I really wanted to be pedantic, I could do this elif request.method=get. 02:02:00.250 --> 02:02:02.830 This would be more symmetric, but it's not really necessary, 02:02:02.830 --> 02:02:05.750 because I know there's only two possibilities. 02:02:05.750 --> 02:02:08.330 Hope that helps. 02:02:08.330 --> 02:02:11.140 All right, let's do one final set of examples here 02:02:11.140 --> 02:02:13.330 that's going to tie the last of these features 02:02:13.330 --> 02:02:15.760 together to something that you probably see 02:02:15.760 --> 02:02:18.612 quite often in real-world applications. 02:02:18.612 --> 02:02:20.320 And that, for better or for worse, is now 02:02:20.320 --> 02:02:23.660 going to involve tying back in some JavaScript from last week. 02:02:23.660 --> 02:02:25.660 The goal at hand of these examples is not 02:02:25.660 --> 02:02:29.043 to necessarily master how you yourself would write the Python code, the SQL 02:02:29.043 --> 02:02:31.960 code, the JavaScript code, but just to give you a mental model for how 02:02:31.960 --> 02:02:33.290 these different languages work. 02:02:33.290 --> 02:02:35.680 So that for final projects, especially if you 02:02:35.680 --> 02:02:39.160 do want to add JavaScript functionality, much more interactive user interface, 02:02:39.160 --> 02:02:42.340 you at least have the bare bones of a mental model for how 02:02:42.340 --> 02:02:44.780 you can tie these languages together. 02:02:44.780 --> 02:02:48.370 Even though our focus, generally, has been more on Python and SQL 02:02:48.370 --> 02:02:50.560 than on JavaScript from last week. 02:02:50.560 --> 02:02:55.660 Let me go ahead and open up an example called shows, version zero of this. 02:02:55.660 --> 02:02:57.260 And let me do Flask run. 02:02:57.260 --> 02:03:01.330 And let me go into my URL here and see what this application looks 02:03:01.330 --> 02:03:06.670 like by default. This has just a simple query text box with a search box. 02:03:06.670 --> 02:03:09.700 Let's take a look at the HTML that just got sent to my browser. 02:03:09.700 --> 02:03:11.810 All right, there's not much going on here at all. 02:03:11.810 --> 02:03:14.920 So there's a form whose action is /search. 02:03:14.920 --> 02:03:16.720 It's going to submit via GET. 02:03:16.720 --> 02:03:21.140 It's going to use a q parameter, just like Google it seems, and submit it. 02:03:21.140 --> 02:03:24.110 So this actually looks like the Google form we did last week. 02:03:24.110 --> 02:03:25.990 So let's see what goes on here. 02:03:25.990 --> 02:03:28.390 Let me search for something like cat. 02:03:28.390 --> 02:03:29.740 Enter. 02:03:29.740 --> 02:03:31.820 OK, so it looks like-- 02:03:31.820 --> 02:03:34.280 all right, so this is actually a somewhat familiar file. 02:03:34.280 --> 02:03:38.470 What I've gone ahead and done is I've grabbed all of the titles of TV shows 02:03:38.470 --> 02:03:41.290 from a couple of weeks ago when we first introduced SQL, 02:03:41.290 --> 02:03:43.840 and I loaded them into this demo so that you can search 02:03:43.840 --> 02:03:45.650 by keyword for any word you want. 02:03:45.650 --> 02:03:46.750 I just searched for cat. 02:03:46.750 --> 02:03:49.900 If we were to do this again, we would see all the title of TV shows 02:03:49.900 --> 02:03:55.100 that contain D-O-G, dog, as a substring somewhere and so forth. 02:03:55.100 --> 02:03:57.130 So this is a traditional way of doing this. 02:03:57.130 --> 02:04:02.740 Just like in Google, it uses /search?q=cat, q=dog, and so forth. 02:04:02.740 --> 02:04:03.620 How does that work? 02:04:03.620 --> 02:04:07.670 Well, let's just take a quick look at app.py here. 02:04:07.670 --> 02:04:12.940 Let me go into my zero example here, show zero, and open up 02:04:12.940 --> 02:04:15.460 app.py and see what's going on. 02:04:15.460 --> 02:04:17.120 All right, very simple. 02:04:17.120 --> 02:04:19.780 Here's the form, that's how we started today. 02:04:19.780 --> 02:04:22.255 And here is the /search route. 02:04:22.255 --> 02:04:23.380 Well, what's going on here? 02:04:23.380 --> 02:04:25.070 This gets a little interesting. 02:04:25.070 --> 02:04:28.270 So I first select a whole bunch of shows by doing this. 02:04:28.270 --> 02:04:32.890 Select star from shows, where title like question mark. 02:04:32.890 --> 02:04:37.570 And then I'm using some percent signs from SQL 02:04:37.570 --> 02:04:40.030 on both the left and the right, and I'm plugging 02:04:40.030 --> 02:04:42.450 in whatever the user's input was for q. 02:04:42.450 --> 02:04:45.040 If I didn't use like and I used equal instead, 02:04:45.040 --> 02:04:48.130 I could get rid of these curly brace, these percent signs, 02:04:48.130 --> 02:04:51.760 but then it would have to be a show called cat or called dog as opposed 02:04:51.760 --> 02:04:54.460 to it being like cat or like dog. 02:04:54.460 --> 02:04:58.030 This whole line returns to me a list of dictionaries, each of which 02:04:58.030 --> 02:05:00.920 represents a show in the database. 02:05:00.920 --> 02:05:04.210 And then, I'm passing all of those shows to a template called search.html. 02:05:04.210 --> 02:05:08.010 So let's just follow that breadcrumb, let's open up shows dot-- 02:05:08.010 --> 02:05:10.390 sorry, search.html. 02:05:10.390 --> 02:05:12.570 All right, so this is where templating gets cool. 02:05:12.570 --> 02:05:15.270 So I just passed back hundreds of results, potentially, 02:05:15.270 --> 02:05:18.750 but the only thing I'm outputting is an unordered list 02:05:18.750 --> 02:05:22.830 and using a Jinja for loop and li tag containing 02:05:22.830 --> 02:05:24.745 the titles of each of those shows. 02:05:24.745 --> 02:05:27.120 And just to prove that this is indeed a familiar data set 02:05:27.120 --> 02:05:32.040 and I actually simplified it a bit, if I look at shows.db with SQLite, 02:05:32.040 --> 02:05:36.330 I threw away all the other stuff like ratings and actors and everyone else 02:05:36.330 --> 02:05:41.070 and I just have, for instance, select star from shows 02:05:41.070 --> 02:05:43.440 limit 10, just so we can see 10 of them. 02:05:43.440 --> 02:05:45.810 There's 10 of the shows from that database. 02:05:45.810 --> 02:05:48.220 So that's all that's in the database itself. 02:05:48.220 --> 02:05:51.810 So it would look like this is a pretty vanilla web application. 02:05:51.810 --> 02:05:53.845 It uses GET, it submits it to the server, 02:05:53.845 --> 02:05:56.220 the server spits out a response, and that response, then, 02:05:56.220 --> 02:06:00.990 looks like this, which is a huge number of li tags, one for each cat 02:06:00.990 --> 02:06:02.700 or one for each dog match. 02:06:02.700 --> 02:06:06.370 But everything else comes from a layout.html. 02:06:06.370 --> 02:06:09.010 All the stuff at the top and at the bottom. 02:06:09.010 --> 02:06:13.020 All right, so these days, though, we're in the habit of seeing autocomplete. 02:06:13.020 --> 02:06:15.720 And you start typing something and you don't have to hit Submit, 02:06:15.720 --> 02:06:18.595 you don't have to click a button, you don't have to go to a new page. 02:06:18.595 --> 02:06:20.950 Web applications, nowadays, are much more dynamic. 02:06:20.950 --> 02:06:24.130 So let's take a look at this version one of this thing. 02:06:24.130 --> 02:06:30.030 Let me go into shows one and close my previous tabs 02:06:30.030 --> 02:06:32.140 and run Flask run in here. 02:06:32.140 --> 02:06:36.840 And it's almost the same thing, but watch the behavior change a little bit. 02:06:36.840 --> 02:06:38.863 I'm reloading the form, there's no button now. 02:06:38.863 --> 02:06:40.530 So gone is the need for a submit button. 02:06:40.530 --> 02:06:42.550 I want to implement autocomplete now. 02:06:42.550 --> 02:06:45.150 So let's go ahead and type in C. OK, there's 02:06:45.150 --> 02:06:48.120 every show that starts with C. A, there's 02:06:48.120 --> 02:06:50.100 every show that has C-A in it, rather. 02:06:50.100 --> 02:06:53.340 T, there's every show with C-A-T in it. 02:06:53.340 --> 02:06:57.150 I can start it again and do dog, but notice how instantaneous it was. 02:06:57.150 --> 02:07:01.050 And notice my URL never changed, there's no /search route, 02:07:01.050 --> 02:07:02.250 and it's just immediate. 02:07:02.250 --> 02:07:05.890 With every keystroke, it is searching again and again and again. 02:07:05.890 --> 02:07:08.520 That's a nice UX, user experience, because it's immediate. 02:07:08.520 --> 02:07:10.770 This is what users are used to these days. 02:07:10.770 --> 02:07:15.630 But if I look at the source code here, notice that in the source code, 02:07:15.630 --> 02:07:20.853 there's just an empty UL by default but there is some fancy JavaScript code. 02:07:20.853 --> 02:07:22.270 So let's see what's going on here. 02:07:22.270 --> 02:07:25.750 This JavaScript code is doing the following. 02:07:25.750 --> 02:07:28.710 Let me zoom in a little bit more. 02:07:28.710 --> 02:07:33.092 This JavaScript code is first selecting, with query selector, 02:07:33.092 --> 02:07:35.800 which you used this past week, quote unquote "input, " all right, 02:07:35.800 --> 02:07:37.740 so that's just getting the text box. 02:07:37.740 --> 02:07:41.375 Then it's adding an event listener to that input for the input event. 02:07:41.375 --> 02:07:43.500 We didn't talk about this last week, but literally, 02:07:43.500 --> 02:07:45.600 when you provide any kind of input by typing, 02:07:45.600 --> 02:07:50.100 by pasting, by any other user interface mechanism, 02:07:50.100 --> 02:07:51.910 it triggers an event called input. 02:07:51.910 --> 02:07:53.970 So similar to key press or key up. 02:07:53.970 --> 02:07:57.780 I then have a function, no worries about this async function for now. 02:07:57.780 --> 02:07:59.482 Then what do I do inside of this? 02:07:59.482 --> 02:08:01.440 All right, so this is new, and this is the part 02:08:01.440 --> 02:08:04.110 that let's just focus on the ideas and not the syntax. 02:08:04.110 --> 02:08:06.000 JavaScript, nowadays, comes with a function 02:08:06.000 --> 02:08:10.290 called fetch that allows you to GET or POST information to a server 02:08:10.290 --> 02:08:12.100 without reloading the whole page. 02:08:12.100 --> 02:08:14.610 You can sort of secretly do it inside of the page. 02:08:14.610 --> 02:08:15.930 What do I want to fetch? 02:08:15.930 --> 02:08:21.270 slash search question mark q equals whatever the value of that input is. 02:08:21.270 --> 02:08:25.320 When I get back a response, I want to get the text of that response 02:08:25.320 --> 02:08:27.570 and store it in a variable called shows. 02:08:27.570 --> 02:08:30.240 And I'm deliberately bouncing around, ignoring special words 02:08:30.240 --> 02:08:33.600 like await and await here, but for now, just focus on what came back. 02:08:33.600 --> 02:08:36.840 A response came back from the server, I'm getting the text from it, 02:08:36.840 --> 02:08:38.670 storing it in a variable called shows. 02:08:38.670 --> 02:08:39.930 What am I then doing? 02:08:39.930 --> 02:08:44.700 I'm using query selector to select my UL, which is empty by default, 02:08:44.700 --> 02:08:48.960 and I'm changing its inner HTML to be equal to the shows that 02:08:48.960 --> 02:08:50.590 came back from the server. 02:08:50.590 --> 02:08:51.810 So let's poke around. 02:08:51.810 --> 02:08:54.640 Here's where, again, developer tools are quite powerful. 02:08:54.640 --> 02:08:58.890 Let me go ahead and reload this page to get rid of everything. 02:08:58.890 --> 02:09:02.190 And let me now open up inspect. 02:09:02.190 --> 02:09:05.530 Let me go to the Network tab and let's just sniff the traffic going 02:09:05.530 --> 02:09:06.780 between my browser and server. 02:09:06.780 --> 02:09:11.460 I'm going to search for C. Notice that immediately triggered an HTTP request 02:09:11.460 --> 02:09:13.530 to /search?q=c. 02:09:16.440 --> 02:09:20.050 So I didn't even finish my cat thought, but notice what came back. 02:09:20.050 --> 02:09:24.900 A bunch of response headers, but let's actually click on the raw response. 02:09:24.900 --> 02:09:29.550 This is literally the response from the server, just a whole bunch of li tags. 02:09:29.550 --> 02:09:32.610 No UL, no HTML, no title, no body, nothing. 02:09:32.610 --> 02:09:33.690 Just li tags. 02:09:33.690 --> 02:09:35.200 And we can actually simulate this. 02:09:35.200 --> 02:09:39.360 Let me manually go to that same URL, q=c, Enter. 02:09:39.360 --> 02:09:41.370 We are just going to get back-- 02:09:41.370 --> 02:09:42.810 whoops, sorry. 02:09:42.810 --> 02:09:47.670 slash search q equals c, we are just going to get back this stuff, 02:09:47.670 --> 02:09:50.130 which if I view source, it's not even a complete web page. 02:09:50.130 --> 02:09:53.550 The browser is trying to show it to me as a complete web page with bullets, 02:09:53.550 --> 02:09:56.100 but it's really just partial HTML. 02:09:56.100 --> 02:09:58.710 But that's perfect, because this is literally 02:09:58.710 --> 02:10:01.620 what I essentially want my Python code to copy paste 02:10:01.620 --> 02:10:04.710 into the otherwise empty UL tag. 02:10:04.710 --> 02:10:09.060 And that's what this JavaScript code then, here, is doing. 02:10:09.060 --> 02:10:13.350 Once it gets back that response from the server, it's using these lines of code 02:10:13.350 --> 02:10:18.370 to plug all of those li's into the UL after the fact. 02:10:18.370 --> 02:10:20.730 Again, changing the so-called dom. 02:10:20.730 --> 02:10:23.700 But there's a slightly better way to do this because, honestly, this 02:10:23.700 --> 02:10:25.170 is not the best design. 02:10:25.170 --> 02:10:29.490 Because if you've got a hundred shows or more, you're sending all of these tags 02:10:29.490 --> 02:10:30.450 unnecessarily. 02:10:30.450 --> 02:10:33.090 Why do I need to send all of these stupid HTML tags? 02:10:33.090 --> 02:10:36.190 Why don't I just create those when I'm ready to create them? 02:10:36.190 --> 02:10:37.920 Well, here's the final flourish. 02:10:37.920 --> 02:10:40.470 Whenever making a web application nowadays, 02:10:40.470 --> 02:10:44.640 where client and server keep talking to one another, Google Maps does this, 02:10:44.640 --> 02:10:47.430 Gmail does this, literally every cool application 02:10:47.430 --> 02:10:49.770 nowadays you load the page once and then it 02:10:49.770 --> 02:10:51.540 keeps on interacting with you without you 02:10:51.540 --> 02:10:54.210 reloading or having to change the URL. 02:10:54.210 --> 02:10:58.710 Let's actually use a format called JSON, JavaScript Object Notation, which 02:10:58.710 --> 02:11:01.800 is to say there's just a better, more efficient, better 02:11:01.800 --> 02:11:04.390 designed way to send that same data. 02:11:04.390 --> 02:11:08.670 I'm going to go into shows two now and do Flask run. 02:11:08.670 --> 02:11:10.410 And I'm going to go back to my page here. 02:11:10.410 --> 02:11:14.760 The user interface is exactly the same, and it still works exactly the same. 02:11:14.760 --> 02:11:18.790 Here's C, C-A, C-A-T, and so forth. 02:11:18.790 --> 02:11:21.270 But let's see what's coming back now. 02:11:21.270 --> 02:11:31.390 If I go to /search?q=cat, Enter, notice that I get this crazy-looking syntax. 02:11:31.390 --> 02:11:34.860 But the fact that it's so compact is actually a good thing. 02:11:34.860 --> 02:11:38.610 This is actually going to-- let me format it a little nicer, well, 02:11:38.610 --> 02:11:39.690 or a little worse. 02:11:39.690 --> 02:11:42.990 This is what's called JavaScript Object Notation. 02:11:42.990 --> 02:11:48.780 In JavaScript, an angle-- a square bracket means here comes an array. 02:11:48.780 --> 02:11:54.780 In JavaScript, a curly bracket says here comes an object, AKA, a dictionary. 02:11:54.780 --> 02:11:57.840 And you might recall from-- 02:11:57.840 --> 02:11:59.020 did we do? 02:11:59.020 --> 02:12:05.010 Yes, sort of recall that you can now have keys and values in JavaScript 02:12:05.010 --> 02:12:07.360 notation using colons like this. 02:12:07.360 --> 02:12:10.950 So long story short, cryptic as this is to you and me 02:12:10.950 --> 02:12:14.310 and not very human friendly, it's very machine friendly. 02:12:14.310 --> 02:12:18.030 Because for every title in that database, 02:12:18.030 --> 02:12:23.800 I get back its ID and its title, its ID and its title, its ID and its title. 02:12:23.800 --> 02:12:27.870 And this is a very generic format that an API, an application programming 02:12:27.870 --> 02:12:29.340 interface, might return to you. 02:12:29.340 --> 02:12:31.380 And this is how APIs, nowadays, work. 02:12:31.380 --> 02:12:36.120 You get back very raw textual data in this format, JSON format, 02:12:36.120 --> 02:12:39.840 and then you can write code that actually programmatically turns 02:12:39.840 --> 02:12:44.530 that JSON data into any language you want, for instance, HTML. 02:12:44.530 --> 02:12:47.490 So here's the third and final version of this program. 02:12:47.490 --> 02:12:49.590 I, again, select my input. 02:12:49.590 --> 02:12:51.870 I, again, listen for input. 02:12:51.870 --> 02:12:54.690 I then, when I get input, call this function. 02:12:54.690 --> 02:13:01.230 I fetch slash search q equals whatever that input was, C or C-A or C-A-T. 02:13:01.230 --> 02:13:04.193 I then wait for the response, but instead of getting text, 02:13:04.193 --> 02:13:07.110 I'm calling this other function that comes with JavaScript these days, 02:13:07.110 --> 02:13:09.330 called JSON, that just parses that. 02:13:09.330 --> 02:13:13.470 It turns it into a dictionary for me, or really a list of dictionaries for me, 02:13:13.470 --> 02:13:15.810 and stores it in a variable called shows. 02:13:15.810 --> 02:13:20.070 And this is where you start to see the convergence of HTML with JavaScript. 02:13:20.070 --> 02:13:23.400 Let me initialize a variable called HTML to nothing, quote unquote, 02:13:23.400 --> 02:13:26.200 using single quotes, but I could also use double quotes. 02:13:26.200 --> 02:13:28.470 This is JavaScript syntax for a loop. 02:13:28.470 --> 02:13:32.340 Let me iterate over every ID in the show's list 02:13:32.340 --> 02:13:36.120 that I just got back in the server, that big chunk of JSON data. 02:13:36.120 --> 02:13:41.430 Let me create a variable called Title that's equal to the shows-- 02:13:41.430 --> 02:13:43.890 the title of the show at that ID. 02:13:43.890 --> 02:13:45.900 But for reasons we'll come back to, let me 02:13:45.900 --> 02:13:47.820 replace a couple of scary characters. 02:13:47.820 --> 02:13:53.910 Then let me dynamically add to this variable, an li tag, the actual title, 02:13:53.910 --> 02:13:55.470 and a close li tag. 02:13:55.470 --> 02:13:58.230 And then very lastly, after this for loop, 02:13:58.230 --> 02:14:04.320 let me update the ULs in our HTML to be the HTML I just created on the fly. 02:14:04.320 --> 02:14:06.900 So in short, don't worry too much about the syntax 02:14:06.900 --> 02:14:09.358 because you won't need to use this unless you start playing 02:14:09.358 --> 02:14:11.470 with more advanced features quite soon. 02:14:11.470 --> 02:14:13.440 But what we're doing is, with JavaScript, we're 02:14:13.440 --> 02:14:16.360 creating a bigger and bigger and bigger string of HTML 02:14:16.360 --> 02:14:19.800 containing all of the open brackets, the li tags, the closed brackets, 02:14:19.800 --> 02:14:23.160 but we're just grabbing the raw data from the server. 02:14:23.160 --> 02:14:25.170 And so in fact in problem set nine, you're 02:14:25.170 --> 02:14:28.890 going to use a real world third party API, application programming 02:14:28.890 --> 02:14:30.330 interface, for which you sign up. 02:14:30.330 --> 02:14:33.090 The data you're going to get back from that API is not 02:14:33.090 --> 02:14:36.540 going to be show titles, but actually stock quotes and stocks 02:14:36.540 --> 02:14:39.280 ticker symbols and the prices of last-- 02:14:39.280 --> 02:14:41.310 at which stocks were last bought or sold, 02:14:41.310 --> 02:14:44.118 and you're going to get that data back in JSON format. 02:14:44.118 --> 02:14:47.160 And you're going to write a bit of code that's then going to convert that 02:14:47.160 --> 02:14:50.230 to the requisite HTML on the page. 02:14:50.230 --> 02:14:53.520 So the final result here is literally the kind of autocomplete 02:14:53.520 --> 02:14:55.890 that you and I see and take for granted every day, 02:14:55.890 --> 02:14:58.050 and that's ultimately how it works. 02:14:58.050 --> 02:15:01.680 HTML and CSS are used to present the data, your so-called view. 02:15:01.680 --> 02:15:06.100 Python might be used to send or get the data on the backend server. 02:15:06.100 --> 02:15:08.370 And then lastly, JavaScript is going to be used 02:15:08.370 --> 02:15:10.757 to make things dynamic and interactive. 02:15:10.757 --> 02:15:12.840 So I know that's a whole bunch of building blocks, 02:15:12.840 --> 02:15:15.632 but the whole point of problem set nine to tie everything together, 02:15:15.632 --> 02:15:18.150 set the stage for hopefully a very successful final project. 02:15:18.150 --> 02:15:20.400 Why don't we go ahead and wrap up there, and we'll see 02:15:20.400 --> 02:15:23.610 you one last time next week for emoji. 02:15:23.610 --> 02:15:26.960 [MUSIC PLAYING] 02:15:26.960 --> 02:15:58.000 [MUSIC PLAYING]