[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.