[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<75), and so this will iterate over each student in the table
whose grade is less than 75, which in this case, is still just me.
And then you could do something within that loop, like send me an email
and tell me to actually turn in my Problem Sets.
>> 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.