CS50x 2026 - Lecture 9 - Flask
Skills:
Prompt Craft90%Prompt Systems Engineering80%Tool Use & Function Calling80%Agent Foundations70%
Key Takeaways
This video introduces Flask, a micro framework for web application development, and demonstrates how to create full-fledged web applications with Python and SQL. It covers topics such as route-based URL control, URL parsing, parameter passing, and template rendering.
Full Transcript
Heat. Heat. [music] >> [music] [music] >> All right, this is CS50. This is already week nine. And I dare say this week is the most representative of what you'll be doing after the class if you so choose to program in the future and tackle some project that's new to you. In fact, the closest to this week was perhaps week six wherein we didn't really introduce all that many new concepts but really translated them from C and to Python. And so this week in particular, the goal is to really synthesize the past 10 weeks of class, drawing upon a lot of the building blocks that are hopefully now uh metaphorically in your toolbox and gives you an opportunity now to apply those ideas to new problems. In particular, web programming. So every day you and I are using the web in some form. Every day you and I are using mobile apps in some form. And we said last week that the languages underlying a lot of those applications are HTML and JavaScript for the layout and aesthetics. and then also in part JavaScript for a lot of the client side interactivity that you might experience nowadays. Well, today we come full circle and bring back a serverside component whereby we'll again write some Python, we'll again write some SQL code and use it to make our full-fledged own web applications and in turn if you so choose mobile applications as for your final project as well. So up until now when we did anything with the web, you ran this command last week HTTP server which literally did just that. It spawned a so-called HTTP server that is a web server whose purpose in life is just to serve up content from like your current folder, any files therein, any folders therein. And so all of the URLs generally followed a certain format. So if your URL were example.com/reall just denotes the root of the web server and so in there typically by default you would see a directory index. We'll see today that that goes away because generally when you visit something.com/ you want to see the actual website, not the contents of everything in the server. So we'll see how to address that. But the URLs up until now have been of a form like file.html literally referencing a file in that folder or folder slash which just means whatever is inside of that folder or folder/file.html or dot dot dot. You can nest these things however long that you want. And recall that more generally we said that you're referring to some kind of path on the server where a p path is a step of folders ending in perhaps a file name. So today we're going to generalize that at least in terms of nomenclature and start talking more about routes because essentially in web programming we are going to exercise a lot more control over what is in the URL. So back in the day it referred to literally a file on the server and as recently as last week the URLs referred to literally a file on the server. However, we'll see in code that we can actually just parse this that is analyze what is after the domain name in a URL and just use this as generic input to the server to figure out what kind of output to produce. We're going to see the same convention though. If you want to pass in specific parameters, key value pairs, uh we'll use a question mark after our so-called route key equals value. And then if there's another one or more, we'll just separate them by amperands. And to do all of this, we're going to recall the inside of those virtual envelopes. Recall that if we did something like on google.com to search for cats, what was really being sent to the server was a request for / search, which notice is not search.html. There's no folder per se there. This is just the name of a program really running on Google servers. And that's going to be the so-called route that we ourselves start programming today. question mark q equals cats just meant that the query parameter the input from the web form is going to contain in this particular example the word cats. So how are we going to do all do this? So we could implement our own web server in C. It would be a nightmare to like use a language as lowle as C and actually deal with something as high level as writing code for the web. We're instead going to use Python for the most part if only because it's much higher level. But even then, we would probably if we wanted to do this thing uh from scratch, we would have to write a lot of Python code to like analyze the insides of these envelopes, figure out what inputs are being passed to the server, and then figure out how to access that in Python code. It's just a lot of work to just get a web application up and working. And so what the world generally does is they don't reinvent the wheel of writing their own web server. Rather, they use an off-the-shelf fairly generic web server or application server as it might be called. And we for instance are going to use something called flask. Now flask is a framework as the world would say or more specifically a micro framework which just means it's a library of code that other people wrote to make it easier for us to implement web applications. So they took the time to figure out how to handle get requests on a server, post requests on a server, figure out how to extract key value pairs from URLs, the sort of commodity stuff that like literally every web application on the internet has to do anyway. So we don't have to retrace those steps ourselves. What this will allow us to do is only implement the problems that we care about by using this framework. And to be clear, a framework much like Bootstrap is not only a library that someone else has written for you, but it's like a set of conventions that you follow in order to use the library in their recommended way. So it's more of a generic term that includes library and a set of conventions. And how do you know how to use either? You just read the documentation or take a class in which we're about to give you an introduction to some of this right here. So instead of running today http-server to start a web server that just serves up static content files and folders in our account we're instead going to run the command moving forward flask space run and this is going to look for code that we've written in our current directory and if it is in accordance with the conventions to which I'm alluding by using the so-called framework then it's going to start our web application on some TCP port for instance 8080 as we discussed last week to do this all we have to have in our current folder is minimally a file called app.py by default. This is hinting at an application written in the language called Python. And what code we put in there we'll soon see. And then ideally we would have another text file called requirements.ext by convention inside of which is just one per line the name of all of the libraries that we want this web application to include. In other words, if I go over here to VS Code, if I don't have such a file, that's fine, but I want to use a framework like Flask, recall our pip command for installing Python packages. I could just say pip install flask enter and that would go ahead and install the flask framework or library for me just like we did a few weeks ago with installing the silly little cows uh library as well. I've already done that in advance and better still I've installed I've come with uh my code today both of these files apt and requirements.ext and in fact if I go ahead and create one just for fun here all you need do in a requirements.ext text file is literally put the name of the library that you want to include and then you run pip in a slightly different way to install that library or any other libraries that are in that file as well. So let me wave my hands at the requirements.ext for uh moving forward. It just means what libraries do you want to use with this web application so you don't have to remember or memorize them and type them all out manually. All right. So what's going to go inside of app.py? Well, the minimal amount of code that we can write to make our own web application that does something like print out hello world to my browser could look like this. Now, there's a bit of new syntax here, but not all that much today moving forward. The very first line just says from flask import flask, which is a weird way of just saying give me access to the flask library. Capitalization no matters. And so, the package that we're using is called flask lowercase, but we want to have access to a special function in there called flask capital F. So this is sort of a copy paste line. The next one's a little weird looking, but it essentially says give me a variable called app and turn this file into a flask application. We haven't seen this in a few weeks, but there was that weird if conditional that we put at the bottom of some of our Python code a few weeks back that just said if uh dot dot dot and it mentioned in there name if name equals equals main_. So we've seen an illusion to name. For our purposes, name just refers to whatever the name of this file here is. No matter what I call it, you can sort of access the current file by way of this special global variable. So this line collectively just means turn this file into a flask application and store the result in a variable called app. So I can now do stuff with flask. And what am I going to do? Well, down here, let me first point out a familiar syntax. I'm defining a function that I called index by convention, but I could have called it anything I want whose sole purpose in life is just to return quote unquote hello world, which is the super simple output this web app is going to display. But, and this is the new syntax, I'm using here what's generally called a Python decorator, which is a type of function that essentially affects the behavior of the function right after it. So, by saying at app.rout route quote unquote slash. This is telling the Flask framework associate this index function with this route the single forward slash. And that's how we're going to take over the default behavior of the slash portion of the URL by telling it to return whatever this function returns. And we'll see this in action now. So let me go over here say to VS Code and within VS Code I'm going to whip up exactly that application in a file called uh app.py just so as to combine this and some subsequent examples maybe the same folder. I'm going to first create a directory or folder called hello. I'm going to go into that hello folder. I'm going to go ahead and recreate that same requirements file just for good measure to tell the world that I want to use the flask library here. And then I'm additionally going to create now app.py pi. And I'll type this fairly quickly, but I'm just reciting what we saw a moment ago. From the flask package, import the flask function, lowercase f, capital f, respectively. Then give me a variable called app. Set it equal to that function call passing in the name of this file, whatever it actually is. And then lastly, let's go ahead and call at app.oute quote unquote slash, which says, hey, Python, whatever the next function is, associate it with this slash route. And so I'm going to define that function. I could call it anything I want, foo or bar or baz. But in so far as slash represents the index of the website, like the default page, I'm just going to go ahead and call it by convention index and then return for now hello, world. And that's it. So whereas last week when I was writing code in HTML files, I was making web pages, now I've created what we'll call a web application. And it's an application in the sense that there's actually some logic going on there. There's some functions, there could be some conditionals, there's clearly a variable, there could be loops, and all of the sort of stuff we've seen in Scratch, NC, and Python as well. We'll now see back in this Python file. So, how do we now run this? Well, let me go back into my terminal window here, and I'll clear it just for good measure. I'm going to go ahead and run flask run enter. I'm gonna see some cryptic looking output, but there's that familiar pop-up with the green button that wants to open up this application, whereas HTTP server uses 8080 by default. Flask uses port 5000 by default. And here we have it. I've just opened up my second tab, and we spent a lot of time there last week. This is the server I'm running, not on port 880, but on port 5000 today. And there is the contents of what was spit out by my very first application. Now, even though the browser is rendering this like it is a web page, notice this. If I uh inspect, if I rightclick or control-click anywhere on the screen and go to view page source, you'll see that there's no actual HTML on this page. It's literally a single line of text, hello, world. If I close that and rightclick or control-click again and go to inspect like we did last week to open up developer tools, you'll see that the browser has actually filled in some blanks here for me by just rendering as it should the minimal possible web page. But the content I actually sent to the web browser is only literally hello, world. So how can I actually send a web page of my own rather than letting the browser do something like this? Well, I could go ahead and close that and go back to my application. I'm going to go ahead now and hide the terminal just because the server is still running. And what I'm going to go ahead and do here is well, nothing's really stopping me from returning not just a string of text, but a string of HTML. And this might not look pretty, but let me go ahead and do open bracket doc type HTML close bracket then HTML then head then title. And I'll just title this for instance hello to keep it simple. back slashtitle back slash head open bracket body hello, world back slashbody back slash HTML uh close quote and I used single quotes in this case, but I could have just as easily used double quotes, but that's a full-fledged web page. Like that's the minimal amount of content we saw last week. Actually, you know what? For good measure, let's actually add lang equals quote unquote en. So, it's actually fortuitous that you use single quotes because now I have some double quotes inside. And even though this is not pretty printed, it's just one massive mouthful of HTML all along one line. When I now go back to the browser, reload the page as by clicking here, and then view page source again, here's what my browser received this time. Indeed, it's the full-fledged HTML. And in fact, if I close that tab and reopen developer tools via inspect, now we'll see in the tab absolutely everything that I sent over, including a title, including the lang equals. And had I typed even more, we would have seen that, too. All right. So, what was the point of this exercise? It feels as though that I've really just taken more time, added more complexity to achieve literally what I could have done last week by just creating index.html myself without any Python code. But I dare say what we're trying to do is lay the foundation for a full-fledged interactive website that maybe has forms that we can submit to the application that allows us to generate not just one page, but maybe two or three or any number. So what you're seeing here is sort of the beginning of google.com's search application or gmail.com itself or facebook.com or any web application you can think of begins with a little code that theoretically looks a little something like this. But this is kind of stupid to put HTML hard-coded no less in one long string here inside of my application. Let's try to factor this out. That was a lesson we preached last week about sort of factoring out our JavaScript, factoring out our CSS. We can do the same thing with our actual HTML here. And so what I'm actually going to do is import not only the Flask function, but also another function that per its documentation comes with Flask called render template with an underscore in between. This is a function whose purpose in life is to render a template, so to speak, of HTML. We'll see what we mean by template in just a bit. But down here, what I'm going to do is now delete all of that code. And let me just assume that I'm going to put that same code in a file called index.html, html just like I did last week. So, let's instead return the return value of render template of quote unquote index.html. Now, that file does not yet exist. Indeed, if I go into my terminal window, create a second terminal just so I can leave the server running but still see what's going on. I'm going to CD into that same hello directory, type ls to list my files, and I only see app.py and requirements.ext. But it turns out per Flask's documentation, if you want to create your own HTML files, you simply have to add a directory that by convention is called templates. And that's it. So in addition to app.py requirements.ext, I need a folder called templates. So let's go back into VS Code, make dur templates. Capitalization matters, all lowercase. Now, let me go ahead and cd into templates and run the code command and create a file called index.html in the templates folder. And then super quickly, let me hide this. Let me whip up that same page again. Doc type HTML html lang equals quote unquote en close bracket uh head close bracket title close bracket hello and then down here body close bracket hello, world. So autocomplete is helping me type quickly. But now I have a file with my HTML that this application I claim is going to spit out automatically for me. So let's see the effect. Let me go back into my other browser tab. Let me close the developer tools and let me quite simply just click reload. And no apparent change. It's working exactly as it did before, but I've laid the foundation for making a much more useful layout of my files so that I can actually keep my logic, my Python code, and my HTML a bit separate from that. All right. Well, how can we make this into something even more interesting? Well, let's start to take some actual user input for instance. So, wouldn't it be nice if I could pass in via the URL something like Q equals cats, but maybe something like name equals David or name equals Kelly and actually see the name that's being outputed. In other words, let me zoom in up here and let me pretend like this happened automatically. Let me do question mark uh name equals David. Enter. Well, it would be nice if I saw hello, David. I'll I'll propose rather than just hello, world. So, how do I actually get access to everything after the question mark? Well, here is where a framework like Flask and any number of alternatives starts to shine. It gives me that answer for uh automatically. And so it turns out in Flask once you've used it, you have access to a special global variable as we'll call it called request.orgs where args just means the arguments or the parameters that were passed in to this HTTP request. So how do we use this? Well, let me go back to VS Code here. And at the very top line, in addition to importing Flask, capital F render template, let's also import request, which is a global variable that comes with the Flask framework. And then I'm going to use it as follows. I'm going to go ahead and say um a second argument to the render template function where I'm going to say placeholder equals request. Actually, let me not do that yet. Let me first create a variable name equals request args. And then let me go ahead and get the name key from the arguments. And then down here, let's go ahead and pass in placeholder equals name. So what am I doing here on line 8, I'm creating a variable called name. I'm storing in that the value that's in the request global variable in what's apparently a dictionary called args, specifically the name key therein. So if the thing after the question mark name equals is David, this should give me David. If it's Kelly, it should give me Kelly instead. Then what I'm doing is rendering this template called index.html, but I'm additionally passing in some named parameters. We talked briefly about that in week six when we introduced the idea that Python can take not only a comma-epparated list of arguments, but some of which can have names. So I'm proposing that one such name of an argument to this render template function can be placeholder, for instance. Now, at the moment, this code isn't going to do anything useful. If I go back indeed to the other tab, click reload after zooming in, even with my name in the URL, you'll see that we still see hello, David. But here's where things now get interesting. And here too is what we mean by template. If I go back into VS Code, open up index.html again, and instead of putting the word world there, what I'd like to see is not hello world, but hello, placeholder. But of course, if I literally type that, I'm going to see literally placeholder unless I surround placeholder with pairs of curly braces like this. And by using these pairs of curly braces, I'm telling Flask that I want to interpolate, so to speak, that variable. I want to substitute in its value. So this is yet another syntax. In Python, we saw fstrings. In C, we saw percent s. When using something like print f in an HTML file, when using flask specifically, we use these pair of curly braces to denote this is indeed a placeholder whose value should be plugged in. So now let's go back over to the second tab. Recall if I zoom in that passed in already to this URL is question mark name equals David. And this time when I click reload, voila. Now I see my actual name. And unlike the JavaScript examples last week which were doing everything client side, notice here if I go to uh rightclick or control-click and view page source, what's noteworthy today is that David in this case literally came from the server. This was not rendered client side. The server sent this HTML and specifically this text. So, if I go back to the same tab here, zoom in and change David for instance to Kelly, what I should see instead when I hit enter is hello, Kelly. And indeed, if I go back to the source code and reload the page there, I should see in the view page source that the server sent indeed hello, Kelly. So, it's in this sense that it's an application. The URL is providing input to the application by way of this URL format, the so-called get for uh the get string that's being passed in. And if I look at the code that I'm running, app.py is the code that's running. It is grabbing that name from the URL. I am then passing it into my index.html file and then my HTML file is plugging the actual value in for me. And so what's going on with for instance these curly braces? Well, here too is where we're actually using a library. And included in Flask is another library called Ginga. And Ginga is what's called a templating library. And there's so many templating libraries in the world. Ginga is actually fairly s simple, which is nice. And which is why Flask uses it. And for now, you can just think of Ginga as being the library that knows how to interpolate variables inside of pairs of curly braces. So why are we introducing it another frame, another library? Well, the folks who implemented Flask decided that it was not worth their time reinventing the wheel of a templating language, a language via which you can figure out what values to plug in where. So, they just lean on another library that someone else wrote years prior so as to not reinvent that wheel themselves. And that's all that's going on with a framework. In this case, it's using perhaps multiple libraries instead. All right. So, what then is a template? So, this then is a template. What you're looking at here, hello, placeholder, is a template in the sense that it's kind of the blueprint for the web page I want the user to see, but it's going to be dynamically generated using indeed this blueprint by plugging in the value of placeholder inside of those pairs of curly braces. And so that's why index.html starting today is in a folder called templates because this is not just static HTML like the stuff we wrote last week. This is the uh the the blueprint for the actual HTML that we want the browser to spit out. But there's a bug here. Notice what's going to happen here. If I go up to this URL and I get rid of the name altogether, for instance, I just visit the slash route without any key value pairs and hit enter. This is sort of bad bad request. It's an HTTP 400. In fact, if you look at the tab, here's another HTTP status code that we probably haven't seen before. But 400 just means the user did something wrong by not passing in the parameter that was expected. Well, that's a little bad design if like the user has to manually type in things to the URLs. Like no human actually does that. That's not good for business or customers in general. So I can go back into app.py and just make a little bit of conditional code here. And here's too where we see what makes this an application and not just a static page. Instead of just blindly getting the name here, I could instead do something like this. Well, if the name parameter is in request.orgs, and this is just Python syntax for asking if this key is in this dictionary, then I'm going to go ahead and define name and set it equal to request.orgs quote unquote name. Else, if there is no name in the request, well, then I might as well give some default value like name equals quote unquote world. And that alone logically makes sure that I only try to access request.org's name if the key is actually there. So, if I go back to the browser now, reload without anything else in the URL. Now, we're back in business and it's saying hello, world. But if I go up to the URL bar and add name equals David, enter, that too now works. So, it's a web application in the sense that not only does it have function calls as well as a variable, but now we've got some conditional logic with boolean expressions as well. All right, questions on anything we've done thus far because it was a lot all at once. Questions thus far? Yeah, >> good question. Let's try that. What if I just did question mark name equals nothing? Well, let me go back to that other tab. Uh, delete the name David and hit enter. And I indeed see hello, nothing. Why? Because the name key is provided now. It just doesn't have a value. And so the conditional has the same answer. Well, yes, name is in request.orgs, but there's just no value associated with it. And here again is the value or a hint at the value of using a framework like Flask. The fact that I can just import the request global variable and then ask questions like is this parameter in this dictionary means I don't have to write any of the code that like figures out what the URL looks like, break it apart between the question mark and the equal signs and any amperands therein. That's all sort of generic logic that every web application has to do. So again, Flask is sort of doing that lift for me and I can just focus on the logic that I actually care about. All right. Well, a quick convention here. It's I've used the word placeholder here just to kind of hit the nail on the head and make clear this is a placeholder, but frankly it's a little more readable stylistically to not just put hello generic placeholder, but to say something like hello, name so that a colleague or even myself looking at this file down the line knows that okay, we're trying to print out the user's name here. That's fine. You can change the name of these variables to be anything you want. And even though it looks weird, it's conventional in Flask to do something like this. Name equals name. But each of these names means something different. This is the name of the placeholder that I'm going to put in my actual template. This is the value that I actually want to give it. And it just keeps me a little ser by just reusing the same name instead of calling it placeholder or placeholder 1, placeholder 2, placeholder 3, or something generic like that. Now it's just a little clear even though it looks weird to say name equals name. Again, that just allows me to do this in my template. All right. Well, what more can I do after that? Well, let me propose that we can actually go in and simplify this code a little bit. It turns out this is so common to just ask a question as to whether the parameter is there and then do something with it or not that flask comes with some logic to do this. And in fact, I can get rid of all four of these lines. Just go ahead and with confidence declare a variable called name, set it equal to request.orgs, arcs, but in the so-called dictionary, use a function called get that comes with it, which technically doesn't relate to the verb that was used by HTTP. This just means literally get me the following. And if you want to get the parameter called name, you literally just say quote unquote name. However, in case there is no name parameter, you can also give this function a default value like world. And so now we've collapsed into four lines uh from four lines into one that exact same logic. So this gets me the HTTP parameter called name. But if it's not there, it gives me a default value of world. So that no matter what, this name variable has what I care about. Indeed, if I go back over here, let's type in how about name equals David again. Enter. That's there. If I type in uh no name, enter. That too is now working as well. All right. Well, let's see if we can refine this a bit more. Let me propose that in our next version of this. Let's introduce a second route. So two URLs. Much like uh Google has many different URLs as does most any web application. At the moment I'm doing everything in my slash route. So how might I move away from this? Well, let me go ahead and not only add a second route but an actual form via which the user can type in their their name. So to do this, let me propose that in index.html HTML. Instead of just printing out the user's name and trusting that they're going to have typed their name in manually to the URL, which again is not normal behavior, let's actually show the user a form via which they can do exactly that. So here's my form tag. Uh let's say the method I'm going to use is get so that I see everything in the URL. Let's give myself an input uh that whose name is name because this is the human's name. And notice somewhat confusingly this name on the left is the HTTP sorry this name on the left is the HTML attribute that we saw last week. So it's different from what we just did in Python even though they're all called the same thing. The type of this input is going to be text. And let's go ahead and make this a little more user friendly. Let's put some placeholder text called name so the human knows what what to type in. Let's go ahead and disable autocomplete just so we don't see previous input into this text box. And let's autofocus it so that the cursor is blinking in the text box by default. Then lastly, let's go ahead and have a button the type of which is submit. So that clicking this button actually submits the form. And I'm just going to call this button like greet because I want the user to be able to greet themselves by clicking this button. Now I should specify action. The only other time we used action is when we actually went to httpsw.google.com/ google.com/arch. That's not relevant today because I'm trying to print hello world, not search for cats and such. But this is where I too have control. If I want to submit this form to a specific location on in my web application, action is where I can specify it. So why don't I pretend that there exists a route in my application called /greet. And if you go to example.com/greet question mark name equalsdavid, this now will greet the user with hello David for instance. But slashgreet does not exist. If we go back to app.pay, literally the only route that currently exists is single slash. But I can change that. I can go into my uh app.pay as I have here and below this function I can go ahead and define app.oute quote unquote greet and just invent any route that I want. I can then define a function that will be called whenever that route is visited. By convention, to keep myself sane, I'm going to call the function the same thing as the route, but you don't have to do this. It's just to minimize uh decisions I have to make. And then in this function, what I'm going to do is this return render template greet.html, which doesn't exist yet, but that's a problem to be solved. And then I can pass in the name of the user. I'm going to go ahead and save myself a line of code and just say request.orgs.get get quote unquote name, world. In other words, strictly speaking, I don't need that variable on its own line. This has the effect of what we already did in index, but I'm doing it all in one elegant oneliner. And now in index, in so far as I want the index of the site to just show the user the form via which they can type in their name, this one's easy. Now, render template quote unquote index.html and return that template. So, to recap, here's index.html, html which is now a form instead of a template for hello, such and such. App.py is going to return that template whenever I visit the index or slash of the page. And then this greet route is going to handle the case of printing out greet.html passing in the user's name. All right, I think I'm not quite good to go yet, but let's try this out. Let me go back to my browser tab, reload, and there we have it. I have a web form now instead of the uh the hello, soandso, I'm going to go ahead and type in my name. And notice the URL at the moment, even though Chrome is hiding it. Technically, it's there slash, but Chrome and most browsers today sort of hide as much stuff as they can if it's not all that intellectually interesting. But watch what happens when I click greet to the URL. It automatically sends me to /greet question mark name equals David. And this is just like the way the forms worked last week when we recreated our own version of Google in search.html because the action there was google.com/arch. the user was whisked away to Google server. Today I stay on the same server because the action I used was quite simply slashgree which is assumed to be on my own server but clearly I screwed something up because I have a big internal server error in front of me as you soon will too. Odds are as you dive into this uh 500 is the status code that means your fault somehow. Now why is that? Well, it's unclear from this generic black and white message. However, because I'm the developer, I can go back to VS Code, open my terminal window, and recall that I have two terminals open now. One that I can type stuff in, the other of which is still running from before. Let me open up that one. And you'll see if I maximize my terminal window, a whole bunch of scary error messages here, but the relevant one is probably going to be, let's see, down here. Raise template not found error. Ginga exceptions template not found. Greet.h. html. So there's a lot of esoteric error messages here, more so than usual, but the simple fact is that I just screwed up and I did not create greet.html. So file not found by the server. So the user doesn't see all that complexity. That's deliberate by design. It's generally not good for cyber security. if you're revealing to the user all of the error messages that are happening on your server because maybe that suggests they can hack in some way some way by taking advantage of those error messages and the information implicit in them. But they are there in your terminal window to actually see and diagnose. So how do I fix this? Well, not a problem. Let me shrink my terminal window back down. Let me code a file called greet.html. And in greet.html, let's create the template via which I'm going to greet the user, which ironically is the exact same as index.html HTML used to be. So, let me recreate that real quick. Uh, doc type HTML. Let me close my terminal. html lang equals en uh head uh title hello body hello, and there's my uh here's my placeholder hello, name. So, to be clear, the index.html template doesn't have any curly braces or anything dynamic. It just spits out the HTML for the form. Greet.html HTML spits out HTML and the actual greeting. And it's app.py that decides which of these to show the user. Either index.html if they visit the slash route or greet.html if they somehow find their way to the slash greet route, which they will automatically by simply submitting that form. All right, so let's go back into this internal server error and go back to the form. Nothing has changed with the form, but now when I type in David, click greet, not only will the URL change to be slashgreet question mark name equals David, I actually now see the content that I expected a moment ago. All right. Well, now it's a opportunity to critique. I have these two templates open, index.html and greet.html. And even if you've never done web programming before and even if you've never did HTML before last week, what is bad about this design intuitively? >> Say again, >> abstraction. >> Abstraction in what sense? >> Yes. So that's exactly the the hang-up I have here. There's a lot of duplication. And technically I didn't copy paste though I might as well have because notice as I very hintingly go back and forth almost every line of code in these files is the same except for the form which is there or not there or the hello comma like all of the boilerplate HTML namely everything I just highlighted here lines one through seven in greet.html HTML and this and this is what we really start to mean about a template. Like wouldn't it be nice if we could factor out all of that HTML that's common to both files, put it in literally a template that both routes can use so that I can write that boilerplate code once instead of again and again. Because imagine in your mind's eye, well, if I have three routes or four routes or five routes, I'm going to be like typing the same darn HTML three, four, five times. That's got to be dumb and that's got to be solvable as we've seen in other languages as well. So, let me indeed go ahead and try to improve this. And the syntax is a little weird, but it's the kind of thing you get used to quite quickly. I'm going to go ahead and create a third HTML file now by going back to my terminal window inside still my templates directory. And by convention, this file is going to be called layout.html. Why this? That's what the flask documentation tells you to do. So, in layout.html, HTML. I can pull all of my boilerplate HTML, the stuff that is invariant and doesn't change. So, here we go. Doc type HTML uh HTML tag lang equals en close bracket open bracket head open bracket title. We'll call it hello for all of the pages. Open bracket body. And here's where it gets interesting. The body is the only thing that has been changing in these two examples. In index.html, it was a web form. In greet.html, HTML it was just a simple string of hello, so and so. So what I want to tell flask is that everything in the body will just be a dynamic block of code and the syntax for that which takes a little bit getting used to but it's also sort of copypasteable block body using percent signs this time and because I don't want any such body in the template I'm going to literally close this block as follows and here you see another example of sort of HTML like syntax but instead of using angled brackets ginga uh the templ templating library that flask uses uses curly brace and percent sign to open the tag and then the opposite to close it. So what you really have here are two ginga tags as we'll call them. This one is called block and I'm defining an arbitrary name here. I could have called it foo bar or baz but because I want this block to refer to the body of the page. By convention I'm going to call it body. And then this weird syntax which is used in some other languages too just means end whatever block you just began. And so again you just see reasonable people disagreeing. The people who invented HTML used nice angled brackets and words like these. The people who came up with Ginga used curly braces and percent signs. Why? Well, odds are these are not normal symbols that a human would type when writing uh code, at least in HTML. So, they just chose something that probably wouldn't collide with actual syntax the human wants to use. So, that's it for the template. This is now a uh this is essentially a blueprint that doesn't have just a placeholder for a single word or value like name. I can put a whole chunk of code here now instead. And how do I do that? Well, let me go into index.html with the moment, which at the moment is a little duplicative in that it's got all of this boilerplate. So, you know what? I'm going to go ahead and delete everything that is already in my layout both above and below that web form. And now I'm going to use a bit more ginger syntax. This too takes a little while to memorize or copy paste. But if I want index.html to use the layout.html HTML blueprint, I can simply say extends layout.html and then close tag using percent sign close bracket here. And then if what I want to plug into that layout is the following code, I can say as before block uh body and then down here I can say end block and that's it. And just to be a little nitpicky, I'm going to de-indent that slightly. And now even though it looks like web pages suddenly look a lot uglier. Well they do because like this is weird looking syntax but I have now distilled index.html into its essence. This is the only thing that changes visav the greeting page. And so I've put my HTML here that I care about. I've said to Flask this is what index.html's body block shall be. Where to put it? Well put it into that particular layout.html file. And so the logic for greet.html is the same thing. It's going to look just as weird, but again, you get used to it. Let's go ahead and delete everything that's boilerplate in greet.html, both above and below. Up at the top, let's tell Flask that greet.html2 extends layout.html. And let's go ahead and say to Flask that the block uh called body shall be this for greet.html. And the end of this block is now down here. And just to be nitpicky, I'll de-indent that, too. So again, the pages look a little weirder now, but it's going to follow a paradigm that we just see again and again, such that the only juicy stuff is what's inside of that body block. So now, if I go back to my layout, it looks exactly like this. This indeed is a placeholder, not just for a single variable like name or the placeholder we did before. This is the placeholder for a whole block of code that came from a file, not from a variable. And so if I go back into my other tab here, go click back to go back to the web form and reload, notice that I have the familiar looking form. But if I now look at my developer or if I look at viewpage source, notice everything that came from the web page, from the server, here's that boiler plate up here. Here's that boiler plate down here. And here's the stuff that's unique to this page. And recall too, aesthetically I de-indented it, which is why it's now no longer pretty printed and what the browser sees. Like that's okay. There's no reason to obsess over the indentation and the pretty printing of what the browser sees. Ultimately, the reason I did this indentation is because arguably when I'm in VS Code here and I look at index.html, this is clearly indented inside of the body block just so I know what's part of that block. The browser does not care about superfluous whites space or less thereof. All right, questions on what we've just done here, which is to truly take this template out for a spin and now remove what redundancies I had accidentally introduced. Questions? No. Okay. Amazing. All right. Well, let's go ahead and look at this URL again. I'm not liking the fact that every example we've done thus far involves putting my name or Kelly's name right there in the URL bar. Well, why is that? Well, if I have like a nosy sibling and they sit down at my browser, they're going to see like every URL I visited, including whose name was greeted. Now, that's not all that big a deal, but now imagine it's a username and a password that the form is submitting or a credit card number that the form is submitting or just search terms that you don't want the world knowing you're searching for. They're going to end up in the URL bar. Why? If you are using method equals get for the form, that's how get works. It literally puts all of the HTTP parameters in the URL, which is wonderfully useful if it's sort of uh low stake stuff like the Google search box or if it is um or potentially low stake stuff like the Google search box or if you just want to be able to hyperlink directly to a URL like this. In other words, if I put this into an anchor tag open bracket a href and a URL like this, I could deep link a user to a web page that just always says hello, David. So, get strings contain all of the requisite information to render a page for the user. But this isn't really good for privacy. So, recall that there's not only get, but there's also something called post. and post is just a different HTTP verb that essentially with respect to those virtual envelopes next last week sort of puts the information more deeply inside of the envelope such that it's not written right there in the URL bar but it's still accessible by the server. So if I do this, watch what happens. Let me uh go back into VS Code. Let me go back into index.html which has the form and let me quite simply change the method from get to post. And now let me go back to my other browser tab, back to the form, and reload so that the form knows that the method has changed. Now type in David and click greet. And before I do that, let me zoom in on the URL bar. Notice that the URL does change. I'm at greet, but I haven't revealed to the world or to anyone with physical access to my browser what URL I just searched for. All they know is that I went to /greet, but not the key value pair or pairs that were passed in. Of course, this clearly hasn't worked. I've got an HTTP status code of 405, which means method not allowed. That's because Flask by default when defining routes simply assumes that you want get instead of post. Now, get is good for the default page. In fact, when I go back here, this is equivalent to me visiting the slash route just in the browser. So, I want my index to generally support get. But the greet route should support post. And the simplest way to do this is to pass in another argument to the route function which we haven't needed before cuz the default is get. And I can instead tell flask a commaepparated list of the HTTP methods that I want this route to support. So if I wanted to support just post, I can pass in a list containing just post. And recall flat uh Python uses square brackets for lists which are their version of arrays in C. Now by default this argument is this methods equals get. And that's why the only thing supported a moment ago was get. That's why I'm now changing it to be post instead. I have to make one other change though. It turns out if you read the documentation, when accessing HTTP parameters via post instead of get, you move from using request.orgs to request.form. This is completely unintuitive that request.orgs is get and request.form is post because they all come from forms. So it's bad naming admittedly. So you just kind of have to remember request.orgs is used for get request.form is used for post. So all I need to do further is change this to be request.form. And that's it. Now my web application will support web form submitting to it via post instead of get. Let me go ahead and type in my name. Now I'll zoom in. Notice that the URL will again change to /greet with no parameters evident. But I will be greeted this time because the server knew to look deeper into that envelope for those key value pairs instead. And just to be now uh sort of diagnostic about this, let me go back once more. Let me rightclick or control-click on my desktop and go to inspect. Here's where developer tools can be super useful as well. I'm going to go in here and I'm going to go ahead and clear this. And now I'm going to type in David again and I'm going to click greet. But because I have the network tab open like we played with last week, it's going to show me all of the requests going from my browser to server, which is going to be useful here because not only do I see, okay, it obviously worked because I got back a 200, but if I click on this diagnostic output, I can actually go to the payload tab here and I'll see that the form data that was submitted was name, the value of which was David. So you can see what you're submitting. So you can do this today like if you want to log into some website uh Gmail or otherwise you can actually see all of the data that your own keyboard is submitting to the server even if it's using post because the browser that you control of course can see the same there. All right any questions now on this transition from get to post kind of on a roll or not going so well. We'll see. All right. So what more can we do with this? Well, let's give ourselves a couple more building blocks before we transition to actually implementing some real world problems as I did years ago with one such example. Suppose that I don't like this direction I'm going in in so far as every time I have a page with a form, it submits to another route altogether. Cuz in your mind's eye, just kind of extrapolate. Well, if I have two forms on my page, I now need four routes. If I have three forms, I need six routes. It seems a little annoying that you use one route just to show the form and another route to proc
Original Description
***
This is CS50, Harvard University's introduction to the intellectual enterprises of computer science and the art of programming.
***
TABLE OF CONTENTS
00:00:00 - Introduction
00:00:43 – Flask
00:28:41 – Forms
00:33:31 – Jinja
00:37:12 – Templates
00:48:10 – Request Methods
00:54:22 – Frosh IMs
01:31:10 – SQLite and Python
01:57:05 – Cookies and Sessions
02:02:47 – Shopping Cart
02:15:55 – Shows
02:20:29 – APIs
***
HOW TO SUBSCRIBE
http://www.youtube.com/subscription_center?add_user=cs50tv
HOW TO TAKE CS50
edX: https://cs50.edx.org/
Harvard Extension School: https://cs50.harvard.edu/extension
Harvard Summer School: https://cs50.harvard.edu/summer
OpenCourseWare: https://cs50.harvard.edu/x
HOW TO JOIN CS50 COMMUNITIES
Bluesky: https://bsky.app/profile/cs50.harvard.edu
Discord: https://discord.gg/cs50
Ed: https://cs50.edx.org/ed
Facebook Group: https://www.facebook.com/groups/cs50/
Faceboook Page: https://www.facebook.com/cs50/
GitHub: https://github.com/cs50
Gitter: https://gitter.im/cs50/x
Instagram: https://instagram.com/cs50
LinkedIn Group: https://www.linkedin.com/groups/7437240/
LinkedIn Page: https://www.linkedin.com/school/cs50/
Medium: https://cs50.medium.com/
Quora: https://www.quora.com/topic/CS50
Reddit: https://www.reddit.com/r/cs50/
Slack: https://cs50.edx.org/slack
Snapchat: https://www.snapchat.com/add/cs50
SoundCloud: https://soundcloud.com/cs50
Stack Exchange: https://cs50.stackexchange.com/
Telegram: https://t.me/cs50x
Threads: https://www.threads.net/@cs50
TikTok: https://www.tiktok.com/@cs50
Twitter: https://twitter.com/cs50
Twitter Community: https://twitter.com/i/communities/1722308663522594923
YouTube: http://www.youtube.com/cs50
HOW TO FOLLOW DAVID J. MALAN
Facebook: https://www.facebook.com/dmalan
GitHub: https://github.com/dmalan
Instagram: https://www.instagram.com/davidjmalan/
LinkedIn: https://www.linkedin.com/in/malan/
Quora: https://www.quora.com/profile/David-J-Malan
Threads: https://www.threads.net/@davidjmalan
TikTok:
Watch on YouTube ↗
(saves to browser)
Sign in to unlock AI tutor explanation · ⚡30
Playlist
Uploads from CS50 · CS50 · 0 of 60
← Previous
Next →
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Hello, World: Hadi Partovi
CS50
Content Distribution and Archival in a Digital Age
CS50
CS50 2014 - Week 1
CS50
CS50 2014 - Week 3
CS50
CS50 2014 - Week 0, continued
CS50
CS50 2014 - Week 4
CS50
Week 3, continued
CS50
Quiz 0 Review
CS50
CS50 2014 - Week 3, continued
CS50
CS50 2014 - Week 7
CS50
CS50 2014 - Week 7, continued
CS50
Breaking Through The (Google) Glass Ceiling by Christopher Bartholomew
CS50
Introduction to Amazon Web Services by Leo Zhadanovsky
CS50
CS50 2014 - Week 9
CS50
How to Build Innovative Technologies by Abby Fichtner
CS50
Light Your World (with Hue Bulbs) by Dan Bradley
CS50
Building Dynamic Web Apps with Laravel by Eric Ouyang
CS50
CS50 2014 - CS50 Lecture by Steve Ballmer
CS50
CS50 2014 - Week 10
CS50
This is CS50 with Steve Ballmer?
CS50
Meteor: a better way to build apps by Roger Zurawicki
CS50
Data Analysis in R by Dustin Tran
CS50
Data Visualization and D3 by David Chouinard
CS50
CS50 2014 - Week 6
CS50
Build Tomorrow's Library by Jeffrey Licht
CS50
CS50 2014 - Week 9, continued
CS50
Essential Scale-Out Computing by James Cuff
CS50
iOS App Development with Swift by Dan Armendariz
CS50
Sam Clark Leads Yale Students on Tour to CS50 at Harvard
CS50
3D Modeling and Manufacture by Ansel Duff
CS50
CS50 2014 - Week 5, continued
CS50
hello, world
CS50
CS50 2014 - Deep Thoughts - Hash Table
CS50
CS50 2014 - Deep Thoughts - Binary Tree
CS50
CS50 2014 - Deep Thoughts - Scratch
CS50
CS50 2014 - Deep Thoughts - MySQL
CS50
LaunchCode Visits CS50
CS50
CS50 Live, Episode 100
CS50
CS50 Field Trip to Google
CS50
This is CS50 AP
CS50
Week 4: Monday - CS50 2011 - Harvard University
CS50
Week 2: Wednesday - CS50 2011 - Harvard University
CS50
Week 1: Wednesday - CS50 2011 - Harvard University
CS50
Week 11: Monday - CS50 2011 - Harvard University
CS50
Week 3: Wednesday - CS50 2011 - Harvard University
CS50
Week 12: Monday - CS50 2011 - Harvard University
CS50
Week 1: Friday - CS50 2011 - Harvard University
CS50
Week 3: Monday - CS50 2011 - Harvard University
CS50
Week 10: Wednesday - CS50 2011 - Harvard University
CS50
Week 2: Monday - CS50 2011 - Harvard University
CS50
Week 9: Monday - CS50 2011 - Harvard University
CS50
Week 7: Monday - CS50 2011 - Harvard University
CS50
Week 5: Monday - CS50 2011 - Harvard University
CS50
Week 5: Wednesday - CS50 2011 - Harvard University
CS50
Week 7: Wednesday - CS50 2011 - Harvard University
CS50
Week 8: Monday - CS50 2011 - Harvard University
CS50
Week 9: Wednesday - CS50 2011 - Harvard University
CS50
Week 8: Wednesday - CS50 2011 - Harvard University
CS50
Week 10: Monday - CS50 2011 - Harvard University
CS50
Week 2: Wednesday - CS50 2010 - Harvard University
CS50
More on: Prompt Craft
View skill →Related Reads
📰
📰
📰
📰
Stop Calling Node.js “Single-Threaded”: A Look Under the Hood
Medium · JavaScript
Best hosts file managers in 2026: SwitchHosts, Gas Mask, and the rest
Dev.to · Locahl
TypeScript `noUncheckedIndexedAccess` in 2026: The Flag You Should Have Turned On Years Ago
Dev.to · jsmanifest
How the Brazilian CPF Validation Algorithm Works in JavaScript (Step-by-Step)
Dev.to · Mehru
Chapters (12)
Introduction
0:43
Flask
28:41
Forms
33:31
Jinja
37:12
Templates
48:10
Request Methods
54:22
Frosh IMs
1:31:10
SQLite and Python
1:57:05
Cookies and Sessions
2:02:47
Shopping Cart
2:15:55
Shows
2:20:29
APIs
🎓
Tutor Explanation
DeepCamp AI