Python Web Apps with Flask by Ezra Zigmond

Python Web Apps with Flask by Ezra Zigmond


[MUSIC PLAYING] EZRA ZIGMOND: Hi, everyone. Thanks for coming out today. This seminar is “Python
Web Apps with Flask.”>>So, I’m going to be talking
a little bit about why you might want to use Flask to make
web apps with Python, as opposed to some other frameworks out there, like
Django, which is the most well-known. The subtitle is “and
peewee,” which is something we’re going to talk about how
to interact with SQL databases. It makes it really nice.>>So, here’s just a quick rundown
of what I want to go over. So, first, just one slide
that’s going to be quickstart, which is how to get everything
set up on your computer. I’m going to be demonstrating
this on my local Mac machine, just because that’s where I’ve done this
before, I’m most comfortable with it, but this is definitely
possible on the CD50 IDE.>>So, after that, I want to
introduce what Flask is, and convince you why you should
use it in the first place. Then, I’ll give a quick
example of what you can do in Flask, a quick example
of what you can do in peewee, and then I will show you a more
complete example application that I put together that we
can walk through together.>>And then finally, in
the last slide, I have some resources that you look
at online for more information. This isn’t a complete
tutorial of how to use Flask. And I’ll hope to leave
some time for questions. Anyone watching locally, just
like, shout out in the middle if you have any questions.>>So quick installation stuff,
if you want to follow along, or if you want to get this
up on your own machine, I’m going to be using Python 2.7.10. Flask does work with
Python 3, but I like using Python 2, because there’s
some Python packages that don’t work with 3 yet. If you have pip installed, which
is a Python package manager, I think if your Python is
greater than or equal to 2.7.9, you have it installed already, it’s
super easy to install these packages.>>You can do pip install
Flask, pip install peewee, usually you have to run sudo just
so that the permissions work out. And, if you are using a
super old version of Python, I recommend updating your Python,
or using EasyInstall to install pip. So, the next question is, what is Flask? And I guess, first a
reasonable question I wondered for a long time is, what is a web app? Because that’s a word that I
think is thrown around a lot that I didn’t really know. And I think that the best
example of what a web app is, really is CS50 Finance, where
it’s not really just a website, but it’s something that you
could sort of interact with. There are user accounts and
all sorts of different things. So, where exactly you draw the
line between what is a website, and what is a web app, is sort of
arbitrary, but I guess the idea, it’s something more than a website
and it’s a useful application.>>So, Flask is a framework for
making web apps using Python. And hopefully, by the end
of this, I will convince you that you could actually write something
like CS50 Finance using Python, which I like a lot better than PHP, personally.>>So, Flask is, they call a
microframework, by which, they mean it’s very
simple, but extensible. So, it has just what you need,
but if you want more features, it’s easy to bring them in. But just because it’s a
microframework, doesn’t mean that it’s only for small projects. I have a link here that says
that Obama used Flask in 2012 for his campaign
website, which hopefully, is something of an endorsement. But the thing that I
really like about Flask is that, it doesn’t really make
any sort of decisions for you. Just because we’re
talking about web apps, I have to compare it to Ruby on
Rails and Django, both of which are great frameworks, but
they both make assumptions about how you want to
interact with databases, how you want to present your
views, and they’re definitely good aspects to that.>>For example, Ruby on Rails
has this active record system, which is a really nice
way to interact with data, but if you want to use Ruby on
Rails, you’re sort of tied into that. But with Flask, as I’ll
show you with peewee, you can use whatever sort
of database you want, and you can just pull that in
as an extension into Flask.>>So that’s why I really
like Flask, it’s that it doesn’t make assumptions for you
based on things you don’t need really.>>So, why should you use Flask? The advantages are, it just
has really the core features built into it that you need. So, you don’t have to worry about
understanding a whole bunch of things that you don’t really need. You don’t have to have to worry
about turning off features that you don’t really need.>>And, as I mentioned, it’s super
easy to add in extensions of things that you do need. Some of them, Flask has
their own support for it. So there’s something called Flask
admin, which mirrors that admin panel that Django provides,
which gives you a nice visual way to administer your website. But again, if you’re
making something simple, you probably don’t need a big admin
panel, so I think that’s super nice. And the disadvantages are, that
you have less power out of the box. So, when you first open up Flask,
and in your Python program, you just type from Flask
import*, or whatever, you don’t exactly get all of
the features you might want. And so you have to more explicitly
list the features you want. So that’s one disadvantage, but I
think that for building small web apps like I’m going to be showing
you, that’s not really a problem. And what the last thing is
supposed to say, which is a typo, is that there are less standardized
conventions for how to use Flask, just because there aren’t as many
people that use it professionally compared to Django. So, if you look something like,
“how do I do x” in Django, you’ll probably find it.>>There’s good design
patterns you can use, but with Flask, it’s
sort of go your own way, just because it’s a
pretty small library. So those are the
disadvantages, but I think that it’s still a good library to use.>>So let’s just jump right into Flask. This is definitely not going
to be a complete tutorial, but it’s to give you an idea
of how to structure things, and so you feel comfortable going
off and looking at the documentation and finding out more.>>So, let me open up a really
simple example at first, and show you what that
looks like, and then we’ll break it down a little bit more.>>So this here, let me get it running. So, I now have my application running. I’m going to open up Safari, and
it’s running on my local host. So I’m just going to make this bigger. But localhost:5000>>And so, right now, all this does
is, when you visit the website, it prints out “Hello Flask,”
which isn’t super useful, but I think it’s cool that
in this little file here, we have a web server running
that is printing out something. So, let’s actually look
at the code for a second, and break it down a little bit. Is the size good for everyone here?>>So, hopefully, you’re somewhat
comfortable using Python. I’m assuming that we can look at
the Python and read through it. If you have any questions about
anything, I can also go over that.>>So the first line is, from Flask,
we import, Flask with a capital “F” which is sort of all the
key features that you need. Whenever you’re writing
a file and using Flask, you’re going to want to import that,
just because that has all main things. The next thing we do, is we
call this function Flask, just to create an app object, and you’re
always going to want to do that.>>And then, jumping down to the
bottom quickly, this part here is, ‘if_name_==”_main_”‘ which is
sort of a Python convention. So this will only execute if you
run this file directly using Python. Then, we call app.run, which will
actually start the app going.>>So, that’s the main structure that
you’ll have in any Flask application, is you will have this
app=Flask(_name_), and then app.run.>>So, the two things that I have
in here, are what we call routes. So, let’s talk about
routing a little bit more. I’ll go back to the slide.>>So the most fundamental
concept in Flask is routing. And it’s the idea that you
assign functions in Python to particular addresses
on your web page. So, whenever you want
to create a new route, you use this @app.route
function decorator. So, if you are unfamiliar with
function decorators in Python, it’s this concept that
lets you take a function and surround it with something else.>>So really, what this decorator does,
is that it takes the function below it, and it adds more information
to it, but what Flask actually does make that happen isn’t super
crucial, but what is important, is that before the route, you
put this @app.route, and then the address of the route,
so the slash is what we saw when we just open up the web page. So that’s the homepage. It’s just the slash route. And then you have this function. The name of the function
can be whatever you want. It doesn’t have to necessarily
have to do with the route. And then, whatever
the function’s return, the function should return a string. And that string can
contain HTML or anything, and that is actually what will get
returned to your web browser as HTML, and it will render that.>>So this is the same code
that was from the example that I just ran, so when we visit slash,
it calls a Hello World function, which just returns a string, Hello Flask,
and that gets printed on the screen.>>So there’s another example,
which is, when you visit /hello, it prints out Hello Word, which
should actually say, Hello World, but let’s pretend that was intentional. So let’s pull that up real quick.>>So if you go to localhost/hello,
it will now print something else. So that’s just a quick example of how
you can create two different routes.>>So, so far this is not super useful,
there isn’t a whole lot you can do, you could just do all that with
having different HTML pages, and when you visit a particular
page it just loads the page. So let’s see some more
useful things you can do. So, one thing that you might have
noticed in the example that I pulled up, is that I had app.run(debug=True). And so, what this
debug argument does, is that when you run your web server,
when you’re in your application, if you change a file, it will
automatically reload the server.>>And so you don’t actually have to
restart Python, which is super useful. I can show that. Let me pull up my code
with the typo in it, and pretend that typo was put there
intentionally for instructive purposes. So let’s add this back in. So now it says Hello World. I will save it. And if we pull back up
the terminal, you’ll say that it’s restarting,
because it detected a change, and so now when we reload this page,
it will print out the correct thing.>>So debug is super useful for that. Also, if you have any
sort of a crash, so let me make this application crash
by having it not return a string. So, let’s just have it
return None for some reason. And then when I visit this
page, it will just crash, but the server doesn’t just crash, it
actually gives you a super useful back trace of everything that went wrong. And what’s really nice, is that
at any step in this back trace, you could open up an interactive
shell here, and sort of print out what variables you want to take a look at.>>And so debugging is really
useful for figuring out what is going on with your
server, rather than just seeing something in PHP like a 500 internal
server error, which is super useless. One thing to be aware of, is
that if you put your app online so it’s visible to the public, you
never want to leave debug mode on, because people can actually
use that console that I showed you to execute arbitrary code.>>So they can print out like any
secret codes you have in there, they can look at exactly
how your website is working. So it’s really useful
for testing, but always make sure to take it out before
you publish anything online.>>So when you’re using something
like PHP, there’s this idea that you can pass
information between web pages by putting the information actually
in the URL, which is a GET request, but in Flask, you can actually
do something sort of like that, by having a route that has
a variable as part of it.>>So if you look at this
example on the screen here, we have a route that’s
(‘/hello/’), and so, if you visit /hello/something, that something
is going to actually get filled into the name variable. And notice that the function
that comes with that route has to take in the parameter
name, so that it will actually get passed into the function.>>And then, once you’re
inside of that function, you can treat that like
a normal Python variable, and so then, it will
print out Hello, and it will fill in the name using
some string formatting.>>So to add variable parts to a routing,
you use the angle bracket marking. And optionally, you can use
what’s called a converter. And so, if you put this
annotation with the colon, you can specify that it’s
an int, or a float, or path, and it will automatically convert that. You can also do the conversion
within the Python function, just by using a cast, but sometimes if
you want to ensure that it’s an int, you can put that
conversion rule in there.>>So let’s pull up an example
of some variable rules. So here, this has the same basic
structure with the from Flask import Flask, the app=Flask(_name_),
and then it’s running at the end. We have these two different
variable routes here. And the first one, is
the one that I showed in the slide, which was that
it just takes in a string name and it will print out Hello, Name.>>And then, the second
one uses the conversion. So this will automatically convert it
to an int, and then double the int, and print that out. And, we don’t do any sort
of conversion within it because Flask takes care of that. So let’s get this running. When you have a Flask
application running, you can Control-C out of it
to stop the server running. And then I will run the variables.>>So let’s go to localhost/hello/ezra and
hopefully, this will say hello to me. >>So it took in my name, in the variable
route, and it filled it in here. So I’ll also show a quick
example of the doubling. So if you go to /double/3,
it will print out 6. So this took care of
the conversion for us.>>So you can also do it with
float, and something special, if you need to specify something like
a path, that lets it accept slashes, but that shouldn’t usually be an issue. So far we, still have
just been returning strings, which is not very interesting.>>We could actually return
literal HTML string. So in the code we can insert something
like the b-tag to make it bold, but most the time you
don’t actually want to be writing HTML code
in your Python code. It gets really messy,
and it’s not a good time.>>Flask allows us to separate out the
HTML into what’s called the Template, and so if you’re thinking
in terms of the MVC model that you’re familiar with,
hopefully, from working with CS50 Finance a little bit,
you can think of the Python files as being more of the Controller, where
they are interacting with whatever data model you might have.>>And then they are calling out to Views
and passing information to that View to fill in the information
in HTML that it needs. And what we call those Views
are Templates in class.>>So Flask uses another Python
module that it will automatically install when you pip install
Flask called Jinja, which lets you add these
annotations to the HTML that you see on the screen, that lets
you put in things like conditionals, and loops into the HTML.>>So it looks a little bit like how you
might use PHP within an HTML file, but this is just when the Flask
server serves up the HTML file, it will run this templating engine and
parse through this and fill things in. So Flask has a render_template function
that you can see at the bottom here. And so when you visit this page, it
would render this hello.html Template, and then fill in this HTML page. So let’s just run this real
quick, and see what it looks like, and then I’ll go through a
little bit in more detail. So, your Templates are going
to go in a Templates folder. It will automatically look within the
Templates folder for that Template. So let’s open this up. So, I’ll run the Templating example. >>So if I go to /hello/ezra,
it has this horrible, obnoxious marquee tag that I put in. Very nice, very dynamic. I’m a big fan. But what happens if
I just go to, /hello? So it just says Hello World. I didn’t pass it a name, and
it filled it in automatically. So let’s see how it did that, and how
we can get rid of that marquee maybe.>>So here, this is sort of
an interesting example of, if you are familiar with how switch
statements work in a language. Say this is sort of like,
that sort of a fall through, where you actually attached two
different routes to the same function. So we attach the /hello route and
the /hello/name route to Hello, and we specify using– Python lets you
specify default function arguments– so if there is no name, so
if we go to just /hello, it will automatically filter
to name is equal to None.>>So then, we render the
Template with name=name, so it will pass in the name parameter
equal to this name function parameter, into the Template. That still doesn’t explain how is
deciding whether to print Hello World, or print my name. So let’s actually look
into the Template itself, and see where that is coming from.>>So, within this Template, we
actually have some conditional logic, which some people would
argue, you actually shouldn’t have a lot of conditional
logic within your Template itself. It should be more within the
Controller, but for this example it’s something pretty small. So here, we check, if name, so
if name is not equal to None, if a name is actually
passed in, then we will say Hello, Name with the
header and the marquee, all this normal HTML, otherwise, we will
print out Hello, World just normally. So a couple things to notice here
about how you format the templating, is that, all of these
conditional statements, sort of like how we PHP it when
you want to insert some PHP, you use the less-than question
mark, it’s sort of analogous here with the {%.>>So here, we have our conditional code. And then, when you actually want to
literally evaluate something, and print it out to the screen, you
use the double braces. So here are the double braces,
and then we specify name, so within that, it will evaluate
to the variable name, which was passed in from the
render template function, rather than just printing
out, if we got rid of these, it would just print out the word “name.” So, that’s something to watch out for.>>So another thing to
notice is that, when we want to use the
render_template function, we actually have to import
it explicitly from Flask. And this is an example of
the modularity of Flask, that you don’t have to import
things that you don’t need. You can just bring in the
functions you actually do need, which is sometimes
nice, so you don’t have to have all these functions
sitting around that you’re not using, but also, if you forget that you
need to import render_template, you’ll probably get a warning
that will let you know about that. So, that is templating.>>So, we’ve shown how to
make simple web pages, and add a little bit more logic to
it, in terms of the variable routing. It lets you do different things based on
what URL you go to, and also then, give the HTML a little bit more sense in
terms of how you want to render things. You don’t have to put all
your HTML in your Python, but for pretty much
every web application, you’re going to want some sort
of data model associated with it.>>And so traditionally, this would
be something like an SQL database. And you can just interact
directly with SQL. Python has, I think it’s called. SQLite 3. You can just import SQLite 3 and
execute SQL queries directly, but I don’t know about you,
but I really don’t like, just, writing out SQL queries. It tends to get really
long and complicated.>>And so, something that
I like to use is what’s known as an ORM, which is an
object-relational mapping. And the point of an
object-relational mapping, is that there are two different
ways you can think about databases.>>So the example that
Professor Malan usually uses in class, is the Excel table, where
you have these rows and these columns, and that’s really useful for
how it is represented in SQL and how you interact with it, but
another way that it’s actually useful to think about it sometimes,
is in terms of classes and objects.>>So instead of thinking
of each table as having this row that has certain
information, you can actually think of it as being
each table is a class, and then every instance of the
class has certain properties. So, in this example, the instances of
the class are the rows in the table, and then each property would
be a column in the table.>>So, the ORM that I like
to use is called peewee. It’s really small, sort of like Flask. I think that they go well together,
but there are a lot of other ORMs that you can use. A more popular one is
known as SQLAlchemy, and I can’t remember why I originally
chose peewee over SQLAlchemy, or I would tell you why I
think it’s the best one, but we’re just going to use this
one because I know how to use it.>>So, one question is, why
should you bother using an ORM, instead of just directly
writing SQL queries? And I think the best case,
is that you don’t actually have to write SQL queries. It’s much easier, as I’ll show you, to
do things like selection, insertion, deletion, especially creating tables. It’s much easier to
write a class structure, than it is to structure a Create
Table statement, but one thing to be aware of, is that
the ORM will try its best to figure out what the most
efficient SQL query would be, but sometimes it gets it wrong.>>And especially if you’re
working with a big database, you can notice that a query
that should be running fast, is actually taking longer. And if you look under the hood at how
the ORM is interpreting that into SQL, it might be doing something
really ridiculous, just because it sort of
gestured your intentions wrong. And, there have been times where
I’ve had to override it, and just execute my own SQL queries, just
because it was parsing in a strange way. So, there is some
overhead, just in the way that it compiles your
statements down into SQL.>>So, let’s look super quickly at
a simple example of a data model that you might use. So, this is Python code, and so the
first thing you want to do is from peewee import*. So, unlike Flask, where you have
all these individual modules, and you want to import Flask, and
write a Template, and some others that we’ll see later, from peewee,
you can just import everything, because it’s a pretty small library.>>So, the first thing you want to do, is
actually create this database object. So, you have db=SqliteDatabase,
and then the name of your database. And this will actually
create a database object that you can interact with, with peewee.>>And then, we have the actual
model that we want to create. So the table we want to create. So, within peewee, each class has
its own table within your database. So, all of the classes
inherit from the base model, and the capital M model is
something that’s defined in peewee.>>So, all of your models should
inherit as their highest superclass, they should inherit from the
model, but what’s really cool, is that you can actually have your
models inherit from each other. And a lot of the time, your
data models don’t necessarily make a nice inheritance hierarchy, but
times where they do, it’s really nice, because you have the model
inherent from each other.>>So, we defined this class
‘student,’ which inherits a model, and it has three properties. It has an ID, which is
a PrimaryKeyField, which is something that’s provided
by peewee, name is a CharField, and a grade is an IntegerField. So this may or may not be
how CS50 actually stores all the students’ grades. It isn’t, but this is how I would do it.>>And then it has, within
this class, and this is something you can do with
Python, you can have nested classes. And this is something
that’s required by peewee. So, this class Meta, you have
to specify that the database is equal to the object
that we created above. And this says what file is this table
actually going to be contained in. So this is something that you have
to do within all of your models. You just have to specify
within this Meta class that database is equal to db. So what I usually do, if I have
a bunch of different models, is that I have one base
model, that I usually just call “base model”
that has the Meta class, and it sets the database equal to db. And then all of my subsequent models
will inherit from that base class. And then I don’t have to worry
about setting the Meta class.>>So, when this actually gets
compiled down into an SQL statement, it looks like this nasty thing down
here, “Create table student ID integer, ” whatever.>>And, I think that it is shorter,
this SQL query right here, but if you look at this class here
you can see exactly what’s going on. You can see what sorts of fields
there are, what they’re are called, and so, I think that
looking at this Python code is a lot more readable than
trying to write this SQL query.>>So, in order to actually
use the database, we have to connect to it within Python. So, I usually write a function called
initialize_db that does two things. It takes in the database
object db and it connects to it, which just opens
up section to the database. If you’re just running though
website on your local machine, it isn’t a super big deal to worry
about connecting and disconnecting, but if you are running
it on a website, you want to make sure that, whenever a user
connects to it, when they close out the website, they disconnect so that you
don’t have a bunch of people connected to your database all at once.>>And then, when you
connect to the database, you want to call db.create_tables,
and list the models for what you want to create tables. So here, I just want to
create it for this student. And then, what’s important, is to
specify safe=True most the time. So what this statement
will do, is that it creates tables for the
student model, but only if that table hasn’t
been created already. That’s what the safe specifies. So it won’t overwrite
your existing table, it will only create a new
table if there isn’t one there. So, you could just create
the tables once using SQL. And then have that database sitting
there, and then connect to each time, but it’s usually nice just to
put in this create_tables call, so that, if you ever delete your
database when you run your web app again, it will recreate it.>>So, just make sure that safe
is specified to be True, or you will find your data just
getting clobbered every time. And then, you can just call
initialize_db to establish a connection, and create
tables if necessary.>>So, the most common thing
that you will want to do, or one of the most common things,
is to actually insert things into your database. And so, rather than having
to write an insert INTO statement with all of
the values specified, you can actually call
function on the student class. So, when you create a class
that inherits from model, it has this create method.>>So, you do class name.create,
and you specify the parameters that you want to pass in. So, if I want to add some students
to our CS50 grade book example, I’ll put in David, who has a
very good grade, he has a 95. And myself, who’s not doing
so well in CS50, I have a 50. And so, the nice thing about
what this create function does, is that it returns the instance, or the
row, that it created within the table, and so then you store that in a
variable, and work with it later. You can change around, which
I’ll show an example of.>>Notice that I didn’t
have to specify the ID, because since it is the
PrimaryKeyField, it will automatically increment it if you don’t specify it. And, in fact, you probably
shouldn’t specify it, because you might accidentally
clobber someone else’s ID. And you want to make
sure that it’s unique.>>So, actually, the most
common thing you want to do, is probably select out
of the database once you have a lot of information in there. And so, if you want to get everything,
so the equivalent of the select star from students statement, it
would just be student.select. And that will give you back an array
with all of the student objects in it that you iterate over you want. You can get things out of it. And most of the time, you
don’t just want to do select, you actually want to specify something. And so, you can chain
together these function calls, like how you would chain
together the statements in SQL. So you can do student.select().where
in this example. And then, you can
specify the conditions, just using normal Python
Booleans to check things.>>So, in this case, you want to
limit what you’re selecting to, where student.grade is equal to 50,
and student.name is equal to Ezra, so that will just get me out of it. And notice, one really
subtle thing here is that, if you want to specify
an and/and or an or/or, in Python, you would normally use,
I think the word “and” actually, but here you use the single ampersand,
which is normally a bitwise operator, but in this special case,
just the way peewee does it, you use the single
ampersand to specify “and.” That’s something that
I get mixed up a lot, but it doesn’t come up
that much in practice.>>And then, once you have all of
the students out of the database, once you have done you’re select
and your wear or whatever, you can use a foreach loop,
just like normal in Python, with any sort of iterator
or with any sort of array. So you can do, for s
in student.select().whe re(Student.grade>So, another thing you can
do, is it’s really easy to update rows within the table. So, remember back here,
your when I inserted, I took the value that was
returned by student.create, and I assigned it to name called Ezra. And so now, you can change the
values within that instance, just like you would a
normal class in Python.>>So you can set ezra.grade=95 and
that will update the local copy, but if you actually want to
commit that change to database, you have to call ezra.save,
so you called the .save method on the instance.>>And so now, I have successfully changed
my own grade within the database. So, then let’s say that I get caught
changing my grade within the database. Professor Malan is probably going
to want to delete me from the class, and so you can call the .delete
instance method just on that thing.>>So, if you wanted to go
back into this loop here, and actually, instead
of sending emails all of the students whose grade is less
than 75, you wanted to delete them, within this loop you could
call s.delete instance. And the very last thing you want to do,
is whenever you establish a connection, and you’re done with your work,
you want to call db.close, where db is that database
object that we had before. And you want to make sure that
everything gets closed out of.>>Cool. So now, I have an example application. I’ve sort of pre-made everything just
so that there won’t be any live coding mistakes, but we can walk through
it and see how you would put Flask and peewee together,
and make a simple app. I call it CS50 rant, and it’s
sort of a simple blog platform.>>So, first, I’ll run it and
show what it looks like, and then we can look more into the code. Okay, so let’s just run this. Cool, I’ll make this
a little bit smaller. It’s not very pretty, just because
I didn’t make a lot of CSS, but what it does is, it has
this database of blog posts, and it goes through all of
them, and it will display them on the page in order of the most recent. And so these are just some posts
that I had saved in the database.>>So if we want to create a new
post, we can go to Add a New Post, and we can enter the title of the
post, so something like, CS50 seminar. Wow, really enjoying the seminar. >>Cool. Then you press post, and it will
redirect you back to the home page, and then you’ll see that
the latest post was added. And we still have all the ones there. So now, let’s step through all of the
code and see how this is implemented.>>So, I think the first thing that let’s
take a look at, is actually the models. A lot of the time, when
you’re designing something, you want to think first about how
you’re going to represent your data, and then design things around that,
so that everything makes sense. And that is actually how I
did it when I was making this, I sat down and thought,
what do I want in a post.>>So, here, we have the same structure
that I was mentioning earlier, where we do db=Sqldatabase(‘posts.db’). In reality, you probably don’t want
to hard code in your name’s databases. That should probably be a parameter
that’s stored somewhere, maybe in a config file, but in
a small example like this, it’s okay to hard code that in.>>So now, we have this Post class,
which inherits from the base model. And it has, again, the
ID=PrimaryKeyField. Actually, if you don’t specify,
if I actually got rid of this, then peewee we will take care of
automatically creating that ID field, and it will automatically
make it a PrimaryKey, which I think is really nice, because usually,
that’s something you want to have, but I like to put it in specifically,
just so I remember that it’s in there. But if you don’t specify that,
that will be there automatically.>>So then, I have a date which
is a DateTimeField, and all these different fields, if you
look at the peewee documentation, it’ll give you a list of the different
types of fields that you can use. For the most part, it’s analogous
to what you would see in SQL. So there is a CharField, a
VarCharFields, TextFields, which are for very long
texts, like a blog post potentially, DateTimeFields,
DoubleFields, FloatFields, all things like that.>>And you can pass in other arguments
to it, which I didn’t specify here. Say, for example, you didn’t want to
allow two posts to have the same title, you could specify
something like unique=True, and that’s just an extra parameter to
the field that when it compiles it down into the SQL, it will specify
that it has to be unique. You can also specify something like
not null and all the other things you normally do in SQL. So, this is a pretty simple
model that has the date. Notice here, within the DateTimeField,
I specified what the default is. I specified it to be
datetime.datetime.now, because of the way that
this gets evaluated, it actually evaluates
the datetime.now when it gets inserted into the database.>>I think that, I’d have
to double check this, but if you did something like this, then
it would actually evaluate that once, and then the DateTime
would always be the same. So, just if you’re doing something
with datetimes, double check that it’s evaluating when
actually gets inserted, or else you might be confused.>>The title is just a
CharField, which there are more arguments you can pass
in specifying exactly how long you want it to be, but here,
it didn’t really matter. And Text is going to be
the text of the whole post, and that’s going to be a
TextField just because you want to allow it to be a pretty long string.>>Then we have this Meta
subclass that just specifies that we want the database
where this is actually opened up into to be the db object that we have here. And last thing we have
here, is just this function that we’re going to
use from our main app to initialize the database to connect to
it, and then to create the Post table.>>Now, let’s look at the main app itself. So this one is quite a
bit longer than the ones that we’ve seen before,
but hopefully not too bad. So, let me extend this out. Okay.>>So, notice and the top I imported
a whole bunch of other things from Flask that we haven’t
really seen before. And hopefully, we can go through
each one of these one by one and talk a little more about
them, sorted by example. So, we have the Flask,
and the render_template, which we’ve seen before,
this request object, which will come up when we look at how
the form that I was showing actually works. Redirect, which lets you redirect
back from the Create New Post back to the original homepage, and then
URL, which is something that lets you figure out where on the
website a particular page is.>>So then, the next
thing I do, is I import all of the information from the Models
files that we were just looking at. And, yeah.>>So, something else new that comes
up when you’re dealing with, especially databases,
is that you can specify a function that gets called before
every request, and function that gets called after every request, using this
function decorator app.before request.>>And so this will get executed
wherever this function is. This doesn’t have to be
called before request, but usually that’s something
sensible to call it. You can specify whatever function
you want to get called there, so I specified this initialize_db
function that we had back in the Models file, so before every request, you
want to connect to the database.>>There are two different
ways you can do this. You can do @app., i
believe it’s after_request. And the difference between
after_request and teardown_request, is that after_request will only happen
if the request actually was valid. And so, only if the
request was successful, if nothing went wrong,
but teardown_request happens in the case of a successful
request, or in the case of an error.>>So, usually, you want
to use teardown_request, unless you want to do
something, especially different in the case of an error. But just for closing the database,
whether it succeeds or if it fails, we do you want to disconnect
from the database. So it’s called, db.close
on the db object.>>Notice that the teardown_request
takes in an exception. So you can check if there was actually
an error when it was closing down, but here, hopefully, there
aren’t a whole lot of errors, so we’re just sort of ignoring that.>>Okay, and the rest of it is not too bad. So, when we go to the home page,
we rendered this home.html Template that will open up. The pass is in post
equal to, and what this does is, remember we have this post
model, so we select all the posts, and then another thing you can do,
you can specify the WHERE clause, you can specify an
order by, and so we take all of the posts that
get selected, and then we order them by the
post.date.descending. And that will specify, when
they actually come out, the most recent one
will be very first one.>>And then, we pass that into
the home.html template, so let’s actually open up
that Template really quick, and take a look at how that’s working. And this is not great HTML, but
hopefully, we can focus on the Python.>>So there’s a link to the Add New
Post, and so this specifies the route within the Flask that we
define, which is right here. This is the new post route,
and we specify that up here. And so that is a link that will then go
to that route within the Flask server.>>The more interesting thing
is this for loop here. So we specify that this
post parameter which was passed into the
render_template function, for every post in the post’s
object that gets passed in. We want to print out
the post title, in H1, and then below, we want to print out
the post text within a paragraph.>>And here, we can actually
call a Python function, so we can call strftime, ST-RF-time,
and you can pass in the format string that you want to print the data out in. So it’s is pretty nice that you can
actually call this Python function from within here. You don’t have to do the formatting on
the controller side, because really, formatting the date is something that
you want to deal within the View.>>And all of these percent
things is not super important. If you look up the documentation
for the strftime function in Python, it specifies all these
things, but that’s how, when we were looking
at the home page here, it formats this with a nice
date, and it specifies AM or PM, but normally, if we
didn’t have this here, you’ll probably get some garbage
date that didn’t look very good. And then we specify the
post.text, and I could have put a couple of
line breaks here, just to put some spaces between each post.>>So, I think the most important
thing in this example, is that you can use this for loop. And this is analogous to
things you can do in PHP. You can iterate through,
everything gets passed in, and so, instead of having to do
copy/paste, copy/paste all the HTML, you just have to write it once, and then
you can iterate over all of the posts.>>And this is something
common that you want to do, when you have a lot of data,
is that for everything in your data, you want to do a similar thing. And then, just remember, that when you
want to print out something explicitly in the HTML, you use
the double braces here, but then when you want to specify
some information about a condition, or about a for loop, you
use the percent bracket.>>So, going back to the Python
code, so that explains what’s happening in the main
route, when we go there, it just displays all the
posts, but then the question is, how do we actually get
posts into the database, which is a little bit more interesting.>>So, when you click on the New
Post link, which we saw here, it redirects you to this form. And that’s just a simple call to the
render_template function, which then passes in the new post in HTML form. So let’s take a look at that. So this one is pretty simple. It has a simple HTML form, which
will look a little bit familiar, based on the forms in CS50 Finance. And so, we specify here, the action. And here, if you’re working
with PHP, normally, it would be something like,
create.PHP, but here we actually specify a route
within the Flask server. And so, this route corresponds
to the create route that we have here, which
we’ll go into in a second.>>And so, we specify that
it’s a post method, because we want to send
this form data, and usually when you’re sending data from a form,
you might want to use a post request, just so you don’t end up
with this big, unwieldy URL. But you could also use a GET request,
and pass it in with variable routing, but for forms, it’s nice
to a post request here. And so then, just like you
would do with HTML and PHP, you can specify these text inputs,
and you can specify the name of them, and that’s the name that will get passed
into the request object within Flask.>>And then we have a Submit
button that says Post. And here, Post is the name of the
button, because it’s a blog post, but here, post is the request method. So those are the same word
but actually unrelated. Yeah>>Going back to the Python code,
when we called the create method, notice here that you can
actually specify within the route the request methods
that you want to accept, and so here, I specify that I
only want to accept a Post method. So, if I actually try to visit the page
directly, which is using a GET request, it will tell me “Method not allowed.”>>And so, you have pages, sort of like
this create page, which I’m only really using as a way for
the form to get submitted, you can specify that you don’t
want people to be able to go there directly via a GET request,
or if you didn’t want, for some reason, a Post request,
you could just specify GET here, but in this example, we just
want the Post request to go out.>>So, when create_post is called, when
we visit that via the Post request, whenever you go to a particular
route, there’s this request object, and we had to import
requests at the very top, but there’s this request
object that gets passed in, and you can access the form data,
which will automatically get filled when you send a request from a form.>>And then, what I think
is really cool, is that the form object to get passed
in, is just a Python dictionary that contains, if you access– so here, let
me pull up the HTML next to it, just so you can have that
as a reference, yes, so the names that we specify here
for the different fields, so the title and the text, we
then just use those over here as indices into the form data. So that’s super convenient.>>So then we call post.create, which
will create and automatically insert this new post object into the database. And I think this create function
here is a really cool example of how powerful Flask is and working with this,
because if you were doing something in PHP, you might have to
do a lot of validation, you would have to then
establish a database connection, you would have to then
execute the SQL query, but here we just have this nice
post.create, which we can then just get the information out
of the request object, and then pass it into a new
post that we’re creating.>>And then, the very last
thing we want to do, is to redirect the
user back to the home. And so we use this
Flask redirect function. And something that we hadn’t seen
before, was this URL function. So the URL for function lets
you pass in actually the name of a function in your Python code,
rather than the particular route that it’s at.>>So I could have just as easily
redirected a user to slash, which would send it back home, but
using the URL for function is nice, because if you change the
location where things are, so let’s say that I change the
home to be at /home instead, this would still then return /home,
because actually goes and looks up the name of the function, and it
will give you back the URL for that.>>So, sort of on the
assumption that you’re more likely to change where things
are, than the names of the functions. You can use this really
nice URL for function. And one thing to be aware of
that is a little bit tricky, is that you think you could just
call redirect on the URL for, but actually all of the routes have
to return some sort of text and HTML, so you actually have to
return the redirect call. Otherwise, you’ll get something
invalid about not returning a string, because all of these have to return
the HTML you actually want to render.>>And so, when you call the redirect,
it redirects you to the page, but it actually returns the HTML
you need to execute that redirect. Go back to the home page. So we have these two different views. We have the home view. Or, I guess I should say templates. We have these two templates,
the home template which displays all of our posts, and
then we have this ad thing, and when you click the post, it goes
to a new route within the Flask, but that route doesn’t necessarily
have a corresponding template. You don’t have to see
anything, but you can still have this work going
on behind the scenes. And then you get redirected
back to the home page. And definitely, it’s easy to work
in some nicer CSS into the template and make this look a lot nicer,
but all of the main logic is there in the Python.>>Any questions about that example? I know there are a lot of
different things going on there, a lot of things we hadn’t seen
before, but like anything. Yeah.>>AUDIENCE 1: Do you have to do anything
special to scrub the data that’s coming in from the form? I noticed you just said “create,” EZRA ZIGMOND: Yeah, so that’s
actually, that’s a really good point. So the question was, do you
need to check and make sure that the data is valid, and
do any sort of scrubbing to make sure that it is valid,
because as you can see here, I’m not doing that. So let’s see what happens
if I post something blank. So, it’ll actually just make a
blank post and fill in the DateTime.>>So in reality, you would probably
want to do something like, maybe specify if title is equal to
the empty string, then don’t do this. Or, only do it if the title is
not equal to the empty string. So it doesn’t actually automatically
take care of that scrubbing for you, so you still need to do that. Yeah, good question.>>AUDIENCE 2: Does it scrub
for sequel injection? Do you know?>>EZRA ZIGMOND: Hopefully,
peewee does that. I think it would certainly be a pretty
bad library if it didn’t do that. I don’t know exactly. I’d have to look at the
query that it generated. I think that, if I typed in a
blog post that sort of looked like a SQL injection attack,
something like this, if this is like a password field or something,
you might do something like this.>>I think that will still
get literally posted, but I think that peewee does do
some sort of scrubbing of the data before it actually executes it.>>AUDIENCE 1: That text field is
designed to take plain text, right?>>EZRA ZIGMOND: Yeah, it is. Yeah. So I think that all of the, so this is
correct behavior, that will do that, but I think that peewee
hopefully does do some sort of protection on their end. And if you wanted to
double check that, there are ways when you generate a query. so you don’t have to
execute it directly. I’d have to take a
look at documentation, but you can actually view the
SQL that it is generating, and take a look at that, and make
sure that it is escaping things.>>Another reason why you might
want to look at the SQL that peewee is outputting, is if
things seem to be going really slowly, you can take a look and see what it’s
actually doing, because it’s sometimes easy to accidentally add
in, the way you write it, you can accidentally have it
select the entire database first, and then do some sort operation
on that, when you really meant to select a subset.>>And so, if things aren’t
quite going right, it’s good to take a look at the request
that’s actually getting generated. Yeah.>>AUDIENCE 2: When you first started,
you put in the port as 5000.>>EZRA ZIGMOND: Yeah. AUDIENCE 2: Is the default with peewee,
or is that something you can change? EZRA ZIGMOND: Yes, so the
port is default with Flask. If you run it without
specifying anything, it will automatically do that. I believe, I’d have
to double check this, but you can specify that in the app.run,
I think you can do something like, port=8080. Let’s give that a try real quick. Yeah, so you can just specify
port=8080, and it will run it there, which I think, if you wanted to run it
on the IDE, I haven’t tried this out, but I think that if you
ran it on port 8080, you’d probably be able
to access the server, just like you were for the website.>>Yeah, but it’s easy
to change that if you have any sort of like port-forwarding
things that you need to do.>>Any other questions? Yeah? AUDIENCE 1: So, I saw in your
models that, as you mentioned, you have to specify the
database for each object. Do you happen to know, does
that make it really easy if you have lots of SQLIte databases that you
want to use for one single web app, that you can just specify a bunch
of different ones in your model?>>EZRA ZIGMOND: Yeah, let me
open that up real quick. So, you’re saying, if you want to have
a bunch of different something, maybe like and the students, for some
reason, something like that? Yeah, so I think that
you still, each model would have to still have just
one database assigned to it, but if you wanted to have
different models that have different database objects assigned
to it, you could definitely do that. So, if I created a new,
something like this, and now this is a student that
looks oddly like a blog post, I could specify that the
database is equal to db_2 here. So, I think that’s the
main way you can do that. >>Cool. Any other questions?>>So just to finish up a little
bit, here are some resources, and these slides will be posted online
so you can actually get to these links. The best resources are really
the documentation for Flask and peewee themselves. They’re written really well, I think. So, the Flask website is here, and
they have a quickstart tutorial that will walk through similar
things to what I walked through, but if you want any sort of review
of the things that I went over, or you thought that I explained
something in a confusing way, they’ll have similar examples there.>>Peewee has documentation, and
they have a quickstart tutorial that goes over the main parameters
that you would want to use. So, the things I talked about with
the unique, and specifying defaults, the different sorts of fields that
you can use, those would all be there.>>Also, if you have
questions about peewee, and you post them on stackoverflow,
the guy who made peewee actually goes on and answers those sometimes. If you have a question, hopefully
he’ll be able to answer it, because he wrote the whole thing. I think that’s everything
I wanted to cover. Thanks for coming out.

Danny Hutson

28 thoughts on “Python Web Apps with Flask by Ezra Zigmond

  1. Hello CS50, Can you tell me the procedure of uploading the Flask app(complete application along with static and templates files)into the Godaddy Cpanel.

  2. CS50 should use Python 3 for demonstration. Python 2 only support tail 2020.
    Beside, Unicode support, brand-new I/O, asynchronize threading are also good.

  3. Makes me enjoy learning Python, I am now trying to build this simple flask blog into my heroku cloud using Postgresql instead. This presentation is awesome, simple and easy to understand! 👍

  4. int so(void){
    long long i; i = 0; while (i<9,223,372,036,854,775,807){printf("so"); i++;}
    return 0;
    })))
    nice tutorial though)

  5. (anyone) How can I get the text input for the "Post Body" to be larger? Is there a way to increase the "window"/box that you type into? (Ezra) Thanks for the tutorial.

  6. Never use: id = "something". id is a keyword in python. For the student model: sid = PrimaryKeyField() would have worked without messing up id.

  7. For those who are wondering about other databases connectivity, Peewee supports Postgres and MySQL. Find more info here – http://docs.peewee-orm.com/en/latest/

Leave a Reply

Your email address will not be published. Required fields are marked *