1 00:00:00,000 --> 00:00:03,472 [MUSIC PLAYING] 2 00:00:03,472 --> 00:01:18,510 3 00:01:18,510 --> 00:01:19,410 DAVID: All right. 4 00:01:19,410 --> 00:01:23,460 So this is CS50 and this is week nine, and this is it 5 00:01:23,460 --> 00:01:25,710 in terms of programming fundamentals. 6 00:01:25,710 --> 00:01:28,488 Today, we come rather full circle with so many of the languages 7 00:01:28,488 --> 00:01:30,780 that we've been looking at over the past several weeks. 8 00:01:30,780 --> 00:01:33,750 And with HTML and CSS and JavaScript last week, 9 00:01:33,750 --> 00:01:37,020 we're going to add back into the mix, Python and SQL. 10 00:01:37,020 --> 00:01:40,420 And with that, do we have the ability to program for the web. 11 00:01:40,420 --> 00:01:42,840 And even though this isn't the only user interface out 12 00:01:42,840 --> 00:01:45,930 there, increasingly-- or people certainly using laptops and desktops 13 00:01:45,930 --> 00:01:48,960 and a browser to access applications that people have written, 14 00:01:48,960 --> 00:01:53,550 but it's also, increasingly, the way that mobile apps are written as well. 15 00:01:53,550 --> 00:01:55,770 There are languages called Swift for iOS, 16 00:01:55,770 --> 00:01:57,930 there are languages called Java for Android, 17 00:01:57,930 --> 00:02:00,750 but coding applications in both of those language 18 00:02:00,750 --> 00:02:04,530 means knowing twice as many language, building twice as many applications, 19 00:02:04,530 --> 00:02:05,190 potentially. 20 00:02:05,190 --> 00:02:07,740 So we're increasingly seeing, for better or for worse, 21 00:02:07,740 --> 00:02:09,840 that the world is starting to really standardize, 22 00:02:09,840 --> 00:02:13,740 at least for the next some number of years, on HTML, CSS, and JavaScript 23 00:02:13,740 --> 00:02:17,730 coupled with other languages like Python and SQL on the so-called backend. 24 00:02:17,730 --> 00:02:20,168 And so today, we'll tie all of those together 25 00:02:20,168 --> 00:02:22,710 and give you the last of the tools in your toolkit with which 26 00:02:22,710 --> 00:02:25,290 to tackle final projects to go off into the real world, 27 00:02:25,290 --> 00:02:28,200 ultimately, and somehow solve problems with programming. 28 00:02:28,200 --> 00:02:33,310 But we need an additional tool today, and we've sort of outgrown HTTP server. 29 00:02:33,310 --> 00:02:36,477 This is just a program that comes on certain computers 30 00:02:36,477 --> 00:02:38,310 that you can install for free, happens to be 31 00:02:38,310 --> 00:02:40,980 written in a language called JavaScript, but it's a program 32 00:02:40,980 --> 00:02:44,340 that we've been using to run a web server in VSCO. 33 00:02:44,340 --> 00:02:47,170 But you can run it on your own Mac or PC or anywhere else. 34 00:02:47,170 --> 00:02:50,940 But all this particular HTTP server does is 35 00:02:50,940 --> 00:02:54,750 serve up static content like HTML files, CSS files, 36 00:02:54,750 --> 00:02:59,220 JavaScript files, maybe images, maybe video files, but just static content. 37 00:02:59,220 --> 00:03:03,600 It has no ability to really interact with the user beyond simple clicks. 38 00:03:03,600 --> 00:03:08,800 You can create a web form and serve it visually using HTTP server, 39 00:03:08,800 --> 00:03:12,630 but if the human types in input into a form and click Submit, unless you 40 00:03:12,630 --> 00:03:16,080 submit it elsewhere to something like google.com like we did last time, 41 00:03:16,080 --> 00:03:19,200 it's not actually going to go anywhere because this server can't actually 42 00:03:19,200 --> 00:03:21,460 process the requests that are coming in. 43 00:03:21,460 --> 00:03:24,390 So today, we're going to introduce another type of server that 44 00:03:24,390 --> 00:03:28,350 comes with Python that allows us to not only serve web pages 45 00:03:28,350 --> 00:03:29,970 but also process user input. 46 00:03:29,970 --> 00:03:33,720 And recall that all that input is going to come ultimately from the URL, 47 00:03:33,720 --> 00:03:36,300 or more deeply inside of those virtual envelopes. 48 00:03:36,300 --> 00:03:40,140 So here's the canonical URL we talked about last week for random website 49 00:03:40,140 --> 00:03:42,280 like www.example.com. 50 00:03:42,280 --> 00:03:46,020 And I've highlighted the slash to connote the root of the web server, 51 00:03:46,020 --> 00:03:48,540 like the default folder where, presumably, there's 52 00:03:48,540 --> 00:03:52,150 a file called index.html or something else in there. 53 00:03:52,150 --> 00:03:54,360 Otherwise, you might have a more explicit mention 54 00:03:54,360 --> 00:03:56,790 of the actual file named file.html. 55 00:03:56,790 --> 00:03:59,873 You can have folders, as you probably gleaned from the most recent problem 56 00:03:59,873 --> 00:04:00,373 set. 57 00:04:00,373 --> 00:04:02,460 You can have files in folders like this, and these 58 00:04:02,460 --> 00:04:06,330 are all examples of what a programmer would typically call a path. 59 00:04:06,330 --> 00:04:08,160 So it might not just be a single word, it 60 00:04:08,160 --> 00:04:12,070 might have multiple slashes and multiple folders and some folders and files. 61 00:04:12,070 --> 00:04:14,070 But this is just more generally known as a path. 62 00:04:14,070 --> 00:04:16,709 But there's another term of our, that's essentially equivalent, 63 00:04:16,709 --> 00:04:17,890 that we'll introduce today. 64 00:04:17,890 --> 00:04:21,300 This is also synonymously called a route, which is maybe 65 00:04:21,300 --> 00:04:25,260 a better generic description of what these things are because it turns out 66 00:04:25,260 --> 00:04:27,750 they don't have to map to, that is, refer 67 00:04:27,750 --> 00:04:30,810 to a specific folder or a specific file, you 68 00:04:30,810 --> 00:04:33,333 can come up with your own routes in a website. 69 00:04:33,333 --> 00:04:36,000 And just make sure that when the user visits that, you give them 70 00:04:36,000 --> 00:04:36,900 a certain website page. 71 00:04:36,900 --> 00:04:39,608 If they visit something else, you give them a different web page. 72 00:04:39,608 --> 00:04:42,480 It doesn't have to map to a very specific file, as we'll soon see. 73 00:04:42,480 --> 00:04:45,660 And if you want to get input from the user, just like Google does, 74 00:04:45,660 --> 00:04:50,460 like q=cats, you can add a question mark at the end of this route. 75 00:04:50,460 --> 00:04:54,300 The key, or the HTTP parameter name that you want to define for yourself, 76 00:04:54,300 --> 00:04:57,263 and then equal sum value that, presumably, the human typed in. 77 00:04:57,263 --> 00:04:59,430 If you have more of these, you can put an ampersand, 78 00:04:59,430 --> 00:05:04,140 and then more key equals value pairs ampersand, repeat, repeat, repeat. 79 00:05:04,140 --> 00:05:08,770 The catch, though, is that using the tools that we had last week alone, 80 00:05:08,770 --> 00:05:13,920 we don't really have the ability to parse, that is, to analyze and extract 81 00:05:13,920 --> 00:05:15,840 things like q equals cats. 82 00:05:15,840 --> 00:05:18,900 You could have appended question mark q equals cats or anything else 83 00:05:18,900 --> 00:05:22,800 to any of URLs in your home page for problem set eight, 84 00:05:22,800 --> 00:05:26,460 but it doesn't actually do anything useful, necessarily, 85 00:05:26,460 --> 00:05:28,350 unless you use some fancy JavaScript. 86 00:05:28,350 --> 00:05:31,480 The server is not going to bother even looking in that for you. 87 00:05:31,480 --> 00:05:34,380 But today, we're going to introduce using a bit of Python. 88 00:05:34,380 --> 00:05:38,220 And in fact, we're going to use a web server implemented in Python, instead 89 00:05:38,220 --> 00:05:41,550 of using HTTP server alone, to automatically, for you, 90 00:05:41,550 --> 00:05:44,430 look for any key value pairs after the question mark 91 00:05:44,430 --> 00:05:48,120 and then hand them to you in the form of a Python dictionary. 92 00:05:48,120 --> 00:05:52,020 Recall that a dictionary in Python, a dict object, is just key value pairs. 93 00:05:52,020 --> 00:05:56,155 That seems like a perfect fit for these kinds of parameters. 94 00:05:56,155 --> 00:05:58,530 And you're not going to have to write that code yourself. 95 00:05:58,530 --> 00:06:02,140 It's going to be handed to you by way of what's called a framework. 96 00:06:02,140 --> 00:06:04,020 So this will be the second of two frameworks, 97 00:06:04,020 --> 00:06:05,562 really, that we look at in the class. 98 00:06:05,562 --> 00:06:08,250 And a framework is essentially a bunch of libraries 99 00:06:08,250 --> 00:06:10,740 that someone else wrote and a set of conventions, 100 00:06:10,740 --> 00:06:12,190 therefore, for doing things. 101 00:06:12,190 --> 00:06:15,000 So those of you who really started dabbling with Bootstrap 102 00:06:15,000 --> 00:06:18,680 this past week to make your home pages prettier and nicely laid out, 103 00:06:18,680 --> 00:06:19,950 you are using a framework. 104 00:06:19,950 --> 00:06:20,450 Why? 105 00:06:20,450 --> 00:06:24,110 Well, you're using libraries, code that someone else wrote, like all the CSS, 106 00:06:24,110 --> 00:06:27,200 maybe some of the JavaScript that the Bootstrap people wrote for you. 107 00:06:27,200 --> 00:06:30,980 But it's also a framework in the sense that you have to go all in. 108 00:06:30,980 --> 00:06:33,500 You have to use Bootstraps classes, and you 109 00:06:33,500 --> 00:06:37,820 have to lay out your divs or your spans or your table tags 110 00:06:37,820 --> 00:06:39,663 in a sort of Bootstrap-friendly way. 111 00:06:39,663 --> 00:06:42,080 And it's not too onerous, but you're following conventions 112 00:06:42,080 --> 00:06:43,920 that a bunch of humans standardized on. 113 00:06:43,920 --> 00:06:47,660 So similarly, in the world of Python, is there another framework 114 00:06:47,660 --> 00:06:49,040 we're going to start using today. 115 00:06:49,040 --> 00:06:52,430 And whereas Bootstrap is used for CSS and JavaScript, 116 00:06:52,430 --> 00:06:54,470 Flask is going to be used for Python. 117 00:06:54,470 --> 00:06:57,170 And it just solves a lot of common problems for us. 118 00:06:57,170 --> 00:07:00,380 It's going to make it easier for us to analyze the URLs 119 00:07:00,380 --> 00:07:03,020 and get key value pairs, it's going to make it easier 120 00:07:03,020 --> 00:07:06,110 for us to find files or images that the human wants 121 00:07:06,110 --> 00:07:07,550 to see when visiting our website. 122 00:07:07,550 --> 00:07:10,442 It's even going to make it easier to send emails automatically, 123 00:07:10,442 --> 00:07:11,900 like when someone fills out a form. 124 00:07:11,900 --> 00:07:15,210 You can dynamically, using code, send them an email as well. 125 00:07:15,210 --> 00:07:17,510 So Flask, and with it some related libraries, 126 00:07:17,510 --> 00:07:20,750 it's just going to make stuff like that easier for us. 127 00:07:20,750 --> 00:07:25,340 And to do this, all we have to do is adhere to some pretty minimalist 128 00:07:25,340 --> 00:07:27,450 requirements of this framework. 129 00:07:27,450 --> 00:07:30,650 We're going to have to create a file for ourselves called app.py, 130 00:07:30,650 --> 00:07:33,650 this is where our web app or application is going to live. 131 00:07:33,650 --> 00:07:38,120 If we have any libraries that we want to use, the convention in the Python world 132 00:07:38,120 --> 00:07:41,750 is to have a very simple text file called requirements.txt 133 00:07:41,750 --> 00:07:44,090 where you list the names of those libraries, 134 00:07:44,090 --> 00:07:48,890 top to bottom, in that text file, similar in spirit to the include 135 00:07:48,890 --> 00:07:52,650 or the import statements that we saw in C and Python, respectively. 136 00:07:52,650 --> 00:07:55,700 We're going to have a static folder or static directory, which 137 00:07:55,700 --> 00:07:58,850 means any files you create that are not ever going to change, 138 00:07:58,850 --> 00:08:01,442 like images, CSS files, JavaScript files, 139 00:08:01,442 --> 00:08:02,900 they're going to go in this folder. 140 00:08:02,900 --> 00:08:05,750 And then lastly, any HTML that you write, 141 00:08:05,750 --> 00:08:08,150 web pages you want the human to see, are going 142 00:08:08,150 --> 00:08:09,990 to go in a folder called templates. 143 00:08:09,990 --> 00:08:12,950 So this is, again, evidence of what we mean by a framework. 144 00:08:12,950 --> 00:08:14,750 Do you have to make a web app like this? 145 00:08:14,750 --> 00:08:16,940 No, but if you're using this particular framework, 146 00:08:16,940 --> 00:08:20,990 this is what people decided would be the human conventions. 147 00:08:20,990 --> 00:08:25,200 If you've heard of other frameworks like Django or asp.net or bunches of others, 148 00:08:25,200 --> 00:08:28,370 there are just different conventions out there for creating applications. 149 00:08:28,370 --> 00:08:32,390 Flask is a very nice microframework in that that's it. 150 00:08:32,390 --> 00:08:35,870 All you have to do is adhere to these pretty minimalist requirements 151 00:08:35,870 --> 00:08:38,840 to get some code up and running. 152 00:08:38,840 --> 00:08:41,620 All right, so let's go ahead and make a web app. 153 00:08:41,620 --> 00:08:43,370 Let me go ahead and switch over to VS Code 154 00:08:43,370 --> 00:08:45,412 here, and let me practice what I'm preaching here 155 00:08:45,412 --> 00:08:47,720 by first creating app.py. 156 00:08:47,720 --> 00:08:51,410 And let's go ahead and create an application that very simply, 157 00:08:51,410 --> 00:08:53,700 maybe, says hello to the user. 158 00:08:53,700 --> 00:08:58,160 So something that, initially, is not all that dynamic, pretty static, in fact. 159 00:08:58,160 --> 00:09:00,120 But we'll build on that as we've always done. 160 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 161 00:09:04,100 --> 00:09:05,420 I had on the screen earlier. 162 00:09:05,420 --> 00:09:11,840 From Flask, import Flask, with a capital F second and a lowercase f first. 163 00:09:11,840 --> 00:09:14,390 And I'm also going to preemptively import 164 00:09:14,390 --> 00:09:18,440 a couple of functions, render template, and request. 165 00:09:18,440 --> 00:09:20,360 More on those in just a bit. 166 00:09:20,360 --> 00:09:23,000 And then below that, I'm going to say, go ahead and do this. 167 00:09:23,000 --> 00:09:25,730 Give me a web-- a variable called app that's 168 00:09:25,730 --> 00:09:29,930 going to be the result of calling the Flask function and passing in it 169 00:09:29,930 --> 00:09:32,400 this weird incantation here, name. 170 00:09:32,400 --> 00:09:35,810 So we've seen this a few weeks back when we played around with Python 171 00:09:35,810 --> 00:09:38,540 and we had that if main thing at the bottom of the screen. 172 00:09:38,540 --> 00:09:44,090 For now, just know that __name__ refers to the name of the current file. 173 00:09:44,090 --> 00:09:48,560 And so this line here, simple as it is, tells Python, hey, Python, 174 00:09:48,560 --> 00:09:52,310 turn this file into a Flask application. 175 00:09:52,310 --> 00:09:56,030 Flask is a function that just figures out, then, how to do the rest. 176 00:09:56,030 --> 00:09:59,630 The last thing I'm going to do for this very simple web application is this. 177 00:09:59,630 --> 00:10:03,290 I'm going to say that I'm going to have a function called 178 00:10:03,290 --> 00:10:05,210 index that takes no arguments. 179 00:10:05,210 --> 00:10:07,130 And whenever this function is called, I want 180 00:10:07,130 --> 00:10:12,560 to return the results of rendering a template called index.html. 181 00:10:12,560 --> 00:10:13,515 And that's it. 182 00:10:13,515 --> 00:10:15,890 So let's assume there's a file somewhere, haven't created 183 00:10:15,890 --> 00:10:17,720 it yet, called index.html. 184 00:10:17,720 --> 00:10:19,940 But render template means render this file 185 00:10:19,940 --> 00:10:23,400 that is printed to the user's screen, so to speak. 186 00:10:23,400 --> 00:10:26,210 The last thing I'm going to do is I have to tell Flask 187 00:10:26,210 --> 00:10:28,500 when to call this index function. 188 00:10:28,500 --> 00:10:34,166 And so I'm going to tell it to define a route for, quote unquote, "slash." 189 00:10:34,166 --> 00:10:35,268 And that's it. 190 00:10:35,268 --> 00:10:37,310 So let's take a look at what I just created here. 191 00:10:37,310 --> 00:10:40,280 This is slightly new syntax, and it's really the only weirdness 192 00:10:40,280 --> 00:10:42,050 that we'll have today in Python. 193 00:10:42,050 --> 00:10:44,750 This is what's known in Python is what's called a decorator. 194 00:10:44,750 --> 00:10:46,760 A decorator is a special type of function 195 00:10:46,760 --> 00:10:49,310 that modifies, essentially, another function. 196 00:10:49,310 --> 00:10:52,350 For our purposes, just know that on line six this says, 197 00:10:52,350 --> 00:10:55,700 hey Python, define a route for slash, the default 198 00:10:55,700 --> 00:10:57,500 page on my website application. 199 00:10:57,500 --> 00:10:59,900 The next two lines, seven and eight, say, hey Python, 200 00:10:59,900 --> 00:11:02,630 define a function called index, takes no arguments. 201 00:11:02,630 --> 00:11:07,520 And the only thing you should ever do is return render template of quote unquote 202 00:11:07,520 --> 00:11:09,540 "index.html." 203 00:11:09,540 --> 00:11:10,650 All right, so that's it. 204 00:11:10,650 --> 00:11:13,700 So really, the next question, naturally, should be all right, well, 205 00:11:13,700 --> 00:11:17,490 what is in index.html? 206 00:11:17,490 --> 00:11:19,230 Well, let me go ahead and do that next. 207 00:11:19,230 --> 00:11:22,500 Let me create a directory called templates, practicing, again, 208 00:11:22,500 --> 00:11:23,760 what I preached earlier. 209 00:11:23,760 --> 00:11:26,520 So I'm going to create a new empty directory called templates, 210 00:11:26,520 --> 00:11:30,240 I'm going to go and CD into that directory 211 00:11:30,240 --> 00:11:33,900 and then do code of index.html. 212 00:11:33,900 --> 00:11:35,520 So here is going to be my index page. 213 00:11:35,520 --> 00:11:38,465 And I'm going to do a very simple web page, doc type HTML. 214 00:11:38,465 --> 00:11:40,590 I'm just going to borrow some stuff from last week. 215 00:11:40,590 --> 00:11:42,840 HTML language equals English. 216 00:11:42,840 --> 00:11:44,010 I'll close that tag. 217 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. 218 00:11:47,730 --> 00:11:50,250 This makes my site recall responsive. 219 00:11:50,250 --> 00:11:52,980 That is, it just grows and shrink to fit the size of the device. 220 00:11:52,980 --> 00:11:56,460 The initial scale for which is going to be one, and the width of which 221 00:11:56,460 --> 00:11:58,500 is going to be device width. 222 00:11:58,500 --> 00:12:00,540 So I'm typing this out, I have it printed here. 223 00:12:00,540 --> 00:12:02,490 This is stuff I typically copy paste. 224 00:12:02,490 --> 00:12:05,190 But then lastly, I'm going to add in my title, which will just 225 00:12:05,190 --> 00:12:06,750 be hello for the name of this app. 226 00:12:06,750 --> 00:12:07,750 And then the body-- 227 00:12:07,750 --> 00:12:08,640 whoops, Bobby. 228 00:12:08,640 --> 00:12:12,510 The body of this tag will be-- 229 00:12:12,510 --> 00:12:13,470 there we go. 230 00:12:13,470 --> 00:12:17,140 The body of this page, rather, will just be hello comma world. 231 00:12:17,140 --> 00:12:20,680 So very uninteresting and really a regression to where we began last week. 232 00:12:20,680 --> 00:12:24,030 But let's go now and experiment with these two files. 233 00:12:24,030 --> 00:12:25,890 I'm not going to bother with a static folder 234 00:12:25,890 --> 00:12:28,890 right now, because I don't have any other files that I want to serve up. 235 00:12:28,890 --> 00:12:30,990 No images, no CSS, nothing like that. 236 00:12:30,990 --> 00:12:34,000 And honestly, requirements.txt is going to be pretty simple. 237 00:12:34,000 --> 00:12:37,800 I'm going to go requirements.txt and just say make sure the system has 238 00:12:37,800 --> 00:12:41,190 access to the Flask library itself. 239 00:12:41,190 --> 00:12:44,190 All right, but that's the only thing we can add in there for now. 240 00:12:44,190 --> 00:12:49,470 All right, so now I have two files, app.py, and I have index.html. 241 00:12:49,470 --> 00:12:52,950 But index.html thank you is inside of my templates directory 242 00:12:52,950 --> 00:12:55,450 so how do I actually start a web server last week, 243 00:12:55,450 --> 00:12:56,730 I would have said HTTP server. 244 00:12:56,730 --> 00:12:59,070 But HTTP server is not a Python thing. 245 00:12:59,070 --> 00:13:03,390 It has no idea about Flask or Python or anything I just wrote. 246 00:13:03,390 --> 00:13:06,220 HTTP server will just spit out static files. 247 00:13:06,220 --> 00:13:09,525 So if I ran HTTP server, and then I clicked on app.py, 248 00:13:09,525 --> 00:13:11,490 I would literally see my Python code. 249 00:13:11,490 --> 00:13:15,660 It would not get executed because HTTP server is just for static content. 250 00:13:15,660 --> 00:13:19,740 But today, I'm going to run a different command called Flask run. 251 00:13:19,740 --> 00:13:23,490 So this framework Flask that I actually preinstalled in advance, 252 00:13:23,490 --> 00:13:27,180 so it wasn't strictly necessary that I create that requirements.txt file just 253 00:13:27,180 --> 00:13:31,140 yet, comes with a program called Flask, takes command line arguments like 254 00:13:31,140 --> 00:13:34,710 the word run, and when I do that, you'll see somewhat similar output to last 255 00:13:34,710 --> 00:13:36,900 week whereby you'll see the name-- 256 00:13:36,900 --> 00:13:39,867 your URL for your unique preview of that. 257 00:13:39,867 --> 00:13:42,450 You might see a pop up saying that your application is running 258 00:13:42,450 --> 00:13:44,400 on TCP port, something or other. 259 00:13:44,400 --> 00:13:46,830 By default, last week, we used port 8080. 260 00:13:46,830 --> 00:13:49,860 Flask, just because, prefers port 5,000. 261 00:13:49,860 --> 00:13:50,940 So that's fine too. 262 00:13:50,940 --> 00:13:53,670 I'm going to go ahead and open up this URL now. 263 00:13:53,670 --> 00:13:55,890 And once it authenticates and redirects me, 264 00:13:55,890 --> 00:13:59,460 just to make sure I'm allowed to access that particular port, let me zoom in. 265 00:13:59,460 --> 00:14:02,460 Voila, there's the extent of this application. 266 00:14:02,460 --> 00:14:06,150 If I view source by right-clicking or control clicking, 267 00:14:06,150 --> 00:14:08,130 there's my HTML that's been spit out. 268 00:14:08,130 --> 00:14:11,070 So really, I've just reinvented the wheel from last week 269 00:14:11,070 --> 00:14:13,510 because there's no dynamism now, nothing at all. 270 00:14:13,510 --> 00:14:15,207 But what if I do this? 271 00:14:15,207 --> 00:14:17,040 Let me close the source and let me zoom out. 272 00:14:17,040 --> 00:14:18,660 So you can see my URL bar. 273 00:14:18,660 --> 00:14:21,840 Let me zoom in now, and I have a very unique cryptic URL. 274 00:14:21,840 --> 00:14:23,820 But the point is that it ends with nothing. 275 00:14:23,820 --> 00:14:26,040 Or implicitly, it ends with slash. 276 00:14:26,040 --> 00:14:27,930 This is just Chrome being a little helpful. 277 00:14:27,930 --> 00:14:31,000 It doesn't bother showing you a slash, even though it's implicitly there. 278 00:14:31,000 --> 00:14:37,110 But let me do something explicit like my name equals, quote unquote, "David." 279 00:14:37,110 --> 00:14:39,480 So there's a key value pair that I've manually 280 00:14:39,480 --> 00:14:42,900 typed into my URL bar and hit Enter. 281 00:14:42,900 --> 00:14:44,400 Nothing happens, nothing changes. 282 00:14:44,400 --> 00:14:45,900 It still says hello, world. 283 00:14:45,900 --> 00:14:50,220 But the opportunity today is to now, dynamically, get at the input 284 00:14:50,220 --> 00:14:53,410 from that URL and start displaying it to the user. 285 00:14:53,410 --> 00:14:57,610 So let me go back over here to my terminal window and code. 286 00:14:57,610 --> 00:15:00,060 Let me move that down to the bottom there. 287 00:15:00,060 --> 00:15:03,280 And what if I want to say, huh, hello, name. 288 00:15:03,280 --> 00:15:05,010 I ideally want to say something like-- 289 00:15:05,010 --> 00:15:06,630 I don't want to hard code David because then it's never 290 00:15:06,630 --> 00:15:08,520 going to say hello to anyone else. 291 00:15:08,520 --> 00:15:14,010 I want to put like a variable name here, like name should go here. 292 00:15:14,010 --> 00:15:17,550 But it's not an HTML tag, so I need some kind of placeholder. 293 00:15:17,550 --> 00:15:19,680 Well, here's what I can do. 294 00:15:19,680 --> 00:15:24,810 If I go back to my Python code, I can now define a variable called name. 295 00:15:24,810 --> 00:15:28,800 And I can ask Flask to go into the current request, 296 00:15:28,800 --> 00:15:32,670 into its arguments, that is in the URL, as they're called, 297 00:15:32,670 --> 00:15:36,960 and get whatever the value of the parameter called name is. 298 00:15:36,960 --> 00:15:39,030 That puts that into a variable for me. 299 00:15:39,030 --> 00:15:41,940 And then, in render template-- this is one of those functions 300 00:15:41,940 --> 00:15:43,800 that can take more than one argument. 301 00:15:43,800 --> 00:15:45,780 If it takes another argument, you can pass 302 00:15:45,780 --> 00:15:47,740 in the name of any variable you want. 303 00:15:47,740 --> 00:15:52,210 So if I want to pass in my name, I can literally say name equals name. 304 00:15:52,210 --> 00:15:56,970 So this is the name of a variable I want to give to the template. 305 00:15:56,970 --> 00:16:01,920 This is the actual variable that I want to get the value from. 306 00:16:01,920 --> 00:16:07,920 And now lastly, in my index.html, the syntax as of today in Flask, 307 00:16:07,920 --> 00:16:12,030 is to do two curly braces and then put the name of the variable 308 00:16:12,030 --> 00:16:13,592 that you want to plug in. 309 00:16:13,592 --> 00:16:15,860 So here's what we mean by a template. 310 00:16:15,860 --> 00:16:18,950 A template is like a blueprint in the real world, where 311 00:16:18,950 --> 00:16:21,080 it's plans to make something. 312 00:16:21,080 --> 00:16:24,950 This is the plan to make a web page that has all of this code literally, 313 00:16:24,950 --> 00:16:28,460 but there's this placeholder with two curly braces here and here 314 00:16:28,460 --> 00:16:32,880 that says go ahead and plug in the value of the name variable right there. 315 00:16:32,880 --> 00:16:35,450 So in this sense, it's similar in spirit to our f strings 316 00:16:35,450 --> 00:16:36,980 or format strings in Python. 317 00:16:36,980 --> 00:16:39,897 The syntax is a little different just because reasonable people 318 00:16:39,897 --> 00:16:42,230 disagree, different people, different frameworks come up 319 00:16:42,230 --> 00:16:43,355 with different conventions. 320 00:16:43,355 --> 00:16:46,220 The convention in Flask, in their templates, 321 00:16:46,220 --> 00:16:48,680 is to use two curly braces here. 322 00:16:48,680 --> 00:16:50,960 The hope is that you, the programmer, will never 323 00:16:50,960 --> 00:16:54,480 want to display two curly braces in your actual web page. 324 00:16:54,480 --> 00:16:56,670 But even if you do, there's a workaround. 325 00:16:56,670 --> 00:16:57,810 We can escape that. 326 00:16:57,810 --> 00:17:01,250 So now let me go ahead and go back to my browser tab here. 327 00:17:01,250 --> 00:17:04,160 Previously, even though I added name equals David 328 00:17:04,160 --> 00:17:07,190 to the end of the URL with a question mark, it still said hello, world. 329 00:17:07,190 --> 00:17:09,920 But now, hopefully, if I made these changes, 330 00:17:09,920 --> 00:17:13,589 let me go ahead and open up my terminal window. 331 00:17:13,589 --> 00:17:17,670 Let me restart Flask so it loads my changes by default. 332 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 333 00:17:21,619 --> 00:17:23,240 from the server. 334 00:17:23,240 --> 00:17:24,710 And there we go, hello, David. 335 00:17:24,710 --> 00:17:27,740 I can play around now and I can change the URL appear to, for instance, 336 00:17:27,740 --> 00:17:28,280 Carter. 337 00:17:28,280 --> 00:17:30,080 Zoom out, hit Enter. 338 00:17:30,080 --> 00:17:32,790 And now we have something more dynamic. 339 00:17:32,790 --> 00:17:36,980 So the new pieces here are, in Python, we have some code here 340 00:17:36,980 --> 00:17:40,790 that allows us to access, programmatically, everything 341 00:17:40,790 --> 00:17:42,950 that's after the question mark in the URL. 342 00:17:42,950 --> 00:17:48,860 And the only thing we have to do that is call this function request.args.get. 343 00:17:48,860 --> 00:17:50,690 You and I don't have to bother figuring out 344 00:17:50,690 --> 00:17:53,107 where is the question mark, where is the equal sign, where 345 00:17:53,107 --> 00:17:54,680 are the ampersands, potentially. 346 00:17:54,680 --> 00:17:58,460 The framework, Flask, does all of that for us. 347 00:17:58,460 --> 00:18:05,490 OK, any questions then on these principles thus far? 348 00:18:05,490 --> 00:18:06,795 Yeah, in back. 349 00:18:06,795 --> 00:18:12,380 AUDIENCE: Why do you say the question mark in the URL? 350 00:18:12,380 --> 00:18:14,810 DAVID: Why do you need a question mark in the URL? 351 00:18:14,810 --> 00:18:21,390 The short answer is just because that is where key value pairs must go. 352 00:18:21,390 --> 00:18:24,990 If you're making a GET request from a browser to a server, 353 00:18:24,990 --> 00:18:29,690 the convention, standardized by the HTTP protocol, is to put them in the URL 354 00:18:29,690 --> 00:18:33,390 after the so-called route or path, then a question mark. 355 00:18:33,390 --> 00:18:35,840 And it delineates what's part of the root or the path, 356 00:18:35,840 --> 00:18:39,990 and what's part of the human input to the right. 357 00:18:39,990 --> 00:18:40,800 Other questions? 358 00:18:40,800 --> 00:18:41,465 Yeah. 359 00:18:41,465 --> 00:18:45,110 AUDIENCE: Can you go over again why the left and right in the [INAUDIBLE]?? 360 00:18:45,110 --> 00:18:45,610 DAVID: Sure. 361 00:18:45,610 --> 00:18:47,740 This is this annoying thing about Python. 362 00:18:47,740 --> 00:18:51,790 When you pass in parameters, two functions that have names, 363 00:18:51,790 --> 00:18:54,710 you typically say something equals something else. 364 00:18:54,710 --> 00:18:57,280 So let me make a slight tweak here. 365 00:18:57,280 --> 00:19:01,610 How about I say name of person here. 366 00:19:01,610 --> 00:19:06,460 This allows me to invent my own variable for my template 367 00:19:06,460 --> 00:19:08,450 and assign it the value of name. 368 00:19:08,450 --> 00:19:14,915 I now, though, have to go into my index file and say name of person-- 369 00:19:14,915 --> 00:19:15,790 did I get that right? 370 00:19:15,790 --> 00:19:17,290 Name of person, yeah. 371 00:19:17,290 --> 00:19:19,310 So these two have to match. 372 00:19:19,310 --> 00:19:22,690 And so this is just stupid because it's unnecessarily verbose. 373 00:19:22,690 --> 00:19:26,680 So what typically people do is they just use the same name as the variable 374 00:19:26,680 --> 00:19:30,600 itself, even though it looks admittedly stupid, but it has two different roles. 375 00:19:30,600 --> 00:19:32,350 The thing to the left of the equal sign is 376 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 377 00:19:36,460 --> 00:19:38,470 is the actual value you're assigning it. 378 00:19:38,470 --> 00:19:40,330 And this is because its general purpose. 379 00:19:40,330 --> 00:19:43,570 I could override this and I could say something like name always 380 00:19:43,570 --> 00:19:46,120 equals Emma, no matter what that variable is. 381 00:19:46,120 --> 00:19:48,430 And now if I go back to my browser and reload, 382 00:19:48,430 --> 00:19:52,360 no matter what's in the URL, David or Carter, It's always-- 383 00:19:52,360 --> 00:19:55,210 OK, Emma broke the server. 384 00:19:55,210 --> 00:19:56,380 What did I do? 385 00:19:56,380 --> 00:19:58,840 Oh, I didn't change my template back. 386 00:19:58,840 --> 00:19:59,380 There we go. 387 00:19:59,380 --> 00:20:02,650 Let me change that back to be name, so that it's name there 388 00:20:02,650 --> 00:20:03,580 and it's name here. 389 00:20:03,580 --> 00:20:06,130 But I've hardcoded Emma's name, so now we're 390 00:20:06,130 --> 00:20:09,730 only ever going to see Emma no matter whose name is in the URL. 391 00:20:09,730 --> 00:20:10,760 That's all. 392 00:20:10,760 --> 00:20:14,020 All right, so this is bad user interface. 393 00:20:14,020 --> 00:20:16,930 If, in order to get a greeting for the day, you, the user, 394 00:20:16,930 --> 00:20:19,420 have to manually change the URL, which none of us ever do. 395 00:20:19,420 --> 00:20:21,220 This is not how web pages work. 396 00:20:21,220 --> 00:20:25,210 What is the more normal mechanism for getting input from the user 397 00:20:25,210 --> 00:20:28,900 and putting it in that URL automatically? 398 00:20:28,900 --> 00:20:31,780 How did we do that last week? 399 00:20:31,780 --> 00:20:33,500 With Google, if you recall. 400 00:20:33,500 --> 00:20:38,190 AUDIENCE: We have the search bar and we [INAUDIBLE] you have 401 00:20:38,190 --> 00:20:44,340 to make something in there [INAUDIBLE]. 402 00:20:44,340 --> 00:20:47,970 DAVID: OK, so we did make something in order to get the input from the user. 403 00:20:47,970 --> 00:20:51,840 And specifically, what was the tag or the terminology we used last week? 404 00:20:51,840 --> 00:20:53,070 AUDIENCE: [INAUDIBLE]. 405 00:20:53,070 --> 00:20:55,000 DAVID: Sorry, a little louder? 406 00:20:55,000 --> 00:20:57,010 Oh, no. 407 00:20:57,010 --> 00:20:57,510 But yeah. 408 00:20:57,510 --> 00:20:58,680 AUDIENCE: Is it input? 409 00:20:58,680 --> 00:21:01,030 DAVID: So the input tag, inside of the form tag. 410 00:21:01,030 --> 00:21:03,775 So in short, forms, or of course, how the web works 411 00:21:03,775 --> 00:21:05,650 and how we typically get input from the user, 412 00:21:05,650 --> 00:21:08,978 whether it's a button or a text box or a dropdown menu or something else. 413 00:21:08,978 --> 00:21:11,020 So let's go ahead and add that into the mix here. 414 00:21:11,020 --> 00:21:14,940 So let's enhance this hello app to do a little something more by, 415 00:21:14,940 --> 00:21:16,240 this time, just doing this. 416 00:21:16,240 --> 00:21:20,340 Let me get rid of this name stuff and let me just 417 00:21:20,340 --> 00:21:26,130 have a very simple index.html file that, by default, is going to simply ask 418 00:21:26,130 --> 00:21:28,480 the user for some input as follows. 419 00:21:28,480 --> 00:21:33,090 I'm going to go back into my index.html, and instead of printing out 420 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 421 00:21:36,090 --> 00:21:36,880 from the user. 422 00:21:36,880 --> 00:21:39,210 So I'm going to create a form tag. 423 00:21:39,210 --> 00:21:42,870 The method I'm going to use for now is going to be, quote unquote, "get." 424 00:21:42,870 --> 00:21:45,330 Then, inside of that form, I'm going to have an input tag. 425 00:21:45,330 --> 00:21:47,872 And I'm going to turn off autocomplete like we did last week. 426 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. 427 00:21:51,780 --> 00:21:55,502 I'm going to give the name of this input the name, name. 428 00:21:55,502 --> 00:21:58,210 Not to be too confusing, but I'm asking the human for their name. 429 00:21:58,210 --> 00:22:01,770 So it makes sense that the name of the input should be, quote unquote, "name." 430 00:22:01,770 --> 00:22:04,470 The placeholder I want the human to see in light gray text 431 00:22:04,470 --> 00:22:07,500 will be Name with a capital N, just so it's a little grammatical. 432 00:22:07,500 --> 00:22:09,750 And then type of this text fiel-- 433 00:22:09,750 --> 00:22:11,610 type of this input is going to be text. 434 00:22:11,610 --> 00:22:14,550 Then I'm just going to give myself, like last week, a submit button. 435 00:22:14,550 --> 00:22:16,467 And I don't care what it says, it's just going 436 00:22:16,467 --> 00:22:18,820 to say the default submit terminology. 437 00:22:18,820 --> 00:22:23,310 Let me go ahead, now, and open up my terminal window again. 438 00:22:23,310 --> 00:22:28,470 Let me go to that same URL so that I can see-- whoops. 439 00:22:28,470 --> 00:22:33,320 440 00:22:33,320 --> 00:22:33,990 There we go. 441 00:22:33,990 --> 00:22:35,540 So that was just cached from earlier. 442 00:22:35,540 --> 00:22:38,840 Let me go back to that same URL, my GitHub preview.dev URL, 443 00:22:38,840 --> 00:22:40,260 and here I have the form. 444 00:22:40,260 --> 00:22:42,170 And now, I can type in anything I want. 445 00:22:42,170 --> 00:22:45,840 The catch, though, is when I click Submit, where is it going to go? 446 00:22:45,840 --> 00:22:46,880 Well, let's be explicit. 447 00:22:46,880 --> 00:22:50,240 It does have a default value, but let me go into my index.html 448 00:22:50,240 --> 00:22:53,000 and let me add, just like we did last week for it, Google. 449 00:22:53,000 --> 00:22:58,427 Whereas previously, I said something like www.google.com/search, but today, 450 00:22:58,427 --> 00:23:00,260 we're not going to rely on some third party. 451 00:23:00,260 --> 00:23:02,720 I'm going to implement the so-called backend, 452 00:23:02,720 --> 00:23:06,990 and I'm going to have the user submit this form to a second route, 453 00:23:06,990 --> 00:23:09,620 not just slash, how about /greet. 454 00:23:09,620 --> 00:23:11,180 I can make it up, whatever I want. 455 00:23:11,180 --> 00:23:15,650 Greet feels like a nice operative word, so /greet is where the user will be 456 00:23:15,650 --> 00:23:18,650 sent when they click Submit on this form. 457 00:23:18,650 --> 00:23:22,130 All right, so let's go ahead now and go back to my browser tab. 458 00:23:22,130 --> 00:23:24,830 Let me go ahead, actually, and let me reload Flask 459 00:23:24,830 --> 00:23:27,320 here so that it reloads all of my changes. 460 00:23:27,320 --> 00:23:31,400 Let me reload this tab so that I get the very latest HTML and, indeed, 461 00:23:31,400 --> 00:23:32,510 quick safety check. 462 00:23:32,510 --> 00:23:35,630 If I view page source, we indeed see that my browser 463 00:23:35,630 --> 00:23:37,490 has downloaded the latest HTML. 464 00:23:37,490 --> 00:23:39,155 So it definitely has changed. 465 00:23:39,155 --> 00:23:40,530 Let's go ahead and type in David. 466 00:23:40,530 --> 00:23:44,300 And when I click Submit here, what's going to happen? 467 00:23:44,300 --> 00:23:44,980 Hypotheses. 468 00:23:44,980 --> 00:23:47,500 469 00:23:47,500 --> 00:23:50,620 What's going to happen visually, functionally, however you 470 00:23:50,620 --> 00:23:54,540 want to interpret when I click Submit. 471 00:23:54,540 --> 00:23:55,040 Yeah? 472 00:23:55,040 --> 00:23:56,585 AUDIENCE: [INAUDIBLE] an empty page. 473 00:23:56,585 --> 00:23:58,710 DAVID: OK, the user's going to go to an empty page. 474 00:23:58,710 --> 00:24:00,002 Pretty good instinct, because-- 475 00:24:00,002 --> 00:24:02,880 no where else, if I mentioned /greet, it doesn't seem to exist. 476 00:24:02,880 --> 00:24:07,110 How's the URL going to change, just to be clear? 477 00:24:07,110 --> 00:24:09,360 What's going to appear, suddenly, in the URL? 478 00:24:09,360 --> 00:24:12,050 479 00:24:12,050 --> 00:24:12,925 Yeah? 480 00:24:12,925 --> 00:24:14,200 AUDIENCE: 404? 481 00:24:14,200 --> 00:24:15,010 DAVID: 404? 482 00:24:15,010 --> 00:24:15,802 No, not in the URL. 483 00:24:15,802 --> 00:24:18,677 Specifically in the URL, something's going to get added automatically 484 00:24:18,677 --> 00:24:19,232 when I click. 485 00:24:19,232 --> 00:24:20,440 AUDIENCE: The key value pair? 486 00:24:20,440 --> 00:24:21,815 DAVID: The key value pair, right. 487 00:24:21,815 --> 00:24:22,900 That's how forms work. 488 00:24:22,900 --> 00:24:25,180 That's why our Google trick last week worked. 489 00:24:25,180 --> 00:24:27,843 I sort of recreated a form on my own website. 490 00:24:27,843 --> 00:24:30,760 And even though I didn't get around to implementing google.com itself, 491 00:24:30,760 --> 00:24:34,420 I can still send the information to Google just relying on browsers, 492 00:24:34,420 --> 00:24:35,590 standardizing-- 493 00:24:35,590 --> 00:24:38,470 to your question earlier, that whenever you submit a form, 494 00:24:38,470 --> 00:24:41,740 it automatically ends up after a question mark in the URL 495 00:24:41,740 --> 00:24:42,670 if you're using GET. 496 00:24:42,670 --> 00:24:45,460 So this both of you are right, this is going to break. 497 00:24:45,460 --> 00:24:49,000 And all three of you are right, in effect, 404 not found. 498 00:24:49,000 --> 00:24:50,450 You can see it in the tab here. 499 00:24:50,450 --> 00:24:51,950 That's the error that has come back. 500 00:24:51,950 --> 00:24:55,720 But what's interesting, and most important, the URL did change. 501 00:24:55,720 --> 00:24:59,890 And it went to /greet?name=david. 502 00:24:59,890 --> 00:25:02,260 So I just, now, need to add some logic that actually 503 00:25:02,260 --> 00:25:04,000 looks for that so-called route. 504 00:25:04,000 --> 00:25:06,520 So let me go back to my app.py. 505 00:25:06,520 --> 00:25:11,680 Let me define another route for, quote unquote, "slash greet." 506 00:25:11,680 --> 00:25:15,200 And then, inside of-- under this, let me define another function. 507 00:25:15,200 --> 00:25:18,280 I'll call it greet, but I could call it anything I want. 508 00:25:18,280 --> 00:25:20,950 No arguments, for now, for this, and then 509 00:25:20,950 --> 00:25:24,430 let me go ahead and do this in my app.py. 510 00:25:24,430 --> 00:25:27,290 This time around, I do want to get the human's name. 511 00:25:27,290 --> 00:25:31,300 So let me say requeste.args get quote unquote "name", 512 00:25:31,300 --> 00:25:33,340 and let me store that in a variable called name. 513 00:25:33,340 --> 00:25:37,030 Then let me return a template, and you know 514 00:25:37,030 --> 00:25:39,610 what, I'm going to give myself a new template, greet.html. 515 00:25:39,610 --> 00:25:41,860 Because this has a different purpose, it's not a form. 516 00:25:41,860 --> 00:25:44,800 I want to say hello to the user in this HTML file, 517 00:25:44,800 --> 00:25:49,960 and I want to pass, into it, the name that the human just typed in. 518 00:25:49,960 --> 00:25:56,320 All right, so now if I go up and reload the page, what might happen now? 519 00:25:56,320 --> 00:25:58,480 Other logical check here. 520 00:25:58,480 --> 00:26:01,930 If I go ahead and hit reload or resubmit the form, what might happen now? 521 00:26:01,930 --> 00:26:05,200 522 00:26:05,200 --> 00:26:08,480 Any instincts? 523 00:26:08,480 --> 00:26:10,890 Let me try, so let's try this. 524 00:26:10,890 --> 00:26:12,470 Let's go ahead and reload the page. 525 00:26:12,470 --> 00:26:13,980 Previously, it was not found. 526 00:26:13,980 --> 00:26:17,900 Now it's worse, and this is the 500 error, internal server 527 00:26:17,900 --> 00:26:22,130 error that I promised next week we will all encounter accidentally, ultimately. 528 00:26:22,130 --> 00:26:24,020 But here we have an internal server error. 529 00:26:24,020 --> 00:26:27,750 Because it's an internal error, this means something's wrong with your code. 530 00:26:27,750 --> 00:26:31,280 So the route was actually found because it's not a 404 this time. 531 00:26:31,280 --> 00:26:36,740 But if we go into VS Code here and we look at the console, the terminal 532 00:26:36,740 --> 00:26:39,860 window, you'll see that-- 533 00:26:39,860 --> 00:26:43,280 this is actually a bit misleading. 534 00:26:43,280 --> 00:26:44,370 Do I want to do this? 535 00:26:44,370 --> 00:26:46,350 Let me reload this. 536 00:26:46,350 --> 00:26:47,370 Let me reload here. 537 00:26:47,370 --> 00:26:49,730 Oh, standby. 538 00:26:49,730 --> 00:26:51,120 Come on. 539 00:26:51,120 --> 00:26:53,100 There we go. 540 00:26:53,100 --> 00:26:54,440 Come on. 541 00:26:54,440 --> 00:26:57,110 OK, here we have this error here, and this 542 00:26:57,110 --> 00:26:59,480 is where your terminal window is going to be helpful. 543 00:26:59,480 --> 00:27:02,390 In your terminal window, by default, is typically 544 00:27:02,390 --> 00:27:05,510 going to go helpful stuff like a log, L-O-G, 545 00:27:05,510 --> 00:27:08,847 of what it is the server is seeing from the browser. 546 00:27:08,847 --> 00:27:11,180 For instance, here's what the server just saw in purple. 547 00:27:11,180 --> 00:27:16,310 Get /greet?name=david using HTTP version 1.0. 548 00:27:16,310 --> 00:27:19,790 Here, though, is the status code that the server returned, 500. 549 00:27:19,790 --> 00:27:20,840 Why, what's the error? 550 00:27:20,840 --> 00:27:24,260 Well, here's where we get these annoying pretty cryptic Python messages 551 00:27:24,260 --> 00:27:26,090 that help50 might ultimately help you with, 552 00:27:26,090 --> 00:27:29,403 or here, we might just have a clue at the bottom. 553 00:27:29,403 --> 00:27:31,820 And this is actually pretty clear, even though we've never 554 00:27:31,820 --> 00:27:32,810 seen this error before. 555 00:27:32,810 --> 00:27:34,130 What did I screw up here? 556 00:27:34,130 --> 00:27:36,710 I just didn't create greet.html, right? 557 00:27:36,710 --> 00:27:37,737 Template not found. 558 00:27:37,737 --> 00:27:40,070 All right, so that must be the last piece of the puzzle. 559 00:27:40,070 --> 00:27:43,460 And again, representative of how you might diagnose problems like these, 560 00:27:43,460 --> 00:27:46,550 let me go into my terminal window. 561 00:27:46,550 --> 00:27:51,050 After hitting Control C, which cancels or interrupts a process, 562 00:27:51,050 --> 00:27:53,030 let me go into my templates directory. 563 00:27:53,030 --> 00:27:55,910 If I type ls, I only have index.html. 564 00:27:55,910 --> 00:27:58,460 So let's code up greet.html. 565 00:27:58,460 --> 00:28:02,360 And in this file let's quickly do doc type. 566 00:28:02,360 --> 00:28:07,160 Doc type HTML, open bracket HTML, language equals English. 567 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. 568 00:28:11,090 --> 00:28:15,740 The name is viewport, the content of which is-- 569 00:28:15,740 --> 00:28:18,020 I always forget this to. 570 00:28:18,020 --> 00:28:25,220 The content of which is initial scale equals one, width equals device width. 571 00:28:25,220 --> 00:28:28,130 Quote unquote, title is still going to be, 572 00:28:28,130 --> 00:28:30,530 I'll call this greet because this is my template. 573 00:28:30,530 --> 00:28:35,880 And then here, in the body, I'm going to have hello comma name. 574 00:28:35,880 --> 00:28:40,100 So I could have kept around the old version of this, but I just recreated, 575 00:28:40,100 --> 00:28:41,570 essentially, my second template. 576 00:28:41,570 --> 00:28:45,200 So index.html now is almost the same, but the title is different 577 00:28:45,200 --> 00:28:46,580 and it has a form. 578 00:28:46,580 --> 00:28:49,790 greet.html is almost the same, but it does not have a form. 579 00:28:49,790 --> 00:28:52,020 It just has the hello comma name. 580 00:28:52,020 --> 00:28:56,510 So let me now go ahead and rerun in the correct directory. 581 00:28:56,510 --> 00:29:01,080 You have to run Flask wherever app.py is, not in your templates directory. 582 00:29:01,080 --> 00:29:04,040 So let me do Flask run to get back to where I was. 583 00:29:04,040 --> 00:29:05,900 Let me go into my other tab. 584 00:29:05,900 --> 00:29:09,080 Cross my fingers this time that, when I go back to slash 585 00:29:09,080 --> 00:29:14,030 and I get index.html's form, now I type in David and click Submit, 586 00:29:14,030 --> 00:29:16,730 now we get hello, David. 587 00:29:16,730 --> 00:29:20,240 And now we have a full-fledged web app that has two different routes, 588 00:29:20,240 --> 00:29:25,430 slash and /greet, the latter of which takes input like this and then, 589 00:29:25,430 --> 00:29:27,470 using a template, spits it out. 590 00:29:27,470 --> 00:29:30,990 But something could go wrong, and let's see what happens here. 591 00:29:30,990 --> 00:29:33,620 Suppose I don't type anything in. 592 00:29:33,620 --> 00:29:35,870 Let me go here and just click Submit. 593 00:29:35,870 --> 00:29:38,662 Now, I mean, it looks stupid. 594 00:29:38,662 --> 00:29:40,620 So there's bunches of ways we could solve this. 595 00:29:40,620 --> 00:29:44,060 I could require that the user have input on the previous page, 596 00:29:44,060 --> 00:29:46,193 I could have some kind of error check for this. 597 00:29:46,193 --> 00:29:48,860 But there's another mechanism I can use that I'll just show you. 598 00:29:48,860 --> 00:29:53,150 It turns out this GET function, in the context of HTTP 599 00:29:53,150 --> 00:29:55,400 and also in general with Python dictionaries, 600 00:29:55,400 --> 00:29:57,810 you can actually supply a default value. 601 00:29:57,810 --> 00:30:02,180 So if there is no name parameter or no value for a name parameter, 602 00:30:02,180 --> 00:30:04,830 you can actually give it a default value like this. 603 00:30:04,830 --> 00:30:07,010 So I'll say world, for instance. 604 00:30:07,010 --> 00:30:08,450 Now, let me go back here. 605 00:30:08,450 --> 00:30:10,580 Let me type in nothing again and click Submit. 606 00:30:10,580 --> 00:30:14,270 And hopefully this time, I'll do-- oops, sorry. 607 00:30:14,270 --> 00:30:17,030 Let me restart Flask to reload the template. 608 00:30:17,030 --> 00:30:20,090 Let me go ahead and type nothing this time, clicking Submit. 609 00:30:20,090 --> 00:30:23,300 And hopefully, we now-- 610 00:30:23,300 --> 00:30:26,210 Oh, interesting. 611 00:30:26,210 --> 00:30:27,860 I should have faked this. 612 00:30:27,860 --> 00:30:31,700 Suppose that the reason this-- 613 00:30:31,700 --> 00:30:33,260 Oh. 614 00:30:33,260 --> 00:30:37,430 Suppose I just get rid of name altogether like this and hit Enter. 615 00:30:37,430 --> 00:30:40,070 Now I see hello, world, and this is a subtlety 616 00:30:40,070 --> 00:30:42,200 that I didn't intend to get into here. 617 00:30:42,200 --> 00:30:45,620 When you have question mark name equals nothing, 618 00:30:45,620 --> 00:30:47,660 you're passing in what's called-- whoops. 619 00:30:47,660 --> 00:30:51,350 When you have greet question mark name equals something, 620 00:30:51,350 --> 00:30:54,080 you actually are giving a value to name. 621 00:30:54,080 --> 00:30:56,720 It is quote unquote with nothing in between. 622 00:30:56,720 --> 00:30:59,190 That is different from having no value at all. 623 00:30:59,190 --> 00:31:03,203 So allow me to just propose that the error here, we 624 00:31:03,203 --> 00:31:05,120 would want to require this in a different way. 625 00:31:05,120 --> 00:31:07,610 And probably the most robust way to do this 626 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. 627 00:31:13,260 --> 00:31:17,990 Now, if I go back to my form after restarting Flask here, 628 00:31:17,990 --> 00:31:21,950 and I go ahead and click reload on my form and type in nothing 629 00:31:21,950 --> 00:31:25,580 and click Submit, now the browser is going to yell at me. 630 00:31:25,580 --> 00:31:27,740 But just as a teaser for something we'll be 631 00:31:27,740 --> 00:31:30,470 doing in the next problem set in terms of error checking, 632 00:31:30,470 --> 00:31:37,010 you should never, ever, ever rely on client side safety checks like this. 633 00:31:37,010 --> 00:31:41,540 Because we know, from last week, that a curious programmer can go to inspect, 634 00:31:41,540 --> 00:31:43,920 and let me poke around the HTML here. 635 00:31:43,920 --> 00:31:46,280 Let me go into the body, the form. 636 00:31:46,280 --> 00:31:48,900 OK, you say required, I say not required. 637 00:31:48,900 --> 00:31:51,960 You can just delete what's in the dom, in the browser, 638 00:31:51,960 --> 00:31:54,920 and now I can go ahead and submit this form. 639 00:31:54,920 --> 00:31:56,510 And it appears to be broken. 640 00:31:56,510 --> 00:31:59,700 Not a big deal with a silly little greeting application like this. 641 00:31:59,700 --> 00:32:02,750 But if you're trying to require that humans actually 642 00:32:02,750 --> 00:32:06,140 provide input that is necessary for the correct operation of the site, 643 00:32:06,140 --> 00:32:11,690 you don't want to trust that the HTML is not altered by some adversary. 644 00:32:11,690 --> 00:32:14,570 All right, any questions, then, on this particular app 645 00:32:14,570 --> 00:32:18,450 before we add another feature here? 646 00:32:18,450 --> 00:32:21,203 Any questions here? 647 00:32:21,203 --> 00:32:22,185 Yeah. 648 00:32:22,185 --> 00:32:24,149 AUDIENCE: Do you guys [INAUDIBLE]. 649 00:32:24,149 --> 00:32:26,493 650 00:32:26,493 --> 00:32:27,660 DAVID: Sorry, little louder. 651 00:32:27,660 --> 00:32:29,163 In the index function-- 652 00:32:29,163 --> 00:32:30,612 AUDIENCE: Oh, sorry. 653 00:32:30,612 --> 00:32:33,030 [INAUDIBLE] 654 00:32:33,030 --> 00:32:33,807 DAVID: Sorry? 655 00:32:33,807 --> 00:32:36,508 AUDIENCE: [INAUDIBLE] 656 00:32:36,508 --> 00:32:38,050 DAVID: Would it be a problem if what? 657 00:32:38,050 --> 00:32:39,523 AUDIENCE: You have to [INAUDIBLE]. 658 00:32:39,523 --> 00:32:42,390 659 00:32:42,390 --> 00:32:42,890 DAVID: No. 660 00:32:42,890 --> 00:32:44,360 I mean no, this is OK. 661 00:32:44,360 --> 00:32:46,760 What you should really do is something we're going to do with another example 662 00:32:46,760 --> 00:32:48,330 where I'm going to start error checking things. 663 00:32:48,330 --> 00:32:50,030 So let me wave my hands at that and propose that we'll 664 00:32:50,030 --> 00:32:51,498 solve this better in just a bit. 665 00:32:51,498 --> 00:32:53,540 But it's not bad to do what I just did here, it's 666 00:32:53,540 --> 00:32:56,480 only going to handle one of the scenarios that I was worried about. 667 00:32:56,480 --> 00:32:57,990 Not all of them. 668 00:32:57,990 --> 00:33:00,290 All right, so even though this is new to most of us 669 00:33:00,290 --> 00:33:05,630 here, consider index.html, my first template, and consider greet.html, 670 00:33:05,630 --> 00:33:07,790 my second template. 671 00:33:07,790 --> 00:33:11,022 What might be arguably badly designed? 672 00:33:11,022 --> 00:33:12,980 Even though this might be the first time you've 673 00:33:12,980 --> 00:33:16,700 ever touched web programming like this. 674 00:33:16,700 --> 00:33:24,140 What's bad or dumb about this design of these two templates alone? 675 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. 676 00:33:28,070 --> 00:33:28,880 Yeah? 677 00:33:28,880 --> 00:33:33,653 AUDIENCE: [INAUDIBLE] you said, stuff like Notepad and [INAUDIBLE].. 678 00:33:33,653 --> 00:33:35,320 DAVID: Yeah, there's so much repetition. 679 00:33:35,320 --> 00:33:38,560 I mean, it was deliberately tedious that I was retyping everything. 680 00:33:38,560 --> 00:33:41,590 The doc type, the HTML tag, the head tag, the title tag. 681 00:33:41,590 --> 00:33:44,230 And little things did change along the way, like the title 682 00:33:44,230 --> 00:33:46,270 and certainly, the content of the body. 683 00:33:46,270 --> 00:33:49,540 But so much of this, I mean, almost all of the page 684 00:33:49,540 --> 00:33:52,837 is a copy of itself in multiple files. 685 00:33:52,837 --> 00:33:56,170 And God forbid we have a third template, a fourth template, a hundredth template 686 00:33:56,170 --> 00:33:57,310 for a really big website. 687 00:33:57,310 --> 00:33:59,680 This is going to get very tedious very quickly. 688 00:33:59,680 --> 00:34:02,208 And suppose you want to change something in one place, 689 00:34:02,208 --> 00:34:05,500 you're going to have to change it now in two, three, a hundred different places 690 00:34:05,500 --> 00:34:06,200 instead. 691 00:34:06,200 --> 00:34:08,980 So just like in programming more generally, 692 00:34:08,980 --> 00:34:11,469 we have this ability to factor out commonalities. 693 00:34:11,469 --> 00:34:13,719 So do you in the context of web programming, 694 00:34:13,719 --> 00:34:16,510 and specifically templating, have the ability 695 00:34:16,510 --> 00:34:18,617 to factor out all of those commonalities. 696 00:34:18,617 --> 00:34:20,409 The syntax is going to be a little curious, 697 00:34:20,409 --> 00:34:23,330 but it functionally is pretty straightforward. 698 00:34:23,330 --> 00:34:24,530 Let me go ahead and do this. 699 00:34:24,530 --> 00:34:28,540 Let me go ahead and copy the contents of index.html. 700 00:34:28,540 --> 00:34:31,420 Let me go into my templates directory and code a file that, 701 00:34:31,420 --> 00:34:34,370 by default, is called layout.html. 702 00:34:34,370 --> 00:34:37,989 And let me go ahead, and per your answer, copy all of those 703 00:34:37,989 --> 00:34:40,520 commonalities into this file now instead. 704 00:34:40,520 --> 00:34:43,389 So here I have a file called layout.html. 705 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. 706 00:34:48,550 --> 00:34:50,020 I'm going to call everything hello. 707 00:34:50,020 --> 00:34:52,900 But in the body of the page, what I'm going to do here is just 708 00:34:52,900 --> 00:34:57,500 have a placeholder for actual contents that do change. 709 00:34:57,500 --> 00:35:00,370 So in this layout, I'm going to go ahead in here 710 00:35:00,370 --> 00:35:05,060 and just put in the body of my page, how about this syntax? 711 00:35:05,060 --> 00:35:06,610 And this is admittedly new. 712 00:35:06,610 --> 00:35:11,020 Block body, and then percent sign close curly brace. 713 00:35:11,020 --> 00:35:13,210 And then I'm going to do end block. 714 00:35:13,210 --> 00:35:18,130 So a curious syntax here, but this is more template syntax. 715 00:35:18,130 --> 00:35:21,460 The other template syntax we saw before was the two curly braces. 716 00:35:21,460 --> 00:35:23,290 That's for just plugging in values. 717 00:35:23,290 --> 00:35:27,550 There's this other syntax with Flask that allows you to, say, a single curly 718 00:35:27,550 --> 00:35:32,350 brace, a percent sign, and then some functionality like this defining 719 00:35:32,350 --> 00:35:33,130 a block. 720 00:35:33,130 --> 00:35:35,620 And this one's a little weird because there's literally 721 00:35:35,620 --> 00:35:38,960 nothing between the close curly and the open curly brace here. 722 00:35:38,960 --> 00:35:41,440 But let's see what this can do for us. 723 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 724 00:35:47,920 --> 00:35:51,190 from, and let me focus on what is minimally different. 725 00:35:51,190 --> 00:35:56,120 The only thing that's really different in this page, title aside, is the form. 726 00:35:56,120 --> 00:35:59,920 So let me go ahead and just cut that form out to my clipboard. 727 00:35:59,920 --> 00:36:02,680 Let me change the first line of index.html 728 00:36:02,680 --> 00:36:08,980 to say this file is going to extend layout.html, 729 00:36:08,980 --> 00:36:11,170 and notice I'm using the curly braces again. 730 00:36:11,170 --> 00:36:14,620 And this file is going to have its own body block 731 00:36:14,620 --> 00:36:19,540 inside of which is just the HTML that I actually 732 00:36:19,540 --> 00:36:22,030 want to make specific to this page. 733 00:36:22,030 --> 00:36:24,290 And I'll keep my indentation nice and neat here. 734 00:36:24,290 --> 00:36:25,780 And let's consider what I've done. 735 00:36:25,780 --> 00:36:28,210 This is starting to look weird fast, and this is now 736 00:36:28,210 --> 00:36:32,110 a mix of HTML with templating code. 737 00:36:32,110 --> 00:36:38,200 Index.html, first line now says, hey, Flask, this file extends layout.html, 738 00:36:38,200 --> 00:36:39,310 whatever that is. 739 00:36:39,310 --> 00:36:42,640 This next line, three through 10, says, hey, Flask, here 740 00:36:42,640 --> 00:36:45,970 is what I consider my body block to be. 741 00:36:45,970 --> 00:36:49,600 Plug this into the layout placeholder. 742 00:36:49,600 --> 00:36:55,850 Therefore, so if I now go back to layout.html, and layout.html, 743 00:36:55,850 --> 00:36:57,910 it's almost all HTML by contrast. 744 00:36:57,910 --> 00:37:00,995 But there is this placeholder, and if I want to put a default value, 745 00:37:00,995 --> 00:37:01,870 I could say-- whoops. 746 00:37:01,870 --> 00:37:03,640 If I want to put a default value, I could 747 00:37:03,640 --> 00:37:07,240 put a default value there just in case some page does not have a body block. 748 00:37:07,240 --> 00:37:09,410 But in general, that's not going to be relevant. 749 00:37:09,410 --> 00:37:13,120 So this is just a placeholder, albeit a little verbose, that says 750 00:37:13,120 --> 00:37:16,730 plug in the page-specific content right here. 751 00:37:16,730 --> 00:37:20,500 So if I go now into greet.html, this one's even easier. 752 00:37:20,500 --> 00:37:23,500 I'm going to cut this content and get rid of everything else. 753 00:37:23,500 --> 00:37:29,080 Greet.html 2 is going to extend layouts, dot HTML extends plural, 754 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. 755 00:37:35,750 --> 00:37:38,200 And then I'm going to go ahead and end that block here. 756 00:37:38,200 --> 00:37:41,050 These are not HTML tags, this is not HTML syntax. 757 00:37:41,050 --> 00:37:44,980 Technically, the syntax we keep seeing with the curly braces, 758 00:37:44,980 --> 00:37:51,220 and these now curly braces with percent signs, is an example of Jinja syntax, 759 00:37:51,220 --> 00:37:56,050 J-I-N-J-A, which is a language, that some humans invented, 760 00:37:56,050 --> 00:37:57,970 for this purpose of templating. 761 00:37:57,970 --> 00:38:00,190 And the people who invented Flask decided, 762 00:38:00,190 --> 00:38:02,290 we're not going to come up with our own syntax, 763 00:38:02,290 --> 00:38:06,305 we're going to use these other people's syntax called Jinja syntax. 764 00:38:06,305 --> 00:38:08,680 So again, there starts to be at this point in the course, 765 00:38:08,680 --> 00:38:12,610 and really in computing, a lot of sharing, now, of ideas and sharing 766 00:38:12,610 --> 00:38:13,190 of code. 767 00:38:13,190 --> 00:38:17,080 So Flask is using this syntax, but other libraries and other languages 768 00:38:17,080 --> 00:38:19,270 might also too. 769 00:38:19,270 --> 00:38:24,070 All right, so now index.html is half HTML, 770 00:38:24,070 --> 00:38:26,500 half templating code, Jinja syntax. 771 00:38:26,500 --> 00:38:30,250 Greet.html is almost all Jinja syntax, no tags even, 772 00:38:30,250 --> 00:38:33,820 but because they both extend layout.html, 773 00:38:33,820 --> 00:38:37,150 now I think I've improved the design of this thing. 774 00:38:37,150 --> 00:38:41,180 If I go back to app.py, none of this really needs to change. 775 00:38:41,180 --> 00:38:44,350 I don't change my templates to mention layout.html, 776 00:38:44,350 --> 00:38:47,860 that's already implicit in the fact that we have the extends keyword. 777 00:38:47,860 --> 00:38:50,980 So now if I go ahead and open my terminal window, 778 00:38:50,980 --> 00:38:55,060 go back to the same folder as app.py and do Flask run, 779 00:38:55,060 --> 00:38:57,970 all right, my application is running on port 5000. 780 00:38:57,970 --> 00:39:01,630 Let me now go back to the /route in my browser and hit Enter, 781 00:39:01,630 --> 00:39:02,920 I have this form again. 782 00:39:02,920 --> 00:39:06,490 And just as a little check, let me view the source of the page 783 00:39:06,490 --> 00:39:08,170 that my browser is seeing. 784 00:39:08,170 --> 00:39:10,600 And there's all of the code. 785 00:39:10,600 --> 00:39:13,660 No mention of Jinja, no curly braces, no percent signs. 786 00:39:13,660 --> 00:39:14,335 It's just HTML. 787 00:39:14,335 --> 00:39:16,960 It's not quite pretty printed in the same way, but that's fine. 788 00:39:16,960 --> 00:39:19,502 Because now, we're starting to dynamically generate websites. 789 00:39:19,502 --> 00:39:22,750 And by that, I mean this isn't quite indented nicely or perfectly. 790 00:39:22,750 --> 00:39:23,350 That's fine. 791 00:39:23,350 --> 00:39:26,110 If it's indented in the source code version, 792 00:39:26,110 --> 00:39:28,150 doesn't matter what the browser really sees. 793 00:39:28,150 --> 00:39:30,640 Let me now go ahead and type in my name, click Submit. 794 00:39:30,640 --> 00:39:32,290 I should see, yep, hello, David. 795 00:39:32,290 --> 00:39:34,390 Let me go ahead and view the source of this page. 796 00:39:34,390 --> 00:39:38,450 And we'll see almost the same thing with what's plugged in there. 797 00:39:38,450 --> 00:39:41,980 So this is, now, web programming in the literal sense. 798 00:39:41,980 --> 00:39:45,280 I did not hard code a page that says hello comma David, hello comma Carter, 799 00:39:45,280 --> 00:39:46,270 hello comma Emma. 800 00:39:46,270 --> 00:39:49,990 I hardcoded a page that has a template with a placeholder, 801 00:39:49,990 --> 00:39:53,710 and now I'm using actual logic, some code in app.py, 802 00:39:53,710 --> 00:39:59,990 to actually tell the server what to send to the browser. 803 00:39:59,990 --> 00:40:05,140 All right, any questions, then, on where we're at here? 804 00:40:05,140 --> 00:40:07,510 This is now a web application. 805 00:40:07,510 --> 00:40:11,290 Simple though it is, it's no longer just a web site. 806 00:40:11,290 --> 00:40:12,161 Yeah? 807 00:40:12,161 --> 00:40:16,760 AUDIENCE: Is what we did just better for design or for memory [INAUDIBLE]?? 808 00:40:16,760 --> 00:40:19,470 DAVID: It better for design or for memory? 809 00:40:19,470 --> 00:40:19,970 Both. 810 00:40:19,970 --> 00:40:22,160 It's definitely better for design because, truly, 811 00:40:22,160 --> 00:40:24,350 if we had a third page, fourth page, I would really 812 00:40:24,350 --> 00:40:26,150 start just resorting to copy paste. 813 00:40:26,150 --> 00:40:29,270 And as you saw with home page, often, in the head of your page, 814 00:40:29,270 --> 00:40:32,870 you might want to include some CSS files like Bootstrap or something else. 815 00:40:32,870 --> 00:40:35,360 You might want to have other information up there. 816 00:40:35,360 --> 00:40:38,992 If you had to upgrade the version of Bootstrap or you change libraries, 817 00:40:38,992 --> 00:40:40,700 so you want to change one of those lines, 818 00:40:40,700 --> 00:40:44,090 you would literally have to go into three, four, a hundred different files 819 00:40:44,090 --> 00:40:45,870 to make one simple change. 820 00:40:45,870 --> 00:40:47,240 So that's bad design. 821 00:40:47,240 --> 00:40:48,740 And in terms of memory, yes. 822 00:40:48,740 --> 00:40:52,970 Theoretically, the server, because it knows there's this common layout, 823 00:40:52,970 --> 00:40:55,632 it can theoretically do some optimizations underneath the hood. 824 00:40:55,632 --> 00:40:58,340 Flask is probably doing that, but not in the mode we're using it. 825 00:40:58,340 --> 00:41:00,260 We're using it in development mode, which 826 00:41:00,260 --> 00:41:03,830 means it's typically reloading things each time. 827 00:41:03,830 --> 00:41:07,040 Other questions on this application? 828 00:41:07,040 --> 00:41:10,210 829 00:41:10,210 --> 00:41:11,080 Anything at all? 830 00:41:11,080 --> 00:41:16,700 All right, so let me ask a question, not just in terms of the code design. 831 00:41:16,700 --> 00:41:18,880 What about the implications for privacy? 832 00:41:18,880 --> 00:41:24,430 Why is this maybe not the best design for users, how I've implemented this? 833 00:41:24,430 --> 00:41:26,470 I've used a web form, but-- 834 00:41:26,470 --> 00:41:27,305 Yeah? 835 00:41:27,305 --> 00:41:28,730 AUDIENCE: For some reason, you wanted your name. 836 00:41:28,730 --> 00:41:30,920 So these private people could just look at the URL. 837 00:41:30,920 --> 00:41:31,420 DAVID: Yeah. 838 00:41:31,420 --> 00:41:33,280 I mean, if you have a nosy sibling or roommate 839 00:41:33,280 --> 00:41:35,290 and they have access to your laptop and they just 840 00:41:35,290 --> 00:41:37,540 go trolling through your autocomplete or your history, 841 00:41:37,540 --> 00:41:40,570 like, literally what you typed into a website is going to be visible. 842 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 843 00:41:43,570 --> 00:41:45,790 card or anything else that's mildly sensitive, 844 00:41:45,790 --> 00:41:49,180 you probably don't want it ending up in the URL at all 845 00:41:49,180 --> 00:41:51,640 even if you're in incognito mode or whatnot. 846 00:41:51,640 --> 00:41:56,390 You just don't want to expose yourself or your users to that kind of risk. 847 00:41:56,390 --> 00:41:58,180 So perhaps, we can do better than that. 848 00:41:58,180 --> 00:42:00,550 And fortunately, this one is actually an easy change. 849 00:42:00,550 --> 00:42:05,740 Let me go into my index.html where my form is. 850 00:42:05,740 --> 00:42:10,510 And in my form, I can just change the method from GET to POST. 851 00:42:10,510 --> 00:42:13,150 It's still going to send key value pairs to the server, 852 00:42:13,150 --> 00:42:15,190 but it's not going to put them in the URL. 853 00:42:15,190 --> 00:42:18,495 The upside of which is that we can assuage this privacy concern, 854 00:42:18,495 --> 00:42:20,620 but I'm going to have to make one other change too. 855 00:42:20,620 --> 00:42:25,090 Because now, if I go ahead and run Flask again after making that change, 856 00:42:25,090 --> 00:42:29,570 and I now reload the form to make sure I have the latest version. 857 00:42:29,570 --> 00:42:32,410 You should be in the habit of going to View, Developer, 858 00:42:32,410 --> 00:42:35,022 View Source, or Developer Tools just to make sure 859 00:42:35,022 --> 00:42:37,480 that what you're seeing in your browser is what you intend. 860 00:42:37,480 --> 00:42:40,300 And yes, I do see what I wanted. 861 00:42:40,300 --> 00:42:41,890 Method equals POST now. 862 00:42:41,890 --> 00:42:44,620 Let me go ahead and type in David and click Submit. 863 00:42:44,620 --> 00:42:47,110 Now I get a different error. 864 00:42:47,110 --> 00:42:51,140 This one is HTTP 405, method not allowed. 865 00:42:51,140 --> 00:42:52,240 Why is that? 866 00:42:52,240 --> 00:42:56,320 Well, in my Flask application, I've only defined a couple of routes so far. 867 00:42:56,320 --> 00:42:59,840 One of which is for slash, then that worked fine. 868 00:42:59,840 --> 00:43:02,990 One of which is for /greet, and that used to work fine. 869 00:43:02,990 --> 00:43:08,540 But apparently, what Flask is doing is it only supports GET by default. 870 00:43:08,540 --> 00:43:13,750 So if I want to change this route to support different methods, I can say, 871 00:43:13,750 --> 00:43:18,920 quote unquote "POST" inside of this parameter here. 872 00:43:18,920 --> 00:43:23,830 So that now, I can actually support POST, not just GET. 873 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. 874 00:43:30,880 --> 00:43:33,520 Let me go back one screen to the form, reload 875 00:43:33,520 --> 00:43:35,440 the page just to make sure I have the latest 876 00:43:35,440 --> 00:43:37,023 even though nothing there has changed. 877 00:43:37,023 --> 00:43:40,630 Type David and click Submit now, now I should see hello, world. 878 00:43:40,630 --> 00:43:47,380 Notice that I'm at the greet route, but there's no mention of name 879 00:43:47,380 --> 00:43:50,260 equals anything in the URL. 880 00:43:50,260 --> 00:43:52,570 All right, so that's an interesting takeaway. 881 00:43:52,570 --> 00:43:57,380 It's a simple change, but whereas GET puts things in the URL, POST does not. 882 00:43:57,380 --> 00:43:59,620 But it still works so long as you tweak the backend 883 00:43:59,620 --> 00:44:04,210 to look as a POST request, which means look deeper in the envelope. 884 00:44:04,210 --> 00:44:06,760 It's not going to be as simple as looking at the URL itself. 885 00:44:06,760 --> 00:44:08,560 Why shouldn't we just always use POST? 886 00:44:08,560 --> 00:44:11,730 887 00:44:11,730 --> 00:44:15,990 Why not use POST everywhere? 888 00:44:15,990 --> 00:44:17,580 Any thoughts? 889 00:44:17,580 --> 00:44:21,060 Right, because it's obnoxious to be putting any information in URLs 890 00:44:21,060 --> 00:44:24,322 if you're leaving these little breadcrumbs in your history and people 891 00:44:24,322 --> 00:44:26,280 can poke around and see what you've been doing. 892 00:44:26,280 --> 00:44:29,940 893 00:44:29,940 --> 00:44:31,377 Yeah, what do you think? 894 00:44:31,377 --> 00:44:33,762 AUDIENCE: You're supposed to duplicate [INAUDIBLE].. 895 00:44:33,762 --> 00:44:37,270 896 00:44:37,270 --> 00:44:37,770 DAVID: Yeah. 897 00:44:37,770 --> 00:44:41,030 I mean, if you get rid of GET requests and put nothing in the URL, 898 00:44:41,030 --> 00:44:44,432 your history, your autocomplete, gets pretty less useful. 899 00:44:44,432 --> 00:44:47,390 Because none of the information is there for storage, so you can't just 900 00:44:47,390 --> 00:44:48,807 go through the menu and hit Enter. 901 00:44:48,807 --> 00:44:50,420 You'd have to re-fill out the form. 902 00:44:50,420 --> 00:44:52,628 And there's this other symptom that you can see here. 903 00:44:52,628 --> 00:44:55,253 Let me zoom out and let me just reload this page. 904 00:44:55,253 --> 00:44:57,170 Notice that you'll get this warning, and it'll 905 00:44:57,170 --> 00:45:01,790 look different in Safari and Firefox and Edge and Chrome here, confirm form. 906 00:45:01,790 --> 00:45:02,580 args 907 00:45:02,580 --> 00:45:06,170 So your browser might remember what your inputs were and that's great, 908 00:45:06,170 --> 00:45:07,950 but just while you're on the page. 909 00:45:07,950 --> 00:45:12,800 And this is in contrast to GET, where the state is information. 910 00:45:12,800 --> 00:45:16,195 Like, key value pairs is embedded in the URL itself. 911 00:45:16,195 --> 00:45:18,320 And if you looked at an email I sent earlier today, 912 00:45:18,320 --> 00:45:21,515 I deliberately linked to https://www.google.c 913 00:45:21,515 --> 00:45:23,120 om/search?q=what+time+is+it. 914 00:45:23,120 --> 00:45:29,210 915 00:45:29,210 --> 00:45:33,710 This is, by definition, a GET request when you click on it. 916 00:45:33,710 --> 00:45:37,100 Because it's going to grab the information, the key value pair, 917 00:45:37,100 --> 00:45:40,410 from the URL, send it to Google server, and it's just going to work. 918 00:45:40,410 --> 00:45:42,830 And the reason I sent this via email earlier was I 919 00:45:42,830 --> 00:45:46,050 wanted people to very quickly be able to check what is the current time. 920 00:45:46,050 --> 00:45:49,970 And so I can sort automate the process of creating a Google search for you, 921 00:45:49,970 --> 00:45:52,190 but that you induce when you click that link. 922 00:45:52,190 --> 00:45:57,620 If Google did not support GET, they only supported this, the best I could do 923 00:45:57,620 --> 00:46:00,410 is send you all to this URL which, unfortunately, 924 00:46:00,410 --> 00:46:02,000 has no useful information. 925 00:46:02,000 --> 00:46:05,210 I would have had to add to my email, by the way, type in the words 926 00:46:05,210 --> 00:46:07,050 what time is it. 927 00:46:07,050 --> 00:46:08,760 So it's just bad for usability. 928 00:46:08,760 --> 00:46:11,960 So there, too, we might have design when it comes to the low level code, 929 00:46:11,960 --> 00:46:15,500 but also the design when it comes to the user experience, or UX, 930 00:46:15,500 --> 00:46:17,180 as a computer scientist would call it. 931 00:46:17,180 --> 00:46:20,450 Just in terms of what you want to optimize for, ultimately. 932 00:46:20,450 --> 00:46:22,320 So GET and POST both have their roles. 933 00:46:22,320 --> 00:46:24,820 It depends on what kind of functionality you want to provide 934 00:46:24,820 --> 00:46:29,150 and what kind of sensitivity there might be around it. 935 00:46:29,150 --> 00:46:32,360 All right, any questions, then, on this, our first web application? 936 00:46:32,360 --> 00:46:35,960 Super simple, just gets someone's name and prints it back out. 937 00:46:35,960 --> 00:46:38,270 But we now have all the plumbing with which 938 00:46:38,270 --> 00:46:41,270 to create really most anything we want. 939 00:46:41,270 --> 00:46:44,058 940 00:46:44,058 --> 00:46:46,350 All right, let's go ahead and take a five minute break. 941 00:46:46,350 --> 00:46:50,190 And when we come back, we'll add to this some first year intramural sports. 942 00:46:50,190 --> 00:46:52,440 All right, so we are back. 943 00:46:52,440 --> 00:46:54,420 And recall that the last thing we just changed 944 00:46:54,420 --> 00:46:57,180 was the route to use POST instead of GET. 945 00:46:57,180 --> 00:47:00,090 So gone is my name and any value in the URL. 946 00:47:00,090 --> 00:47:06,270 But there was a subtle bug or change here that we didn't call out earlier. 947 00:47:06,270 --> 00:47:09,300 I did type David into the form and I did click Submit, 948 00:47:09,300 --> 00:47:12,690 and yet here it is saying hello comma world. 949 00:47:12,690 --> 00:47:15,730 So that seems to be broken all of a sudden, 950 00:47:15,730 --> 00:47:18,810 even though we added support for POST. 951 00:47:18,810 --> 00:47:21,030 But something must be wrong. 952 00:47:21,030 --> 00:47:23,580 Logically, it must be the case here. 953 00:47:23,580 --> 00:47:27,700 Intuitively, that if I'm seeing hello, world, that's the default value 954 00:47:27,700 --> 00:47:29,400 I gave the name variable. 955 00:47:29,400 --> 00:47:32,340 It must be that it's not seeing a key called 956 00:47:32,340 --> 00:47:36,720 name in request.args, which is this. 957 00:47:36,720 --> 00:47:39,090 Gives you access to everything after the URL. 958 00:47:39,090 --> 00:47:41,710 That's because there's this other thing we should know about, 959 00:47:41,710 --> 00:47:43,980 which is not just request.args but request.form. 960 00:47:43,980 --> 00:47:48,450 These are horribly named, but request.args is for GET requests, 961 00:47:48,450 --> 00:47:51,042 request.form is for POST requests. 962 00:47:51,042 --> 00:47:53,250 Otherwise, they're pretty much functionally the same. 963 00:47:53,250 --> 00:47:55,830 But the onus is on you, the user or the programmer, 964 00:47:55,830 --> 00:47:58,070 to make sure you're using the right one. 965 00:47:58,070 --> 00:48:00,910 So I think if we want to get rid of the world 966 00:48:00,910 --> 00:48:03,240 and actually see what I, the human, typed in, 967 00:48:03,240 --> 00:48:07,440 I think I can just change request.args to request.form. 968 00:48:07,440 --> 00:48:10,020 Still dot get, still quote unquote "name," 969 00:48:10,020 --> 00:48:14,490 and now, if I go ahead and rerun Flask in my terminal window, 970 00:48:14,490 --> 00:48:17,015 go back to my browser, go back to-- and actually, 971 00:48:17,015 --> 00:48:18,390 I won't even go back to the form. 972 00:48:18,390 --> 00:48:21,630 I will literally just reload, Command R or Control R, 973 00:48:21,630 --> 00:48:24,810 and what this warning is saying is it's going to submit 974 00:48:24,810 --> 00:48:27,240 the same information to the website. 975 00:48:27,240 --> 00:48:31,140 When I click Continue, now I should see hello comma David. 976 00:48:31,140 --> 00:48:33,420 So again, you, too, are going to encounter, probably, 977 00:48:33,420 --> 00:48:35,040 all these little subtleties. 978 00:48:35,040 --> 00:48:38,040 But if you focus on, really, the first principles of last week, 979 00:48:38,040 --> 00:48:40,920 like what it HTTP, how does it get request work, 980 00:48:40,920 --> 00:48:43,025 how does a POST request work now, you should 981 00:48:43,025 --> 00:48:45,150 have a lot of the mental building blocks with which 982 00:48:45,150 --> 00:48:47,340 to solve problems like these. 983 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. 984 00:48:50,580 --> 00:48:54,900 This framework called Flask is just an example of many different frameworks 985 00:48:54,900 --> 00:48:58,500 that all implement the same paradigm, the same way of thinking 986 00:48:58,500 --> 00:49:00,720 and the same way of programming applications. 987 00:49:00,720 --> 00:49:04,170 And that's known as MVC, model view controller. 988 00:49:04,170 --> 00:49:08,670 And here's a very simple diagram that represents the process that you 989 00:49:08,670 --> 00:49:10,540 and I have been implementing thus far. 990 00:49:10,540 --> 00:49:13,530 And actually, this is more than we've been implementing thus far. 991 00:49:13,530 --> 00:49:17,362 In app.py is what a programmer would typically call the controller. 992 00:49:17,362 --> 00:49:19,320 That's the code you're writing, this are called 993 00:49:19,320 --> 00:49:23,370 business logic that makes all of the decisions, decides what to render, 994 00:49:23,370 --> 00:49:25,710 what values to show, and so forth. 995 00:49:25,710 --> 00:49:32,100 In layout.html, index.html, greet.html is the so-called view templates 996 00:49:32,100 --> 00:49:34,890 that is the visualizations that the human actually 997 00:49:34,890 --> 00:49:36,510 sees, the user interface. 998 00:49:36,510 --> 00:49:40,800 Those things are dumb, they pretty much just say plop some values here. 999 00:49:40,800 --> 00:49:43,230 All of the hard work is done in app.py. 1000 00:49:43,230 --> 00:49:48,240 So controller, AKA app.py, is where your Python code generally is. 1001 00:49:48,240 --> 00:49:53,730 And in your view is where your HTML and your Jinja code, your Jinja templating, 1002 00:49:53,730 --> 00:49:57,750 the curly braces, the curly braces with percent signs, usually is. 1003 00:49:57,750 --> 00:50:01,650 We haven't added an M to MVC yet model, that's 1004 00:50:01,650 --> 00:50:04,560 going to refer to things like CSV files or databases. 1005 00:50:04,560 --> 00:50:08,355 The model, where do you keep actual data, typically long term. 1006 00:50:08,355 --> 00:50:10,230 So we'll come back to that, but this picture, 1007 00:50:10,230 --> 00:50:14,550 where you have one of these-- each of these components communicating with one 1008 00:50:14,550 --> 00:50:17,460 another is representative of how a lot of frameworks work. 1009 00:50:17,460 --> 00:50:21,030 What we're teaching today, this week, is not really specific to Python. 1010 00:50:21,030 --> 00:50:23,820 It's not really specific to Flask, even though we're using Flask. 1011 00:50:23,820 --> 00:50:25,710 It really is a very common paradigm that you 1012 00:50:25,710 --> 00:50:30,450 could implement in Java, C sharp, or bunches of other languages as well. 1013 00:50:30,450 --> 00:50:34,833 All right, so let's now pivot back to VS Code here. 1014 00:50:34,833 --> 00:50:36,750 Let me stop running Flask, and let me go ahead 1015 00:50:36,750 --> 00:50:42,160 and create a new folder altogether after closing these files here. 1016 00:50:42,160 --> 00:50:46,830 And let me go ahead and create a folder called FroshIMS, 1017 00:50:46,830 --> 00:50:50,100 representing freshman intramural sports or first year intramural sports 1018 00:50:50,100 --> 00:50:51,660 that I can now CD into. 1019 00:50:51,660 --> 00:50:54,840 And now I'm going to code an app.py. 1020 00:50:54,840 --> 00:50:58,590 And in anticipation, I'm going to create another templates directory. 1021 00:50:58,590 --> 00:51:00,490 This one in the FroshIMS folder. 1022 00:51:00,490 --> 00:51:04,590 And then in my templates directory, I'm going to create a layout.html. 1023 00:51:04,590 --> 00:51:06,720 and I'm just going to get myself started here. 1024 00:51:06,720 --> 00:51:08,460 FroshIMS will go here. 1025 00:51:08,460 --> 00:51:10,980 I'm just copying my layout from earlier because most 1026 00:51:10,980 --> 00:51:15,270 of my interesting work, this time, is now going to be, initially, in app.py. 1027 00:51:15,270 --> 00:51:16,750 So what is it we're creating? 1028 00:51:16,750 --> 00:51:20,520 So literally, the very first thing I wrote as a web application 1029 00:51:20,520 --> 00:51:24,280 20 years ago, was a site that literally looked like this. 1030 00:51:24,280 --> 00:51:26,370 So I was like a sophomore or junior at the time. 1031 00:51:26,370 --> 00:51:29,220 I'd taken CS50 and a follow-on class only. 1032 00:51:29,220 --> 00:51:31,127 I had no idea how to do web programming. 1033 00:51:31,127 --> 00:51:33,960 Neither of those two courses taught web programming back in the day. 1034 00:51:33,960 --> 00:51:36,630 So I taught myself, at the time, a language called Perl. 1035 00:51:36,630 --> 00:51:40,238 And I learned a little something about CSV files, and I sort of read enough-- 1036 00:51:40,238 --> 00:51:42,780 can't even say googled enough, because Google didn't come out 1037 00:51:42,780 --> 00:51:44,380 for a couple of years later. 1038 00:51:44,380 --> 00:51:48,670 Read enough online to figure out how to make a web application so that students 1039 00:51:48,670 --> 00:51:51,520 on campus, first years, could actually register 1040 00:51:51,520 --> 00:51:54,400 via a website for intramural sports. 1041 00:51:54,400 --> 00:51:57,400 Back in my day, you would literally fill out a piece of paper 1042 00:51:57,400 --> 00:52:00,730 and then walk it across the yard to Wigglesworth Hall, one of the dorms, 1043 00:52:00,730 --> 00:52:03,100 slide it under the dorm of the Proctor or RA, 1044 00:52:03,100 --> 00:52:05,620 and thus you were registered for sports so. 1045 00:52:05,620 --> 00:52:07,240 1996, 1997. 1046 00:52:07,240 --> 00:52:08,770 We could do better by then. 1047 00:52:08,770 --> 00:52:10,750 There was an internet, just wasn't really being 1048 00:52:10,750 --> 00:52:13,040 used much on campus or more generally. 1049 00:52:13,040 --> 00:52:16,540 So background images that repeat infinitely 1050 00:52:16,540 --> 00:52:18,850 was in vogue, apparently, at the time. 1051 00:52:18,850 --> 00:52:21,190 All of this was like images that I had to hand make 1052 00:52:21,190 --> 00:52:26,120 because we did not have the features that JavaScript and CSS nowadays have. 1053 00:52:26,120 --> 00:52:30,040 So it was really just HTML, and it was really just controller code written, 1054 00:52:30,040 --> 00:52:31,750 not in Python, but in Perl. 1055 00:52:31,750 --> 00:52:34,540 And it was really just the same building blocks 1056 00:52:34,540 --> 00:52:37,370 that we hear already today now have. 1057 00:52:37,370 --> 00:52:39,940 So we'll get rid of all of the imagery and focus more 1058 00:52:39,940 --> 00:52:42,190 on the functionality and the aesthetics, but let's see 1059 00:52:42,190 --> 00:52:45,490 if we can whip up a web application via which someone could 1060 00:52:45,490 --> 00:52:48,520 register for one such intramural sport. 1061 00:52:48,520 --> 00:52:52,210 So in app.py, me go ahead and import some familiar things now. 1062 00:52:52,210 --> 00:52:55,720 From Flask, let's import capital Flask, which 1063 00:52:55,720 --> 00:52:58,720 is that function we need to kick everything kick start everything. 1064 00:52:58,720 --> 00:53:01,900 Render templates, so we have the ability to render, that is print out, 1065 00:53:01,900 --> 00:53:04,360 those templates, and request so that we have the ability 1066 00:53:04,360 --> 00:53:07,088 to get at input from the human. 1067 00:53:07,088 --> 00:53:09,130 Let me go ahead and create the application itself 1068 00:53:09,130 --> 00:53:11,540 using this magical incantation here. 1069 00:53:11,540 --> 00:53:18,380 And then let's go ahead and define a route for slash for instance first. 1070 00:53:18,380 --> 00:53:20,260 I'm going to define a function called index. 1071 00:53:20,260 --> 00:53:22,900 But just to be clear, this function could be anything. 1072 00:53:22,900 --> 00:53:25,460 Foo, bar, baz, anything else. 1073 00:53:25,460 --> 00:53:27,520 But I tend to name them in a manner that's 1074 00:53:27,520 --> 00:53:29,260 consistent with what the route is called. 1075 00:53:29,260 --> 00:53:31,300 But you could call it anything you want, it's 1076 00:53:31,300 --> 00:53:34,840 just the function that will get called for this particular route. 1077 00:53:34,840 --> 00:53:37,090 Now, let me go ahead here and just get things started. 1078 00:53:37,090 --> 00:53:40,533 Return, render template of index.html. 1079 00:53:40,533 --> 00:53:41,950 Just keep it simple, nothing more. 1080 00:53:41,950 --> 00:53:44,860 So there's nothing really FroshIM specific about this here, 1081 00:53:44,860 --> 00:53:47,410 I just want to make sure I'm doing everything correctly. 1082 00:53:47,410 --> 00:53:49,390 Meanwhile, I've got my layout. 1083 00:53:49,390 --> 00:53:53,800 OK, let me go ahead, and in my templates directory, code a file 1084 00:53:53,800 --> 00:53:55,990 called index.html. 1085 00:53:55,990 --> 00:54:02,290 And let's just do extends layout.html at the top 1086 00:54:02,290 --> 00:54:04,390 just so that we get benefit from that template. 1087 00:54:04,390 --> 00:54:06,250 And down here, I'm just going to say to do. 1088 00:54:06,250 --> 00:54:09,250 Just so that I have something going on visually to make sure 1089 00:54:09,250 --> 00:54:10,690 I've not screwed up yet. 1090 00:54:10,690 --> 00:54:14,020 In my FroshIMS directory, let me do Flask run. 1091 00:54:14,020 --> 00:54:17,200 Let me now go back to my previous URL, which used to be my hello example. 1092 00:54:17,200 --> 00:54:21,850 But now, I'm serving up the FroshIM site. 1093 00:54:21,850 --> 00:54:23,590 Oh, and I'm seeing nothing. 1094 00:54:23,590 --> 00:54:26,860 That's because I screwed up accidentally. 1095 00:54:26,860 --> 00:54:30,090 What did I do wrong in index.html? 1096 00:54:30,090 --> 00:54:34,580 1097 00:54:34,580 --> 00:54:35,600 What am I doing wrong? 1098 00:54:35,600 --> 00:54:38,468 This file extends layout.html, but-- 1099 00:54:38,468 --> 00:54:40,010 AUDIENCE: You left out the block tag? 1100 00:54:40,010 --> 00:54:40,510 DAVID: Yeah. 1101 00:54:40,510 --> 00:54:44,900 I forgot to tell Flask what to plug into that layout. 1102 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 1103 00:54:49,040 --> 00:54:51,080 or whatever I want to eventually get around to. 1104 00:54:51,080 --> 00:54:52,310 Then end the block. 1105 00:54:52,310 --> 00:54:53,990 Let me end this tag here. 1106 00:54:53,990 --> 00:54:56,330 OK, so now it looks ugly, more cryptic. 1107 00:54:56,330 --> 00:54:59,210 But this is, again, the essence of doing templating. 1108 00:54:59,210 --> 00:55:03,340 Let me now restart Flask up here, let me go back to the page. 1109 00:55:03,340 --> 00:55:03,972 Let me reload. 1110 00:55:03,972 --> 00:55:05,930 Crossing my fingers this time, and there we go. 1111 00:55:05,930 --> 00:55:06,320 To do. 1112 00:55:06,320 --> 00:55:08,480 So it's not the application I want, but at least I 1113 00:55:08,480 --> 00:55:10,868 know I have some of the plumbing there by default. 1114 00:55:10,868 --> 00:55:13,160 All right, so if I want the user to be able to register 1115 00:55:13,160 --> 00:55:15,350 for one of these sports, let's enhance, now, 1116 00:55:15,350 --> 00:55:18,290 index.html to actually have a form that's 1117 00:55:18,290 --> 00:55:22,050 maybe got a dropdown menu for all of the sports for which you can register. 1118 00:55:22,050 --> 00:55:24,230 So let me go into this template here. 1119 00:55:24,230 --> 00:55:27,410 And instead of to do, let's go ahead and give myself, 1120 00:55:27,410 --> 00:55:31,160 how about an H1 tag that just says register so the user knows what it is 1121 00:55:31,160 --> 00:55:31,970 they're looking at. 1122 00:55:31,970 --> 00:55:35,270 How about a form tag that's going to use POST, 1123 00:55:35,270 --> 00:55:38,450 just because it's not really necessary to put this kind of information 1124 00:55:38,450 --> 00:55:39,530 in the URL. 1125 00:55:39,530 --> 00:55:42,260 The action for that, how about we plan to create 1126 00:55:42,260 --> 00:55:47,210 a register route so that we're sending information from to a register route. 1127 00:55:47,210 --> 00:55:48,770 So we'll have to come back to that. 1128 00:55:48,770 --> 00:55:54,650 In here, let me go ahead and create, how about an input with autocomplete 1129 00:55:54,650 --> 00:55:58,020 equals off, auto focus on. 1130 00:55:58,020 --> 00:56:00,062 How about a name equals name, because I'm 1131 00:56:00,062 --> 00:56:03,020 going to ask the student for their name using placeholder text of quote 1132 00:56:03,020 --> 00:56:03,867 unquote "name." 1133 00:56:03,867 --> 00:56:05,450 And the type of this box will be text. 1134 00:56:05,450 --> 00:56:07,610 So this is pretty much identical to before. 1135 00:56:07,610 --> 00:56:11,060 But if you've not seen this yet, let's create a select menu, 1136 00:56:11,060 --> 00:56:13,280 a so-called dropdown menu in HTML. 1137 00:56:13,280 --> 00:56:16,790 And maybe the first option I want to be in there 1138 00:56:16,790 --> 00:56:20,570 is going to be, oh, how about the current three 1139 00:56:20,570 --> 00:56:27,560 sports for the fall, which are basketball, and another option 1140 00:56:27,560 --> 00:56:30,140 is going to be soccer, and a third option is 1141 00:56:30,140 --> 00:56:35,700 going to be ultimate frisbee for first year intramurals right now. 1142 00:56:35,700 --> 00:56:37,160 So I've got those three options. 1143 00:56:37,160 --> 00:56:38,300 I've got my form. 1144 00:56:38,300 --> 00:56:42,650 I haven't implemented my route yet, but this feels like a good time 1145 00:56:42,650 --> 00:56:45,920 to go back now and check if my form has reloaded. 1146 00:56:45,920 --> 00:56:48,658 So let me go ahead and stop and start Flask. 1147 00:56:48,658 --> 00:56:51,200 You'll see there's ways to automate the process of restarting 1148 00:56:51,200 --> 00:56:53,360 the server that we'll do for you for problem set nine, 1149 00:56:53,360 --> 00:56:55,070 so you don't have to keep stopping Flask. 1150 00:56:55,070 --> 00:56:59,060 Let me reload my index route and OK, it's not that pretty. 1151 00:56:59,060 --> 00:57:01,220 It's not though, maybe-- 1152 00:57:01,220 --> 00:57:02,000 nor was this. 1153 00:57:02,000 --> 00:57:04,070 But it now has at least some functionality 1154 00:57:04,070 --> 00:57:07,160 where I can type in my name and then type in the sport. 1155 00:57:07,160 --> 00:57:09,740 Now, I might be biasing people toward basketball. 1156 00:57:09,740 --> 00:57:14,210 Like UX wise, user experience wise, it's obnoxious to precheck 1157 00:57:14,210 --> 00:57:15,810 basketball but not the others. 1158 00:57:15,810 --> 00:57:17,810 So there's some little tweaks we can make there. 1159 00:57:17,810 --> 00:57:20,060 Let me go back into index.html. 1160 00:57:20,060 --> 00:57:26,348 Let me create an empty option up here that, technically, this option is not 1161 00:57:26,348 --> 00:57:27,890 going to have the name of any sports. 1162 00:57:27,890 --> 00:57:30,348 But it's just going to have a word I want the human to see, 1163 00:57:30,348 --> 00:57:34,860 so I'm actually going to disable this option and make it selected by default. 1164 00:57:34,860 --> 00:57:37,250 But I'm going to say sport up here. 1165 00:57:37,250 --> 00:57:40,730 And there's different ways to do this, this is just one way of creating, 1166 00:57:40,730 --> 00:57:42,470 essentially, a-- 1167 00:57:42,470 --> 00:57:43,430 whoops, option. 1168 00:57:43,430 --> 00:57:44,660 Yep, that looks right. 1169 00:57:44,660 --> 00:57:47,000 Creating a placeholder sports so that the user 1170 00:57:47,000 --> 00:57:49,100 sees something in the dropdown. 1171 00:57:49,100 --> 00:57:52,170 Let me go ahead and restart Flask, reload the page, 1172 00:57:52,170 --> 00:57:54,170 and now it's just going to be marginally better. 1173 00:57:54,170 --> 00:57:56,360 Now you see sport that's checked by default, 1174 00:57:56,360 --> 00:57:59,330 but you have to check one of these other ones ultimately. 1175 00:57:59,330 --> 00:58:00,720 All right, so that's pretty good. 1176 00:58:00,720 --> 00:58:02,930 So let me now type in David. 1177 00:58:02,930 --> 00:58:05,360 I'll register for ultimate frisbee. 1178 00:58:05,360 --> 00:58:08,750 OK, I definitely forgot something. 1179 00:58:08,750 --> 00:58:09,540 Submit button. 1180 00:58:09,540 --> 00:58:10,940 So let's add that. 1181 00:58:10,940 --> 00:58:15,020 All right, so input type equals submit. 1182 00:58:15,020 --> 00:58:16,340 All right, let's put that in. 1183 00:58:16,340 --> 00:58:19,010 Restart Flask, reload. 1184 00:58:19,010 --> 00:58:19,783 Getting better. 1185 00:58:19,783 --> 00:58:21,200 Submit could be a little prettier. 1186 00:58:21,200 --> 00:58:25,730 Recall that we can change some of these HTTP-- these HTML attributes. 1187 00:58:25,730 --> 00:58:27,890 The value of this button should be register, maybe, 1188 00:58:27,890 --> 00:58:29,480 just to make things a little prettier. 1189 00:58:29,480 --> 00:58:32,695 Let me now reload the page and register. 1190 00:58:32,695 --> 00:58:35,570 All right, so now we really have the beginnings of the user interface 1191 00:58:35,570 --> 00:58:39,980 that I created some years ago to let people actually register for the sport. 1192 00:58:39,980 --> 00:58:43,880 So let's go, now, and create maybe the other route that we might need. 1193 00:58:43,880 --> 00:58:44,930 Let me go into app.py. 1194 00:58:44,930 --> 00:58:47,925 And in here, if we want to allow the user to register, 1195 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. 1196 00:58:51,050 --> 00:58:52,850 What could the user do wrong? 1197 00:58:52,850 --> 00:58:55,040 Because assume that they will. 1198 00:58:55,040 --> 00:58:56,870 One, they might not type their name. 1199 00:58:56,870 --> 00:58:58,743 Two, they might not choose a sport. 1200 00:58:58,743 --> 00:59:00,410 So they might just submit an empty form. 1201 00:59:00,410 --> 00:59:02,540 So that's two things we could check for, just 1202 00:59:02,540 --> 00:59:05,960 so that we're not scoring bogus entries in our database, ultimately. 1203 00:59:05,960 --> 00:59:09,590 So let's create another route called greet, /greet. 1204 00:59:09,590 --> 00:59:12,740 And then in this route, let's create a function called greet 1205 00:59:12,740 --> 00:59:14,900 but can be called anything we want. 1206 00:59:14,900 --> 00:59:18,293 And then let's go ahead, and in the greet function, let's go ahead 1207 00:59:18,293 --> 00:59:19,460 and validate the submission. 1208 00:59:19,460 --> 00:59:21,590 So a little comment to myself here. 1209 00:59:21,590 --> 00:59:30,270 How about if there is not a request.form GET name value, 1210 00:59:30,270 --> 00:59:32,450 so that is if that function returns nothing, 1211 00:59:32,450 --> 00:59:36,300 like quote unquote, or the special word none in Python. 1212 00:59:36,300 --> 00:59:47,280 Or request.form.get"sport" not in quote unquote, what were they? 1213 00:59:47,280 --> 00:59:54,360 Basketball, the other one was soccer, and the last was ultimate frisbee. 1214 00:59:54,360 --> 00:59:58,200 Getting a little long, but notice what I'm-- the question I'm asking. 1215 00:59:58,200 --> 01:00:01,290 If the user did not give us a name, that is, 1216 01:00:01,290 --> 01:00:03,910 if this function returns the equivalent of false, 1217 01:00:03,910 --> 01:00:07,830 which is, quote unquote, or literally none if there's no such parameter. 1218 01:00:07,830 --> 01:00:14,490 Or if the sport the user provided is not some value in basketball, soccer, 1219 01:00:14,490 --> 01:00:18,300 or ultimate frisbee, which I've defined as a Python list, then let's go ahead 1220 01:00:18,300 --> 01:00:19,890 and just yell at the user in some way. 1221 01:00:19,890 --> 01:00:25,200 Let's return render template of failure.html. 1222 01:00:25,200 --> 01:00:28,450 And that's just going to be some error message inside of that file. 1223 01:00:28,450 --> 01:00:30,990 Otherwise, if they get this far, let's go ahead 1224 01:00:30,990 --> 01:00:34,350 and confirm registration by just returning-- whoops, 1225 01:00:34,350 --> 01:00:40,420 returning render template quote unquote "success" dot HTML. 1226 01:00:40,420 --> 01:00:42,700 All right, so a couple quick things to do. 1227 01:00:42,700 --> 01:00:47,350 Let me first go in and in my templates directory, 1228 01:00:47,350 --> 01:00:50,530 let's create this failure.html file. 1229 01:00:50,530 --> 01:00:53,430 And this is just meant to be a message to the user 1230 01:00:53,430 --> 01:00:56,500 that they fail to provide the information correctly. 1231 01:00:56,500 --> 01:00:59,280 So let me go ahead and in failure.html. 1232 01:00:59,280 --> 01:01:02,250 not repeat my past mistake. 1233 01:01:02,250 --> 01:01:07,140 So let me extend layout.html and in the block body, you are not registered. 1234 01:01:07,140 --> 01:01:10,140 I'll just yell at them like that so that they know something went wrong. 1235 01:01:10,140 --> 01:01:14,760 And then let me create one other file called success.html, that 1236 01:01:14,760 --> 01:01:17,143 similarly is mostly just Jinja syntax. 1237 01:01:17,143 --> 01:01:19,560 And I'm just going to say for now, even though they're not 1238 01:01:19,560 --> 01:01:22,140 technically registered in any database, you are registered. 1239 01:01:22,140 --> 01:01:24,270 That's what we mean by success. 1240 01:01:24,270 --> 01:01:27,450 All right, so let me go ahead, and back in my FroshIMS, 1241 01:01:27,450 --> 01:01:29,370 directory run Flask run. 1242 01:01:29,370 --> 01:01:31,560 Let me go back to the form and reload. 1243 01:01:31,560 --> 01:01:33,150 Should look the same. 1244 01:01:33,150 --> 01:01:36,120 All right, so now let me not cooperate and just 1245 01:01:36,120 --> 01:01:39,300 immediately click Register impatiently. 1246 01:01:39,300 --> 01:01:42,960 OK, what did I do wrong. 1247 01:01:42,960 --> 01:01:47,008 Register-- oh, I'm confusing our two examples. 1248 01:01:47,008 --> 01:01:48,300 All right, I spotted the error. 1249 01:01:48,300 --> 01:01:49,133 What did I do wrong? 1250 01:01:49,133 --> 01:01:51,600 1251 01:01:51,600 --> 01:01:54,480 Unintentional. 1252 01:01:54,480 --> 01:01:58,050 There's where I am, what did I actually invent over here? 1253 01:01:58,050 --> 01:02:01,910 1254 01:02:01,910 --> 01:02:05,140 Where did I screw up? 1255 01:02:05,140 --> 01:02:06,800 Anyone? 1256 01:02:06,800 --> 01:02:08,050 AUDIENCE: Register, not greet. 1257 01:02:08,050 --> 01:02:08,758 DAVID: Thank you. 1258 01:02:08,758 --> 01:02:09,850 So register, not greet. 1259 01:02:09,850 --> 01:02:12,970 I had last example on my mind, so the route should be register. 1260 01:02:12,970 --> 01:02:16,360 Ironically, the function could be greet, because that actually doesn't matter. 1261 01:02:16,360 --> 01:02:20,030 But to keep ourselves sane, let's use the one and the same words there. 1262 01:02:20,030 --> 01:02:22,457 Let me go ahead now and start Flask as intended. 1263 01:02:22,457 --> 01:02:24,790 Let me reload the form just to make sure all is working. 1264 01:02:24,790 --> 01:02:29,140 Now, let me not cooperate and be a bad user, clicking register-- 1265 01:02:29,140 --> 01:02:30,220 oh my God. 1266 01:02:30,220 --> 01:02:32,890 OK, other unintended mistake. 1267 01:02:32,890 --> 01:02:35,120 But this one we've seen before. 1268 01:02:35,120 --> 01:02:37,960 Notice that by default, route only support GET. 1269 01:02:37,960 --> 01:02:40,960 So if I want to specifically support POST, 1270 01:02:40,960 --> 01:02:47,710 I have to pass in, by a methods parameter, a list of allowed route 1271 01:02:47,710 --> 01:02:50,590 methods that could be GET comma POST, but if I 1272 01:02:50,590 --> 01:02:54,460 don't have no need for a GET in this context, I can just do POST. 1273 01:02:54,460 --> 01:02:56,980 All right, now let's do this one last time. 1274 01:02:56,980 --> 01:02:59,680 Reload the form to make sure everything's OK, click Register, 1275 01:02:59,680 --> 01:03:01,433 and you are not registered. 1276 01:03:01,433 --> 01:03:02,350 So it's catching that. 1277 01:03:02,350 --> 01:03:04,767 All right, let me go ahead and at least give them my name. 1278 01:03:04,767 --> 01:03:05,500 Register. 1279 01:03:05,500 --> 01:03:06,550 You are not registered. 1280 01:03:06,550 --> 01:03:11,900 Fine, I'm going to go ahead and be David with ultimate frisbee register. 1281 01:03:11,900 --> 01:03:14,260 Huh. 1282 01:03:14,260 --> 01:03:15,640 OK. 1283 01:03:15,640 --> 01:03:20,780 What should I-- what did I mean to do here? 1284 01:03:20,780 --> 01:03:22,810 All right, so let's figure this out. 1285 01:03:22,810 --> 01:03:26,950 How to debug something like this, which is my third and final unintended, 1286 01:03:26,950 --> 01:03:29,320 unforced error? 1287 01:03:29,320 --> 01:03:32,590 How can we go about troubleshooting this? 1288 01:03:32,590 --> 01:03:35,440 Turn this into the teachable moment. 1289 01:03:35,440 --> 01:03:38,020 All right, well first, some safety checks. 1290 01:03:38,020 --> 01:03:39,610 What did I actually submit? 1291 01:03:39,610 --> 01:03:42,760 Let me go ahead and view page source, a good rule of thumb. 1292 01:03:42,760 --> 01:03:45,320 Look at the HTML that you actually sent to the user. 1293 01:03:45,320 --> 01:03:49,490 So here, I have an input with a name name. 1294 01:03:49,490 --> 01:03:51,950 So that's what I intended, that looks OK. 1295 01:03:51,950 --> 01:03:54,790 Ah, I see it already, even though you, if you've never 1296 01:03:54,790 --> 01:03:58,030 used a select menu, you might not know what, apparently, 1297 01:03:58,030 --> 01:04:04,150 is missing from here that I did have for my text input. 1298 01:04:04,150 --> 01:04:07,705 Just intuitively, logically. 1299 01:04:07,705 --> 01:04:09,580 What's going through my head, embarrassingly, 1300 01:04:09,580 --> 01:04:13,930 is, all right, if my form thinks that it's missing a name or a sport, 1301 01:04:13,930 --> 01:04:17,710 how did I create a situation in which name is blank or sport is blank? 1302 01:04:17,710 --> 01:04:19,720 Well, name, I don't think it's going to be blank 1303 01:04:19,720 --> 01:04:23,500 because I explicitly gave this text field a name name 1304 01:04:23,500 --> 01:04:25,210 and that did work last time. 1305 01:04:25,210 --> 01:04:28,660 I've now given a second input in the form of the select menu. 1306 01:04:28,660 --> 01:04:35,350 But what seems to be missing here that I'm assuming exists here? 1307 01:04:35,350 --> 01:04:38,620 It's just a dumb mistake I made. 1308 01:04:38,620 --> 01:04:41,686 What might be missing here? 1309 01:04:41,686 --> 01:04:45,790 If request.form gives you all of the inputs that the user might 1310 01:04:45,790 --> 01:04:48,790 have typed in, let me go into my actual code 1311 01:04:48,790 --> 01:04:52,690 here in my form and name equals sport. 1312 01:04:52,690 --> 01:04:54,710 I just didn't give a name to that input. 1313 01:04:54,710 --> 01:04:56,890 So it exists, and the browser doesn't care. 1314 01:04:56,890 --> 01:04:58,723 It's still going to display the form to you, 1315 01:04:58,723 --> 01:05:02,450 it just hasn't given it a unique name to actually transmit to the server. 1316 01:05:02,450 --> 01:05:04,790 So now, if I'm not going to put my foot in my mouth, 1317 01:05:04,790 --> 01:05:06,980 I think that's what I did wrong. 1318 01:05:06,980 --> 01:05:08,950 And again, my process for figuring that out 1319 01:05:08,950 --> 01:05:11,282 was looking at my code, thinking through logically, 1320 01:05:11,282 --> 01:05:12,490 is this right, is this right? 1321 01:05:12,490 --> 01:05:14,660 No, I was missing the name there. 1322 01:05:14,660 --> 01:05:17,620 So let's run Flask, let's reload the form 1323 01:05:17,620 --> 01:05:22,180 just to make sure it's all defaults again, type in my name and type 1324 01:05:22,180 --> 01:05:26,680 in ultimate frisbee, crossing my fingers extra hard this time. 1325 01:05:26,680 --> 01:05:27,470 And there. 1326 01:05:27,470 --> 01:05:28,337 You are registered. 1327 01:05:28,337 --> 01:05:29,170 So I can emphasize-- 1328 01:05:29,170 --> 01:05:30,903 I did not intend to screw up in that way, 1329 01:05:30,903 --> 01:05:33,070 but that's exactly the right kind of thought process 1330 01:05:33,070 --> 01:05:34,390 to diagnose issues like this. 1331 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, 1332 01:05:38,260 --> 01:05:40,670 and just rule things in and out. 1333 01:05:40,670 --> 01:05:43,420 There's only a finite number of ways I could have screwed that up. 1334 01:05:43,420 --> 01:05:44,292 Yeah? 1335 01:05:44,292 --> 01:05:45,648 AUDIENCE: Are you [INAUDIBLE]. 1336 01:05:45,648 --> 01:05:47,820 1337 01:05:47,820 --> 01:05:49,320 DAVID: Excuse-- say a little louder? 1338 01:05:49,320 --> 01:05:53,220 AUDIENCE: I don't understand why name equals sport [INAUDIBLE].. 1339 01:05:53,220 --> 01:05:55,870 DAVID: Why did name equal sport address the problem? 1340 01:05:55,870 --> 01:05:58,110 Well, let's first go back to the HTML. 1341 01:05:58,110 --> 01:06:05,410 Previously, it was just the reality that I had this user input dropdown menu, 1342 01:06:05,410 --> 01:06:06,840 but I never gave it a name. 1343 01:06:06,840 --> 01:06:10,170 But names, or more generally, key value pairs, 1344 01:06:10,170 --> 01:06:13,450 is how information is sent from a form to the server. 1345 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. 1346 01:06:18,660 --> 01:06:22,320 It would be like nothing equals ultimate frisbee, and that just doesn't work. 1347 01:06:22,320 --> 01:06:24,670 The browser is just not going to send it. 1348 01:06:24,670 --> 01:06:30,570 However, in app.py, I was naively assuming that in my requests form, 1349 01:06:30,570 --> 01:06:33,300 there would be a name called quote unquote "sport." 1350 01:06:33,300 --> 01:06:35,940 It could have been anything, but I was assuming it was sport. 1351 01:06:35,940 --> 01:06:37,770 But I never told the form that. 1352 01:06:37,770 --> 01:06:41,260 And if I really wanted to dig in, we could do a little something more. 1353 01:06:41,260 --> 01:06:44,250 Let me go back to the way it was a moment ago. 1354 01:06:44,250 --> 01:06:48,040 Let me get rid of the name of the sport dropdown menu. 1355 01:06:48,040 --> 01:06:53,220 Let me rerun Flask down here and reload the form itself 1356 01:06:53,220 --> 01:06:55,650 after it finishes being served. 1357 01:06:55,650 --> 01:06:56,820 And now, let me do this. 1358 01:06:56,820 --> 01:07:01,515 View Developer Tools, and then let me watch the Network tab, which recall, 1359 01:07:01,515 --> 01:07:03,390 we played around with a little bit last week. 1360 01:07:03,390 --> 01:07:06,572 And we also played around with Curl, which let us see the HTTP requests. 1361 01:07:06,572 --> 01:07:08,280 Here's another-- here's what I would have 1362 01:07:08,280 --> 01:07:11,650 done if I still wasn't seeing the error and was really embarrassed on stage. 1363 01:07:11,650 --> 01:07:15,750 I would have typed in my name as before, I would have chosen ultimate frisbee. 1364 01:07:15,750 --> 01:07:17,490 I would have clicked register. 1365 01:07:17,490 --> 01:07:21,480 And now, I would have looked at the HTTP request. 1366 01:07:21,480 --> 01:07:23,580 And I would click on Register here. 1367 01:07:23,580 --> 01:07:27,180 And just like we did last week, I would go down to the request down here. 1368 01:07:27,180 --> 01:07:29,910 And there's a whole lot of stuff that we can typically ignore. 1369 01:07:29,910 --> 01:07:33,030 But here, let me zoom in, way at the bottom, 1370 01:07:33,030 --> 01:07:35,370 what Chrome's developer tools are doing for me, 1371 01:07:35,370 --> 01:07:38,380 it's showing me all of the form data that was submitted. 1372 01:07:38,380 --> 01:07:40,950 So this really would have been my telltale clue. 1373 01:07:40,950 --> 01:07:44,220 I'm just not sending the sport, even if the human typed it in. 1374 01:07:44,220 --> 01:07:46,230 And logically, because I've done this before, 1375 01:07:46,230 --> 01:07:49,170 that must mean I didn't give the thing a name. 1376 01:07:49,170 --> 01:07:50,340 But another good tool. 1377 01:07:50,340 --> 01:07:53,610 Like good programmers, web developers are using these kinds of tools 1378 01:07:53,610 --> 01:07:54,660 all the time. 1379 01:07:54,660 --> 01:07:56,398 They're not writing bug-free code. 1380 01:07:56,398 --> 01:07:57,690 That's not the point to get to. 1381 01:07:57,690 --> 01:08:00,780 The point to get to is being a good diagnostician, 1382 01:08:00,780 --> 01:08:02,790 I would say, in these cases. 1383 01:08:02,790 --> 01:08:07,790 OK, other questions on this? 1384 01:08:07,790 --> 01:08:09,050 Yeah. 1385 01:08:09,050 --> 01:08:14,390 AUDIENCE: What if you want to edit one HTML in CSS, [INAUDIBLE].. 1386 01:08:14,390 --> 01:08:16,682 DAVID: I'm sorry, a little bit louder? 1387 01:08:16,682 --> 01:08:19,250 AUDIENCE: If you want to edit in CSS or anything, 1388 01:08:19,250 --> 01:08:23,660 in HTML, once you have to fix the template, how do you that? 1389 01:08:23,660 --> 01:08:27,770 DAVID: So how would you edit CSS if you have these templates? 1390 01:08:27,770 --> 01:08:30,080 That process we'll actually see before long. 1391 01:08:30,080 --> 01:08:31,705 It's almost going to be the exact same. 1392 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, 1393 01:08:34,788 --> 01:08:37,640 but we'll give you some distribution code to automate this process. 1394 01:08:37,640 --> 01:08:40,100 You can absolutely still do something like this. 1395 01:08:40,100 --> 01:08:44,899 Link href equals quote unquote "styles" dot 1396 01:08:44,899 --> 01:08:49,609 CSS rel equals style sheet, that's one of the techniques we showed last week. 1397 01:08:49,609 --> 01:08:53,660 The only difference today, using Flask, is that all of your static files, 1398 01:08:53,660 --> 01:08:56,279 by convention, should go in your static folder. 1399 01:08:56,279 --> 01:08:58,310 So the change you would make in your layout 1400 01:08:58,310 --> 01:09:02,240 would be to say that styles dot CSS is in your static folder. 1401 01:09:02,240 --> 01:09:06,229 And then, if I go into my FroshIMS directory, 1402 01:09:06,229 --> 01:09:08,569 I can create a static folder. 1403 01:09:08,569 --> 01:09:11,060 I can CD into it, nothing's there by default. 1404 01:09:11,060 --> 01:09:14,000 But if I now code a file called styles.css, 1405 01:09:14,000 --> 01:09:17,060 I could now do something like this body. 1406 01:09:17,060 --> 01:09:28,220 And in here, I could say background color, say FF0000 to make it red. 1407 01:09:28,220 --> 01:09:32,450 Let me go ahead now and restart Flask in the FroshIMS directory. 1408 01:09:32,450 --> 01:09:35,060 Cross my fingers because I'm doing this on the fly. 1409 01:09:35,060 --> 01:09:38,399 Go back to my form and reload. 1410 01:09:38,399 --> 01:09:41,990 Voila, now we've tied together last week's stuff as well. 1411 01:09:41,990 --> 01:09:45,092 If I answered the right question? 1412 01:09:45,092 --> 01:09:49,440 AUDIENCE: [INAUDIBLE] change one page and not the other. 1413 01:09:49,440 --> 01:09:52,440 DAVID: If you want to change one page and not the other in terms of CSS? 1414 01:09:52,440 --> 01:09:53,250 AUDIENCE: Yes. 1415 01:09:53,250 --> 01:09:54,360 DAVID: That depends. 1416 01:09:54,360 --> 01:09:59,190 In that case, you might want to have different CSS files for each page 1417 01:09:59,190 --> 01:10:00,390 if they're different. 1418 01:10:00,390 --> 01:10:04,620 You could use different classes in one template than you did in the other. 1419 01:10:04,620 --> 01:10:06,040 There's different ways to do that. 1420 01:10:06,040 --> 01:10:09,990 You could even have a placeholder in your layout 1421 01:10:09,990 --> 01:10:14,460 that allows you to plug in the URL of a specific style 1422 01:10:14,460 --> 01:10:15,900 sheet in your individual files. 1423 01:10:15,900 --> 01:10:18,670 But that starts to get more complicated quickly. 1424 01:10:18,670 --> 01:10:20,410 So in short, you can absolutely do it. 1425 01:10:20,410 --> 01:10:24,180 But typically, I would say most websites try not 1426 01:10:24,180 --> 01:10:25,890 to use different style Sheets per page. 1427 01:10:25,890 --> 01:10:28,560 They reuse the styles as much as they can. 1428 01:10:28,560 --> 01:10:30,810 All right, let me go ahead and revert this real quick. 1429 01:10:30,810 --> 01:10:33,750 And let's start to add a little bit more functionality here. 1430 01:10:33,750 --> 01:10:36,630 I'm going to go ahead and just remove the static folder just so as 1431 01:10:36,630 --> 01:10:38,400 to not complicate things just yet. 1432 01:10:38,400 --> 01:10:41,490 And let's go ahead and just play around with a different user interface 1433 01:10:41,490 --> 01:10:42,090 mechanism. 1434 01:10:42,090 --> 01:10:45,503 In my form here, the dropdown menu is perfectly fine. 1435 01:10:45,503 --> 01:10:46,420 Nothing wrong with it. 1436 01:10:46,420 --> 01:10:49,650 But suppose that I wanted to change it to checkboxes instead. 1437 01:10:49,650 --> 01:10:53,670 Maybe I want students to be able to register for multiple sports instead. 1438 01:10:53,670 --> 01:10:57,060 Well, it might make sense to clean this up in a couple of ways. 1439 01:10:57,060 --> 01:10:57,810 And let's do this. 1440 01:10:57,810 --> 01:11:03,000 Before we even get into the checkboxes, there's one subtle bad design here. 1441 01:11:03,000 --> 01:11:07,450 Notice that I've hardcoded basketball, soccer, and ultimate frisbee here. 1442 01:11:07,450 --> 01:11:11,830 And if you recall, in app.py, I also enumerated all three of those here. 1443 01:11:11,830 --> 01:11:15,170 And any time you see copy paste or the equivalent thereof, 1444 01:11:15,170 --> 01:11:16,840 feels like we could do better. 1445 01:11:16,840 --> 01:11:18,640 So what if I instead do this. 1446 01:11:18,640 --> 01:11:22,950 What if I instead give myself a global variable of Sports, 1447 01:11:22,950 --> 01:11:25,350 I'll capitalize the word just to connote that it's 1448 01:11:25,350 --> 01:11:29,400 meant to be constant even though Python does not have constants, per se. 1449 01:11:29,400 --> 01:11:31,980 The first sport will be basketball. 1450 01:11:31,980 --> 01:11:33,960 The second will be soccer. 1451 01:11:33,960 --> 01:11:38,160 The third will be ultimate frisbee. 1452 01:11:38,160 --> 01:11:42,180 Now I have one convenient place to store all of my sports 1453 01:11:42,180 --> 01:11:44,700 if it changes next semester or next year or whatnot. 1454 01:11:44,700 --> 01:11:46,710 But notice what I could do to. 1455 01:11:46,710 --> 01:11:48,340 I could now do something like this. 1456 01:11:48,340 --> 01:11:52,350 Let me pass into my index template a variable 1457 01:11:52,350 --> 01:11:56,700 called sports that's equal to that global variable sports. 1458 01:11:56,700 --> 01:12:00,030 Let me go into my index now, and this is really, now, 1459 01:12:00,030 --> 01:12:04,020 going to hint at the power of templating and Jinja, in this case here. 1460 01:12:04,020 --> 01:12:07,690 Let me go ahead and get rid of all three of these hard coded options 1461 01:12:07,690 --> 01:12:12,570 and let me show you some slightly different syntax for sport, in sports. 1462 01:12:12,570 --> 01:12:15,090 Then end for. 1463 01:12:15,090 --> 01:12:17,110 We've not seen this end for syntax. 1464 01:12:17,110 --> 01:12:19,930 There's like end block syntax, but it's as simple as that. 1465 01:12:19,930 --> 01:12:23,190 So you have a start and an end to your block without indentation mattering. 1466 01:12:23,190 --> 01:12:24,630 Watch what I can do here. 1467 01:12:24,630 --> 01:12:30,720 Option curly brace sport close curly brace. 1468 01:12:30,720 --> 01:12:32,070 Let me save that. 1469 01:12:32,070 --> 01:12:35,220 Let me go back into my terminal window, do Flask run. 1470 01:12:35,220 --> 01:12:38,400 And if I didn't mess up here, let me go back to this. 1471 01:12:38,400 --> 01:12:41,040 The red's going to go away because I deleted my CSS. 1472 01:12:41,040 --> 01:12:44,070 And now I still have a sport dropdown and all of those sports 1473 01:12:44,070 --> 01:12:45,010 are still there. 1474 01:12:45,010 --> 01:12:46,590 I can make one more improvement now. 1475 01:12:46,590 --> 01:12:49,540 I don't need to mention these same sports manually in app.py. 1476 01:12:49,540 --> 01:12:53,910 I can now just say if the user's inputed sport is not 1477 01:12:53,910 --> 01:12:57,308 in my global variable, sports, and ask the same question. 1478 01:12:57,308 --> 01:12:59,100 And this is really handy because if there's 1479 01:12:59,100 --> 01:13:03,240 another sport, for instance, that gets added, like say football, 1480 01:13:03,240 --> 01:13:06,160 all I have to do is change my global variable. 1481 01:13:06,160 --> 01:13:09,780 And if I reload the form now and look in the dropdown, boom, 1482 01:13:09,780 --> 01:13:11,970 now I have support for a fourth sport. 1483 01:13:11,970 --> 01:13:13,660 And I can keep adding and adding there. 1484 01:13:13,660 --> 01:13:17,250 So here's where templating starts to get really powerful in that 1485 01:13:17,250 --> 01:13:22,710 now, in this template, I'm using Jinja's for loop syntax, which 1486 01:13:22,710 --> 01:13:25,200 is almost identical to Python here, except you 1487 01:13:25,200 --> 01:13:28,500 need the curly brace and the percent sign and you need the weird ending 1488 01:13:28,500 --> 01:13:29,370 and for. 1489 01:13:29,370 --> 01:13:31,050 But it's the same idea as in Python. 1490 01:13:31,050 --> 01:13:35,310 Iterating over something with a for loop lets you generate more and more HTML. 1491 01:13:35,310 --> 01:13:37,230 And this is like every website out there. 1492 01:13:37,230 --> 01:13:38,100 For instance, Gmail. 1493 01:13:38,100 --> 01:13:42,090 When you visit your inbox and you see all of this big table of emails, 1494 01:13:42,090 --> 01:13:44,730 Google has not hardcoded your emails manually. 1495 01:13:44,730 --> 01:13:46,560 They have grabbed them from a database. 1496 01:13:46,560 --> 01:13:48,310 They have some kind of for loop like this, 1497 01:13:48,310 --> 01:13:54,295 and are just outputting table row after table row or div after div dynamically. 1498 01:13:54,295 --> 01:13:56,670 All right, so now, let's go ahead and change this, maybe, 1499 01:13:56,670 --> 01:14:02,470 to, oh, how about little checkboxes or radio buttons. 1500 01:14:02,470 --> 01:14:03,810 So let me go ahead and do this. 1501 01:14:03,810 --> 01:14:08,670 Instead of a select menu, I'm going to go ahead and do something like this. 1502 01:14:08,670 --> 01:14:14,590 For each of these sports let me go ahead and output, not an option, 1503 01:14:14,590 --> 01:14:17,520 but let me go ahead and output an input tag, 1504 01:14:17,520 --> 01:14:21,540 the name for which is quote unquote "sport," the type of which 1505 01:14:21,540 --> 01:14:27,240 is checkbox, the value of which is going to be the current "sport," 1506 01:14:27,240 --> 01:14:31,642 quote unquote, and then afterward I need to redundantly, seemingly, 1507 01:14:31,642 --> 01:14:32,350 output the sport. 1508 01:14:32,350 --> 01:14:34,150 So you see a word next to the checkbox. 1509 01:14:34,150 --> 01:14:36,400 And we'll look at the result of this in just a moment. 1510 01:14:36,400 --> 01:14:39,960 So it's actually a little simpler than a select menu, a dropdown menu, 1511 01:14:39,960 --> 01:14:43,200 because now watch what happens if I reload my form. 1512 01:14:43,200 --> 01:14:46,560 Different user interface, and it's not as pretty, 1513 01:14:46,560 --> 01:14:49,840 but it's going to allow users to sign up for multiple sports at once now, 1514 01:14:49,840 --> 01:14:50,590 it would seem. 1515 01:14:50,590 --> 01:14:53,920 Now I can click on basketball and football and soccer 1516 01:14:53,920 --> 01:14:56,260 or some other combination thereof. 1517 01:14:56,260 --> 01:15:00,040 If I view the page's source, this is, again, the power of templating. 1518 01:15:00,040 --> 01:15:04,220 I didn't have to type out four inputs, I got them now automatically. 1519 01:15:04,220 --> 01:15:07,540 And these things all have the same name, but that's OK. 1520 01:15:07,540 --> 01:15:11,170 It turns out with Flask, if it sees multiple values for the same name, 1521 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. 1522 01:15:15,160 --> 01:15:18,430 All right, but suppose we don't want users registering for multiple sports. 1523 01:15:18,430 --> 01:15:19,810 Maybe capacity is an issue. 1524 01:15:19,810 --> 01:15:23,440 Let me go ahead and change this checkbox to radio button, which 1525 01:15:23,440 --> 01:15:25,400 a radio button is mutually exclusive. 1526 01:15:25,400 --> 01:15:27,130 So you can only sign up for one. 1527 01:15:27,130 --> 01:15:31,780 So now, once I reload the page, there we go. 1528 01:15:31,780 --> 01:15:34,240 It now looks like this. 1529 01:15:34,240 --> 01:15:39,010 And because I've given each of these inputs the same name, quote unquote, 1530 01:15:39,010 --> 01:15:42,410 "sport," that's what makes them mutually exclusive. 1531 01:15:42,410 --> 01:15:45,730 The browser knows all four of these things are types of sports, 1532 01:15:45,730 --> 01:15:48,970 therefore I'm only going to let you select one of these things. 1533 01:15:48,970 --> 01:15:51,310 And that's simply because they all have the same name. 1534 01:15:51,310 --> 01:15:54,730 Again, if I view page source, notice all of them, name equal sport, 1535 01:15:54,730 --> 01:15:58,780 name equals sport, name equals sport, but what differs is the value 1536 01:15:58,780 --> 01:16:01,942 that each one is going to have. 1537 01:16:01,942 --> 01:16:07,490 All right, any questions, then, on this approach? 1538 01:16:07,490 --> 01:16:07,990 All right. 1539 01:16:07,990 --> 01:16:09,990 Well, let me go ahead and open a version of this 1540 01:16:09,990 --> 01:16:13,690 that I made in advance that's going to now start saving the information. 1541 01:16:13,690 --> 01:16:15,580 So thus far, we're not quite at the point 1542 01:16:15,580 --> 01:16:18,910 of where this website was, which actually allowed the proctors to see, 1543 01:16:18,910 --> 01:16:21,430 like in a database, everyone who had registered for sports. 1544 01:16:21,430 --> 01:16:24,130 Now, we're literally telling students you are registered 1545 01:16:24,130 --> 01:16:26,050 or you are not registered, but we're literally 1546 01:16:26,050 --> 01:16:28,130 doing nothing with this information. 1547 01:16:28,130 --> 01:16:30,770 So how might we go about implementing this? 1548 01:16:30,770 --> 01:16:32,740 Well, let me go ahead and close these tabs, 1549 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. 1550 01:16:38,140 --> 01:16:41,950 And let me go into my source nine directory, FroshIMS3, 1551 01:16:41,950 --> 01:16:44,890 and let me go ahead and open up app.py. 1552 01:16:44,890 --> 01:16:46,450 So this is a premade version. 1553 01:16:46,450 --> 01:16:48,380 I've gotten rid of football, in this case. 1554 01:16:48,380 --> 01:16:51,100 But I've added one thing at the very top. 1555 01:16:51,100 --> 01:16:56,110 What's, in English, does this represent on line seven? 1556 01:16:56,110 --> 01:16:58,150 What would you describe what that thing is? 1557 01:16:58,150 --> 01:17:01,230 1558 01:17:01,230 --> 01:17:02,700 What are we looking at? 1559 01:17:02,700 --> 01:17:03,690 What do you think? 1560 01:17:03,690 --> 01:17:04,410 AUDIENCE: It's an empty dictionary. 1561 01:17:04,410 --> 01:17:05,610 DAVID: Yeah, it's an empty dictionary, right? 1562 01:17:05,610 --> 01:17:07,860 Registrants is apparently a variable on the left. 1563 01:17:07,860 --> 01:17:10,168 It's being assigned an empty dictionary on the right. 1564 01:17:10,168 --> 01:17:12,210 And a dictionary, again, is just key value pairs. 1565 01:17:12,210 --> 01:17:15,690 Here, again, is where dictionaries are just such a useful data structure. 1566 01:17:15,690 --> 01:17:16,198 Why? 1567 01:17:16,198 --> 01:17:18,990 Because this is going to allow me to remember that David registered 1568 01:17:18,990 --> 01:17:21,510 for ultimate frisbee, Carter registered for soccer, 1569 01:17:21,510 --> 01:17:23,070 Emma registered for something else. 1570 01:17:23,070 --> 01:17:26,550 You can associate keys with values, names with sports, 1571 01:17:26,550 --> 01:17:29,770 assuming a model where you can only register for one sport for now. 1572 01:17:29,770 --> 01:17:35,040 And so let's see what the logic is that handles this. 1573 01:17:35,040 --> 01:17:38,520 Here in my register route in the code I've premade, 1574 01:17:38,520 --> 01:17:40,440 notice that I'm validating the user's name. 1575 01:17:40,440 --> 01:17:42,630 Slightly differently from before but same idea. 1576 01:17:42,630 --> 01:17:45,720 I'm using request.form.get to get the human's name. 1577 01:17:45,720 --> 01:17:48,750 If not name, so if the human did not type a name, 1578 01:17:48,750 --> 01:17:51,480 I'm going to output error.html. 1579 01:17:51,480 --> 01:17:55,890 But notice I've started to make the user interface more expressive. 1580 01:17:55,890 --> 01:17:59,790 I'm telling the user, apparently, with a message what they did wrong. 1581 01:17:59,790 --> 01:18:00,870 Well how? 1582 01:18:00,870 --> 01:18:03,420 I'm apparently passing to my error template, 1583 01:18:03,420 --> 01:18:06,773 instead of just failure.html, a specific message. 1584 01:18:06,773 --> 01:18:08,190 So let's go down this rabbit hole. 1585 01:18:08,190 --> 01:18:14,290 Let me actually go into templates/error.hml, and sure enough, 1586 01:18:14,290 --> 01:18:18,000 here's a new file I created here, that adorably is apparently going to have 1587 01:18:18,000 --> 01:18:21,570 a grumpy cat as part of the error message, but notice what I've done. 1588 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. 1589 01:18:26,820 --> 01:18:29,370 I then have a paragraph tag that plugs in whatever 1590 01:18:29,370 --> 01:18:33,120 the error message is that the controller, app.py, is passing in. 1591 01:18:33,120 --> 01:18:36,450 And then just for fun, I have a picture of a grumpy cat connoting 1592 01:18:36,450 --> 01:18:37,990 that there was, in fact, an error. 1593 01:18:37,990 --> 01:18:39,000 Let's keep looking. 1594 01:18:39,000 --> 01:18:40,660 How do I validate sport? 1595 01:18:40,660 --> 01:18:45,030 I do similarly request.form.get of sport, 1596 01:18:45,030 --> 01:18:46,800 and I store it in a variable called sport. 1597 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, 1598 01:18:50,590 --> 01:18:53,010 then I'm going to render error.html two, but I'm 1599 01:18:53,010 --> 01:18:55,600 going to give a different message, missing sport. 1600 01:18:55,600 --> 01:19:00,690 Else, if the sport they did type in is not in my sports global variable, 1601 01:19:00,690 --> 01:19:04,230 I'm going to render error.html, but complain differently, 1602 01:19:04,230 --> 01:19:07,020 you gave me an invalid sport somehow. 1603 01:19:07,020 --> 01:19:09,270 As if a hacker went into the HTML of the page, 1604 01:19:09,270 --> 01:19:11,617 changed it to add their own sport like volleyball. 1605 01:19:11,617 --> 01:19:13,950 Even though it's not offered, they submitted volleyball. 1606 01:19:13,950 --> 01:19:17,400 But that's OK, I'm rejecting it, even though they might have maliciously 1607 01:19:17,400 --> 01:19:20,910 tried to send it to me by changing the dom locally. 1608 01:19:20,910 --> 01:19:23,070 And then really, the magic is just this. 1609 01:19:23,070 --> 01:19:25,680 I remember that this person has registered 1610 01:19:25,680 --> 01:19:28,860 by indexing into the registrant dictionary 1611 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. 1612 01:19:33,870 --> 01:19:35,140 Why is this useful? 1613 01:19:35,140 --> 01:19:37,470 Well, I added one final route here. 1614 01:19:37,470 --> 01:19:41,910 I have a /registrants route with a registrants function that renders 1615 01:19:41,910 --> 01:19:43,860 a template called registrants.html. 1616 01:19:43,860 --> 01:19:48,820 But it takes as input that global variable just like before. 1617 01:19:48,820 --> 01:19:55,200 So let's go down this rabbit hole let me go into templates registrants dot HTML. 1618 01:19:55,200 --> 01:19:56,640 Here's this template. 1619 01:19:56,640 --> 01:20:00,420 It looks a little crazy big, but it extends the layout. 1620 01:20:00,420 --> 01:20:01,620 Here comes the body. 1621 01:20:01,620 --> 01:20:04,560 I've got an H1 tag that says registrants, big and bold. 1622 01:20:04,560 --> 01:20:06,930 Then I've got a table that we saw last week. 1623 01:20:06,930 --> 01:20:10,800 This has a table head that just says name sport for two columns. 1624 01:20:10,800 --> 01:20:16,200 Then it has a table body where in, using this for loop in Jinja syntax, 1625 01:20:16,200 --> 01:20:19,410 I'm saying, for each name in the registrants variable, 1626 01:20:19,410 --> 01:20:23,400 output a table row, start tag, and end tag, inside of which, 1627 01:20:23,400 --> 01:20:26,580 two table datas, two cells, table data for name, 1628 01:20:26,580 --> 01:20:30,790 table data for registrants bracket name. 1629 01:20:30,790 --> 01:20:33,210 So it's very similar to Python syntax. 1630 01:20:33,210 --> 01:20:37,100 It essentially is Python syntax, albeit with these curly braces and the percent 1631 01:20:37,100 --> 01:20:37,600 sign. 1632 01:20:37,600 --> 01:20:39,750 So the net effect here is what? 1633 01:20:39,750 --> 01:20:43,050 Let me open up my terminal window, run Flask run. 1634 01:20:43,050 --> 01:20:47,110 Let me now go into the form that I premade here. 1635 01:20:47,110 --> 01:20:48,270 So gone is football. 1636 01:20:48,270 --> 01:20:50,190 Let me go ahead and type in David. 1637 01:20:50,190 --> 01:20:52,560 Let me choose, oh, no sport. 1638 01:20:52,560 --> 01:20:53,880 Register. 1639 01:20:53,880 --> 01:20:55,470 Error, missing sport. 1640 01:20:55,470 --> 01:20:57,120 And there is the grumpy cat. 1641 01:20:57,120 --> 01:21:00,120 So missing sport, though, specifically was outputed. 1642 01:21:00,120 --> 01:21:00,910 All right, fine. 1643 01:21:00,910 --> 01:21:03,660 Let me go ahead and say no name. 1644 01:21:03,660 --> 01:21:04,920 But I'll choose basketball. 1645 01:21:04,920 --> 01:21:06,210 Register. 1646 01:21:06,210 --> 01:21:06,990 Missing name. 1647 01:21:06,990 --> 01:21:09,480 All right, and let me maliciously, now, do this. 1648 01:21:09,480 --> 01:21:10,530 Now I'm hacking. 1649 01:21:10,530 --> 01:21:11,820 Let me go into this. 1650 01:21:11,820 --> 01:21:15,930 I'll type my name, sure, but let me go into the body tag down here. 1651 01:21:15,930 --> 01:21:20,130 Let me maliciously go down in ultimate frisbee, heck with that, let's 1652 01:21:20,130 --> 01:21:21,780 volleyball. 1653 01:21:21,780 --> 01:21:26,580 Change that and change this to volleyball. 1654 01:21:26,580 --> 01:21:27,360 Enter. 1655 01:21:27,360 --> 01:21:31,230 So now, I can register for any sport I want to create. 1656 01:21:31,230 --> 01:21:34,200 Let me click register, but invalid sports. 1657 01:21:34,200 --> 01:21:36,420 So again, that speaks to the power and the need 1658 01:21:36,420 --> 01:21:39,600 for checking things on backend and not trusting users. 1659 01:21:39,600 --> 01:21:43,830 It is that easy to hack websites otherwise if you're not validating data 1660 01:21:43,830 --> 01:21:44,530 server side. 1661 01:21:44,530 --> 01:21:46,530 All right, finally, let's just do this for real. 1662 01:21:46,530 --> 01:21:48,530 David is going to register for ultimate frisbee. 1663 01:21:48,530 --> 01:21:49,530 Clicking register. 1664 01:21:49,530 --> 01:21:52,800 And now, the output is not very pretty, but notice 1665 01:21:52,800 --> 01:21:54,790 I'm at the registrants route. 1666 01:21:54,790 --> 01:21:56,880 And if I zoom out, I have an HTML table. 1667 01:21:56,880 --> 01:22:00,370 Two columns, name and sport, David and ultimate frisbee. 1668 01:22:00,370 --> 01:22:04,120 Let me go back to the form, letting me pretend Carter walked up to my laptop 1669 01:22:04,120 --> 01:22:05,560 and registered for basketball. 1670 01:22:05,560 --> 01:22:06,460 Register. 1671 01:22:06,460 --> 01:22:11,350 Now we see two rows in this table, David, ultimate frisbee, Carter, 1672 01:22:11,350 --> 01:22:11,860 basketball. 1673 01:22:11,860 --> 01:22:13,652 And if we do this one more time, maybe Emma 1674 01:22:13,652 --> 01:22:16,000 comes along and registers for soccer register. 1675 01:22:16,000 --> 01:22:20,905 All of this information is being stored in this dictionary, now. 1676 01:22:20,905 --> 01:22:22,030 All right, so that's great. 1677 01:22:22,030 --> 01:22:26,230 Now we have a database, albeit in the form of a Python dictionary. 1678 01:22:26,230 --> 01:22:31,277 But why is this, maybe, not the best implementation? 1679 01:22:31,277 --> 01:22:32,110 Why is it not great? 1680 01:22:32,110 --> 01:22:32,954 Yeah. 1681 01:22:32,954 --> 01:22:35,858 AUDIENCE: You are storing [INAUDIBLE]. 1682 01:22:35,858 --> 01:22:40,590 1683 01:22:40,590 --> 01:22:41,090 DAVID: Yeah. 1684 01:22:41,090 --> 01:22:43,715 So we're only storing this dictionary in the computer's memory, 1685 01:22:43,715 --> 01:22:46,970 and that's great until I hit Control C and kill Flask, 1686 01:22:46,970 --> 01:22:48,410 stopping the web server. 1687 01:22:48,410 --> 01:22:51,650 Or the server reboots, or maybe I close my laptop or whatever. 1688 01:22:51,650 --> 01:22:54,950 If the server stops running, memory is going to be lost. 1689 01:22:54,950 --> 01:22:56,420 RAM is volatile. 1690 01:22:56,420 --> 01:22:59,550 It's thrown away when you lose power or stop the program. 1691 01:22:59,550 --> 01:23:01,340 So maybe this isn't the best approach. 1692 01:23:01,340 --> 01:23:03,600 Maybe it would be better to use a CSV file. 1693 01:23:03,600 --> 01:23:06,230 And in fact, some 20 years ago, that's literally what I did. 1694 01:23:06,230 --> 01:23:08,130 I stored everything in a CSV file. 1695 01:23:08,130 --> 01:23:10,640 But let's skip that step, because we already saw last week, 1696 01:23:10,640 --> 01:23:13,790 or a couple of weeks ago now, how we can use SQLite. 1697 01:23:13,790 --> 01:23:16,370 Let's see if we can't marry in some SQL here 1698 01:23:16,370 --> 01:23:20,370 to store an actual database for the program. 1699 01:23:20,370 --> 01:23:22,310 Let me go back here and let me open up, say, 1700 01:23:22,310 --> 01:23:25,850 version four of this, which is almost the same but it 1701 01:23:25,850 --> 01:23:27,530 adds a bit more functionality. 1702 01:23:27,530 --> 01:23:32,780 Let me close these tabs and let me open up app.py now in version four. 1703 01:23:32,780 --> 01:23:35,990 So notice it's almost the same, but at the top, 1704 01:23:35,990 --> 01:23:40,490 I'm creating a database connection to a database called FroshIMS.db. 1705 01:23:40,490 --> 01:23:42,283 So that's a database I created in advance. 1706 01:23:42,283 --> 01:23:43,700 So let's go down that rabbit hole. 1707 01:23:43,700 --> 01:23:44,720 What does it look like? 1708 01:23:44,720 --> 01:23:46,860 Let me make my terminal window bigger. 1709 01:23:46,860 --> 01:23:50,600 Let me run SQLite 3 of FroshIMS.db. 1710 01:23:50,600 --> 01:23:51,290 OK, I'm in. 1711 01:23:51,290 --> 01:23:52,580 Let's do .schema. 1712 01:23:52,580 --> 01:23:55,670 and let's just infer what I designed this to be. 1713 01:23:55,670 --> 01:23:59,960 I have a table called registrants, which has one, two, three columns. 1714 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, 1715 01:24:04,340 --> 01:24:06,950 and a sport column that's also text, cannot be null, 1716 01:24:06,950 --> 01:24:08,600 and the primary key is just ID. 1717 01:24:08,600 --> 01:24:11,630 So that I have a unique ID for every registration. 1718 01:24:11,630 --> 01:24:14,150 Let's see if there's anyone in there yet. 1719 01:24:14,150 --> 01:24:17,843 Select star from registrants. 1720 01:24:17,843 --> 01:24:19,010 OK, there's no one in there. 1721 01:24:19,010 --> 01:24:20,600 No one is yet registered for sports. 1722 01:24:20,600 --> 01:24:23,180 So let's go back to the code and continue on. 1723 01:24:23,180 --> 01:24:26,030 In my code now, I've got the same global variable 1724 01:24:26,030 --> 01:24:29,270 for validation and generation of my HTML. 1725 01:24:29,270 --> 01:24:31,760 Looks like my index route is the same. 1726 01:24:31,760 --> 01:24:35,480 It's dynamically generating the menu of sports. 1727 01:24:35,480 --> 01:24:37,137 Interestingly, we'll come back to this. 1728 01:24:37,137 --> 01:24:39,470 There's a deregister route that's going to allow someone 1729 01:24:39,470 --> 01:24:44,330 to deregister themselves if they want to exit the sport 1730 01:24:44,330 --> 01:24:45,530 or undo their registration. 1731 01:24:45,530 --> 01:24:46,940 But this is the juicy part. 1732 01:24:46,940 --> 01:24:49,580 Here's my new and improved register route. 1733 01:24:49,580 --> 01:24:52,940 Still works on POST, so some mild privacy there. 1734 01:24:52,940 --> 01:24:55,770 I'm validating the submission as follows. 1735 01:24:55,770 --> 01:24:59,180 I'm getting the user's inputted name, the user's inputted sport, 1736 01:24:59,180 --> 01:25:03,380 and if it is not a name or the sport is not in sports, 1737 01:25:03,380 --> 01:25:05,197 I'm going to render failure.html. 1738 01:25:05,197 --> 01:25:06,030 So I kept it simple. 1739 01:25:06,030 --> 01:25:07,380 There's no cat in this version. 1740 01:25:07,380 --> 01:25:08,780 It just says failure. 1741 01:25:08,780 --> 01:25:12,500 Otherwise, recall how we co-mingled SQL and Python before. 1742 01:25:12,500 --> 01:25:15,800 We're using CS50's SQL library, but that just 1743 01:25:15,800 --> 01:25:19,130 makes it a little easier to execute SQL queries and we're executing this. 1744 01:25:19,130 --> 01:25:22,760 Insert into registrants name comma sport. 1745 01:25:22,760 --> 01:25:27,390 What two values, the name and the sport, that came from that HTML form. 1746 01:25:27,390 --> 01:25:30,260 And then lastly, and this is a new function that we're calling out 1747 01:25:30,260 --> 01:25:33,020 explicitly now, Flask also gives you access 1748 01:25:33,020 --> 01:25:39,645 to a redirect function, which is how safetyschool.org, Harvardsucks.org, 1749 01:25:39,645 --> 01:25:42,020 and all these other sites we played around with last week 1750 01:25:42,020 --> 01:25:45,500 we're all implemented redirecting the user from one place to another. 1751 01:25:45,500 --> 01:25:48,980 This Flask function redirect comes from my just 1752 01:25:48,980 --> 01:25:52,460 having imported it at the very top of this file. 1753 01:25:52,460 --> 01:25:57,380 It handles the HTTP 301 or 302 or 307 code, whatever the appropriate one is. 1754 01:25:57,380 --> 01:25:59,450 It does that for me. 1755 01:25:59,450 --> 01:26:04,520 All right, so that's it for registering via this route. 1756 01:26:04,520 --> 01:26:08,150 Let's look at what the registrant's route is. 1757 01:26:08,150 --> 01:26:11,270 Here, we have a new route for /registrants. 1758 01:26:11,270 --> 01:26:14,280 And instead of just iterating over a dictionary like before, 1759 01:26:14,280 --> 01:26:18,710 we're getting back, let's see, db.execute of select star 1760 01:26:18,710 --> 01:26:19,550 from registrants. 1761 01:26:19,550 --> 01:26:22,640 So that's literally the programmatic version of what I just did manually. 1762 01:26:22,640 --> 01:26:24,650 That gives me back a list of dictionaries, 1763 01:26:24,650 --> 01:26:27,890 each of which represents one row in the table. 1764 01:26:27,890 --> 01:26:31,070 Then, I'm going to render register and start HTML, 1765 01:26:31,070 --> 01:26:34,520 passing in literally that list of dictionaries 1766 01:26:34,520 --> 01:26:37,800 just like using CS50's library in the past. 1767 01:26:37,800 --> 01:26:40,760 So let's go and look at these-- that form. 1768 01:26:40,760 --> 01:26:46,610 If I go into templates and open up registrants.html, 1769 01:26:46,610 --> 01:26:49,610 oh, OK, it's just a table like before. 1770 01:26:49,610 --> 01:26:52,910 And actually, let me change this syntactically for consistency. 1771 01:26:52,910 --> 01:26:58,550 We have a Jinja for loop that iterates over each registrant 1772 01:26:58,550 --> 01:27:01,910 and for each of them, outputs a table row. 1773 01:27:01,910 --> 01:27:03,140 Oh, but this is interesting. 1774 01:27:03,140 --> 01:27:06,860 Instead of just having two columns with the person's name and sport, 1775 01:27:06,860 --> 01:27:09,892 notice that I'm also outputting a full-fledged form. 1776 01:27:09,892 --> 01:27:11,600 All right, this is starting to get juicy. 1777 01:27:11,600 --> 01:27:14,900 So let's actually go back to my terminal window, 1778 01:27:14,900 --> 01:27:18,770 run Flask, and actually see what this example looks like now. 1779 01:27:18,770 --> 01:27:20,550 Let me reload the page. 1780 01:27:20,550 --> 01:27:21,050 All right. 1781 01:27:21,050 --> 01:27:22,830 In the home page, it looks exactly the same. 1782 01:27:22,830 --> 01:27:24,413 But let me now register for something. 1783 01:27:24,413 --> 01:27:27,030 David for ultimate frisbee, register. 1784 01:27:27,030 --> 01:27:27,680 Oh, damn it. 1785 01:27:27,680 --> 01:27:30,195 1786 01:27:30,195 --> 01:27:31,070 Let's try this again. 1787 01:27:31,070 --> 01:27:34,800 David registering for ultimate frisbee, register. 1788 01:27:34,800 --> 01:27:35,300 OK. 1789 01:27:35,300 --> 01:27:37,370 So good thing I have deregister. 1790 01:27:37,370 --> 01:27:39,170 So this is what it should now look like. 1791 01:27:39,170 --> 01:27:44,358 I have a page at the route called /registrants that has a table with two 1792 01:27:44,358 --> 01:27:46,400 columns, name and sport, David, ultimate frisbee. 1793 01:27:46,400 --> 01:27:47,760 But oh, wait, a third column. 1794 01:27:47,760 --> 01:27:48,260 Why? 1795 01:27:48,260 --> 01:27:52,700 Because if I view the page source, notice that it's not the prettiest UI. 1796 01:27:52,700 --> 01:27:56,810 For every row in this table, I'm also going to be outputting a form just 1797 01:27:56,810 --> 01:27:58,770 to deregister that user. 1798 01:27:58,770 --> 01:28:02,330 But before we see how that works, let me go ahead and register Carter, 1799 01:28:02,330 --> 01:28:02,920 for instance. 1800 01:28:02,920 --> 01:28:04,520 So Carter will give you basketball. 1801 01:28:04,520 --> 01:28:05,780 Again, register. 1802 01:28:05,780 --> 01:28:07,200 The table grows. 1803 01:28:07,200 --> 01:28:10,310 Now, let me go back and let's register Emma for soccer. 1804 01:28:10,310 --> 01:28:12,290 And the table should grow. 1805 01:28:12,290 --> 01:28:17,720 Before we look at that HTML, let's go back to my terminal window. 1806 01:28:17,720 --> 01:28:21,980 Let's go into SQLite FroshIMS. 1807 01:28:21,980 --> 01:28:32,390 Let me go into FroshIMS, and let me open up with SQLite 3 FroshIMS.db. 1808 01:28:32,390 --> 01:28:34,700 And now do select star from registrants. 1809 01:28:34,700 --> 01:28:38,630 And whereas, previously, when I executed this there were zero people, now 1810 01:28:38,630 --> 01:28:39,710 there's indeed three. 1811 01:28:39,710 --> 01:28:43,350 So now we see exactly what's going on underneath the hood. 1812 01:28:43,350 --> 01:28:46,700 So let's look at this form now-- this page now. 1813 01:28:46,700 --> 01:28:51,380 If I want to unregister, deregister one of these people specifically, 1814 01:28:51,380 --> 01:28:53,180 how do we do this? 1815 01:28:53,180 --> 01:28:55,280 Clicking one of those buttons will indeed 1816 01:28:55,280 --> 01:28:58,260 delete the row from the database. 1817 01:28:58,260 --> 01:29:03,650 But how do we go about linking a web page with Python code with a database? 1818 01:29:03,650 --> 01:29:05,690 This is the last piece of the puzzle. 1819 01:29:05,690 --> 01:29:09,470 Up until now, everything's been with forms and also with URLs. 1820 01:29:09,470 --> 01:29:11,600 But what if the user is not typing anything in, 1821 01:29:11,600 --> 01:29:13,790 they're just clicking a button? 1822 01:29:13,790 --> 01:29:15,660 Well, watch this. 1823 01:29:15,660 --> 01:29:18,135 Let me go ahead and sniff the traffic, which 1824 01:29:18,135 --> 01:29:19,760 you could be in the habit of doing now. 1825 01:29:19,760 --> 01:29:23,570 Any time you're curious how a website works, let me go to the Network tab. 1826 01:29:23,570 --> 01:29:28,010 And Carter, shall we deregister you from basketball? 1827 01:29:28,010 --> 01:29:31,550 Let's deregister Carter and let's see what just happened. 1828 01:29:31,550 --> 01:29:35,630 If I look at the deregister request, notice that it's a POST. 1829 01:29:35,630 --> 01:29:38,450 The status code that eventually came back as 302, 1830 01:29:38,450 --> 01:29:40,880 but let's look at the request itself. 1831 01:29:40,880 --> 01:29:43,610 All the headers there we'll ignore. 1832 01:29:43,610 --> 01:29:48,110 The only thing that button submits, cleverly, 1833 01:29:48,110 --> 01:29:52,130 is an ID parameter, a key equaling two. 1834 01:29:52,130 --> 01:29:56,390 What does two presumably represent or map to? 1835 01:29:56,390 --> 01:29:59,300 Where did this two come from? 1836 01:29:59,300 --> 01:30:03,020 It doesn't say Carter, it doesn't say basketball? 1837 01:30:03,020 --> 01:30:03,568 What is it? 1838 01:30:03,568 --> 01:30:05,360 AUDIENCE: The second person who registered. 1839 01:30:05,360 --> 01:30:06,770 DAVID: The second person that registered. 1840 01:30:06,770 --> 01:30:09,920 So those primary keys that we started talking about a couple of weeks 1841 01:30:09,920 --> 01:30:13,220 ago, why it's useful to be able to uniquely identify a row in a table, 1842 01:30:13,220 --> 01:30:15,470 here is just one of the reasons why. 1843 01:30:15,470 --> 01:30:20,510 If it suffices for me just to send the ID number of the person 1844 01:30:20,510 --> 01:30:25,380 I want to delete from the database, because I can then have code like this. 1845 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, 1846 01:30:31,560 --> 01:30:33,410 notice that I got this. 1847 01:30:33,410 --> 01:30:37,880 I first go into the form, and I get the ID that was submitted, hopefully. 1848 01:30:37,880 --> 01:30:42,110 If there was, in fact, an ID, and the form wasn't somehow empty, 1849 01:30:42,110 --> 01:30:43,880 I execute this line of code. 1850 01:30:43,880 --> 01:30:46,880 Delete from registrants where ID equals question mark, 1851 01:30:46,880 --> 01:30:51,440 and then I plug-in that number, deleting Carter and only Carter. 1852 01:30:51,440 --> 01:30:54,710 And I'm not using his name, because what if we have two people named Carter, 1853 01:30:54,710 --> 01:30:56,240 two people named Emma or David? 1854 01:30:56,240 --> 01:30:57,840 You don't want to delete both of them. 1855 01:30:57,840 --> 01:31:01,970 That's why these unique IDs are so, so important. 1856 01:31:01,970 --> 01:31:03,860 And here's another reason why. 1857 01:31:03,860 --> 01:31:07,250 You don't want to store some things in URLs. 1858 01:31:07,250 --> 01:31:11,990 Suppose we went to this URL, deregister?ID=3. 1859 01:31:11,990 --> 01:31:15,500 1860 01:31:15,500 --> 01:31:20,370 Suppose I, maliciously, emailed this URL to Emma. 1861 01:31:20,370 --> 01:31:22,370 It doesn't matter so much what the beginning is, 1862 01:31:22,370 --> 01:31:28,490 but supposed I emailed her this URL, /deregister?ID=3, and I said, hey, 1863 01:31:28,490 --> 01:31:29,930 Emma, click this. 1864 01:31:29,930 --> 01:31:32,570 And it uses GET instead of POST. 1865 01:31:32,570 --> 01:31:34,565 What did I just trick her into doing? 1866 01:31:34,565 --> 01:31:37,608 1867 01:31:37,608 --> 01:31:39,400 What's going to happen if Emma clicks this? 1868 01:31:39,400 --> 01:31:39,900 Yeah? 1869 01:31:39,900 --> 01:31:41,260 AUDIENCE: Deregistering? 1870 01:31:41,260 --> 01:31:43,760 DAVID: You would trick her into deregistering herself. 1871 01:31:43,760 --> 01:31:44,260 Why? 1872 01:31:44,260 --> 01:31:47,050 Because if she's logged into this FroshIMS website, 1873 01:31:47,050 --> 01:31:51,100 and the URL contains her ID just because I'm being malicious, 1874 01:31:51,100 --> 01:31:54,160 and she clicked on it and the website is using GET, 1875 01:31:54,160 --> 01:31:56,950 unfortunately, GET URLs are, again, stateful. 1876 01:31:56,950 --> 01:31:59,020 They have state information in the URLs. 1877 01:31:59,020 --> 01:32:01,770 And in this case, it's enough to delete the user and boom, 1878 01:32:01,770 --> 01:32:04,382 she would have accidentally deregistered herself. 1879 01:32:04,382 --> 01:32:05,590 And this is pretty innocuous. 1880 01:32:05,590 --> 01:32:08,110 Suppose that this was her bank account trying 1881 01:32:08,110 --> 01:32:09,910 to make a withdrawal or a deposit. 1882 01:32:09,910 --> 01:32:13,060 Suppose that this were some other website, a Facebook URL, 1883 01:32:13,060 --> 01:32:15,577 trying to trick her into posting something automatically. 1884 01:32:15,577 --> 01:32:17,410 Here, too, is another consideration when you 1885 01:32:17,410 --> 01:32:21,640 should use POST versus GET, because GET requests can 1886 01:32:21,640 --> 01:32:26,570 be plugged into emails sent via Slack messages, text messages, or the like. 1887 01:32:26,570 --> 01:32:28,840 And unless there's a prompt saying, are you sure 1888 01:32:28,840 --> 01:32:31,990 you want to deregister yourself, you might blindly 1889 01:32:31,990 --> 01:32:34,090 trick the user into being vulnerable to what's 1890 01:32:34,090 --> 01:32:36,460 called a cross-site request forgery. 1891 01:32:36,460 --> 01:32:39,640 A fancy way of saying you trick them into clicking a link 1892 01:32:39,640 --> 01:32:43,480 that they shouldn't have, because the website was using GET alone. 1893 01:32:43,480 --> 01:32:47,350 All right, any question, then, on these building blocks? 1894 01:32:47,350 --> 01:32:49,168 Yeah. 1895 01:32:49,168 --> 01:32:54,514 AUDIENCE: What do the first thing in the instance of the SQL 1896 01:32:54,514 --> 01:32:56,470 [INAUDIBLE] where they have three slashes? 1897 01:32:56,470 --> 01:32:57,740 What does that mean? 1898 01:32:57,740 --> 01:32:59,695 DAVID: When three columns, you mean? 1899 01:32:59,695 --> 01:33:04,550 AUDIENCE: No, three forward slashes. 1900 01:33:04,550 --> 01:33:07,120 DAVID: The three forward slashes. 1901 01:33:07,120 --> 01:33:08,645 I'm not sure I follow. 1902 01:33:08,645 --> 01:33:11,615 AUDIENCE: Yeah, so I think it's in [INAUDIBLE].. 1903 01:33:11,615 --> 01:33:15,090 1904 01:33:15,090 --> 01:33:18,270 DAVID: Sorry, it's in where? 1905 01:33:18,270 --> 01:33:19,473 Which file? 1906 01:33:19,473 --> 01:33:24,303 AUDIENCE: It's in [INAUDIBLE] scroll up. 1907 01:33:24,303 --> 01:33:25,269 [INAUDIBLE] 1908 01:33:25,269 --> 01:33:28,543 1909 01:33:28,543 --> 01:33:29,960 DAVID: Sorry, the other direction? 1910 01:33:29,960 --> 01:33:30,780 AUDIENCE: Yeah. 1911 01:33:30,780 --> 01:33:32,216 DAVID: OK. 1912 01:33:32,216 --> 01:33:35,084 AUDIENCE: [INAUDIBLE]. 1913 01:33:35,084 --> 01:33:38,645 So please scroll a little bit more. 1914 01:33:38,645 --> 01:33:39,770 DAVID: Keep scrolling more? 1915 01:33:39,770 --> 01:33:40,790 Oh, this thing. 1916 01:33:40,790 --> 01:33:42,320 OK, sorry. 1917 01:33:42,320 --> 01:33:50,000 This is a URI, it's typical syntax that's referring to the SQLite 1918 01:33:50,000 --> 01:33:54,590 protocol, so to speak, which means use SQLite to talk to a file locally. 1919 01:33:54,590 --> 01:33:57,560 :// is just like you and I see in URLs. 1920 01:33:57,560 --> 01:34:00,200 The third slash, essentially, means current folder. 1921 01:34:00,200 --> 01:34:00,860 That's all. 1922 01:34:00,860 --> 01:34:03,700 So it's a weird curiosity, but it's typical 1923 01:34:03,700 --> 01:34:06,200 whenever you're referring to a local file and not one that's 1924 01:34:06,200 --> 01:34:07,490 elsewhere on the internet. 1925 01:34:07,490 --> 01:34:10,550 That's a bit of an oversimplification, but that's indeed a convention. 1926 01:34:10,550 --> 01:34:12,710 Sorry for not clicking earlier. 1927 01:34:12,710 --> 01:34:15,530 All right, let's do one other iteration of FroshIMS 1928 01:34:15,530 --> 01:34:18,860 here just to show what I was actually doing too, back in the day, 1929 01:34:18,860 --> 01:34:21,800 was not only storing these things in CSV files, as I recall. 1930 01:34:21,800 --> 01:34:24,285 I was also automatically generating an email 1931 01:34:24,285 --> 01:34:26,660 to the proctor in charge of the intramural sports program 1932 01:34:26,660 --> 01:34:29,750 so that they would have sort of a running history of people registering 1933 01:34:29,750 --> 01:34:31,890 and they could easily reply to them as well. 1934 01:34:31,890 --> 01:34:35,520 Let me go into FroshIMS version five, which I precreated here, 1935 01:34:35,520 --> 01:34:40,880 and let me go ahead and open up, say, app.py this time. 1936 01:34:40,880 --> 01:34:43,370 And this is some code that I wrote in advance. 1937 01:34:43,370 --> 01:34:47,160 And it looks a little scary at first glance, but I've done the following. 1938 01:34:47,160 --> 01:34:52,550 I have now added the Flask mail library to the picture 1939 01:34:52,550 --> 01:34:55,700 by adding Flask mail to requirements.txt and running a command 1940 01:34:55,700 --> 01:34:59,340 to automatically install email support for Flask as well. 1941 01:34:59,340 --> 01:35:02,090 And this is a little bit cryptic, but it's honestly mostly 1942 01:35:02,090 --> 01:35:03,860 copy paste from the documentation. 1943 01:35:03,860 --> 01:35:06,590 What I'm doing here is I'm configuring my Flask 1944 01:35:06,590 --> 01:35:09,960 application with a few configuration variables, if you will. 1945 01:35:09,960 --> 01:35:13,460 This is the syntax for that. app.config is a special dictionary that 1946 01:35:13,460 --> 01:35:17,840 comes with Flask that is automatically created when you create the app appear 1947 01:35:17,840 --> 01:35:21,200 on line nine, and I just had to fill in a whole bunch of configuration 1948 01:35:21,200 --> 01:35:23,720 values for the default sender address that I 1949 01:35:23,720 --> 01:35:27,710 want to send email as, the default password I want to use to send email, 1950 01:35:27,710 --> 01:35:30,890 the port number, the TCP port, that we talked about last week. 1951 01:35:30,890 --> 01:35:34,730 The mail server, I'm going to use Gmail's smtp.gmail.com server. 1952 01:35:34,730 --> 01:35:36,770 Use TLS, this means use encryption. 1953 01:35:36,770 --> 01:35:37,910 So I set that to true. 1954 01:35:37,910 --> 01:35:40,800 Mail username, this is going to grab it from my environment. 1955 01:35:40,800 --> 01:35:44,210 So for security purposes, I didn't want to hard code my own Gmail username 1956 01:35:44,210 --> 01:35:46,262 and password into the code. 1957 01:35:46,262 --> 01:35:49,220 So I'm actually storing those in what are called environment variables. 1958 01:35:49,220 --> 01:35:51,350 You'll see more of these in problem set nine, 1959 01:35:51,350 --> 01:35:53,360 and it's a very common convention on a server 1960 01:35:53,360 --> 01:35:59,010 in the real world to store sensitive information in the computer's memory 1961 01:35:59,010 --> 01:36:01,880 so that it can be accessed when your website is running, 1962 01:36:01,880 --> 01:36:03,440 but not in your source code. 1963 01:36:03,440 --> 01:36:06,470 It's way too easy if you put credentials, 1964 01:36:06,470 --> 01:36:09,770 sensitive stuff in your source code, to post it to GitHub 1965 01:36:09,770 --> 01:36:13,070 or to screenshot it accidentally, or for information to leak out. 1966 01:36:13,070 --> 01:36:18,200 So for today's purposes, know that the OS.environ dictionary refers to what 1967 01:36:18,200 --> 01:36:19,820 are called environment variables. 1968 01:36:19,820 --> 01:36:23,330 And this is like an out-of-band, a special way of defining key value 1969 01:36:23,330 --> 01:36:26,360 pairs in the computer's memory by running a certain command 1970 01:36:26,360 --> 01:36:28,730 but that never show up in your actual code. 1971 01:36:28,730 --> 01:36:31,370 Otherwise, there would be so many usernames and passwords 1972 01:36:31,370 --> 01:36:34,160 accidentally visible on the internet. 1973 01:36:34,160 --> 01:36:36,560 So I've installed this in advance. 1974 01:36:36,560 --> 01:36:38,390 Let me see if I can do this correctly. 1975 01:36:38,390 --> 01:36:41,820 Let me go over to another tab in just a moment. 1976 01:36:41,820 --> 01:36:45,680 And here, I have on my second screen here, John Harvards inbox. 1977 01:36:45,680 --> 01:36:48,560 It's currently empty, and I'm going to go ahead and register 1978 01:36:48,560 --> 01:36:50,780 for some sport as John Harvard here, hopefully. 1979 01:36:50,780 --> 01:36:55,070 So let me go ahead and run Flask run on this version five. 1980 01:36:55,070 --> 01:36:58,550 Let me go ahead and reload the main screen. 1981 01:36:58,550 --> 01:36:59,240 Not that one. 1982 01:36:59,240 --> 01:37:00,920 Let me reload the main screen here. 1983 01:37:00,920 --> 01:37:03,650 This time, clearly, I'm asking for name and email. 1984 01:37:03,650 --> 01:37:05,735 So name will be John Harvard. 1985 01:37:05,735 --> 01:37:08,010 jharvard@cs50.harvard.edu. 1986 01:37:08,010 --> 01:37:13,580 He'll register for, how about soccer. 1987 01:37:13,580 --> 01:37:15,140 Register. 1988 01:37:15,140 --> 01:37:19,850 And if I did this correctly, not only is John Harvard, on his screen, 1989 01:37:19,850 --> 01:37:28,940 seeing you are registered, but when he checks his email on this other screen, 1990 01:37:28,940 --> 01:37:35,240 crossing his fingers that this actually works as a demonstration, 1991 01:37:35,240 --> 01:37:37,325 and I promise it did right before class. 1992 01:37:37,325 --> 01:37:43,370 1993 01:37:43,370 --> 01:37:45,090 Horrifying. 1994 01:37:45,090 --> 01:37:47,480 I don't think there's a mistake this time. 1995 01:37:47,480 --> 01:37:50,640 1996 01:37:50,640 --> 01:37:54,060 Let me try something over here real quick, 1997 01:37:54,060 --> 01:37:57,900 but I don't think this is broken. 1998 01:37:57,900 --> 01:38:01,410 It wouldn't have said success if it were. 1999 01:38:01,410 --> 01:38:07,230 I just tried submitting again, so I just did another you are registered. 2000 01:38:07,230 --> 01:38:09,395 Oh, I'm really sad right now. 2001 01:38:09,395 --> 01:38:12,110 2002 01:38:12,110 --> 01:38:13,590 AUDIENCE: [INAUDIBLE] 2003 01:38:13,590 --> 01:38:14,547 DAVID: What's that? 2004 01:38:14,547 --> 01:38:15,920 AUDIENCE: Check spam. 2005 01:38:15,920 --> 01:38:19,760 DAVID: I could check spam, but then it's-- 2006 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. 2007 01:38:24,230 --> 01:38:27,110 Oh, maybe. 2008 01:38:27,110 --> 01:38:27,900 Oh! 2009 01:38:27,900 --> 01:38:29,990 [LAUGHTER AND APPLAUDING] 2010 01:38:29,990 --> 01:38:30,740 Thank you. 2011 01:38:30,740 --> 01:38:35,300 2012 01:38:35,300 --> 01:38:35,990 OK. 2013 01:38:35,990 --> 01:38:37,760 Wow, that was a risky click I worried. 2014 01:38:37,760 --> 01:38:41,488 All right, so you are registered is the email that I sent out, 2015 01:38:41,488 --> 01:38:43,530 and it doesn't have any actual information in it. 2016 01:38:43,530 --> 01:38:45,500 But back in the day it would have, because I 2017 01:38:45,500 --> 01:38:47,330 included the student's name and their dorm 2018 01:38:47,330 --> 01:38:49,872 and all of the other fields of information that we asked for. 2019 01:38:49,872 --> 01:38:52,550 So let's just take a quick look at how that code might work. 2020 01:38:52,550 --> 01:38:55,760 I did have to configure Gmail in a certain way to allow, 2021 01:38:55,760 --> 01:38:58,430 what they call, less secure apps using SMTP, 2022 01:38:58,430 --> 01:39:00,830 which is the protocol used for outbound email. 2023 01:39:00,830 --> 01:39:05,198 But besides setting these things, let's look at the register route down here. 2024 01:39:05,198 --> 01:39:06,740 It's actually pretty straightforward. 2025 01:39:06,740 --> 01:39:09,870 In my register route, I validated the submission just like before. 2026 01:39:09,870 --> 01:39:11,100 Nothing new there. 2027 01:39:11,100 --> 01:39:15,000 I then confirmed the registration down here, nothing new there. 2028 01:39:15,000 --> 01:39:17,720 All I did was use two new lines of code. 2029 01:39:17,720 --> 01:39:20,222 And it's this easy to automate the sending of emails. 2030 01:39:20,222 --> 01:39:21,930 I apparently have done it too many times, 2031 01:39:21,930 --> 01:39:23,840 which is why it ended up in spam. 2032 01:39:23,840 --> 01:39:25,820 I created a variable called message. 2033 01:39:25,820 --> 01:39:29,220 I used a message function that I must have imported higher up, 2034 01:39:29,220 --> 01:39:30,330 so we'll go back to that. 2035 01:39:30,330 --> 01:39:33,110 Here's, apparently, the subject line as the first argument. 2036 01:39:33,110 --> 01:39:37,430 And the second argument is the named parameter recipients, 2037 01:39:37,430 --> 01:39:40,920 which takes a list of emails that should get the confirmation email. 2038 01:39:40,920 --> 01:39:43,280 So in brackets, I just put the one user's email 2039 01:39:43,280 --> 01:39:46,320 and then mail.send that message. 2040 01:39:46,320 --> 01:39:51,260 So let's scroll back up to see what message and what mail actually is. 2041 01:39:51,260 --> 01:39:52,800 Mail, I think, we saw. 2042 01:39:52,800 --> 01:39:56,030 Yep, mail is this, which I have as a variable 2043 01:39:56,030 --> 01:39:58,460 because I followed the documentation for this library. 2044 01:39:58,460 --> 01:40:04,048 You simply configure your current app with Mail support, capital M here. 2045 01:40:04,048 --> 01:40:05,840 And if you look up here now, on line seven, 2046 01:40:05,840 --> 01:40:08,990 here's the new library from Flask mail I imported. 2047 01:40:08,990 --> 01:40:13,190 Capital Mail, capital Message, so that I had the ability to create a message 2048 01:40:13,190 --> 01:40:14,690 and send a mail. 2049 01:40:14,690 --> 01:40:17,540 So such a simple thing whether you want to confirm things for users, 2050 01:40:17,540 --> 01:40:19,070 you want to do password resets. 2051 01:40:19,070 --> 01:40:22,940 It can be this easy to actually generate emails 2052 01:40:22,940 --> 01:40:25,992 provided you have the requisite access and software installed. 2053 01:40:25,992 --> 01:40:28,200 And just to make clear that I did add something here, 2054 01:40:28,200 --> 01:40:31,340 let me open up my requirements.txt file, and indeed, I 2055 01:40:31,340 --> 01:40:36,050 have both Flask and Flask-mail ready to go. 2056 01:40:36,050 --> 01:40:39,050 But I ran the command in advance to actually do that. 2057 01:40:39,050 --> 01:40:44,320 All right, any questions, then, on these examples here? 2058 01:40:44,320 --> 01:40:45,180 No? 2059 01:40:45,180 --> 01:40:45,680 All right. 2060 01:40:45,680 --> 01:40:51,810 So what other pieces might actually remain for us let me flip over here. 2061 01:40:51,810 --> 01:40:54,913 It turns out that a key component of most any web 2062 01:40:54,913 --> 01:40:57,080 application nowadays that we haven't touched on yet, 2063 01:40:57,080 --> 01:41:00,635 but it'll be one of our final flourishes today, is the notion of a session. 2064 01:41:00,635 --> 01:41:03,440 And a session is actually a feature that derives 2065 01:41:03,440 --> 01:41:06,080 from all of the basics we talked about today and last week, 2066 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. 2067 01:41:09,590 --> 01:41:13,160 When you go to amazon.com and you start adding things to your shopping cart, 2068 01:41:13,160 --> 01:41:15,843 they follow you from page to page to page. 2069 01:41:15,843 --> 01:41:18,260 Heck if you close your browser, come back to the next day, 2070 01:41:18,260 --> 01:41:21,907 they're typically still your shopping cart, which is great for Amazon 2071 01:41:21,907 --> 01:41:23,240 because they want your business. 2072 01:41:23,240 --> 01:41:25,950 They don't want you to have to start from scratch the next day. 2073 01:41:25,950 --> 01:41:29,720 Similarly, when you log into any website these days, 2074 01:41:29,720 --> 01:41:33,530 even if it's not an e-commerce thing but it has usernames and passwords, 2075 01:41:33,530 --> 01:41:35,240 you and I are not in the habit of logging 2076 01:41:35,240 --> 01:41:37,430 into every darn page we visit on a website. 2077 01:41:37,430 --> 01:41:41,420 Typically, you log in once, and then for the next hour, day, week, year, 2078 01:41:41,420 --> 01:41:43,400 you stay logged into that website. 2079 01:41:43,400 --> 01:41:47,540 So somehow, the website is remembering that you have logged in. 2080 01:41:47,540 --> 01:41:50,128 And that is being implemented by way of this thing called 2081 01:41:50,128 --> 01:41:51,920 a session, and perhaps a more familiar term 2082 01:41:51,920 --> 01:41:55,027 that you might know as, and worry about, called cookies. 2083 01:41:55,027 --> 01:41:57,360 Let's go ahead and take one more five minute break here. 2084 01:41:57,360 --> 01:41:59,652 And when we come back, we'll look at cookies, sessions, 2085 01:41:59,652 --> 01:42:01,910 and these final features. 2086 01:42:01,910 --> 01:42:03,190 All right. 2087 01:42:03,190 --> 01:42:06,200 So the promise now is that we're going to implement 2088 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 2089 01:42:09,620 --> 01:42:12,500 them logged in and even implement things like a shopping cart. 2090 01:42:12,500 --> 01:42:16,880 And the overarching goal here is to build an application that 2091 01:42:16,880 --> 01:42:18,380 is, quote unquote, "stateful." 2092 01:42:18,380 --> 01:42:21,560 Again, state refers to information, and something that's stateful 2093 01:42:21,560 --> 01:42:23,240 remembers information. 2094 01:42:23,240 --> 01:42:27,950 And in this context, the curiosity is that HTTP is technically 2095 01:42:27,950 --> 01:42:29,510 a stateless protocol. 2096 01:42:29,510 --> 01:42:33,620 Once you visit a URL, http://something, hit Enter, 2097 01:42:33,620 --> 01:42:36,710 web page is downloaded to your browser, like that's it. 2098 01:42:36,710 --> 01:42:39,710 You can unplug from the internet, you can turn off your Wi-Fi, 2099 01:42:39,710 --> 01:42:42,230 but you still have the web page locally. 2100 01:42:42,230 --> 01:42:45,410 And yet we somehow want to make sure that the next time you 2101 01:42:45,410 --> 01:42:48,060 click on a link on that website, it doesn't forget who you are. 2102 01:42:48,060 --> 01:42:50,060 Or the next thing you add to your shopping cart, 2103 01:42:50,060 --> 01:42:51,870 it doesn't forget what was already there. 2104 01:42:51,870 --> 01:42:55,100 So we somehow want to make HTTP stateful, 2105 01:42:55,100 --> 01:42:58,430 and we can actually do this using the building blocks we've seen thus far. 2106 01:42:58,430 --> 01:43:03,140 So concretely, here's a form you might see occasionally, but pretty rarely, 2107 01:43:03,140 --> 01:43:04,730 when you log into Gmail. 2108 01:43:04,730 --> 01:43:08,660 And I say rarely because most of you don't log into Gmail frequently, 2109 01:43:08,660 --> 01:43:11,690 you just stay logged in, pretty much endlessly, in your browser. 2110 01:43:11,690 --> 01:43:14,390 And that's because Google has made the conscious choice 2111 01:43:14,390 --> 01:43:18,100 to give you a very long session time, maybe a day, a week, 2112 01:43:18,100 --> 01:43:19,850 a month, a year, because they don't really 2113 01:43:19,850 --> 01:43:23,510 want to add friction to using their tool and making you log in every darn day. 2114 01:43:23,510 --> 01:43:26,430 By contrast, there's other applications on campus, 2115 01:43:26,430 --> 01:43:29,270 including some of the CS50 zone, that makes you log in every time. 2116 01:43:29,270 --> 01:43:31,340 Because we want to make sure that it's indeed you 2117 01:43:31,340 --> 01:43:35,280 accessing the site, and not a roommate or friend or someone maliciously. 2118 01:43:35,280 --> 01:43:39,060 So once you do fill out this form, how does Google subsequently 2119 01:43:39,060 --> 01:43:42,390 know that you are you, and when you reload the page even 2120 01:43:42,390 --> 01:43:45,150 or open a second tab for your same Gmail account, 2121 01:43:45,150 --> 01:43:48,907 how do they know that you're still David or Carter or Emma or someone else? 2122 01:43:48,907 --> 01:43:51,240 Well, let's look underneath the hood of what's going on. 2123 01:43:51,240 --> 01:43:54,690 When you log into Gmail, essentially, you initially 2124 01:43:54,690 --> 01:43:57,420 see a form like this using a GET request. 2125 01:43:57,420 --> 01:44:00,090 And the website responds like we saw last week 2126 01:44:00,090 --> 01:44:01,510 with some kind of HTTP response. 2127 01:44:01,510 --> 01:44:03,630 Hopefully 200 OK with the form. 2128 01:44:03,630 --> 01:44:08,460 Meanwhile, the website might also respond with an HTTP header 2129 01:44:08,460 --> 01:44:11,670 that, last week we didn't care about, this week, we now do. 2130 01:44:11,670 --> 01:44:15,240 Whenever you visit a website, it is very commonly the case 2131 01:44:15,240 --> 01:44:18,310 that the website is putting a cookie on your computer. 2132 01:44:18,310 --> 01:44:20,910 And you may generally know that cookies can be bad 2133 01:44:20,910 --> 01:44:25,650 and they track you in some way, and that's both a blessing and a curse. 2134 01:44:25,650 --> 01:44:31,050 Without cookies, you could not implement things like shopping carts and log-ins 2135 01:44:31,050 --> 01:44:32,580 as we know them today. 2136 01:44:32,580 --> 01:44:34,980 Unfortunately, they can also be used for ill purposes 2137 01:44:34,980 --> 01:44:38,280 like tracking you on every website and serving you ads more effectively and so 2138 01:44:38,280 --> 01:44:38,780 forth. 2139 01:44:38,780 --> 01:44:40,710 So with good comes some bad. 2140 01:44:40,710 --> 01:44:43,890 But the basic primitive for us, the computer scientist, 2141 01:44:43,890 --> 01:44:46,860 boils down to just HTTP headers. 2142 01:44:46,860 --> 01:44:51,750 A cookie is typically a big number, a big, seemingly random value, 2143 01:44:51,750 --> 01:44:55,980 that a server tells your browser to store in memory, or even 2144 01:44:55,980 --> 01:44:57,640 longer term, store on disk. 2145 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. 2146 01:45:01,180 --> 01:45:05,250 And the promise that HTTP makes is that if a server sets 2147 01:45:05,250 --> 01:45:08,220 a cookie on your computer, you will represent 2148 01:45:08,220 --> 01:45:12,220 that same cookie or that same value on every subsequent request. 2149 01:45:12,220 --> 01:45:14,100 So when you visit the website like Gmail, 2150 01:45:14,100 --> 01:45:17,850 they plop a cookie on your computer like this with some session 2151 01:45:17,850 --> 01:45:19,980 equals value, some long random value. 2152 01:45:19,980 --> 01:45:22,310 One, two, three, A, B, C, something like that. 2153 01:45:22,310 --> 01:45:26,730 And when you then visit another page on gmail.com or any other website, 2154 01:45:26,730 --> 01:45:31,590 you send the opposite header, not set cookie, but just cookie colon, 2155 01:45:31,590 --> 01:45:33,610 and you send the exact same value. 2156 01:45:33,610 --> 01:45:36,017 It's similar to going to a club or an amusement park 2157 01:45:36,017 --> 01:45:38,100 where you pay once, you go through the gates once, 2158 01:45:38,100 --> 01:45:40,020 you get checked by security once, and then 2159 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. 2160 01:45:44,910 --> 01:45:47,743 And then for you, efficiency-wise, if you come back later in the day 2161 01:45:47,743 --> 01:45:50,077 or later in the evening, you can just present your hand. 2162 01:45:50,077 --> 01:45:51,690 You've been stamped, presumably. 2163 01:45:51,690 --> 01:45:54,360 They've already-- you've already paid, you've 2164 01:45:54,360 --> 01:45:55,900 already been searched or whatnot. 2165 01:45:55,900 --> 01:45:57,870 And so it's this sort of fast track ticket 2166 01:45:57,870 --> 01:45:59,700 back into the club, back into the park. 2167 01:45:59,700 --> 01:46:03,480 That's essentially what a cookie is doing for you, whereby 2168 01:46:03,480 --> 01:46:06,060 it's a way of reminding the website we've already done this, 2169 01:46:06,060 --> 01:46:08,290 you already asked me for my username and password. 2170 01:46:08,290 --> 01:46:10,770 This is my path to now come and go. 2171 01:46:10,770 --> 01:46:14,760 Now, unlike this hand stamp, which can be easily copied or transferred 2172 01:46:14,760 --> 01:46:17,370 or duplicated or kept on over multiple days, 2173 01:46:17,370 --> 01:46:22,180 these cookies are really big, seemingly random values, letters and numbers. 2174 01:46:22,180 --> 01:46:24,900 So statistically, there's no way someone else 2175 01:46:24,900 --> 01:46:28,110 is just going to guess your cookie value and pretend to be you It's just 2176 01:46:28,110 --> 01:46:31,110 very low probability, statistically. 2177 01:46:31,110 --> 01:46:34,950 But this is all it boils down to is this agreement between browser and server 2178 01:46:34,950 --> 01:46:39,010 to send these values back and forth in this way. 2179 01:46:39,010 --> 01:46:41,370 So when we actually translate this, now, to code, 2180 01:46:41,370 --> 01:46:43,470 let's do something like a simple login app. 2181 01:46:43,470 --> 01:46:46,590 Let me go into a folder I made in advance today called login. 2182 01:46:46,590 --> 01:46:51,160 And let me code up app.py and let's take a look in here. 2183 01:46:51,160 --> 01:46:52,710 So what's going on? 2184 01:46:52,710 --> 01:46:54,150 A couple of new things up top. 2185 01:46:54,150 --> 01:46:59,040 If I want to have the ability to stamp my users hands, virtually, 2186 01:46:59,040 --> 01:47:02,850 and implement sessions, I'm going to have to import from Flask support 2187 01:47:02,850 --> 01:47:03,790 for sessions. 2188 01:47:03,790 --> 01:47:06,810 So this is another feature you get for free by using a framework 2189 01:47:06,810 --> 01:47:09,040 and not having to implement all this yourself. 2190 01:47:09,040 --> 01:47:11,190 And from the Flask session library, I'm going 2191 01:47:11,190 --> 01:47:13,740 to import Session, capital S. Why? 2192 01:47:13,740 --> 01:47:15,990 I'm going to configure the session as follows. 2193 01:47:15,990 --> 01:47:18,990 Long story short, there's different ways to implement sessions. 2194 01:47:18,990 --> 01:47:22,950 The server can store these cookies in a database, in a file, 2195 01:47:22,950 --> 01:47:25,390 in memory, in RAM, in other places too. 2196 01:47:25,390 --> 01:47:30,220 We are telling it to store these cookies on the server's hard drive. 2197 01:47:30,220 --> 01:47:33,690 So in fact, whenever you use sessions as you will for problem set nine, 2198 01:47:33,690 --> 01:47:37,530 you'll actually see a folder suddenly appear called Flask_session, 2199 01:47:37,530 --> 01:47:40,080 inside of which are the cookies, essentially, 2200 01:47:40,080 --> 01:47:42,450 for any users or friends or yourself who've been 2201 01:47:42,450 --> 01:47:45,040 visiting your particular application. 2202 01:47:45,040 --> 01:47:46,800 So I'm setting it to use the file system, 2203 01:47:46,800 --> 01:47:49,008 and I don't want them to be permanent because I want, 2204 01:47:49,008 --> 01:47:51,720 when you close your browser, the session to go away. 2205 01:47:51,720 --> 01:47:54,270 They could be made to be permanent and last much longer. 2206 01:47:54,270 --> 01:47:56,460 Then I tell my app to support sessions. 2207 01:47:56,460 --> 01:47:58,140 And that's it for now. 2208 01:47:58,140 --> 01:48:01,810 Let's see what this application actually does before we dissect the code. 2209 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 2210 01:48:07,140 --> 01:48:11,440 and reload my preview URL. 2211 01:48:11,440 --> 01:48:13,740 Give it a second to kick back in. 2212 01:48:13,740 --> 01:48:15,600 Let me go ahead and open my URL. 2213 01:48:15,600 --> 01:48:17,550 Come on. 2214 01:48:17,550 --> 01:48:20,557 Oops, let me go ahead. 2215 01:48:20,557 --> 01:48:21,390 Too long of a break. 2216 01:48:21,390 --> 01:48:22,030 There we go. 2217 01:48:22,030 --> 01:48:24,465 So this website simply has a login form. 2218 01:48:24,465 --> 01:48:26,340 There's no password, though I could certainly 2219 01:48:26,340 --> 01:48:28,140 add that and check for that too. 2220 01:48:28,140 --> 01:48:29,650 It just asks for your name. 2221 01:48:29,650 --> 01:48:32,310 So I'm going to log in as myself, David, and click Login. 2222 01:48:32,310 --> 01:48:35,130 And now notice I'm currently at the /login route. 2223 01:48:35,130 --> 01:48:36,120 But notice this. 2224 01:48:36,120 --> 01:48:38,445 If I try to go to the default route, just, 2225 01:48:38,445 --> 01:48:41,070 slash, which is where most websites live by default, 2226 01:48:41,070 --> 01:48:44,010 notice that I magically get redirected to log in. 2227 01:48:44,010 --> 01:48:47,160 So somehow, my code knows, hey, if you're not logged in, you're going 2228 01:48:47,160 --> 01:48:48,930 to /login instead. 2229 01:48:48,930 --> 01:48:51,540 Let me type in my name, David, and click Login. 2230 01:48:51,540 --> 01:48:54,345 And now notice I am back at slash. 2231 01:48:54,345 --> 01:48:57,570 Chrome is sort of annoyingly hiding it, but this is the same thing as just 2232 01:48:57,570 --> 01:48:58,720 a single slash. 2233 01:48:58,720 --> 01:49:01,470 And now notice it says you are logged in as David. 2234 01:49:01,470 --> 01:49:02,130 Log out. 2235 01:49:02,130 --> 01:49:05,400 What's cool is notice if I reload the page, it still knows that. 2236 01:49:05,400 --> 01:49:08,820 If I create a second tab and go to the same URL, it still knows that. 2237 01:49:08,820 --> 01:49:10,532 I could even-- 2238 01:49:10,532 --> 01:49:12,240 I could keep doing this in multiple tabs, 2239 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. 2240 01:49:15,960 --> 01:49:17,290 So how does that work? 2241 01:49:17,290 --> 01:49:22,380 Especially when I click Log Out, then I get forgotten altogether. 2242 01:49:22,380 --> 01:49:24,030 All right, so let's see how this works. 2243 01:49:24,030 --> 01:49:25,980 And it's some basic building blocks. 2244 01:49:25,980 --> 01:49:29,790 Under my /route, notice I have this. 2245 01:49:29,790 --> 01:49:34,660 If there is no name in the session, redirect the user to /login. 2246 01:49:34,660 --> 01:49:37,230 So these two lines together are what implement 2247 01:49:37,230 --> 01:49:41,850 that automatic redirection using HTTP 301 or 302 automatically. 2248 01:49:41,850 --> 01:49:43,930 It's handled for me with these two lines. 2249 01:49:43,930 --> 01:49:45,590 Otherwise, show index.html. 2250 01:49:45,590 --> 01:49:47,340 All right, let's go down that rabbit hole. 2251 01:49:47,340 --> 01:49:48,990 What's in index.html? 2252 01:49:48,990 --> 01:49:52,230 Well, if I look in my-- 2253 01:49:52,230 --> 01:49:58,890 let me look in my templates folder for my login demo and look 2254 01:49:58,890 --> 01:50:02,280 at templates/index.html. 2255 01:50:02,280 --> 01:50:03,870 All right, so what's going on here? 2256 01:50:03,870 --> 01:50:08,460 I extend layout.html, I have a block body, 2257 01:50:08,460 --> 01:50:10,200 and then I've got some other syntax. 2258 01:50:10,200 --> 01:50:12,742 So we haven't seen this yet, but it's more Jinja stuff, which 2259 01:50:12,742 --> 01:50:14,460 again, is almost identical to Python. 2260 01:50:14,460 --> 01:50:17,550 If there's a name in the session variable, 2261 01:50:17,550 --> 01:50:22,320 then literally say you are logged in as curly braces session bracket name. 2262 01:50:22,320 --> 01:50:26,880 And then notice this, I've got a simple HTML link to log out via /logout. 2263 01:50:26,880 --> 01:50:29,550 Else, if there is no name in the session, 2264 01:50:29,550 --> 01:50:33,360 then it apparently says you are not logged in and it leads me to an HTML 2265 01:50:33,360 --> 01:50:35,850 link to /login and then end diff. 2266 01:50:35,850 --> 01:50:38,460 So again, Jinja does not rely on indentation. 2267 01:50:38,460 --> 01:50:41,460 Recall the HTML and CSS don't really care about indentation, 2268 01:50:41,460 --> 01:50:42,690 only the human does. 2269 01:50:42,690 --> 01:50:46,260 But in code with Jinja, you need these end tags, 2270 01:50:46,260 --> 01:50:49,110 end block, end for, end if, to make super obvious 2271 01:50:49,110 --> 01:50:51,330 that you're done with that thought. 2272 01:50:51,330 --> 01:50:54,840 So session is just this magic variable that we now 2273 01:50:54,840 --> 01:50:59,130 have access to because we've included these two lines of code 2274 01:50:59,130 --> 01:51:04,140 and these that handle that whole process of stamping every user's hand 2275 01:51:04,140 --> 01:51:06,090 with a different, unique identifier. 2276 01:51:06,090 --> 01:51:08,760 If I made my code space public and I let all of you 2277 01:51:08,760 --> 01:51:12,270 visit the exact same URL, all of you would be logged out by default. 2278 01:51:12,270 --> 01:51:14,340 You could all type your own names individually, 2279 01:51:14,340 --> 01:51:18,330 all log in at the same URL using different sessions. 2280 01:51:18,330 --> 01:51:21,570 And in fact, I would then see, if I go into my terminal window 2281 01:51:21,570 --> 01:51:26,230 here and my login directory, notice the Flask session directory I mentioned. 2282 01:51:26,230 --> 01:51:30,120 And if I CD into that and type ls, notice that I had two tabs open, 2283 01:51:30,120 --> 01:51:32,790 or actually, I think I started the server twice. 2284 01:51:32,790 --> 01:51:34,180 I have two files in there. 2285 01:51:34,180 --> 01:51:36,930 I would ultimately have one file for every one of you. 2286 01:51:36,930 --> 01:51:38,940 And that's what's beautiful about sessions 2287 01:51:38,940 --> 01:51:43,530 is it creates the illusion of per user storage. 2288 01:51:43,530 --> 01:51:47,860 Inside of my session is my name, inside of your session, so to speak, 2289 01:51:47,860 --> 01:51:48,720 is your name. 2290 01:51:48,720 --> 01:51:52,750 And the same is going to apply to shopping carts, ultimately, as well. 2291 01:51:52,750 --> 01:51:54,450 Let's see how login works here. 2292 01:51:54,450 --> 01:51:58,860 My login route supports both GET and POST, so I could play around if I want. 2293 01:51:58,860 --> 01:52:03,430 And notice this, this login route is kind of interesting as follows. 2294 01:52:03,430 --> 01:52:07,633 If the user got to this route via POST, my inference 2295 01:52:07,633 --> 01:52:09,300 is that they must have submitted a form. 2296 01:52:09,300 --> 01:52:09,800 Why? 2297 01:52:09,800 --> 01:52:13,230 Because that's how I'm going to design the HTML form in a second. 2298 01:52:13,230 --> 01:52:15,870 And if they did submit the form via POST, 2299 01:52:15,870 --> 01:52:18,930 I'm going to store, in the session, at the name 2300 01:52:18,930 --> 01:52:21,330 key, whatever the human's name is. 2301 01:52:21,330 --> 01:52:23,505 And then, I'm going to redirect them back to slash. 2302 01:52:23,505 --> 01:52:26,590 Otherwise, I'm going to show them the login form. 2303 01:52:26,590 --> 01:52:27,870 So this is what's cool. 2304 01:52:27,870 --> 01:52:31,200 If I go to this login form, which lives at, 2305 01:52:31,200 --> 01:52:35,100 literally, slash login, by default, when you visit a URL like that, 2306 01:52:35,100 --> 01:52:36,960 you're visiting via GET. 2307 01:52:36,960 --> 01:52:39,120 And so that's why I see the form. 2308 01:52:39,120 --> 01:52:40,650 However, notice this. 2309 01:52:40,650 --> 01:52:44,550 The form, very cleverly, submits to itself, 2310 01:52:44,550 --> 01:52:49,230 like the one route/login submits to its same self, /login, 2311 01:52:49,230 --> 01:52:51,820 but it uses POST when you submit the form. 2312 01:52:51,820 --> 01:52:55,950 And this is a nice way of having one route but for two different types 2313 01:52:55,950 --> 01:52:58,380 of operations or views. 2314 01:52:58,380 --> 01:53:02,890 When I'm just there visiting /login via URL, it shows me the form. 2315 01:53:02,890 --> 01:53:07,980 But if I submit the form, then this logic, these three lines, kick in, 2316 01:53:07,980 --> 01:53:11,970 and this just avoids my having to have both an index route and a greet route, 2317 01:53:11,970 --> 01:53:12,540 for instance. 2318 01:53:12,540 --> 01:53:17,640 I can just have one route that handles both GET and POST. 2319 01:53:17,640 --> 01:53:18,450 How about logout? 2320 01:53:18,450 --> 01:53:19,420 What does this do? 2321 01:53:19,420 --> 01:53:21,030 Well, it's as simple as this. 2322 01:53:21,030 --> 01:53:23,460 Change whatever name is in the session to be 2323 01:53:23,460 --> 01:53:27,690 none, which is Python's version of null, essentially, and then redirect the user 2324 01:53:27,690 --> 01:53:28,320 back to slash. 2325 01:53:28,320 --> 01:53:34,390 Because now, in index.html, I will not notice a name there anymore. 2326 01:53:34,390 --> 01:53:35,710 This will be false. 2327 01:53:35,710 --> 01:53:39,160 And so I'll tell the user instead, you are not logged in. 2328 01:53:39,160 --> 01:53:40,613 So like it's-- 2329 01:53:40,613 --> 01:53:42,780 I want to say as simple as this is, though I realize 2330 01:53:42,780 --> 01:53:44,640 this is a bunch of steps involved. 2331 01:53:44,640 --> 01:53:47,820 This is the essence of every website on the internet that 2332 01:53:47,820 --> 01:53:49,190 has usernames and passwords. 2333 01:53:49,190 --> 01:53:52,440 And we skip the password name step for that, more on that in problem set nine, 2334 01:53:52,440 --> 01:53:56,850 but this is how every website out there remembers that you're logged in. 2335 01:53:56,850 --> 01:53:59,460 And how this works, ultimately, is that as soon 2336 01:53:59,460 --> 01:54:03,240 as you use in Python lines like this and lines like this, 2337 01:54:03,240 --> 01:54:07,380 Flask takes care of stamping the virtual hand of all of your users 2338 01:54:07,380 --> 01:54:12,630 and whenever Flask sees the same cookie coming back from a user, 2339 01:54:12,630 --> 01:54:15,870 it grabs the appropriate file from that folder, 2340 01:54:15,870 --> 01:54:18,280 loads it into the session global variable 2341 01:54:18,280 --> 01:54:23,250 so that your code is now unique to that user and their name. 2342 01:54:23,250 --> 01:54:26,160 Let's do one other example with sessions here 2343 01:54:26,160 --> 01:54:28,890 that'll show how we might use these, now, for shopping carts. 2344 01:54:28,890 --> 01:54:31,380 Let me go into the store example here. 2345 01:54:31,380 --> 01:54:33,210 Let me go ahead and run this thing first. 2346 01:54:33,210 --> 01:54:39,090 If I run store in my same tab and go back over here, 2347 01:54:39,090 --> 01:54:42,120 we'll see a very ugly e-commerce site that 2348 01:54:42,120 --> 01:54:44,010 just sells seven different books here. 2349 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. 2350 01:54:48,497 --> 01:54:50,580 All right, well where are these books coming from? 2351 01:54:50,580 --> 01:54:51,600 Well, let's poke around. 2352 01:54:51,600 --> 01:54:54,990 Let me go into my terminal window again. 2353 01:54:54,990 --> 01:54:58,230 Let me go into this example, which is called store, 2354 01:54:58,230 --> 01:55:03,150 and let me open up about index dot ht-- whoops. 2355 01:55:03,150 --> 01:55:10,620 Let's open up index, how about, books.html 2356 01:55:10,620 --> 01:55:12,910 is the default one, not index this time. 2357 01:55:12,910 --> 01:55:17,700 So if I look here, notice that that route that we just saw 2358 01:55:17,700 --> 01:55:22,410 uses a for loop in Jinja to iterate over a whole bunch of books, apparently, 2359 01:55:22,410 --> 01:55:25,770 and it outputs, in an H2 tag, the title of the book, 2360 01:55:25,770 --> 01:55:27,960 and then another one of these forms. 2361 01:55:27,960 --> 01:55:29,320 So that's interesting. 2362 01:55:29,320 --> 01:55:30,310 Let's go back one step. 2363 01:55:30,310 --> 01:55:34,230 Let's go ahead and open up app.py, because that must be-- excuse me, 2364 01:55:34,230 --> 01:55:35,670 what's ticking all of this off. 2365 01:55:35,670 --> 01:55:39,090 Notice that this file is importing session support. 2366 01:55:39,090 --> 01:55:42,090 It's configuring sessions down here, but it's also 2367 01:55:42,090 --> 01:55:44,530 connecting to a store.db file. 2368 01:55:44,530 --> 01:55:46,080 So it's adding some SQLite. 2369 01:55:46,080 --> 01:55:51,600 And notice this, in my /route, I'm selecting star from books, 2370 01:55:51,600 --> 01:55:53,880 which is going to give me a list of dictionaries, 2371 01:55:53,880 --> 01:55:55,890 each of which represents a row of books. 2372 01:55:55,890 --> 01:56:00,120 And I'm going to pass that list of books into my books.html template, which 2373 01:56:00,120 --> 01:56:03,840 is why this for loop works the way it does. 2374 01:56:03,840 --> 01:56:05,520 Let's look at this actual database. 2375 01:56:05,520 --> 01:56:10,710 Let me increase my terminal window and do SQLite of store.db.schema 2376 01:56:10,710 --> 01:56:11,760 will show me everything. 2377 01:56:11,760 --> 01:56:13,120 There's not much there. 2378 01:56:13,120 --> 01:56:17,770 It's a book-- it's a table called books with two columns, ID and title. 2379 01:56:17,770 --> 01:56:20,790 Let's do select star from books semicolon. 2380 01:56:20,790 --> 01:56:23,550 There are the seven books, each of which has a unique ID. 2381 01:56:23,550 --> 01:56:25,480 And you might see where this is going. 2382 01:56:25,480 --> 01:56:29,040 If I go to the UI and I look at each of these buttons 2383 01:56:29,040 --> 01:56:34,200 for add to cart, just like Amazon might have, notice that each of these buttons 2384 01:56:34,200 --> 01:56:35,550 is just a form. 2385 01:56:35,550 --> 01:56:37,503 And what's magical here, just like deregister, 2386 01:56:37,503 --> 01:56:39,420 even though I didn't highlight it at the time, 2387 01:56:39,420 --> 01:56:41,580 there's another type of input that allows 2388 01:56:41,580 --> 01:56:45,480 you to specify a value without the human being able easily to change it. 2389 01:56:45,480 --> 01:56:48,270 Instead of type equals text or type equals submit, 2390 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. 2391 01:56:53,230 --> 01:56:56,340 So that's how I'm saying that the idea of this book is one, 2392 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. 2393 01:57:00,550 --> 01:57:03,120 And each of these forms, then, will submit, apparently, 2394 01:57:03,120 --> 01:57:08,080 to /cart using POST and that would seem to be what adds things to cart. 2395 01:57:08,080 --> 01:57:08,830 So let's try this. 2396 01:57:08,830 --> 01:57:10,860 Let me click on one or two of these. 2397 01:57:10,860 --> 01:57:13,830 Let's add the first book, add to cart. 2398 01:57:13,830 --> 01:57:14,880 Here's my cart. 2399 01:57:14,880 --> 01:57:17,160 Notice my route change to /cart. 2400 01:57:17,160 --> 01:57:21,120 All right, let's go back and let's add the book number two. 2401 01:57:21,120 --> 01:57:22,110 There we have that one. 2402 01:57:22,110 --> 01:57:25,110 And let's skip ahead to the seventh book, Deathly Hallows, 2403 01:57:25,110 --> 01:57:27,850 and now we have all three books here. 2404 01:57:27,850 --> 01:57:31,530 So what does the cart route do at /cart? 2405 01:57:31,530 --> 01:57:32,280 Well, let's look. 2406 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, 2407 01:57:37,590 --> 01:57:39,960 there's a lot going on here, but let's see. 2408 01:57:39,960 --> 01:57:43,530 So the /cart route supports both GET or POST, 2409 01:57:43,530 --> 01:57:47,130 which is a nice way to consolidate things into one URL. 2410 01:57:47,130 --> 01:57:49,030 All right, this is interesting. 2411 01:57:49,030 --> 01:57:53,183 If there is not a, quote unquote, "cart" key in session, 2412 01:57:53,183 --> 01:57:54,850 we haven't technically seen this syntax. 2413 01:57:54,850 --> 01:57:59,010 But long story short, these lines here do ensure that the cart exists. 2414 01:57:59,010 --> 01:58:00,220 What do I mean by that? 2415 01:58:00,220 --> 01:58:05,400 It makes sure that there's a cart key in the session, global variable, 2416 01:58:05,400 --> 01:58:07,980 and it's by default going to be an empty list. 2417 01:58:07,980 --> 01:58:08,520 Why? 2418 01:58:08,520 --> 01:58:10,520 That just means you have an empty shopping cart. 2419 01:58:10,520 --> 01:58:17,850 But if the user visits this route via POST and the user did provide an ID, 2420 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, 2421 01:58:21,480 --> 01:58:24,930 they gave me a valid ID, then I'm going to use this syntax. 2422 01:58:24,930 --> 01:58:27,780 If session bracket cart is a list-- 2423 01:58:27,780 --> 01:58:30,085 recall from a couple of weeks ago that dot append just 2424 01:58:30,085 --> 01:58:31,210 adds something to the list. 2425 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. 2426 01:58:35,440 --> 01:58:40,850 Otherwise, if the user is at /cart via GET, implicitly, we just do this. 2427 01:58:40,850 --> 01:58:44,330 Select star from books where ID is in. 2428 01:58:44,330 --> 01:58:47,170 And this might be syntax you recall from Pset six. 2429 01:58:47,170 --> 01:58:49,780 It lets you look for multiple IDs all at once, 2430 01:58:49,780 --> 01:58:52,000 because if I have a list of session-- 2431 01:58:52,000 --> 01:58:56,470 list of IDs in my cart, I can get all of those books at once. 2432 01:58:56,470 --> 01:58:59,170 So long story short, what has happened here? 2433 01:58:59,170 --> 01:59:05,170 I am storing, in the cart, the books that I myself have added to my cart. 2434 01:59:05,170 --> 01:59:08,030 My browser is sending the same hand stamp again and again, 2435 01:59:08,030 --> 01:59:11,150 which is how this website knows that it's me adding these books to my cart 2436 01:59:11,150 --> 01:59:12,940 and not you or not Carter or not Emma. 2437 01:59:12,940 --> 01:59:16,240 Indeed, if all of us visited the same long URL and I made it public 2438 01:59:16,240 --> 01:59:19,030 and allowed that, then we would all have our own illusions 2439 01:59:19,030 --> 01:59:20,650 of our own separate carts. 2440 01:59:20,650 --> 01:59:23,140 And each of those carts, in practice, would just 2441 01:59:23,140 --> 01:59:26,920 be stored in this Flask session directory on the server 2442 01:59:26,920 --> 01:59:28,720 so that the server can keep track of each 2443 01:59:28,720 --> 01:59:31,690 of us using, again, these cookie values that are being 2444 01:59:31,690 --> 01:59:35,420 sent back and forth via these headers. 2445 01:59:35,420 --> 01:59:35,920 All right. 2446 01:59:35,920 --> 01:59:38,230 I know that's a lot, but again, it's just 2447 01:59:38,230 --> 01:59:41,710 the new Python way of just leveraging those HTTP 2448 01:59:41,710 --> 01:59:44,360 headers from last week in a clever way. 2449 01:59:44,360 --> 01:59:48,340 Any questions before we look at one final set of examples? 2450 01:59:48,340 --> 01:59:49,648 Yeah. 2451 01:59:49,648 --> 01:59:53,560 AUDIENCE: [INAUDIBLE] understand how a log in has to do with [INAUDIBLE]?? 2452 01:59:53,560 --> 01:59:56,983 How does it use [INAUDIBLE],, how do you change [INAUDIBLE]?? 2453 01:59:56,983 --> 02:00:01,384 Because in order to use a GET request dot [INAUDIBLE] equals, 2454 02:00:01,384 --> 02:00:05,320 there has to be an exchange in [INAUDIBLE].. 2455 02:00:05,320 --> 02:00:08,530 DAVID: So I think you're asking about using the GET 2456 02:00:08,530 --> 02:00:10,700 and POST in the same function. 2457 02:00:10,700 --> 02:00:15,220 So this is just a nice aesthetic, if you will. 2458 02:00:15,220 --> 02:00:18,670 If I had to have separate routes for GET and POST, I mean, 2459 02:00:18,670 --> 02:00:21,430 it literally might mean I need twice as many routes in my file. 2460 02:00:21,430 --> 02:00:23,680 And it just starts to get a little annoying. 2461 02:00:23,680 --> 02:00:26,420 And these days, too, in terms of user experience, 2462 02:00:26,420 --> 02:00:30,400 this is maybe only appeals to the geek in us, but having clean URLs 2463 02:00:30,400 --> 02:00:31,690 is actually a thing. 2464 02:00:31,690 --> 02:00:33,700 You don't want to have lots of words in the URL, 2465 02:00:33,700 --> 02:00:37,160 it's nice if the URLs are nice and succinct and canonical, if you will. 2466 02:00:37,160 --> 02:00:43,270 So it's nice if I can centralize all of my shopping cart functionality in /cart 2467 02:00:43,270 --> 02:00:47,890 only, and not in multiple routes, one for GET, one for POST. 2468 02:00:47,890 --> 02:00:52,250 It's a little nitpicky of me, but this is commonly done here. 2469 02:00:52,250 --> 02:00:56,860 So what this code here means is that this route, this function, 2470 02:00:56,860 --> 02:01:00,460 henceforth will support both GET requests and POST requests. 2471 02:01:00,460 --> 02:01:04,450 But then I need to distinguish between whether it's GET or POST coming in. 2472 02:01:04,450 --> 02:01:07,120 Because if it's a GET request, I want to show the cart. 2473 02:01:07,120 --> 02:01:09,580 If it's a POST request, I want to update the cart. 2474 02:01:09,580 --> 02:01:13,780 And the simplest way to do that is just to check this value here. 2475 02:01:13,780 --> 02:01:17,230 In the request variable that we imported from Flask up above, 2476 02:01:17,230 --> 02:01:20,080 you can check what is the current type of request. 2477 02:01:20,080 --> 02:01:23,170 Is it a GET, is it a POST, or is it something else altogether? 2478 02:01:23,170 --> 02:01:24,820 There are other verbs. 2479 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, 2480 02:01:29,890 --> 02:01:33,160 that the user clicked the Add to Cart button. 2481 02:01:33,160 --> 02:01:38,350 Otherwise, if it's not POST, it's implicitly going to be logically GET. 2482 02:01:38,350 --> 02:01:42,610 Then, I just want to show the user the contents of the cart 2483 02:01:42,610 --> 02:01:44,120 and I use these lines instead. 2484 02:01:44,120 --> 02:01:47,920 So it's just one way of avoiding having two routes for two different HTTP 2485 02:01:47,920 --> 02:01:48,430 verbs. 2486 02:01:48,430 --> 02:01:51,340 You can combine them so long as you have a check like this. 2487 02:01:51,340 --> 02:01:57,670 If I really wanted to be pedantic, I could do this elif request.method=get. 2488 02:01:57,670 --> 02:02:00,250 2489 02:02:00,250 --> 02:02:02,830 This would be more symmetric, but it's not really necessary, 2490 02:02:02,830 --> 02:02:05,750 because I know there's only two possibilities. 2491 02:02:05,750 --> 02:02:08,330 Hope that helps. 2492 02:02:08,330 --> 02:02:11,140 All right, let's do one final set of examples here 2493 02:02:11,140 --> 02:02:13,330 that's going to tie the last of these features 2494 02:02:13,330 --> 02:02:15,760 together to something that you probably see 2495 02:02:15,760 --> 02:02:18,612 quite often in real-world applications. 2496 02:02:18,612 --> 02:02:20,320 And that, for better or for worse, is now 2497 02:02:20,320 --> 02:02:23,660 going to involve tying back in some JavaScript from last week. 2498 02:02:23,660 --> 02:02:25,660 The goal at hand of these examples is not 2499 02:02:25,660 --> 02:02:29,043 to necessarily master how you yourself would write the Python code, the SQL 2500 02:02:29,043 --> 02:02:31,960 code, the JavaScript code, but just to give you a mental model for how 2501 02:02:31,960 --> 02:02:33,290 these different languages work. 2502 02:02:33,290 --> 02:02:35,680 So that for final projects, especially if you 2503 02:02:35,680 --> 02:02:39,160 do want to add JavaScript functionality, much more interactive user interface, 2504 02:02:39,160 --> 02:02:42,340 you at least have the bare bones of a mental model for how 2505 02:02:42,340 --> 02:02:44,780 you can tie these languages together. 2506 02:02:44,780 --> 02:02:48,370 Even though our focus, generally, has been more on Python and SQL 2507 02:02:48,370 --> 02:02:50,560 than on JavaScript from last week. 2508 02:02:50,560 --> 02:02:55,660 Let me go ahead and open up an example called shows, version zero of this. 2509 02:02:55,660 --> 02:02:57,260 And let me do Flask run. 2510 02:02:57,260 --> 02:03:01,330 And let me go into my URL here and see what this application looks 2511 02:03:01,330 --> 02:03:06,670 like by default. This has just a simple query text box with a search box. 2512 02:03:06,670 --> 02:03:09,700 Let's take a look at the HTML that just got sent to my browser. 2513 02:03:09,700 --> 02:03:11,810 All right, there's not much going on here at all. 2514 02:03:11,810 --> 02:03:14,920 So there's a form whose action is /search. 2515 02:03:14,920 --> 02:03:16,720 It's going to submit via GET. 2516 02:03:16,720 --> 02:03:21,140 It's going to use a q parameter, just like Google it seems, and submit it. 2517 02:03:21,140 --> 02:03:24,110 So this actually looks like the Google form we did last week. 2518 02:03:24,110 --> 02:03:25,990 So let's see what goes on here. 2519 02:03:25,990 --> 02:03:28,390 Let me search for something like cat. 2520 02:03:28,390 --> 02:03:29,740 Enter. 2521 02:03:29,740 --> 02:03:31,820 OK, so it looks like-- 2522 02:03:31,820 --> 02:03:34,280 all right, so this is actually a somewhat familiar file. 2523 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 2524 02:03:38,470 --> 02:03:41,290 from a couple of weeks ago when we first introduced SQL, 2525 02:03:41,290 --> 02:03:43,840 and I loaded them into this demo so that you can search 2526 02:03:43,840 --> 02:03:45,650 by keyword for any word you want. 2527 02:03:45,650 --> 02:03:46,750 I just searched for cat. 2528 02:03:46,750 --> 02:03:49,900 If we were to do this again, we would see all the title of TV shows 2529 02:03:49,900 --> 02:03:55,100 that contain D-O-G, dog, as a substring somewhere and so forth. 2530 02:03:55,100 --> 02:03:57,130 So this is a traditional way of doing this. 2531 02:03:57,130 --> 02:04:02,740 Just like in Google, it uses /search?q=cat, q=dog, and so forth. 2532 02:04:02,740 --> 02:04:03,620 How does that work? 2533 02:04:03,620 --> 02:04:07,670 Well, let's just take a quick look at app.py here. 2534 02:04:07,670 --> 02:04:12,940 Let me go into my zero example here, show zero, and open up 2535 02:04:12,940 --> 02:04:15,460 app.py and see what's going on. 2536 02:04:15,460 --> 02:04:17,120 All right, very simple. 2537 02:04:17,120 --> 02:04:19,780 Here's the form, that's how we started today. 2538 02:04:19,780 --> 02:04:22,255 And here is the /search route. 2539 02:04:22,255 --> 02:04:23,380 Well, what's going on here? 2540 02:04:23,380 --> 02:04:25,070 This gets a little interesting. 2541 02:04:25,070 --> 02:04:28,270 So I first select a whole bunch of shows by doing this. 2542 02:04:28,270 --> 02:04:32,890 Select star from shows, where title like question mark. 2543 02:04:32,890 --> 02:04:37,570 And then I'm using some percent signs from SQL 2544 02:04:37,570 --> 02:04:40,030 on both the left and the right, and I'm plugging 2545 02:04:40,030 --> 02:04:42,450 in whatever the user's input was for q. 2546 02:04:42,450 --> 02:04:45,040 If I didn't use like and I used equal instead, 2547 02:04:45,040 --> 02:04:48,130 I could get rid of these curly brace, these percent signs, 2548 02:04:48,130 --> 02:04:51,760 but then it would have to be a show called cat or called dog as opposed 2549 02:04:51,760 --> 02:04:54,460 to it being like cat or like dog. 2550 02:04:54,460 --> 02:04:58,030 This whole line returns to me a list of dictionaries, each of which 2551 02:04:58,030 --> 02:05:00,920 represents a show in the database. 2552 02:05:00,920 --> 02:05:04,210 And then, I'm passing all of those shows to a template called search.html. 2553 02:05:04,210 --> 02:05:08,010 So let's just follow that breadcrumb, let's open up shows dot-- 2554 02:05:08,010 --> 02:05:10,390 sorry, search.html. 2555 02:05:10,390 --> 02:05:12,570 All right, so this is where templating gets cool. 2556 02:05:12,570 --> 02:05:15,270 So I just passed back hundreds of results, potentially, 2557 02:05:15,270 --> 02:05:18,750 but the only thing I'm outputting is an unordered list 2558 02:05:18,750 --> 02:05:22,830 and using a Jinja for loop and li tag containing 2559 02:05:22,830 --> 02:05:24,745 the titles of each of those shows. 2560 02:05:24,745 --> 02:05:27,120 And just to prove that this is indeed a familiar data set 2561 02:05:27,120 --> 02:05:32,040 and I actually simplified it a bit, if I look at shows.db with SQLite, 2562 02:05:32,040 --> 02:05:36,330 I threw away all the other stuff like ratings and actors and everyone else 2563 02:05:36,330 --> 02:05:41,070 and I just have, for instance, select star from shows 2564 02:05:41,070 --> 02:05:43,440 limit 10, just so we can see 10 of them. 2565 02:05:43,440 --> 02:05:45,810 There's 10 of the shows from that database. 2566 02:05:45,810 --> 02:05:48,220 So that's all that's in the database itself. 2567 02:05:48,220 --> 02:05:51,810 So it would look like this is a pretty vanilla web application. 2568 02:05:51,810 --> 02:05:53,845 It uses GET, it submits it to the server, 2569 02:05:53,845 --> 02:05:56,220 the server spits out a response, and that response, then, 2570 02:05:56,220 --> 02:06:00,990 looks like this, which is a huge number of li tags, one for each cat 2571 02:06:00,990 --> 02:06:02,700 or one for each dog match. 2572 02:06:02,700 --> 02:06:06,370 But everything else comes from a layout.html. 2573 02:06:06,370 --> 02:06:09,010 All the stuff at the top and at the bottom. 2574 02:06:09,010 --> 02:06:13,020 All right, so these days, though, we're in the habit of seeing autocomplete. 2575 02:06:13,020 --> 02:06:15,720 And you start typing something and you don't have to hit Submit, 2576 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. 2577 02:06:18,595 --> 02:06:20,950 Web applications, nowadays, are much more dynamic. 2578 02:06:20,950 --> 02:06:24,130 So let's take a look at this version one of this thing. 2579 02:06:24,130 --> 02:06:30,030 Let me go into shows one and close my previous tabs 2580 02:06:30,030 --> 02:06:32,140 and run Flask run in here. 2581 02:06:32,140 --> 02:06:36,840 And it's almost the same thing, but watch the behavior change a little bit. 2582 02:06:36,840 --> 02:06:38,863 I'm reloading the form, there's no button now. 2583 02:06:38,863 --> 02:06:40,530 So gone is the need for a submit button. 2584 02:06:40,530 --> 02:06:42,550 I want to implement autocomplete now. 2585 02:06:42,550 --> 02:06:45,150 So let's go ahead and type in C. OK, there's 2586 02:06:45,150 --> 02:06:48,120 every show that starts with C. A, there's 2587 02:06:48,120 --> 02:06:50,100 every show that has C-A in it, rather. 2588 02:06:50,100 --> 02:06:53,340 T, there's every show with C-A-T in it. 2589 02:06:53,340 --> 02:06:57,150 I can start it again and do dog, but notice how instantaneous it was. 2590 02:06:57,150 --> 02:07:01,050 And notice my URL never changed, there's no /search route, 2591 02:07:01,050 --> 02:07:02,250 and it's just immediate. 2592 02:07:02,250 --> 02:07:05,890 With every keystroke, it is searching again and again and again. 2593 02:07:05,890 --> 02:07:08,520 That's a nice UX, user experience, because it's immediate. 2594 02:07:08,520 --> 02:07:10,770 This is what users are used to these days. 2595 02:07:10,770 --> 02:07:15,630 But if I look at the source code here, notice that in the source code, 2596 02:07:15,630 --> 02:07:20,853 there's just an empty UL by default but there is some fancy JavaScript code. 2597 02:07:20,853 --> 02:07:22,270 So let's see what's going on here. 2598 02:07:22,270 --> 02:07:25,750 This JavaScript code is doing the following. 2599 02:07:25,750 --> 02:07:28,710 Let me zoom in a little bit more. 2600 02:07:28,710 --> 02:07:33,092 This JavaScript code is first selecting, with query selector, 2601 02:07:33,092 --> 02:07:35,800 which you used this past week, quote unquote "input, " all right, 2602 02:07:35,800 --> 02:07:37,740 so that's just getting the text box. 2603 02:07:37,740 --> 02:07:41,375 Then it's adding an event listener to that input for the input event. 2604 02:07:41,375 --> 02:07:43,500 We didn't talk about this last week, but literally, 2605 02:07:43,500 --> 02:07:45,600 when you provide any kind of input by typing, 2606 02:07:45,600 --> 02:07:50,100 by pasting, by any other user interface mechanism, 2607 02:07:50,100 --> 02:07:51,910 it triggers an event called input. 2608 02:07:51,910 --> 02:07:53,970 So similar to key press or key up. 2609 02:07:53,970 --> 02:07:57,780 I then have a function, no worries about this async function for now. 2610 02:07:57,780 --> 02:07:59,482 Then what do I do inside of this? 2611 02:07:59,482 --> 02:08:01,440 All right, so this is new, and this is the part 2612 02:08:01,440 --> 02:08:04,110 that let's just focus on the ideas and not the syntax. 2613 02:08:04,110 --> 02:08:06,000 JavaScript, nowadays, comes with a function 2614 02:08:06,000 --> 02:08:10,290 called fetch that allows you to GET or POST information to a server 2615 02:08:10,290 --> 02:08:12,100 without reloading the whole page. 2616 02:08:12,100 --> 02:08:14,610 You can sort of secretly do it inside of the page. 2617 02:08:14,610 --> 02:08:15,930 What do I want to fetch? 2618 02:08:15,930 --> 02:08:21,270 slash search question mark q equals whatever the value of that input is. 2619 02:08:21,270 --> 02:08:25,320 When I get back a response, I want to get the text of that response 2620 02:08:25,320 --> 02:08:27,570 and store it in a variable called shows. 2621 02:08:27,570 --> 02:08:30,240 And I'm deliberately bouncing around, ignoring special words 2622 02:08:30,240 --> 02:08:33,600 like await and await here, but for now, just focus on what came back. 2623 02:08:33,600 --> 02:08:36,840 A response came back from the server, I'm getting the text from it, 2624 02:08:36,840 --> 02:08:38,670 storing it in a variable called shows. 2625 02:08:38,670 --> 02:08:39,930 What am I then doing? 2626 02:08:39,930 --> 02:08:44,700 I'm using query selector to select my UL, which is empty by default, 2627 02:08:44,700 --> 02:08:48,960 and I'm changing its inner HTML to be equal to the shows that 2628 02:08:48,960 --> 02:08:50,590 came back from the server. 2629 02:08:50,590 --> 02:08:51,810 So let's poke around. 2630 02:08:51,810 --> 02:08:54,640 Here's where, again, developer tools are quite powerful. 2631 02:08:54,640 --> 02:08:58,890 Let me go ahead and reload this page to get rid of everything. 2632 02:08:58,890 --> 02:09:02,190 And let me now open up inspect. 2633 02:09:02,190 --> 02:09:05,530 Let me go to the Network tab and let's just sniff the traffic going 2634 02:09:05,530 --> 02:09:06,780 between my browser and server. 2635 02:09:06,780 --> 02:09:11,460 I'm going to search for C. Notice that immediately triggered an HTTP request 2636 02:09:11,460 --> 02:09:13,530 to /search?q=c. 2637 02:09:13,530 --> 02:09:16,440 2638 02:09:16,440 --> 02:09:20,050 So I didn't even finish my cat thought, but notice what came back. 2639 02:09:20,050 --> 02:09:24,900 A bunch of response headers, but let's actually click on the raw response. 2640 02:09:24,900 --> 02:09:29,550 This is literally the response from the server, just a whole bunch of li tags. 2641 02:09:29,550 --> 02:09:32,610 No UL, no HTML, no title, no body, nothing. 2642 02:09:32,610 --> 02:09:33,690 Just li tags. 2643 02:09:33,690 --> 02:09:35,200 And we can actually simulate this. 2644 02:09:35,200 --> 02:09:39,360 Let me manually go to that same URL, q=c, Enter. 2645 02:09:39,360 --> 02:09:41,370 We are just going to get back-- 2646 02:09:41,370 --> 02:09:42,810 whoops, sorry. 2647 02:09:42,810 --> 02:09:47,670 slash search q equals c, we are just going to get back this stuff, 2648 02:09:47,670 --> 02:09:50,130 which if I view source, it's not even a complete web page. 2649 02:09:50,130 --> 02:09:53,550 The browser is trying to show it to me as a complete web page with bullets, 2650 02:09:53,550 --> 02:09:56,100 but it's really just partial HTML. 2651 02:09:56,100 --> 02:09:58,710 But that's perfect, because this is literally 2652 02:09:58,710 --> 02:10:01,620 what I essentially want my Python code to copy paste 2653 02:10:01,620 --> 02:10:04,710 into the otherwise empty UL tag. 2654 02:10:04,710 --> 02:10:09,060 And that's what this JavaScript code then, here, is doing. 2655 02:10:09,060 --> 02:10:13,350 Once it gets back that response from the server, it's using these lines of code 2656 02:10:13,350 --> 02:10:18,370 to plug all of those li's into the UL after the fact. 2657 02:10:18,370 --> 02:10:20,730 Again, changing the so-called dom. 2658 02:10:20,730 --> 02:10:23,700 But there's a slightly better way to do this because, honestly, this 2659 02:10:23,700 --> 02:10:25,170 is not the best design. 2660 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 2661 02:10:29,490 --> 02:10:30,450 unnecessarily. 2662 02:10:30,450 --> 02:10:33,090 Why do I need to send all of these stupid HTML tags? 2663 02:10:33,090 --> 02:10:36,190 Why don't I just create those when I'm ready to create them? 2664 02:10:36,190 --> 02:10:37,920 Well, here's the final flourish. 2665 02:10:37,920 --> 02:10:40,470 Whenever making a web application nowadays, 2666 02:10:40,470 --> 02:10:44,640 where client and server keep talking to one another, Google Maps does this, 2667 02:10:44,640 --> 02:10:47,430 Gmail does this, literally every cool application 2668 02:10:47,430 --> 02:10:49,770 nowadays you load the page once and then it 2669 02:10:49,770 --> 02:10:51,540 keeps on interacting with you without you 2670 02:10:51,540 --> 02:10:54,210 reloading or having to change the URL. 2671 02:10:54,210 --> 02:10:58,710 Let's actually use a format called JSON, JavaScript Object Notation, which 2672 02:10:58,710 --> 02:11:01,800 is to say there's just a better, more efficient, better 2673 02:11:01,800 --> 02:11:04,390 designed way to send that same data. 2674 02:11:04,390 --> 02:11:08,670 I'm going to go into shows two now and do Flask run. 2675 02:11:08,670 --> 02:11:10,410 And I'm going to go back to my page here. 2676 02:11:10,410 --> 02:11:14,760 The user interface is exactly the same, and it still works exactly the same. 2677 02:11:14,760 --> 02:11:18,790 Here's C, C-A, C-A-T, and so forth. 2678 02:11:18,790 --> 02:11:21,270 But let's see what's coming back now. 2679 02:11:21,270 --> 02:11:31,390 If I go to /search?q=cat, Enter, notice that I get this crazy-looking syntax. 2680 02:11:31,390 --> 02:11:34,860 But the fact that it's so compact is actually a good thing. 2681 02:11:34,860 --> 02:11:38,610 This is actually going to-- let me format it a little nicer, well, 2682 02:11:38,610 --> 02:11:39,690 or a little worse. 2683 02:11:39,690 --> 02:11:42,990 This is what's called JavaScript Object Notation. 2684 02:11:42,990 --> 02:11:48,780 In JavaScript, an angle-- a square bracket means here comes an array. 2685 02:11:48,780 --> 02:11:54,780 In JavaScript, a curly bracket says here comes an object, AKA, a dictionary. 2686 02:11:54,780 --> 02:11:57,840 And you might recall from-- 2687 02:11:57,840 --> 02:11:59,020 did we do? 2688 02:11:59,020 --> 02:12:05,010 Yes, sort of recall that you can now have keys and values in JavaScript 2689 02:12:05,010 --> 02:12:07,360 notation using colons like this. 2690 02:12:07,360 --> 02:12:10,950 So long story short, cryptic as this is to you and me 2691 02:12:10,950 --> 02:12:14,310 and not very human friendly, it's very machine friendly. 2692 02:12:14,310 --> 02:12:18,030 Because for every title in that database, 2693 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. 2694 02:12:23,800 --> 02:12:27,870 And this is a very generic format that an API, an application programming 2695 02:12:27,870 --> 02:12:29,340 interface, might return to you. 2696 02:12:29,340 --> 02:12:31,380 And this is how APIs, nowadays, work. 2697 02:12:31,380 --> 02:12:36,120 You get back very raw textual data in this format, JSON format, 2698 02:12:36,120 --> 02:12:39,840 and then you can write code that actually programmatically turns 2699 02:12:39,840 --> 02:12:44,530 that JSON data into any language you want, for instance, HTML. 2700 02:12:44,530 --> 02:12:47,490 So here's the third and final version of this program. 2701 02:12:47,490 --> 02:12:49,590 I, again, select my input. 2702 02:12:49,590 --> 02:12:51,870 I, again, listen for input. 2703 02:12:51,870 --> 02:12:54,690 I then, when I get input, call this function. 2704 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. 2705 02:13:01,230 --> 02:13:04,193 I then wait for the response, but instead of getting text, 2706 02:13:04,193 --> 02:13:07,110 I'm calling this other function that comes with JavaScript these days, 2707 02:13:07,110 --> 02:13:09,330 called JSON, that just parses that. 2708 02:13:09,330 --> 02:13:13,470 It turns it into a dictionary for me, or really a list of dictionaries for me, 2709 02:13:13,470 --> 02:13:15,810 and stores it in a variable called shows. 2710 02:13:15,810 --> 02:13:20,070 And this is where you start to see the convergence of HTML with JavaScript. 2711 02:13:20,070 --> 02:13:23,400 Let me initialize a variable called HTML to nothing, quote unquote, 2712 02:13:23,400 --> 02:13:26,200 using single quotes, but I could also use double quotes. 2713 02:13:26,200 --> 02:13:28,470 This is JavaScript syntax for a loop. 2714 02:13:28,470 --> 02:13:32,340 Let me iterate over every ID in the show's list 2715 02:13:32,340 --> 02:13:36,120 that I just got back in the server, that big chunk of JSON data. 2716 02:13:36,120 --> 02:13:41,430 Let me create a variable called Title that's equal to the shows-- 2717 02:13:41,430 --> 02:13:43,890 the title of the show at that ID. 2718 02:13:43,890 --> 02:13:45,900 But for reasons we'll come back to, let me 2719 02:13:45,900 --> 02:13:47,820 replace a couple of scary characters. 2720 02:13:47,820 --> 02:13:53,910 Then let me dynamically add to this variable, an li tag, the actual title, 2721 02:13:53,910 --> 02:13:55,470 and a close li tag. 2722 02:13:55,470 --> 02:13:58,230 And then very lastly, after this for loop, 2723 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. 2724 02:14:04,320 --> 02:14:06,900 So in short, don't worry too much about the syntax 2725 02:14:06,900 --> 02:14:09,358 because you won't need to use this unless you start playing 2726 02:14:09,358 --> 02:14:11,470 with more advanced features quite soon. 2727 02:14:11,470 --> 02:14:13,440 But what we're doing is, with JavaScript, we're 2728 02:14:13,440 --> 02:14:16,360 creating a bigger and bigger and bigger string of HTML 2729 02:14:16,360 --> 02:14:19,800 containing all of the open brackets, the li tags, the closed brackets, 2730 02:14:19,800 --> 02:14:23,160 but we're just grabbing the raw data from the server. 2731 02:14:23,160 --> 02:14:25,170 And so in fact in problem set nine, you're 2732 02:14:25,170 --> 02:14:28,890 going to use a real world third party API, application programming 2733 02:14:28,890 --> 02:14:30,330 interface, for which you sign up. 2734 02:14:30,330 --> 02:14:33,090 The data you're going to get back from that API is not 2735 02:14:33,090 --> 02:14:36,540 going to be show titles, but actually stock quotes and stocks 2736 02:14:36,540 --> 02:14:39,280 ticker symbols and the prices of last-- 2737 02:14:39,280 --> 02:14:41,310 at which stocks were last bought or sold, 2738 02:14:41,310 --> 02:14:44,118 and you're going to get that data back in JSON format. 2739 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 2740 02:14:47,160 --> 02:14:50,230 to the requisite HTML on the page. 2741 02:14:50,230 --> 02:14:53,520 So the final result here is literally the kind of autocomplete 2742 02:14:53,520 --> 02:14:55,890 that you and I see and take for granted every day, 2743 02:14:55,890 --> 02:14:58,050 and that's ultimately how it works. 2744 02:14:58,050 --> 02:15:01,680 HTML and CSS are used to present the data, your so-called view. 2745 02:15:01,680 --> 02:15:06,100 Python might be used to send or get the data on the backend server. 2746 02:15:06,100 --> 02:15:08,370 And then lastly, JavaScript is going to be used 2747 02:15:08,370 --> 02:15:10,757 to make things dynamic and interactive. 2748 02:15:10,757 --> 02:15:12,840 So I know that's a whole bunch of building blocks, 2749 02:15:12,840 --> 02:15:15,632 but the whole point of problem set nine to tie everything together, 2750 02:15:15,632 --> 02:15:18,150 set the stage for hopefully a very successful final project. 2751 02:15:18,150 --> 02:15:20,400 Why don't we go ahead and wrap up there, and we'll see 2752 02:15:20,400 --> 02:15:23,610 you one last time next week for emoji. 2753 02:15:23,610 --> 02:15:26,960 [MUSIC PLAYING] 2754 02:15:26,960 --> 02:15:58,000 [MUSIC PLAYING]