[MUSIC PLAYING] SPEAKER 1: Welcome back,
everyone, to Web Programming with Python and JavaScript. So we've been spending
the last couple of weeks working particularly
on JavaScript, working on using JavaScript to be able to
write code that runs on the client, runs inside the web browser, and
allows us to have dynamic web applications that allow for animation,
for instance, or for sending messages back and forth in real time without the
user needing to reload the web browser. And so what we're going to do today is
transition a little bit back to Python. So in order to build these more
complicated web applications, we've been using a Python
web framework known as Flask, which let us using
relatively few lines of code pretty quickly set up a web application. We were able to control routes where
users are requesting information at a particular URL. And we could direct them
one place or another to show one page or another with
whatever information we wanted. And we were able to hook Flask
up to a back end database using PostgreSQL in order to make
sure that our web application could manipulate data in order to add rows
to tables, remove rows from tables, edit them, and so on and so forth. But a Flask is what we would
call a micro framework. It's a pretty lightweight framework
that doesn't have a whole lot to it, just enough to
get us up and running. And so as people begin to work on
more complicated web applications, there are a lot of things that start to
become repetitive, that Flask will just start to get frustrating to have to
deal with the complexities of trying to build things out for yourself. For instance, in many
web applications you'll find that you need to add users and
have systems for users to log in and be authenticated and log out. Or you'll want a way for users of
your website, managers of your website to be able to add data to it,
update existing information, add information to any
of your various tables. Or you might have more complicated
relationships between your data that you want to manage. And if you are writing a
web application in Flask, you would need to build all
of that out for yourself. You would need to design pages that
let people add data to your database, have separate pages and let people
edit them or remove that data. And all of that would be manual
work that you would need to do. But because this is so common, there
are other web frameworks out there that give you a little
bit more out of the box. And so today what we're
going to be introducing is a new web framework that
looks similar to Flask in spirit but it is much heavier weight, has
a lot more features and capabilities that it has built into
it, known as Django. And we're going to be exploring
Django and some of the features that it has to offer when it comes
towards building more complex web applications. And our goal as we look
through all of this is that we'll start by looking
at simple web applications where it seems like we're doing the
same things that Flask could do, but it seems like it's just
a little more complicated. But we'll soon see how using
a framework like Django, we can actually take
advantage of the leverage the Django gives us in order to add
a whole lot of additional features and complexities and nuances
to our application that would take much more time to build out
for yourself by hand using something like Flask and that Django just
makes much simpler for us to build. So let's go ahead and get started by
building our first Django application. And so once you've install
Django, and in this course we're going to be using the
latest version of Django-- Django 2-- which was
released pretty recently, you'll run a command that
looks something like this. You'll get access to a
command called Django Admin. And Django divides all
of its web applications into what are called projects. So think of a project
as just your entire web application that might be composed of
different parts, which we'll soon see. But to create a new project,
you'll run a command like django-admin, which is the name
of the command, startproject to create a brand new project, followed
by the name of the project that you want to create. So let's take a look
at an example of that. If I go under the
command line and I type django-admin startproject and I'll
just call it mysite, for instance. What that's going to do
is it's going to create a new directory for me called mysite. And if I cd into it, I see that
I have a file called manage.py and another directory inside
of it also called mysite. And if I go into that directory, I
have a whole bunch of other files that have just been created for me. I didn't create any of these files--
init.py, settings, URLs, and WSGI. These are just files that are created
for me by default when Django first creates a project for me. So what are these files
and what do they mean? Well, this init.py file tells
us that the mysite directory is a Python package. And a Python package
is just a collection of a bunch of different
Python files that are grouped together for some purpose. And Django was really built on
the idea of these Python packages where we're going to end up
with multiple different Python packages, multiple
different groups of files that each are serving a
slightly different purpose. And Django is going to allow us
to have a web application that uses and integrates with all of these. Manage.py is a particularly
important file. You won't need to edit manage.py. But manage.py is going
to be a Python script that you can use in order to perform
some very useful operations on your web application as you're working on it. And we'll see multiple examples
of using manage.py later. Settings.py is a file
that's created for us that just has all of the
settings for our web application, things like what time zone
the application should be in or what other applications are installed
within our project, for instance. Or what sort of database do we
want to use with our application. These are all things that
are defined in the settings. urls.py is interesting. We'll take it urls.py in a moment. But urls is going to do
is act as the file that takes care of determining
what URLs or what routes the user can go to when
they visit a web application. So in a Flask, if you recall,
at the top of every function, how did we indicate how to get there? How did we in associate
functions with what URL the user had to type
into the web browser? Yeah. We used this route decorator. We say, atapp.route followed by
the route that we wanted to go to. And when a user typed in
that route and press Return, that would be associated
with that function. And every function had
the route on top of it that corresponded with that function. What Django does is it
separates out this functionality into two different places. So we have one file called urls.py
which only contains the URLs that people can go to. And it associates those URLs
with the particular functions that are ultimately going to run in
order to render the response that we want to give back to the user. Wsgi.py, you don't need
to worry about too much. But if you were to ever take a Django
web application and deploy it to a web server, for instance, so that
it could live and hosted online. wsgi.py is a common way
to allow that to happen. And all of that, of course, is stored
inside of this project name directory. So now that we have all of
that set up, the next step is to create what's called
a Django application. So a Django project is
our big picture project that contains all of the
files needed for our website. And a Django project consists of
one or more Django applications or apps where each one
of these individual apps usually just serves a particular
purpose or has some particular function. Django has some built in apps for us. And what we're going
to do is create an app within this project that's going to
handle the routes that we care about in this case. And for this simple
project, we're just going to create a single app
that's going to allow us to take care of some
basic routing, very similar to what Flask allowed us to do. So to create an app, what we're going
to do is inside the mysite directory, we're going to go and we
see this manage.py file, which is the file that allows us to
run some basic commands on this Django project. And the command here is a
python manage.py startapp followed by the name of the
app that we want to create. So let's create a basic Hello
World app, much like we did when we created our first Flask app. And we'll call this app hello. And so what that's going to
do is give us a new directory called hello inside of mysite. And inside of this hello directory
are a whole bunch of other files. And we won't worry too
much about these just yet. But we'll get to them soon. And so now, let's actually
edit some of these files and see what's going on
inside of all of this. So hello is the name of the app that
we've just created, this hello app. And inside of views.py, this is
analogous to what application.py was inside of our Flask application. It's the place where
we're going to be writing the code that ultimately
decides what the user sees when they go to a particular route. And so in this case, I'm
going to define an index function much like I have before. And in Django, all of our view functions
are going to take as an argument this object called request. And you may have seen
something like this in Flask where we had a request object
that contained information like what were the arguments
that were passed into the request and what sorts of cookies, for instance,
might be associated with that request. And so this request objects are as
much the same behavior in this case. And all our index function
is going to do right now is return some basic HTTP response
that says hello, for instance. And so in Django that's going
to look something like this, something like hello world. And of course, HTTP response is
not an object that we've created. It's something built into Django. So in order to use it, I'm
going to have to import it. So from django.http
import http response. So immediately what we're noticing
is that even just to get something as basic as hello world, it looks like
there is a lot more complexity involved in it. Soon the reason for that
complexity will become clear as we begin to build more sophisticated,
more complex web applications. But for now, try to focus on drawing
the parallels and analogies between this and Flask rather than try to worry
about all the specific syntax. So we've added an index
function that says hello world. Is this going to be enough now for
someone to be able to go to a URL and see hello world appear
in their web browser? What are we missing, at least if
you're comparing this to Flask? AUDIENCE: HTML, or-- SPEAKER 1: There is no HTML, certainly. And so later on what we'll see is we'll
modify our view slightly so that we can return a fully fledged HTML file. But even if we were to
add HTML here, what else is missing if you think about how
this file compares to a file in Flask for instance? AUDIENCE: A route. SPEAKER 1: A route, yeah. I didn't specify at what
URL the user is going to. Is it just the empty route
like the default slash? Or is it something else they
have to type in the URL in order to get to this function? I haven't defined any of that yet. And so what I'm going to do inside
of my app is create a new file. And we're going to call it urls.py. Notice this is a different urls.py
from the other urls.py we had. The first urls.py, which is
inside the mysite directory, is the project's URLs file. Whereas this urls.py
file that I just created is a urls.py file specific
to this particular app. In this case, this Django project,
I've only created one app inside of it. But you'll see, as project
start to get larger, that oftentimes Django projects will
be composed of multiple different apps, each of which has their own routes. And often this is a good way to separate
different types of functionality into different places just to
keep your entire project organized as projects tend to get big. And separating things out
into separate applications helps to keep things organized,
makes those individual apps reusable if need be, and can ultimately be a
good organizational tool for a Django project. So inside of urls.py,
we're going to need to include some code that
looks something like this. We're going to import
this path variable. And also, we're going
to import from dot, in other words, the current
directory, the current package, we're going to import our views. So we were just editing
views.py over here where we defined this function
called index inside of it which returns hello world. And now inside of
urls.py I'm going to need to tell Django that I want
to associate a particular URL with a particular view. And the way Django does that is
through a variable called urlpatterns. And urlpatterns is just going
to be a list of all of the URLs that are supported by
this particular app. So in this case, I'm going to say,
path empty string meaning empty route. Just user type slash. They don't need to have
anything after the url in order to get to this particular view. And then I need to associate it with
what function, what view function, do I actually want to run when
someone goes to this empty route. And so in this case, it's
going to be views.index because views is this Python
module that I just imported, this views.py file that I have here. And the name of the function
inside of views is called index. And so I'm saying, when the
user goes to this empty route, here's what you should run, go to the
views file, and run the index function. And here is views.py. Here's that index function. And it returns an http
response, hello world. So that's what that's doing. And we're almost ready to
make this application actually work when we run this project, except
we're missing one last piece, which is that when the Django project
starts up and starts looking for URLs, it's only going to look at the
URLs file of the entire project, this mysite urls.py file
which you'll notice already has a whole bunch of
code in here already. And we'll take a look at some
of what this means later. But what I need to do is this last
final step is to inside of the projects urls.py file, link it to
my app's urls.py file. So this was the urls.py file that
was created for me when I first created this project. And the hello urls.py file
is the file I just created. And I need to tell these
two files about each other, make sure my project file know that
my application's URL file exists. And the syntax for doing that
is a little unclear at first. But what we'll do is
add another path that just says if someone goes to
empty string the empty route, we're going to just route
them to the hello app's URLs and have hello take care of all of that. And so the reason you might do
something like this is that oftentimes if you separated things
into multiple different apps and you want those different apps to be
handled by multiple different routes, for instance. You might say, later, something like
if someone goes to the app one route and goes to app1/foo or
app1/bar, then you might want to direct them to app1'd URLs. And if someone likewise goes
to app2 slash something, you might want to route them
to some second app's URLs. And so this file is
the first place where URLs are dispatched from,
that when someone goes to URL, we look through this file. And if it turns out we should
send them one way or another, then we'll send them to potentially
a different URLs file after that. But for now, this is all
we need to say, just direct the person to the hello app's URLs. Yeah, question? AUDIENCE: How does it know
where [INAUDIBLE] parallel to mysite directory [INAUDIBLE]. SPEAKER 1: So good question. The question was, how
does the application know where the hello module is. When we're running
the application, we're going to be running the application
from the parent mysite directory so that the parent directory can see
all of the individual Python packages. Great question. All right. So after all that
complexity, we're finally ready to actually go ahead
and run the application. And the way we run the application
is again using manage.py. We do python manage.py high
and there is a special command called run server such that
when we type run server, that's going to start
up this application. Here's the URL where it lives at. We'll go ahead and copy that,
paste it into a web browser. And now we see hello world. So that was our very first
web application in Django. It seemed quite complicated. But we'll soon see in just
a moment how we can actually use the additional features
given to us by Django in order to make our application
a whole lot more powerful. But before we move on, questions
about anything we've gone ever so far? OK. So what I thought we'd do now is take
a look at that Flight application that we created way back in
one of the earlier lectures when we were first introducing
Flask and databases where we wanted to create an
application that could maintain flights and passengers and so on. And we'll take a look at
how we can quite easily build this application
in Django and actually make it even more featured than
our Flask version using potentially fewer lines of code, in fact. And so what I'm going to
do is I've already created a basic application called airline. And all this airline
Django project does is it has an app within it called Flights. Inside of Flights is a URLs
file that looks almost exactly like the one we just
created that just directs the empty route to the views.index. And inside of my views file, I return
an http response that just says Flights. And so all my application
does right now is sort of what we just created
in our hello world example, is that if I run the
server and go to this URL, it just says Flights instead
of hello world in this case. But what we want to do
now is start to set up the database for this application. And so Django was designed with
wanting to interact with data in mind. And so it makes it very easy for
us to do something like this. So what I'm going to introduce
now is this models.py file that's sitting inside of
my Flights application. Right now it's empty. And Django by default just
says, create your models here. This is the place where
I'm going to start to define the classes that are
going to define the types of data that I want to be able to
store inside of my database for this particular application. And so, this is very
analogous to what we were doing with Flask SQL Alchemy,
which was a separate package that we had to install in order to allow us
to do these same sorts of things. Django has this all built in into its
own ORM which functions very similarly. So you'll see a lot of parallels,
although the syntax is a little bit different. And so if we want to create a
model for representing a flight, we might define a class called flight
which is going to be a type of model. And if we recall from last time, what
properties did a flight need to have? What stuff did we include
inside of our flights table? AUDIENCE: Origin and destination. SPEAKER 1: Origin and destination. Great. So our origin, for now we're just going
to represent as a character field. And we'll say the maximum
length is going to be 64. Not all databases
require a maximum length. But for some databases, it
can often be helpful to do so. So if you know that there is a bound
upon the length of a particular field, that's often good design practice
to enforce that in there. I believe that there are no cities
that are longer than 64 characters. So that feels like a reasonable limit
to place on the origin of a flight. For a destination, that's likewise
going to be a character field that we'll say has the maximum length of 64. And finally, we might
also add a duration field that is going to be an integer field. And that is going to represent
how long the flight is. And I could do other things, certainly. And Django comes in with all of these
built in types of fields, so character fields, integer fields, Boolean
fields, and other types of fields that map to different types of data
inside of a SQL database, for instance. But this will be quite
simply a basic flight class that is going to represent
origins and destinations and durations. And so now what we're going to do is
introduce the concept of migrations. And so what will often happen is you
build a web application is very rarely is it the case that you will, when you
first start building the application, define all of the tables exactly the way
that you like them with all the columns that you want. Because usually you'll start
out with some basic framework. But as your application starts to
grow, as people start to use it, as you start to want
to add features, you'll want to make modifications
to your database. You're going to want to add tables. You're going to want to modify
the columns in those tables or modify the
relationships between them. And it would be quite
tedious if every time you wanted to make a change
to your database, you would need to not only change this
sort of code, this model code that defines your classes, but
also likewise actually run the the like Alter Table
commands in SQL on your database in order to actually make
those changes happen. What Django does is it uses
something called migrations to solve that problem for us. What Django is going
to do is automatically detect any time we make a change to
our models.py file and to automatically generate the SQL code necessary
to run on our database in order to allow for those changes
to happen inside of our database. And so I'll show you
what that looks like. So what we're going to do is-- oh, and before we can do this, there
is one other configuration step that we just need to take, which is that
we need to make sure that this Django project knows about these
models, that my project know is that this application
exists and has these models. And the way that I do that is
by going into the settings file for the application and
going to this installed apps variable, which is a
list of all of the apps that are installed on
this Django project. There are already a whole
bunch of apps in here. These apps just happen
to be built in apps into Django that serve easy purposes. So we have an admin app,
which we'll look at later, an authentication app
which we'll look at later, and other apps that serve other purposes
that many web projects might want. And we'll see examples of those later. But right now, we can
just say that we have this Flights app that is installed. And the way to indicate that we
actually have this app installed is to include flights.app.FlightsConfig. Reason being, inside of
our flights directory here, we have inside of it apps.py which is a
configuration file for the application. And inside of that is
FlightsConfig, which is the class that defines the
settings for the application. So no need to worry too much about that. But just know that we do need
to insert this line here just to say this application is
installed on this project. But what we really want to do now is to
create the table for managing flights inside of our database. So what is that going to look like? What I'm going to do is, using
this manage.py script yet again, I'm going to run python
manage.py makemigrations. What python manage.py
makemigrations is going to do is look through my model files,
look for any changes that have been made to those model
files, and automatically generate a migration, which is going
to represent the changes that need to be made to the database in
order to allow for these new changes that I've made to the model
file to actually work. So I run python
manage.py makemigrations. And what we see is that it's created
this new file called 001_initial.py. And it said it's created
the model Flight. It's detected that I've
added this new Flight model. And now it's telling me
that it's now been created. So what's inside of this
001_initial.py file? Well, if we take a look
at it, we see that it's created this class called migration. And inside of it, it's
got all this information about things that should
happen to the database. In other words, we should add
an ID, origin, destination, and duration column to this
new model that we're creating. And notice that I never
actually specified in Django that I wanted an ID column. Django adds an ID column
by default because it assumes if I'm creating
a table, then it's going to have some sort
of primary key column by which I can identify
individual items in that table. So I don't need to explicitly
create an ID column for every single one of my tables. Django takes care of that for me. So what would actually happen if I were
to apply this migration to a database? Well, we can take a look at
what that code would look like. You likely don't actually ever need
to run this command unless you're curious to take a look at it. But what if I run python manage.py
sqlmigrate followed by the name of the app, Flights, followed
by the migration number 001, what I'm going to get is the SQL code
that actually corresponds to this migration, migration 001. And what we see is it's
a command that looks very similar to the types of things that
we were doing way back when we first introduced SQL. It's the Create Table command. It's creating this table that has
an ID column that's a primary key, has an origin column, a destination
column, and a duration column. And that's all SQL code
that I didn't need to write. It was generated for me by
Django's migration system just by looking at what I
said I wanted in my database and generating the code to
run that for me automatically. And so now, if I want to apply
this migration to the database, actually now creating the
table inside of my database, I'm going to say python
manage.py migrate. And what that did is it applied
to all of these migrations, including some default Django ones,
but it also applied this flights 0001_initial migration. And now my database is going to have
this table that represents flights. Questions about the migration
system so far, before we move on? Yeah? AUDIENCE: How does it know what
database to make changes to? SPEAKER 1: Great question. How does it know what
database to make changes to? That's all defined inside of the
settings for the application. And so if we go into
the settings.py file and scroll down to this databases
variable, what we'll see is that we've said that the engine
for this database should be SQL Light. And SQL Light is just a slightly
different version of SQL, much like PostgreSQL, that happens
to store everything locally on a file instead of running separately
on some server somewhere which makes it very useful for local testing. If you wanted to use PostgreSQL
as we did in project one on a project in Django,
you could change the engine to just be a PostgreSQL
database, which Django supports. And in this case, we've just specified
what the name of the databases is. In this case, it's
db.sqlight 3 which happens to be a file that exists
inside of my directory. But if you change this
to a PostgreSQL database rather than providing a
name here, you would likely specify the host where the database
exists, the port on which it's on, and a username and password, much in
the same way that you did in Project 1. But that's all configured
inside of the settings file. Good question. So that database file now has
the models that I've created. So let's go ahead and actually try
and create some of these flights by looking at how that happens. And so what Django allows us
to do is it gives us access to what's called a shell. So much like Python's
interpreter, but that is tied into this Django application
and lets us directly modify the database without too much effort. And so to run the shell, again I'm
going to run python manage.py followed by the word shell. And now I'm inside of Django's shell,
where I can run Python commands. And in particular, I'm going
to say from flights.models-- remember I had a Python
package called Flights, my app, inside of which was
a file called models? I'm going to import Flight. And now what I'm going to do is
I'm going to create a new flight. I'm going to say f is going to be
equal to a flight whose origin is New York, whose destination is London,
and whose duration is 415 minutes, for instance. And so I've now created that flight. And much like in SQL
Alchemy where any time you add something or
edit something, you need to save that change, likewise in
Django you need to do the same thing. And the syntax for doing that is f.save. That is it effectively
running that commit operation on a bunch of lines of SQL. And so f.save says actually
save this new flight that I've created to the database. And now if I want to query
for something in the database, if I just want a query for all
flights, I can run Flight.objects.all. It's just a simple query that
will get me all of the flights. And that returns to me a query set--
which you can think of as a fancy type of list that has
additional features to it-- of all of my flights which right now
is a list of just a single flight, this flight object 1. So this flight object 1 in
parentheses, isn't very helpful for me to look at if I'm just looking
at this in the Python terminal trying to understand it. And so very often what you'll see
is that we'd like a better string representation of this flight. I'd like some easier way to look at this
flight and understand what it means. And so what we can do is
inside of our models file, I can add a underscore underscore
STR function or a STR function. And what the STR function does, and this
works for any class not just classes in Django, is it defines
what the object should look like when it's printed out to a screen. And this will apply whether
it's printed out to a terminal or even to an HTML page,
as we'll see shortly. So if I want the string
representation of the flight, I'm just going to say you should
return the ID of the flight followed by the origin of the flight to
the destination of the flight. And so I'm just going to return that. And so now if I go back into the shell,
go to from Flights.models import flight and go Flight.objects.all, now I
see that in the response that's come back to me, I have this
Flight 1 New York to London. And I can very clearly see
that that flight is there. And if I wanted to access it, I might
do F equals Flight.objects.first to get me that first flight. F is now flight from New York to London. I can say F.origin, that's New York. F.destination, that's London. F.id is 1. And so I've now created my flight. If I want to delete that flight
later, it's as simple as a F.delete. And that deletes the flight. Questions about anything so far? All right. So now, we'll build on
this idea of migrations, build on the idea of being able to
change the contents of our database after we've already gone ahead
and created some basic tables. Let's say that now rather than just
defining the origin and destination as character fields where I type in text
for the origin and destination, what would make more sense
from a design perspective if I'm going about designing an
application managed by an airline? If I want to maintain flights? Rather than a text field for
the origin and destination, what might I use instead? Yeah? AUDIENCE: [INAUDIBLE]
fields [INAUDIBLE].. SPEAKER 1: Yeah,
something like an ID field where I actually have
a table of airports, for instance, that maintains
all the different airports. And my Flights table now
rather than just be the text New York, the string New York as the
origin could really be referencing the ID of some table of airports. So certainly that would
be a good design decision that we could make if
we're going to start to manage additional
information about our airports. So let's take a look at that. So I go ahead and exit the shell. We'll go back into this models file. And I'll create a new class. We'll create a new class that's
going to represent an airport. And my airport is going
to have a code which is just going to be that three character
code that represents an airport. And it'll also have a city, that
whatever city the airport is in. And that's going to be a
char field of max length 64. And if someone tries to access the
string representation of an airport, I'm just going to return to
them the name of the city and then in parentheses
the code of that airport. And so define a new
class called airport. And what other change
do I now need to make? AUDIENCE: Change Flight. SPEAKER 1: Yeah, I need to
change Flight now because origin and destination are no
longer character fields. What origin is going to be
instead is a models.Foreignkey. And so, it's going to be
models.Foreignkey referencing some other class. And the class I'm going
to reference is airport, meaning origin is going
to be a foreign key. It's going to point
to some other airport. And I'm going to add a special on
delete property called models.cascade. And so, what Django
models allow me to do is determine what happens when I
delete an airport, for instance. Because if I have airport
JFK International Airport, and I have flights that have an origin
of JFK International Airport, what should happen to my data if I were
to ever delete the airport at JFK? What should happen to any of the
flights that have an origin of JFK? You know, there might
be multiple options. And depending upon how your
application is designed, you might want things
to operate differently. Maybe you want there to
be some sort of error where we say, don't allow
you to delete the JFK airport if there are flights that
have an origin of JFK. Or maybe you want the behavior to
be, when I delete the JFK airport, go ahead and just delete all of the
flights that have an origin of JFK because those flights
can no longer exist. And there are other options as well. But what models.cascade
is going to say is that if I delete the airport
that has that particular origin, then delete the corresponding flights
as well because those flights are no longer valid. And so these are just
decisions that you'll have to make based on the desires
of your particular application and what you need for that. And I'm using
models.cascade in this case. And I'm also going to use
a related name argument. And I'm going to call it departures. And what related name is going to
allow me to do, which you'll see later, is that if I have an
airport and I want to access all of the flights whose
origin is that airport, I can use the name departures
to be able to access that. And we'll see an example
of that later probably. And likewise for
destination, that's also going to be a foreign key that
points to another airport, also have it cascade upon deletion. And it's related name is
going to be arrivals instead. Because if I have an airport
and I get its arrivals, that should get me all of the flights
whose destination is that airport. Now notice here that I am not
saying origin ID or destination ID. I'm just saying the origin is an airport
and the destination is an airport. And I'm leaving it up to Django to worry
about how actually to name the columns and how to do the associations of
having an ID map to something else. All I have to worry about here is
the data that I want to deal with, that the origin should be an airport and
the destination should be an airport. And it's up to Django to figure out how
to make sure those relationships work. So now, I've made those changes. And if I want to apply them to the
database, what do I need to do? Migrate them. Great. So I'm going to run python
manage.py makemigrations. That's going to detect any changes
that I've made to my models. And in this case, it's
detected that I've created a new model called airport. And I've altered the destination and
the origin fields on my existing flight. And now if I go python
manage.py migrate, it's going to apply that migration. And now my database is updated with
this new format that I've just created. And so, let's go ahead
and go into the shell and experiment with that a little bit. So let's go from Flights.models,
let's import Airport and Flight. Let me define a new
airport called JFK which is going to be an airport whose code
is JFK and whose city is New York City. And let me define a new airport
called LHR whose code is LHR and whose city is London, for instance. And now if I want to create a
new flight, all I need to say is f is going to be a flight
whose origin is-- or actually, let me first save JFK and save LHR
because I've just created those. But if I want to create
a new flight, I can say I want a new flight whose origin
is JFK, whose destination is LHR, and whose duration is 415 minutes. The origin is, in fact, JFK Airport. The destination is
London Heathrow Airport. I don't need to specifically
specify use the ID column. Django is trying to
abstract some of this away so that I don't need
to worry about it as much. So I create that new flight. I'm going to go ahead
and save that flight. And now I have two airports and
a flight saved in my database. And so if I go to f.origin, I notice
that f.origin is this airport object. Now I can just say
f.origin.code, for instance, to get JFK as the code of
the origin of this flight, or f.origin.city to get New York City. And likewise, if I have JFK,
and I go to jfk.departures.all what that's going to do is query
for all of the departures that are departing from JFK. It's going to give me this
query setback that has flight from New York City to London. So questions about the models that we
created or the migration that we made or how our data is now going to operate? Let's move on to talking
about rendering templates. And so one feature that we
talked about just a moment ago was that we might want
for our application to be able to not just return text
but to actually return an HTML template, much like we saw in Flask. And so just like in Flask
where we used an HTML file to say, render this
template, we're going to do something very similar
in this application as well. Rather than return HTTP response
flight, I'm going to return render. And render takes a couple of arguments. The first argument is the request. And the second argument is
the name of the HTML template that we want to render. And so let's go ahead and render
flights/index.html as the template I want to render. Of course, now I need a
flight/index.html file. And by default, those are going to be
stored in a directory called templates. So I'll create a new
folder, called it Templates. Inside of Templates, I'll create
a new folder called Flights. And inside of Flights, I'll go out and
create a new file called index.html. And that's just going to
be a basic HTML file that is going to be the template that I want
rendered when I run my application. So right now, it's just
going to say flights. And now if I exit and run the
server, when I go to this URL, I now see HTML page that has a big
heading called Flights and a title up here that says Flights. And so this is very analogous
to what we had in Flask. We're rendering this
particular HTML file. And here's the contents
of the HTML file. But now, of course, we don't
just want our Flights page to say Flights in this big heading. We want it to actually
show us the flights that exist inside of our database. So in Flask, how would we
have done something like that? AUDIENCE: [INAUDIBLE] SPEAKER 1: Yeah. With Jinja. We would pass information
into this template in order to allow it to render. And Django has its own templating system
that looks very, very similar to Jinja. And in fact, in many
ways, it looks identical. And we'll go ahead and use that now. And so the way Django passes
information into the template is through a special context dictionary. And so this context
dictionary is just going to be a Python dictionary that
will maintain keys and values. Keys as the things we want
to plug in to the template, as the names of those things. And the values are what we
actually want to plug in. So let's say Flights is going to
be something we want our template to know something about. And it should have a value
of flight.objects.all. I just want a query for all the flights. Save that inside of Flights. And we're going to
ultimately pass that context in to our template as
a third argument to it. Of course, now I'm using
this variable flight which doesn't exist in this file. So I'm going to need to import it. I'll say, from .models import Flight. In other words, from this
current packages models package, our models module, import
the flight variable. And so now I can query for all of the
flights, save it inside of the context, past the context in to my template. And now in my template, I
can create an unordered list. And this syntax is going to look
basically identical to Jinja which we saw from Flask. I can say, for flight in
flights, create a for loop that's going to loop over all of my flights. And for each one of those, let's have a
list item where that list item is going to be just plugging in .flight. And so now, if I refresh
the page, what I get is Flights with this unordered list
of the single flight that I have. And notice that I didn't specifically
say, use the ID of the flight and the origin and the destination. I just said, in curly braces,
plug in the flight here. So why is it rendering nicely
with the ID and the origin and the destination with
the word to in between? AUDIENCE: [INAUDIBLE] string. SPEAKER 1: Yes. Exactly, from the string
function, the underscore underscore STR function that
defines what should the object look like when you just paste
it into the terminal or when you just render
it on an HTML page. It will use that STR function to
determine how that flight actually shows up on the page. So now we've recreated a lot
of that Flask functionality, of taking context information, passing
it into a template and then using it. Questions about anything so far? Yeah? AUDIENCE: Why is the [INAUDIBLE]? SPEAKER 1: Oh, good question. So the question was, why is this
directory structure inside of templates a separate directory
called Flights inside of which is index.html as opposed
to just having index.html there. You could do it where you just
had index.html inside of Flights. And you change the template name and
views.py to just be flights.html. It's generally good practice to
what's called namespace templates by putting them in a directory with
the name corresponding to the app. Because if in larger
Django projects, you had multiple different apps, each
of which had an index.html file, that would potentially create ambiguity,
where if you tried to render index.html Django wouldn't know,
do you mean index.html from app number one over here
or app number two over there. And so what namespacing the templates
does is it means that I can explicitly say, flight/index.html is the
index.html template of the Flights app. And that way, even if there are
other apps that have an index.html, there's no ambiguity as to
which one I actually mean. AUDIENCE: So this template's folder is
already specific to the Flights app? SPEAKER 1: Correct. So this template's folder is
specific to the Fights app. But when Django is
rendering a response, it's not looking only in
this templates folder. It could look in templates
all across the entire project. So that ambiguity is still possible. Good question, and certainly something
that's not very intuitive at first and it seems sort of redundant. Other questions about the
structure of things so far? So what we'll take a
look at now is what we need to happen if we wanted to
allow ourselves to add or edit data inside of our database. And so right now, we've been doing
this all via the command line, that I logged into the shell. I then use that shell to create
new flights, create new airports. What I'd really like to do is
not have to write Python code to be able to do that, but to be
able to go to part of my website where I can click add a new flight
from this location to that location and be able to save that information
or update existing flights. And if you were doing this in Flask,
it would be a non-trivial task. You'd have to build out a
page to list all the flights, build out a page to list
all of the airports, figure out a way to make sure
that when you add a new flight you can choose from one
of the valid airports. And this would be quite
a fair bit of code. But what Django does, because
it's designed for applications that use a lot of
different types of data and they know-- the
designers of Django-- knew it would be quite
tedious if anytime you wanted to have an application that
let us add or modify information, you had to build that yourself,
Django comes with a built in app called Admin which makes it very
easy to add and modify existing data. And so I'll show you what that admin
interface looks like right now. And this is perhaps one of the
most powerful features of Django, especially when it comes to
dealing and manipulating with data. So I'm going to go into admin.py now,
this file inside of my Flights app. And the first thing I'm going to do
is say from .models import Airport and Flight because I want my admin
interface to know something about the airport and flight. And what I'm going to do is
say, admin.site.register.airport and admin.site.register.flight. So the Admin app is an app
that's built into Django that makes it very easy
to update existing models, to modify the data inside of models. And what I'm doing here is registering
these models with my admin site, saying that I want my admin
site to be able to manipulate Airports and Flights. In order to access
the Admin site, you're going to need to log
into the Admin site. And see, here's another case where in a
Flask application, if you wanted a way to log into something, you'd have
to build this out for yourself, design a users table, figure out
how to let people be authenticated. But in Django, this is all
built for you out of the box. And so what I'm going to do now is
just create a first user account inside of my web application. And there's a special command for that,
python manage.py createsuperuser which is going to create a super
user account, an account that has access to everything in
my database, so to speak. And so I'm going to create that. It asks me to type in a user name. I'll do brian. Email address, I'll
type in email address. Password is just the
password I want to use. I'll type in a password. Type it in twice. And now it's created that user for me. And so that's been saved into a users
table that I didn't have to create. It was built into Django,
built into this project without me needing to do anything. So now let's take a look
at the Admin site itself. So if you were paying very careful
attention to the urls.py file inside of the project, you'll
notice that what we did is we first created a path that just
took us to our application's URLs. But there was also this
admin route that was built in to the URL patterns for
us, that came with the application, that I didn't put there. And so let's go there
now and see what it is. So I'm going to say python
manage.py run server. I'll go ahead and go to this URL. And I'm going to go to /admin, because
that was the URL that was given to me. And when I go there, I'm taken to
this login page that I didn't write. It's an app built into Django
called Django's Admin App. I'll type in that username that
I just created, that password. And what I'm taken to
now is a user interface that lets me add and manipulate
the data inside of my database, that I can click on
airports for instance, and see here my two airports,
London and New York. I can click on London if I want to
change the code for London airport, I can change that here, change the city. But let's just say now that I want to
create a new airport, for instance. So let's say I add a new airport. I want the code to be PVG
and the city to be Shanghai. And I save that. And now I've created a new
airport inside my database. And this is all stuff I'd have
to build for myself in Flask but you don't need to in Django
because it all comes for you inside of Django's admin interface. You just click Add Airport. You type in the city
and code that you want. You save it. And then that airport is added. Likewise, I can do the same
thing with individual flights. I can click on Flights. I see that I have this existing
flight from New York to London. If I want to create a new flight,
I just click Add Flight here. And the whole process
of the origin needs to be an airport is
auto populated for me. I can just select, OK, this is
a flight from Shanghai to Paris. And the duration of that flight
is going to be 760 minutes. And I can save that and now
I've created a second flight inside of my admin interface as well. And so this just makes
it very easy for me to manipulate data using a
web user interface that's created for me, without me needing
to go through the effort of writing that for myself. Questions about this interface? So this comes out of the box
with all Django applications. And all you need to do to be able to
use it was for Airports and Flights to show up here. I just needed to register
these models with the admin site on that admin.py
file just a moment ago. So yes? Question? AUDIENCE: So this is
on the project level? Or do you have it on that
[? app ?] [? level? ?] SPEAKER 1: So the Admin
interface you're looking at here, this is part of an Admin app. So it is an app within my project. But it's not an app that I wrote. It's an app written,
that's built into Django. Yeah. AUDIENCE: But it's not
an each individual-- Flights1 and I could have another app
that did something else [INAUDIBLE]?? SPEAKER 1: Oh, good question. So what would happen if I
had multiple different apps with multiple different models? Where would these show up? They would all show
up on this Admin site. What you'll notice here is
that this Flights heading, that corresponds with the name of
the app, that these models Airports and Flights, these are models
inside of my Flights app. And likewise, I have this
authentication and authorization app-- which I didn't create, it's
just built into Django-- that has users already
and groups of users if you want a group users
into different permission levels, which is a common thing that you
might want to do in a web application. That's built into the
authentication authorization app that comes with Django. And so those models
are already registered without me needing to do anything. And so if I added yet
another app and had models that I wanted
to interact with, it would show up as a separate
heading underneath it with additional models below that. AUDIENCE: Is there ever any
error checking of what you do? Like if I could enter a flight that
went from Paris to Paris, which I could, is there a way to stop me
from doing this [INAUDIBLE]?? SPEAKER 1: Great question. Is there a way to stop
us from doing something stupid like adding a flight from Paris
to Paris, which right now we could do? And the answer is yes. So this is Django's
default Admin interface. But Django lets you customize
this Admin interface in almost any way you want adding your
own logic and your own code to it. It's a little bit beyond what
will have time to go into here. But certainly it's
something that you could do. You could modify this Admin
interface to add constraints, make it behave the way
that you want it to behave. And certainly that's a possibility. So this is often just
a great place to jump start the process of allowing
yourself to modify data. And this Admin interface isn't meant to
be used by all users of your website. It's generally meant
for content managers of your website, the
people that are managing the information that other people
are seeing, where you will likely use the Admin interface to populate
your models, add the data that you want. And the users will be
looking at a page sort of like this that is
rendering information that comes from the data that's been
populated into your site already. Let's take a look at adding some
more routes to this application. Maybe I now want the ability
to have arguments in my URLs, like an integer saying if I go
to myurl/1, that should take me to information about flight number 1. And likewise /2 should take me
information about flight number 2. So to do that, I'll
need to add a new URL. I'll go into my urls.py file. And I'm going to say whenever
someone goes to a particular URL-- and I could just type a URL are here-- but I want someone to be able to
type in any number, any integer. And so the syntax for this
looks a lot like Flask. If they type an integer,
we'll call it flight id, then the function I want
to run is views.flight. Of course, view.flight
doesn't exist yet. So let me go ahead and create it. I go into views.py,
add a flight function. And inside of the flight function, I'm
going to say let's try and say flight equals flight.objects.get pk equals-- and then I would need the flight id. So actually, sorry,
the flight id is going to be provided as a second
argument to the flight function. Because it was a
parameter in the URL, it gets passed into the
function when it gets called. And so the flight, I'm going to get the
flight with PL, Primary Key, flight id. I could also say, id equals flight id. They're almost the same thing. There's some slight, subtle differences
because your primary key might not be called id for instance or you might
have a more complicated primary key. But here I'm saying, get the flight
number whose number is flight id. And what could go wrong here? Well, if the flight doesn't exist, if
I tried to access flight number four but I don't have a flight number four,
for instance, or flight number 28, then it's going to throw an exception. So I'm going to say,
except Flight.doesnotexist. This is a special exception built
into all of my Django models that is thrown whenever I try and
access a flight that doesn't exist. So if the flight doesn't
exist, then what I want to do is raise some sort of error. There should be some sort of error. And Django comes in with a built in
http 404 for 404 not found error. Or I can just say, flight
does not exist here. And if the flight does exist,
then let me just go ahead and put it inside of a context object. The flight is going to be the flight. And I'm going to render a response. I'm going to render the flight.html
page, which I haven't created yet but I will in a moment,
passing in that context. And so, what I'll do here is I need
to import this http 404 function. And I'll import it up top. And so now I have this
http 404 function. And now what I need to do is actually
create this flight.html file, the file that is going to show me what's
happening on any individual flight. And so I'll go ahead and
inside my Flights directory, add a new file called flight.html. It's going to look a lot
like index.html so I'll just go ahead and copy it for now. But the fact that I'm copying
this code should suggest to you that there may be design
improvements to be made that we'll take a look at it a moment. And this is going to
be flight, flight id. And let's go ahead and include
some information about its-- origin is going to be flight.origin. And its destination is going
to be flight.destination. So now, if I go back here, this default
route shows me all of my flights. If I go to /1, now it shows me flight
1 whose origin is New York City, whose destination is London. And likewise if I go to flight/2,
that shows me Shanghai to Paris. If I go to /5, for instance,
a flight that doesn't exist, then I get page not found,
the flight doesn't exist. This is Django's default 404 error page
when I have the debug settings to true. You can customize the error pages,
of course, if you'd like to as well. But questions about
anything we did so far? So the three steps that we needed to do
to make this happen were add the URL. So when the user goes
to this URL, what should happen when they type in a number? Send them to the views.flight function. Then in the flight function,
we said try to get the flight. If it doesn't exist, raise a 404 error. And then render this template passing
in the context of the new flight that we just wanted to
display information about. And inside of our html
page, we were then able to, in the body of the html,
display information about that particular flight. Questions about any of that? We'll take a short break. And when we come back, we'll take a look
at some additional features of Django and some more powerful things you can do
with its model and database structure. Welcome back, everyone. So let's pick up where we left off. We were working on this
airline application and working on building
a system for people to look at-- an airline could
use to see a list of flights and look at individual flight pages. And right now, our airline
program has two main routes. We have the default route which
just displays a list of flights. And then if we go to slash
something, it displays details for that particular flight. What we might like now is the
ability to link between these two such that the individual flight page
can link back to the original page and the original page can link
back to the individual flight page. And a common paradigm for doing
this in Django is to name our URLs. So right now, each one of
our URL paths has two things. It has the route you would
go to and the function that would be called when
you were to go to that route. But what we're going to do
now is add a third thing to each of these paths, which is a name. I going to call the-- I'm going to name the
default route just index. And I'm going to name the
individual flight route, flight. And the reason I'm
giving it a name is such that when I link to a different
route at some other page, I would like to be able to refer to
that route by name rather than by route. I certainly could link to just, if you
click here, you go to flash. /1 or /2. But Django is designed such
that what the user sees and the code are as
separated as possible such that if you wanted to change
the route that the user sees, you wouldn't need to go
into the HTML template and actually change any code
in order to make that possible. You could change the route and
leave the name in the same. And then it would work just fine. So now that I've named these
routes, how do I actually use them? Well, inside of Flight.html here, let's
at the bottom offer the user a way to get back to where they were before. So let's add an a href which is
going to be equal to something, we don't know quite yet. And it's going to be
back to full listing, taking me back to the full
listing of all the flights. And the link that I want
to link to in Django syntax is to say I want to link to a
URL, and then in quotation marks-- single quotation marks
because I've already used double quotation marks here-- I'm going to give the name
of the URL I want to link to. So you URL index to say,
when you click here, take me to the URL for the index page. So now if I am on this
page on the flight 1 page, I had this back to full listing
click button, which takes me back to the original Flights page. Likewise, on the original
Flights page, on index.html, instead of just printing
out the flight, I'd like to link to that
specific flight page. And to do that, I wanted
to say, URL flight, because that was the name of the
flight URL, but what's missing here? Why is this not going to work? AUDIENCE: You need the ID. SPEAKER 1: Exactly. I need the ID because
the flight route isn't just a route that stays the same
anytime I refer to it by name. It has this variable in it,
this flight ID variable, that I need to somehow
provide to Django. And in Django the way to do that
is just include the ID right after the name of the URL. So URL to flight, and the
argument to pass into it is the flight's ID such that now if I
refresh this page flight 1 and flight 2 are links. If I click on flight 1, that takes
me to the detail page for flight 1. I can go back, click on flight 2, now
here is the detail page for flight 2. So that was linking
between different pages. Questions about that? One other thing that I alluded to
before that I'll talk about very briefly is that inside of this
HTML, this flight.html template and my index.html template, there
is a lot of repetitive code. This header is basically the same. I have body tags in both of them. And so just like in Flask, we
could use template inheritance to say I want some base template from
which other templates are derived from. I can do the same thing
in Django as well. And so the way I'll do that is
by creating a new template, which we'll call base.html. And base.html is going to
contain all of this original code except the body I'm just
going to call block body. This looks very, very similar to what
Jinja syntax for the same thing is. And then end block. And I'll make the title-- I'll actually make the
title a block title as well. And then end block there. And so now inside of index.html
rather than have all of this, I can just say, first
of all, I want to extend the flight slash base.html
template inside of block title. Let me go ahead and say Flights. And that's the end of that block. And now inside of the body block,
I have all of this content. End block there. And I don't need to indent this as much. And so now this is my index.html page. And now my flight.html will likewise
extend the flight/base.html template as the block title. I can add in a title. Before the title was just Flights. I'll go ahead and be a
little bit nicer about it and call it flight.id so the title of
the page includes the ID of the flight. And then inside of the block body,
I'll go ahead and include this content that was previously in the body. And I'll indent that just for clarity. And so now what I have is the
same exact thing except I've divided things up a
little bit into templates such that my base template
has all the HTML code that is common to both of my files. And then index.html and flight.html
are able to modify those blocks in much the same way as
we did in Flask as well. And the result is that this page
looks basically the same except I now have flight 2 up here in the title. And if I go back, this page
is basically the same as well. This was just a nice
organizational measure to do that didn't add a whole lot of
additional features to the application itself. But now-- go ahead. AUDIENCE: [INAUDIBLE] still Jinja? SPEAKER 1: The templating
language is no longer Jinja. It is Django's templating language. Django just happens to have its own
templating language that in many ways resembles Jinja. And the syntax for most
things is almost identical. And the documentation for Django's
templating language is very good and it's all available online. We won't have time to go
through all the features, but there are all sorts
of filters you can apply to things to make things
look precisely the way that you want them to. And it can be quite
powerful in that respect. So now what I want to do is think back
to our original airline application where, in addition to representing
flights and potential airports, we also wanted a way to
represent passengers and have passengers on individual flights. And the way that we did this back
in Flask was to create a table called Passengers where our
Passenger table, if you recall, had a Flight ID column that
referenced a specific flight such that every passenger was
associated with one flight. And that was how we were able
to relate passengers to flights. What is a possible design flaw in
that set up for flights and passengers whereby in our Passengers table,
we had a Flight ID column that linked to one particular flight? AUDIENCE: You could have multiple
flights associated with a passenger. SPEAKER 1: Exactly. We could have multiple flights
associated with the same passenger. One passenger might want
to be on multiple flights or might be registered
for multiple flights. And it would be nice if we
could say for Alice here, what are all the flights
that Alice is registered for, and to be able to see them. And that wouldn't be quite as easy if
we just had a single table of passengers where each passenger can only be
associated with one single flight. So how might we get around this problem? How might we structure
tables in order to allow for this what's called a many-to-many
relationship, where one passenger might be associated with
many different flights and one flight might be associated
with many different passengers? Any thoughts on how to
construct a table for that? AUDIENCE: [INAUDIBLE] table that
has just like two ID [INAUDIBLE].. SPEAKER 1: Yeah, exactly. So one conventional way to solve
this is with an in-between table that has two columns where we
have one column for a passenger ID and one column for a flight ID. So we have one separate
table for flights, one separate table for passengers,
and this in-between table that maps passengers and flights together. We could have as many
of these relationships as we want such that a flight
can have multiple passengers and a passenger can be on
multiple different flights. So that's what we'd like to do. What Django's actually
going to allow us to do is do that without needing to
worry about explicitly creating the in-between table and just describing
the relationship that we want, describing this
many-to-many relationship that we want to define our models. So I'm going to go ahead
and open up models.py. We have this existing Airport class
and this existing Flight class. But now what I'm going to
do is add a new class called Passenger which is going to be a model. And a passenger is going to have,
let's say, a first name which will be a char field of
max length of 64 again and a last name which will be also
a char field of max length 64. And also, a passenger, if
we just think about this in terms of what a passenger
is associated with, a passenger is associated with flights. They have some flights that they are on. And this is not a single foreign key,
not a single flight we're pointing to. This is a many-to-many field
that one passenger might be associated with many
flights, one flight might be associated
with many passengers. So it's a many-to-many field that
corresponds to what type of data? Well, it corresponds to a flight. I'm going to add blank equals true,
just to allow for the possibility that maybe a passenger
is not on any flights. So their flights field is blank. That's certainly a possibility. And I'll also add this related name,
just like we had before, such that-- and I'll call the
related name Passengers-- such that if I have a flight and
I want to get at its passengers, I can use this related named passengers
to be able to get at that as well. And we'll how that works in a moment. And finally, I'll go out and add a
STR function to the passenger such that if I ever wanted to
reference a passenger, it'll just be self.first self.last. We print out their full name when
we want to represent a passenger. So this is our Passenger class. And now I would like to just
add this to the database. Notice I'm going to-- I want-- the solution to this
problem is this in-between table that maps passengers and flights together. But I haven't written a class
for this in-between table. I just said I want a many-to-many field. But watch what will
happen is that if I now say python manage.py
makemigrations, it's now detected that I've created
this new model Passenger. It's added a new migration file for me. And if I now say python manage.py,
let's take a look at what the actual SQL contents of this migration is. So I'll do a sqlmigrate flights 003. What's actually happening
here, if we take a look at it, is even though we only
added one model what Django is going to do when I apply
this migration is create two tables. It's creating one table here
called Flights_passenger, which is going to manage in the
Flights app, all of the passengers. And this has an ID field, a first
name field, a last name field just as before. And we're also, Django's going
to create another table called, oddly enough, Flights Passenger
Flights, meaning in the Flights app, mapping individual passengers
to individual flights which has an ID field, a passenger ID field,
and this other flight ID field such that these tables are created for me. I didn't need to worry about
creating the in-between table, defining the names of these columns. I tell Django what my data looks
like, that my passengers are associated with multiple flights. And Django figures out what
SQL needs to run in order to create the models in such
a way that I can use them. And so this is all designed to
make it easier on you as the person writing the application to not
need to worry about specifically what the tables look like and
what the columns look like. But just to be able to deal with
the data that you actually want. And so I'll go ahead and
migrate this into the database. That's going to apply
the migration, actually create all of these new tables. And so now, if I go
into the shell and say, from flights.models, let's import
Flight and let's import Passenger. Let me go ahead and
get the first flight, flight.objects.get PK equals 1
to get the first flight, that's our flight from New York to London. And let me create a
new passenger now. p is going to be equal to a
passenger whose first name is Alice and whose last name-- I'll do another A name-- Adams. So now I have this passenger. I'm going to save this new passenger. And my passenger has a
flights attribute to it. And so I have p.flights. And so if I want to add a
new flight to this passenger, I can say p.flights.add f. And now, if I do
p.flights.all, for instance, to query for all this
passenger's flights, I see that this passenger is now on
this flight from New York to London. And this is a query set that
could be multiple flights. So this passenger can now be on
multiple flights potentially. And likewise, if I have f, I
can do f.passengers.all to get all of the passengers that are
on this particular flight, which might be multiple as well. So I've been able to encode
this many-to-many relationship without needing to worry
about the specifics of what goes into each one of
these individual tables. Questions about that so far? So what I might want to
do now is offer some way to be able to see passengers inside
the detail view of my application. So inside of views.py,
I might want it such that in addition to displaying the
flight, I also display the passengers. So flight.passengers.all in order
to query for all the passengers. And that gets passed
into my flight.html file. And so inside of flight.html
now, I might also want to say, let's add a heading called
Passengers and another unordered list where for each one of my passengers
in my list of passengers, I display the passenger's name. And Django has a special syntax, this
is slightly different from Jinja, called empty for what to do
if the for loop never ran, if nothing ever happened. And here we'll just say no passengers. And so I'm looping over
all of my passengers, just printing out the
passenger name such that now if I click on one
of these individual links, after I start up the server, I get
here's the flight details and here's my list of passengers. I have one passenger on this flight. I go back to the full listing, click
on this flight that has no passengers, I see that this flight
has no passengers on it. Questions about any of that? One other thing we can do very quickly
just to show you what this is like is if I go into admin.py
and say, you know what, admin.site.registser.passenger,
I also want to be able to modify
passengers on my Admin site. I'll import that as well. And I go to this admin
link and I log in. Now I see this additional passengers
option where I could likewise create if I wanted to add a new
passenger called Bob Baker or whatever, I can add this new passenger. And I can also select what
flights I want Bob Baker to be on. I can say, I want to be on flight
number 2 and flight number 1. I can select multiple, but
maybe just flight number 2. I can save that. And now Bob is on flight number 2. And if I refresh this flight number
2 page, now Bob is on that flight. So I'm able to use the admin
interface to be able to manipulate this data however I like. And I didn't need to
design that UI for myself. Questions about any of that? As one last edition for
now to this application, let's see if we can
add some way for people to be able to register for
flights via the web UI, to be able to submit a form that allows
them to register for a new flight. So what might that look like? Well, let's go ahead and go
into urls.py of my Flights app and let's add a new route, the
route that will happen when someone tries to book a flight, for instance. So the path for that will
be int flight_id/book. In other words, if someone is
submitting a request to my URL, /2/book, that will mean I want to book
a ticket on flight number 2. So that's going to link to the function
views.book, which doesn't exist yet. But I'll create it in just a moment. And I'll go ahead and name this
book just for good measure. So now, let's go into
views.py and create this book function, this function
that I want to happen when I try and book a flight. It takes in the request
as its first argument. It also takes in a flight ID. And so what do I need to do
inside this book function? Well, several things could go
wrong as I try and book the flight. So let me try to do some things
and figure out what could happen. I'll say passenger_id is equal
to int request.POST passenger. And so I'm going to
assume for now, and we'll see how to implement this later, that
when someone is booking a flight, they're going to submit a POST request. And one of the arguments
in that POST request, the data that passed gets
passed in, is named passenger. I'm going to convert that to an
integer and store that as passenger_id. Once I have that, now
I'm going to say, well, passenger better be
passenger.objects.get PK equals passenger_id. I want to get the
passenger that has that ID. And in order to make that work, I need
to make sure that I import Passenger up here as well. And I'm also going to
want to get my flight. So flight equals Flight.objects.get
PK equal flight_id. So now I tried to get the passenger
ID, I tried to get the passenger, I tried to get the flight. And now I'll handle the possible
exceptions that could be thrown, the things that could go wrong. So what could go wrong here? Any thoughts? AUDIENCE: There's no passenger
with that passenger ID. SPEAKER 1: There is no passenger
with that passenger ID. Certainly. So passenger.doesnotexist. And in that case, I'll return-- in this case, I'll render
a special HTML form, error.html which I'll create which will
just be my default way of displaying error messages, for instance. And to my error.html, I'll pass in
the context of message no passenger. And likewise, I'll go ahead
and copy this and say, if the exception was
flight.doesnotexist, there was no flight, then my
error message will be no flight. And one other thing that could go
wrong in this case is a key error. Any idea why I might get a key
error by running this code? So if hypothetically someone
submits a POST request but doesn't include the passenger
data in it, for instance. Or they submit a get request so
I don't have the POST request, then I'll get a key error in this case
because there was no passenger data to extract. And so here I'll go ahead and also
return the error.html with the message no selection. You didn't select a
passenger, for instance. So these are things that
could possibly go wrong. There are other things as well. But for now, that will
work for our purposes. And what I'll do now is say,
in order to book the flight, let's take the passenger,
let's go to their flights because every passenger has flights
that's associated with that passenger, and add that flight. And that's all the code
I need in order to add this new passenger to this flight. I take the passenger, I take the
flights that are associated with them, and I add this new flight
that I've just created. And what I likely want to do
now is redirect them somewhere, redirect the user to some other page. And so to do that in Django,
the syntax looks like this-- Httpresponsredirect. And where do I want to redirect them to? Well, I probably want to redirect
them to this particular flight page. So the syntax for getting
the URL if you know the name of the route you want to
go to is called reverse in Django. Because I want to go in the
reverse direction of going from the name of the
URL to the actual URL. So it's reverse followed by flight,
which is the name of the URL that I want to go to. This route here has a name of flight. And that's what I want to go to. And it also takes additional arguments. So my argument is going
to be a tupple of what arguments get passed into the flight. And in this case, the
argument is the flight ID. And so I'll go ahead and
pass that in as well. And I will also need to up here
import Httpresponseredirect and also from django.urls import
reverse, just to give me access to that reverse function. But the long story short
of what this is doing is I'm going to first try and
extract the passenger's ID, get at the passenger
itself and their flight, add the flight to the
passenger's collection of flights that they're associated
with, and then redirect the user back to the flight page. And so now what I need
to do is actually create some kind of form that is going
to lead to this book route. So inside of flight.html, I'll go ahead
and add that functionality to here. So, underneath this list of passengers,
I'll go ahead and add a horizontal row. I'll add a new heading that is
like Add a Passenger, for instance. And so what I want to do now is add some
sort of form here where I might say, form action is where I
want the form to go to. So any thoughts on what belongs
in the action for this form? AUDIENCE: URL and then
the name of [INAUDIBLE].. SPEAKER 1: Yep. So the name of that function was book,
as the name of this particular path. And I need to pass in the
individual flight ID for it. So it'll be book followed by flight.id. And so inside this form, and the form's
method that I'm submitting it via, is going to be a POST method. What am I going to need? Well, what I probably want to
do is offer some way for me to select from the people
that are not on this flight. So all the people that are not
currently passengers on this flight, I want there to be some
sort of select dropdown where I can select this is the
passenger I want to add to the flight, and let me add them. So before I get there,
what I probably want to do is up here in this flight route,
when I render this template also pass in not just passengers
but non_passengers as well, people that are not
passengers on this flight. And the syntax for doing that,
which we'll take a look at, is going to be passenger.objects.exclude
flights equals flight.all. And so the reason why this
works, passengers.objects gets me all the passenger objects. And then I can additionally
filter this information. So if I did .filter, that would mean
get me passenger objects that have a particular property. If I did passenger.objects.filter
first equals Alice, that would return to me all of
the passengers whose name is Alice, which in this case is just one. But likewise, if I say
passenger.objects.exclude, that's the opposite, saying
get rid of things that don't have this particular property. In other words, get rid
of all the passengers who have this particular flight
already in their lists of flights. Because they're already on the flight. I don't need to allow them to
be added to this flight again. And so I'm saying, exclude
from the passenger objects those people and call that
result non_passengers. Questions so far? We'll see how this all fits
together in just a moment. So now, for Add Passenger, one thing
that I might immediately want to do is say if non_passengers, and wrap
this whole thing inside of an if block. Because if there aren't any people that
have yet to register for this flight, then there is no reason
to display a form to allow them to sign up for the flight
because there's nobody else to add. And so if there really is no
one, then let me just display a div that says no passengers to add. There's nobody left out. But if there are people that could be
on this flight, then what I want to do is add a select dropdown whose
name is going to be Passenger. I'm going to have a dropdown
field called Passenger where I select the passenger that I want. Why is it called Passenger? Well, it's because in views.py,
inside of my book function, when I tried to extract the
data from the POST request, I'm looking for something
whose name is passenger. I want to get that data
out of the POST request. And so this word passenger
here needs to correspond with the name of the select dropdown
or whatever input field of the form corresponds to where the user is
actually selecting their data. Now I'm going to loop for
passenger in non_passengers, so for each one of those people that
are not currently on the flight, well, I want to give them the
option of being selected. So the value of this option is
going to be this passengers.id. Why is it their ID? Well, it's because when I
extract this information, I'm saving it as
passenger.id and I'm trying to extract that passenger by their ID. That is the data I'm using in
order to extract the passenger. So that's going to be
the value of this option. And what should actually
be displayed in the option, well, let me just print
out the passenger's name. By plugging in the
passenger there, we'll use the STR function associated
with that passenger that will just be first name, last name. And that way, my dropdown will
be populated with their name. Then finally at the end, we'll
say input type equals submit. And value, what displays on the Submit
button, will just be Book Flight. And that will be my input route. And so now assuming I
haven't done anything wrong, if I refresh this page now, I see
flight 2, passenger is currently Bob. And now I see this Add
a Passenger list where I have a dropdown of just Alice, only
the people that are not currently on flight number 2. And I can now book the flight. I go back to the full
listing, go to number 1. Alice is currently a passenger. And I can add Bob to the flight. And so, what I would like now
is if I clicked on Book Flight for that to then trigger the
book route and for that to cause Bob to be added to flight number 1. But in Django, there's
a little bit of nuance. When I click Book Flight,
that doesn't actually happen. I get this 403, forbidden
error, CSRF verification failed. And so CSRF stands for
Cross Site Request Forgery. It's a potential type of attack,
of a security vulnerability in forms whereby someone
might be able to forge where the form was coming from. And Django is built in to try and
protect against these types of attacks. We'll talk more about
this type of security when we get to the
security lecture later. But in particular, anytime you're
dealing with a form in Django, you're going to need to add
a little bit of extra syntax. You're just going to need
to add in these curly brace parentheses, csrf_token. This is just going to be a special
value that gets entered here such that when I submit the form, this
token is submitted along with it. And Django's server can verify that it
is, in fact, my web application that's submitting this request. It's just an added layer of
security that isn't present in Flask but that's useful in
Django in order to make sure our forms are even more secure. No need to worry about
the specifics there. We'll certainly talk about it later. But know that for forms, you do need
to include that token there as well. So now, if I go to flight 1, I see
the flight's origin and destination, passenger is this person. If I try and add Bob to it by going
to Bob and clicking Book Flight, now I was taken back
to the original page. Bob is now on my list of passengers. And under Add a
Passenger, it says I have no passengers to add because there
is no longer any passenger that is not on this flight. Questions about any of that so far? So that was us being
able to create forms, being able to handle get
and POST requests as well, and using this book function to be able
to update our many-to-many relationship between passengers and flights. So with this example, if you look at
the source code for the example online, you'll see that I do a couple
other things with this example, in particular, adding
static files like CSS files and customizing the admin
interface a little bit. We'll probably talk a little
more about that next week. But for now, this will give you the
basic idea of the Django application. But one other thing that
I wanted to talk about with regards to Django's features is
the log in and authentication system that comes built into Django. So you probably remember that when
we were looking at Django's admin interface, we went to slash admin. In addition to seeing these models
that we wrote, this Airports, Flights, and Passengers model and being
able add and manipulate that, we also have these
users, for instance, that are part of this authentication
authorization app, this app that I didn't create that
comes built into Django. This app is designed
to make it easy for you as the web application
developer to handle things like user log in and registration and
making sure that users stay logged in and making sure users can log out. It comes built in with
all of that functionality because so many applications
need it and use it. And so unlike in Project 1, for
instance, that Book application where people had to log in
and rate books where you needed to build out that user
model for yourself, the log in and log out systems, you had
to write all that for yourself. Django comes out with most
of this out of the box. And so what I'll do now
is show you an example of us using Django's authentication
model to handle things like log in and log out such that when you need
to write an application in Django, as you will for Project 3, you'll
be able to take advantage of this and not need to go through nearly as
much effort as you did in Project 1 to be able to design log in and log
out functionality for an application because it is so common. So I'm going to go now to
a different application. I'm going to go into the
authentication application. And we'll go in and open this up. And this is just another
Django project inside of which is a single app
that I created called Users. And we'll take a look at the URLs
that are present first inside of this application. So URLs that I have inside
my Users application are a default route that just
takes me to the index page and then I have a special log in route. If I go to /login, that is going
to take me to the login view. Log ou it's going to take
me to the log out view. And let me go ahead and show you what's
actually happening inside of views.py, inside of this application. So up at the top, I import a whole
bunch of interesting and useful features from Django's default packages. But in particular, line one is of note. From django.contrib.auth,
that is the package that corresponds to Django's
authentication library. So this is what contains-- the user model is contained inside
of django.contrib.auth.models, and these interesting functions. Authenticate, log in, and log out our
functions written for us by Django that we can now use. So what's happening inside
of index, this function? This is just the default route when
someone goes to my Django application. If not, request.user.isauthenticated. This is a feature that
comes with Django. This request.user.isauthenticated
evaluates to either true or false. True if the user has been
authenticated, they've logged in. False, otherwise. So if the user is authenticated,
then I'm going to go ahead and return renderinglogin.html, meaning
they have now logged in-- or sorry. If the user is not
authenticated, in other words, the user has not yet
logged in, then I want to display the login form for the user. So I'm going to render
the login.html page. And I'm going to say, pass in message
none because maybe my login page gets passed in a message if someone
types in their password incorrectly, for instance. And we'll take a look at what
login.html looks like in just a moment. But otherwise, if this wasn't the case,
if the user is, in fact, authenticated, they have logged in,
then let me go ahead and just get the user by
using request.user, saving that inside the context, and
returning the user.html template, passing in that context as well. And so all of these
things of request.user and request.user.isauthenticated,
these are things that come built
in for me in Django that I don't need to
worry about writing. I can just use this out of the box. Questions about the index route so far? Let's take a look at log in and log out. So log in is going to
be a form submission. Someone is going to type in
a user name and password. They're going to click Log in. And that is going to trigger,
ideally, them being logged in. So let me first get their
username by extracting the user name from their POST request. I might want to put this in a try
except to say if there was no user name key, that's going to cause some kind of
error, and I might want to handle that. But for now, I'm just assuming their
username and password will be in there. Next to password, I say
request.POST extract the password. And now in order to
authenticate the user, it's as simple as calling
this authenticate function, this function that is given to me by
Django where I pass in three things. I first pass in the request. I pass in their username. And I pass in their password. And it's going to
return for me the user. Or if it failed for some reason, it's
going to return none, no user, to me if there was no
successful authentication. So if the user is not
none, in other words, if a user came back
from this request, then that means the authentication
was successful. Let me actually log them in. To log them in, I just call
Django's log in function, which is given to me, again. It's built into Django, passing
in again, the request and the user to log in. And then I'm going to redirect the
user back to the index page, back to that original default route of
the page to show them their user page up there. Else, in other words, if
user is none, in other words the authenticate function failed, there
wasn't a successful authentication, then let me render the login
page again, but this time pass in a message that
says, invalid credentials. It says invalid
credentials as the context that I'm passing into the login page. Questions about that log in route? Yeah? AUDIENCE: Why does, for
example, the login method, why does it need the request? SPEAKER 1: So the request object-- so the question was, why
do we pass the request object when we are
trying to log someone in. So the request object in Django
contains a lot of information. It contains information about
the current session cookies. It contains information about who is
currently logged in, for instance, if anyone is currently logged in. And so in order to be able to modify
things like request.user, for instance, you might think that login needs
access to that request object in order to make sure that
log in can be successful. And this request object
is very frequently passed around because a
lot of Django functions need access to that request
to work successfully. So render, for instance, also requires
us to pass in that request as well. It's a common paradigm in
Django to require that request in several of the functions. So at the end of that, we
render invalid credentials if they typed in their user name wrong. And on the user page presumably
once they've logged in they'll have the ability to log out. And what does log out look like? AUDIENCE: I have a question. SPEAKER 1: Yeah? AUDIENCE: Is there a session object? SPEAKER 1: Is there a session object? Yes, there is a-- so the question
is, is there a session object. There is. Django does have support for sessions. We're probably not going have
a chance to talk about it here. But if you look at the settings.py
page for this entire application, you'll notice that one of the
default installed applications that comes with Django is this Sessions
application that's explicitly designed for helping to manage sessions. And if you go to Django's
documentation, you can find pretty clear
instructions for how to use these and how to take advantage of Sessions. But just like Flask does have sessions,
you can use sessions in Django as well. But unlike Flask, where
we needed to explicitly set like session square bracket user
ID to say this user is logged in, and we had to manually manipulate
the session, in large part in Django you don't need to worry about that. Because Django will take care
of that for you via this log in and log out function that's given
to us by the Authentication app. So I never needed to explicitly put the
user ID inside of a session variable, for instance, in order
to make this work. Certainly if you wanted to store
other things inside of the session, you could. But for log in and authentication,
that's not strictly necessary. AUDIENCE: Does it keep the
request scope across requests? SPEAKER 1: Good question. So does it keep the request
scope across requests? Each request is a different request
because it's a different HTTP request to a different page. But if the user is
logged in, that is saved in the session much in
the same way that Flask does it such that if I log in and
refresh the page or close the page and then reload it, the request will
still have the user associated with it. It'll still be logged in. And using the Authentication app,
you can modify and configure it such that there is like a
maximum expiration time, after a certain point the user is
automatically logged out, for instance. Django gives you a
bunch of these options for how to configure the users
and sessions the way you want to. Yeah? AUDIENCE: On the
httpresponseredirect, why did you use that rathering than
just rendering [INAUDIBLE]?? SPEAKER 1: Oh, good question. So the question is, why am I using
responseredirect rather than just rendering the index page directly. The answer is, I certainly
could have just rendered it. But if, in fact, the index
page were more complicated, if there was more logic in
rendering the index page where for instance for my flight's
index page, in order to render it, it wasn't as simple as
just rendering index.html. I had to first query
for all the flights, put that inside of the
context, and then render it. So to allow for the possibility that the
index page might get more complicated in the future, I just want
a redirect to index such that the index function can take care of
any additional complexities of querying the database or performing
computations that it might need to do before I actually render that template. Yeah? AUDIENCE: What is the reverse here? SPEAKER 1: Oh, good question. Question is, what is the reverse here. So when I redirect somewhere,
I need to redirect to a URL but Django generally doesn't like
us putting URLs inside of views.py for instance. Because the idea is that we want to
keep things as separate as possible. Django separates its projects
into distinct applications. Within applications, it separates
URLs from the actual view functions. And so it's generally good Django
design practice to in the URLs associate the route
itself with the name. And then if we want to extract
the route from having a name, that's where the reverse
function comes in. It takes index, this name, and
gives me back the route that I would need in order to get there. And finally, log out,
this log out view, is just going to call the log out
function, which again is built into Django in django.contrib.auth. And that's going to log the
current user out and say that they are now logged out. So what does this look like in practice? Using just this code, I can now
go ahead and run the server. Or actually, let me show
you the HTML files first. I'll show login.html, which
is fairly straightforward. It displays a header that says, log in. If there was a message like
invalid credentials, for instance, it displays that inside
of a div up at the top. Then I have a form whose
action is this login route. So when I click the form
and submit the form, it will take me to the login route. I have this token for security
and an input field for a username, input field for a password, and
a button to actually log in. Then on the user page, it's just
going to say, hello to user.firstname. Again, user is a model that's
been created for me by Django. It by default has fields like
first name, last name, user name, email address, and like are you an
administrator of the site or not. It is possible, if you want to,
to extend the existing user class, to add additional attributes
to the user if you want to. Though it's often not
recommended to do that. But certainly something you could do. But first name is built in to Django. Username is built in. So this is is currently
logged in as this user name. And if I want to log out,
I can click on this link. That will take me to the log out route. And so now, if I go to this
default route, I was not logged in. So it took me to this
login page first of all where I can now type in my username. And I already created an
account, but I'll show you how to do that in just a moment. And if I type in an incorrect
password and click Login, it says invalid credentials by
re-rendering this login page. If I type in my correct
password, then it takes me to this user page that says,
hello to my first name, currently logged in as--
there is my user name. And this Log Out button will log me out. And again, if I close this page,
reopen it later, it keeps me logged in. The user stays logged
in and you configure the details of how that works. And then if I click Log Out,
now I'm logged back out and back at the login page. So, how do you actually create users? Presumably in your application and one
thing you'll have to do in Project 3 is allow for some way of
registering new users. Well, that just means adding
a new user to the database. So multiple ways you can do this. One way is by going into
the admin interface. If I log in with my
username and password-- oh, this is not a staff account. So if you had a superuser
account, you could log into the admin interface using that
superuser account and modify things. But you can also make the
modifications using the shell. So I'll show you that,
python manage.py shell. And we'll say from django.contrib.auth-- remember django.contrib.auth is
the name of that app that handles authentication-- we'll do .models
because we want their models and we want to import user. And then if we want to create
a user, I can say user equals User.objects.create_user, it's
a function that creates a user. And it takes three arguments. It takes the user's user name,
an email address, and a password. So I go ahead and say
Alice as the username. We'll use alice@something.com
as the email address. And for the password,
we'll just say Alice12345. That's the password. And so that's all we need to
do in order to create a user. And once I've created that user,
I can modify properties of it. I can say user.firstname is
going to be equal to Alice with a capital A for instance. And then user.save at the
end to save that user. And now I've created a new user
taking advantage of this User model that's given to me by Django. So now that that user is created,
if I go python manage.py run server to start up my web server
again, and then go to that URL, I can now log in using
those credentials. Alice is the username,
Alice12345 as the password, and now I'm logged in as Alice. Then with the last couple
minutes, what I thought we'd do is it takes some time to talk
a little bit about Project 3, which is going to be released today. And what Project 3 is
going to be about is it's going to be about building
an application in Django and taking advantage of the features we
saw here, taking advantage of the fact that we can build a registration
and log in and log out system just by using the functions that were
given to us without needing to build out those models for ourself,
taking advantage of the fact that we can use Django's admin
interface to be able to very easily add and manipulate data without
needing to actually build out a page that lets me add things
and edit things and delete things. And what you're going to be doing
is building the web application for handling online orders
of a pizza restaurant. And one particular pizza restaurant
in particularly, actually. It will be Pinocchio's Pizza
which is quite a popular pizza place in Cambridge. And so what we're going
to give you access to is Pinocchio's Pizza's menu of
all of their different types of pizzas and sandwiches and such. And your job is going to be able
to design a web application that allows users to first of all register
and log in and log out the website. But once they're on it, to be
able to then go in and place an order by adding one or more
items to their shopping cart and then checking out such that
you can then place that order. And so the first thing
that you're going to want to do when thinking about
designing this web application is really taking a look at this
menu that we'll give you access to and figuring out what are
the different models that you might want to create in order to
represent all of this information. And so we saw a whole bunch of
ways to relate different tables to each other today. We looked at just tables that have
different fields for themselves. But we also looked at
foreign keys that let us relate one object to
potentially many objects where multiple different
flights can be correspond-- an airport can correspond to
multiple different flights where a flight has an
origin and a destination. We looked at a lot of
many-to-many relationships whereby we had multiple
different passengers that could be on multiple different
flights in any number of different combinations. So you might want to give some thought
to here are different types of pizza, but also different sizes, where any
individual pizza might have one, two, or three different types of toppings. And figuring out what forms
those data should take, what the relationship
between them should be is going to be a
helpful good first step. And once you've created those models,
when it comes time for the restaurant owner or the site
administrator in your case to actually go about populating
the menu that the user then sees, rather than have to build out a
web interface that lets a site administrator type in,
here is a new topping that I want to now
allow for Canadian bacon to be offered as a
topping for my menu, you can just go into Django's admin user
interface, which is built for you, and ideally be able
to just add of topping very easily using the interface
that's given for you there. And so, that's ultimately going
to be the goal of Project 3, to be able to take
advantage of the features that Django gives you in order to
simplify the process of building an application like this. And that will be released later today. But questions about that at a high
level before we wrap up for today? And certainly, once the specification's
out feel free to email the staff or reach out on the
[INAUDIBLE] if you have further questions about any of that. So that was sort of a look at Django
and the features that Django offers. It looked very similar to
a Flask, especially when it comes to looking at rendering
templates and having functions and having HTML files where we had these
Jinja-like templates that were actually in a slightly different language. What you'll ultimately, hopefully
find is that Django is quite a bit more powerful, that it gives
you a lot more out of the box, that it gives you the ability to deal
with more complicated relationships between data much more easily, that it
makes it much more easy for you to be able to add and edit and update and
delete existing entries that exist inside of your database,
that it makes it easier to make modifications to your
database, to change a database, and then migrate those
changes so that you don't need to worry about actually applying
the Alter Table or Create Table commands on your own, and that ultimately it's
going to make it easier to build more sophisticated
applications by giving you more of these features right out of the box. So that was a first look at Django. We'll wrap up today today and
best of luck with Project 3.