Intermediate Python Practice: Flask App
Transcript from the "Practice: Flask App" Lesson
>> Nina Zakharenko: Just a reminder, this is what our final exercise looks like. We can select different languages here, and then go and hit the,
>> Nina Zakharenko: GitHub search API and display the results. Pretty straightforward Flask app, we're not using a database here, we're not really maintaining any state. We're just checking what the user checked and unchecked at each request.
[00:00:34] Pretty easy peasy.
>> Nina Zakharenko: Now, let's go through how to implement this.
>> Nina Zakharenko: I'm gonna exit out of the development server that I was using, and I'm gonna run a command called tree that's going to list the directory structure for this chapter. If you're on Mac OS just know that,
>> Nina Zakharenko: Know that this utility doesn't come with Mac, you have to install it. So at the root, we have api.py. I have exceptions.py where I put my custom exception. I have models.py,
>> Nina Zakharenko: Where I put the model that stores my GitHub repository.
>> Nina Zakharenko: Then I have a static folder with a favicon for my website, CSS styles that I've provided for you.
[00:01:40] Please don't get the pitchfoks out if my CSS isn't good. I'm one of those full stack developers. And then, we have a few templates. We have a template for an error page and a template for our index page, as well as some texts that we can run.
>> Nina Zakharenko: Let's go through and look at these files.
>> Nina Zakharenko: Our exceptions.py file is pretty straightforward.
>> Nina Zakharenko: I have a class here, and like you saw before, it inherits from Exception.
>> Nina Zakharenko: In the constructor I'm taking a status code, and if the status code was 403 it's gonna say Rate limited reached. If the status code was any other type of error status code it's just gonna print that out.
[00:02:39] And then I pass this new modified message that I created to our super()._init.
>> Nina Zakharenko: Looking at the models file, we live coded this earlier. This class, the GithubRepo class, is used to represent a single GitHub repository. So in my constructor I take the name, the language, the number of stars, and I also return a handy string representation in the __repr__.
>> Nina Zakharenko: Now, looking at api.py, this is where I put my create_query and my repos_with_most_stars methods. And remember that we wrote methods that look almost exactly like this in the intro to Python final exercise. Going through these, for creating a query I take a list of languages in the minimum amount of stars.
[00:03:48] In yesterday's code we were looping over each query, adding it on to the string using concatenate, using this string join is much more efficient. String concatenation is a pretty inefficient operation, and using string join is much faster. So now we can make that same query using an f string, for language, going through each language and languages, stripping out any trailing or leading white space.
[00:04:24] And then creating our new query with just one concatenation, and concatenating the minimum number of stars that we'd like to see. Now in the repos with most stars method, we get our query by using that create query method. We have the same parameters. I've printed them out here, but you don't need to do that in your code.
[00:04:50] I get the response by using the request library. To get the resource at this GitHub api search URL, passing in the parameters I set up. Now, if the status code was not 200, that means if anything went wrong, I'm just gonna raise instance of my GitHub API exception, passing in the response.status_code.
[00:05:21] Next, I get the JSON from the response. I get the items at the Items key, and I'm just gonna use a list comprehension to return an instance of my GitHub repo. A list of instances of my GitHub repo class, passing in the name, the language, and the star gazers count.
[00:05:43] And using a list comprehension to do that for each item in items. Notice there is no main method here. We're not running our code in the same file. Instead we're gonna be using this as a library from our Flask app. Scrolling to the top, notice that we imported requests so that we can use it as an available library.
[00:06:06] We're also importing a few things from our own custom classes, so our GitHubApiException and our GitHubRepo model.
>> Nina Zakharenko: Okay, those are all the building blocks. Now, open up .py and see how it all gets put together. For you Windows folks, I'm going to put in additional instructions here, just so you have them in one place with the final exercise.
>> Nina Zakharenko: But we have a few imports here as well. We're importing a few things from Flask, we're importing the Flask class, we're importing render_template, and we're also importing something called request, which gives us information about the request that we are currently processing. From my API I'm also importing this repos_with_most_stars method, and I'm importing my GitHubApiException so that I can catch it, do something with it.
>> Nina Zakharenko: And creating a new app, a new Flask app here. By passing in under name. And then I'm making a list of available languages that I want my users of my web application to be able to choose from.
>> Nina Zakharenko: And I have an index method here. If you've noticed here at the top, my app.route takes a list of methods that it accepts.
[00:07:44] So if I tried to call this route with a method that is not accepted, like put, my server's just gonna throw an error. Now, if the requested method was GET, meaning we haven't submitted anything yet, we're not hitting that Submit button. I'm just gonna set the selected_languages to all of the available_languages by default.
[00:08:07] If the method was 'POST' instead, I'm gonna use that request variable that I imported from Flask. That variable has a form which is attached to,
>> Nina Zakharenko: The form that I've used to submit to this endpoint. And there's a helper method on this called getlist, and this tells me which form I would like to look at.
>> Nina Zakharenko: Now, I get my results from the repos_with_most_stars method just like I did in my Introduction to Python final exercise. This time, instead of passing in all of the languages, I'm just gonna pass in the ones that the user selected. Next, I'm gonna render my template so the first argument to render template is index.HTML, we'll take a look at that template in just a second.
[00:09:08] I'm also passing in which languages were selected, so that when the page reloads, the languages that we had checked off stay checked off. And I'm passing in that list of all the available languages so that the check boxes know whether they're selected or not and to display the check on or off.
[00:09:31] And lastly, I'm just passing in one additional variable to the template, and that's our list of results which contain those instances of GitHub repo. There's one last thing that happens down here. I'm using another decorator. This one is called app.airhandler. And to this decorator I can pass in an exception type, and just provide a single method that will run if this class of exception occurs.
>> Nina Zakharenko: And here I'm gonna render a different type of template, error.html. And the message is just gonna be the string representation of the error. Let's take a look at what's in these templates. First, I'm going to open error.html from my final exercise files, and we'll see that I linked to a stylesheet.
[00:10:32] I have a div here. But really the most important bit is I'm just gonna display the message that got passed in.
>> Nina Zakharenko: One second, let me close some of these files.
>> Nina Zakharenko: And I'm gonna open my app.py. Oops, that's not what I wanted. Sorry, one second, app.py is right here.
[00:11:12] And I'm gonna open my,
>> Nina Zakharenko: error.html file. Okay, let me see if I can display these side by side with my current font size.
>> Nina Zakharenko: Okay, so here I'm returning render_template, I'm passing in the message which is the exception that was thrown, and here in my template I'm looking at that message and with these curly brace printing it out.
[00:11:48] Okay, now I'm gonna open index.html from the,
>> Nina Zakharenko: Final Exercise Templates directory. And we'll see I have a title here, but the body is pretty strong. Or, the body of this is pretty short, pretty brief, pretty easy to read.
>> Nina Zakharenko: So here is the form action that I'm taking.
[00:12:15] I'm using this built-in that's available in templates. And I'm asking for the URL for this index,
>> Nina Zakharenko: Method that I've created right here. So I don't have to do any weird things with paths, and I'm specifying my form that the method is POST. Now you'll remember that here in my index method, I'm rendering a template and I'm passing in a few things to this template.
[00:12:50] The selected languages, the available languages and the results. So, I'm gonna loop through each language in my available languages. And I'm gonna do, I'm gonna set the value to the language for that checkbox so that when I submit this form I have that language available to me. And then I'm gonna return checked.
[00:13:16] I'm gonna insert here checked if the languages in the selected languages. Otherwise, the check box is going to be unchecked. And finally, I'm gonna print out the label for the language. That generates this section right here. Lastly, I just have a Submit button. Remember that for templates I'm gonna have to end this for statement, because just like HTML there's an opening tag and a closing tag.
[00:13:50] Okay, now I'm gonna print out my popular GitHub repos by stars. And all I'm doing here, again, I passed in results to my template. If there are no results, I don't wanna bother rendering the table, it's gonna look ugly. So instead, I'm just gonna print out No Results.
[00:14:09] Otherwise, I'm going to print out the table header. Now I'm gonna loop through every result in my results. And for table data I'll pull the result name, result language, and result.num*, and that's it. This is a 60-line index.html file. And that's all that we need to generate this functional website.
>> Nina Zakharenko: This is the kind of project that you can easily do in an afternoon. One more thing I wanted to show you, I wrote a few simple unit tests,
>> Nina Zakharenko: For my application. I also wrote a few unit tests for this simple application, those tests are included in the repo.
[00:15:04] So if you'd like, take a look at those and run them and let's make sure our app works.