00:00:16.320
i'll make a start
00:00:17.600
uh my name's george and i work for
00:00:19.600
thoughtbot we're a
00:00:20.880
web and mobile consultancy most of the
00:00:22.960
web stuff we do is with rails but
00:00:24.880
i haven't always worked for thorlbot and
00:00:26.320
i haven't always worked with rails i
00:00:27.599
spent more than a year writing django
00:00:29.199
for a couple of different
00:00:30.240
startups and i found moving from rails
00:00:32.480
to django and then back to rails again
00:00:34.079
to be really fascinating
00:00:35.600
and there were lots of interesting
00:00:36.640
comparisons and interesting differences
00:00:38.719
that came up in that process so that's
00:00:40.320
why i wanted to talk to you all about
00:00:41.760
django today
00:00:42.840
um really quick caveat before we start
00:00:45.600
properly
00:00:46.320
i've called this sleeping with the enemy
00:00:47.840
django people aren't really the enemy
00:00:49.280
they're actually lovely if you get to
00:00:50.399
know them
00:00:51.039
um so you know this is a comparison and
00:00:54.480
i will be focusing on places where i
00:00:56.079
think django's made better decisions
00:00:58.320
but that's only because this is
00:00:59.520
railsconf and that's where we can learn
00:01:01.199
the most if this was djangocon then i
00:01:03.120
would be talking about where rails have
00:01:04.400
made better decisions so
00:01:06.000
there's no favoritism here it's just you
00:01:07.920
know it's just a comparison and a place
00:01:09.600
we can learn
00:01:10.560
but if i'm not trying to pick a winner
00:01:12.000
and i'm not trying to say which
00:01:13.200
framework's best then
00:01:14.400
why are we doing this well rails and
00:01:17.280
django solve really
00:01:18.640
similar problems um but in different
00:01:21.439
ways in different languages
00:01:22.880
this is probably literally true for this
00:01:24.640
audience i say
00:01:26.080
well you probably say tomato most of you
00:01:28.000
and i say tomato
00:01:30.079
but it's interesting seeing those
00:01:31.439
differences in language it's interesting
00:01:33.040
seeing
00:01:33.759
that you know the same idea can be
00:01:35.920
expressed in different ways
00:01:37.360
and similarly when we look at rails and
00:01:38.799
django we see the same problems we see
00:01:40.880
all the boring repetitive or tricky bits
00:01:43.360
of web development being abstracted for
00:01:45.200
us
00:01:46.320
and we see it done in different ways so
00:01:49.119
we're going to look at django and we're
00:01:50.560
going to see
00:01:51.439
unfamiliar solutions to very familiar
00:01:53.600
problems
00:01:55.200
which is hopefully something that can
00:01:57.040
help us understand those problems better
00:01:59.200
and improve rails and improve our own
00:02:00.960
apps
00:02:02.240
um so that's where we're going now i did
00:02:04.719
say in my abstract i was going to do
00:02:06.000
some live coding
00:02:07.280
and then everyone said what that's a
00:02:09.200
terrible idea so i've chickened out on
00:02:10.959
that
00:02:11.280
um the all the code you're going to see
00:02:13.920
is
00:02:14.319
uh is from a github repository which
00:02:16.640
i'll be linking to at the end
00:02:18.000
so this is a working application but
00:02:20.239
actually the real reason for not doing
00:02:21.520
the live coding is there's quite a bit
00:02:23.120
of configuration to do when you first
00:02:25.200
set up a django app and watching me type
00:02:27.440
database configuration for 10 minutes
00:02:29.360
really isn't what anyone's here for so
00:02:32.239
we'll just look at the interesting bits
00:02:33.440
of the code and you can
00:02:34.400
prove it all works by looking at github
00:02:36.160
later on
00:02:37.680
so this is the anatomy of a django
00:02:39.120
project um this is
00:02:41.040
the project that's on on github the
00:02:43.599
project is called demo and it's a simple
00:02:45.440
blog
00:02:47.040
so first of all we have a directory
00:02:48.319
called demo which contains project level
00:02:50.560
stuff
00:02:51.760
so there's some settings in here that's
00:02:53.440
where all that database config that i
00:02:54.879
had to type lives
00:02:56.239
uh there's a urls file which is like the
00:02:58.560
roots file in a rails app and there's a
00:03:00.239
whizzi file which is like the config.ru
00:03:02.480
file in a rails app
00:03:04.480
then we have some apps apps is really
00:03:06.800
where the meat of a django project lives
00:03:08.400
this is where the
00:03:09.280
the kind of the models and the views
00:03:13.040
are organized but they're grouped
00:03:14.800
together into
00:03:16.080
into different apps so this project is
00:03:19.200
just a blog
00:03:19.920
so it only has one app which is called
00:03:21.440
blog and in there there's a blog post
00:03:23.599
model
00:03:24.159
and any other models we might need for
00:03:25.760
our blog there aren't comments in this
00:03:27.760
one but you might add a comment in there
00:03:29.040
that kind of thing
00:03:30.560
and all the views you need to display
00:03:32.080
those blog posts
00:03:34.080
then we have manage.py this is the
00:03:36.480
command line interface
00:03:37.840
to a django app so in rails you type
00:03:41.040
rails server
00:03:42.159
in django you type python manage.pi run
00:03:44.879
server
00:03:45.680
it's the same idea also any rake tasks
00:03:49.120
that we would have in rails or any
00:03:50.239
custom extensions that would all be done
00:03:52.000
through this then we have a requirements
00:03:54.879
file
00:03:55.519
this is the python equivalent of your
00:03:57.040
gem file it's just a list of
00:03:58.239
dependencies and their versions
00:04:01.120
and finally we have some templates which
00:04:02.879
is just the chunks of html that
00:04:05.280
django is going to insert things into
00:04:06.799
and use to render our pages
00:04:09.920
so we'll start from the database and
00:04:11.599
work our way up so start with models
00:04:15.519
here is a blog post
00:04:19.199
so we're declaring a class which is
00:04:21.199
called blog post
00:04:22.800
and the parentheses here in python mean
00:04:24.800
inheritance so it's inheriting from
00:04:26.840
models.model this is the django
00:04:29.199
equivalent of inheriting from active
00:04:30.639
record base
00:04:31.600
in a rails app and so this class is a
00:04:34.479
model it's tied to a database table we
00:04:36.400
can
00:04:37.040
save instances to that database table we
00:04:39.120
can get them back out again
00:04:41.440
and then something that might be
00:04:42.639
unfamiliar if you've used active record
00:04:44.400
but if you've used data map assay then
00:04:46.320
you'll recognize this pattern we then
00:04:48.320
define the fields that the model has
00:04:50.320
here in the model class so the schema
00:04:52.320
isn't defined separately it's defined
00:04:54.160
in this object so the assignment here
00:04:56.639
isn't
00:04:57.360
local variables these are attributes on
00:04:59.199
the class so i could do blog post dot
00:05:01.039
title to get at this title object
00:05:03.840
and the title field is an instance of
00:05:05.800
models.charfield
00:05:07.120
so in ruby we'd say childfield.new to
00:05:09.360
create an instance of a classical child
00:05:10.800
field in python we just use parentheses
00:05:12.720
and call it as if it was a function
00:05:15.759
and we're passing in a maximum length
00:05:17.360
and then we have some other fields
00:05:18.960
body published publish date but really
00:05:22.000
this is just setting the scene like
00:05:24.080
a website is much more than models what
00:05:26.400
we need to do is display blog posts
00:05:28.320
um to make this really interesting
00:05:31.440
and to do that we need another concept
00:05:33.759
in django called a view
00:05:35.120
now a view in django is nothing like a
00:05:36.800
view in rails i will try and keep these
00:05:38.720
two things very clear as i'm speaking
00:05:41.280
but if i'm talking about django views
00:05:43.280
they're much more like rails actions
00:05:45.680
so in rails you would have a show action
00:05:49.360
which would show a blog post in django
00:05:52.400
you would have a detailed view which
00:05:53.600
would show the detail of a blog post
00:05:56.400
and views use quite a simple mechanism
00:05:58.880
django instantiates a request object
00:06:01.039
it passes it to the view which is just
00:06:02.479
some kind of callable it could be
00:06:04.080
anything it could be a lambda it could
00:06:05.440
be a function
00:06:06.240
it could be a callable object which is a
00:06:08.720
quite nice thing that
00:06:10.080
python has and it returns a response
00:06:13.919
which
00:06:14.800
django gives back to the user um
00:06:20.319
but this kind of code gets very boring
00:06:22.560
and very repetitive
00:06:23.680
we've all seen show actions in in rails
00:06:26.080
controllers that look the same
00:06:27.919
you know at blog post equals blog post
00:06:29.600
dot find for ams id like it's the
00:06:31.680
it's a very repetitive thing that we
00:06:33.840
need to do over and over again
00:06:36.000
so how does django abstract away this
00:06:38.000
repetition rails has all these
00:06:39.840
conventional structures we have restful
00:06:41.520
resources
00:06:42.960
and you know we have the restful routing
00:06:45.520
so we have all these conventions that
00:06:47.120
sort of
00:06:47.520
help us to not have to write the same
00:06:49.039
code again and again we don't have to
00:06:50.160
name the template again and again
00:06:51.440
what does django give us well it gives
00:06:53.759
us a lot
00:06:54.880
via a mechanism called generic views so
00:06:57.759
i said a view was something which
00:06:58.960
handles a request and returns a response
00:07:01.759
well our blog post detail view which
00:07:03.840
we're declaring here
00:07:05.599
just inherits from detail view which is
00:07:08.000
a generic view that handles all the
00:07:10.000
stuff that you might want to do with
00:07:11.360
showing
00:07:12.000
a single instance of a model and we
00:07:14.400
configure it we tell it which model to
00:07:15.919
use
00:07:16.319
we set an attribute on it which is the
00:07:18.479
model class to use
00:07:19.680
and that's it this will now work i mean
00:07:22.000
yes there's a line in the urls file that
00:07:23.599
hooks up a url to this view
00:07:25.440
but when you send a get request to that
00:07:27.120
url you get back a page showing the blog
00:07:29.280
post
00:07:30.400
but notice all the code that isn't here
00:07:32.960
there's no code here to pull the id out
00:07:35.120
of the url there's no code here to load
00:07:36.960
the blog post instance from the database
00:07:39.280
there's no code to assign it to a
00:07:40.800
variable to pass down to the template
00:07:42.240
layer
00:07:43.199
all of that is handled for us by the
00:07:44.720
detail view superclass it's all kind of
00:07:46.720
abstracted away into the framework
00:07:48.639
and the first time i saw this i was a
00:07:50.160
little bit worried i thought that's too
00:07:52.240
much convention i mean
00:07:53.440
convention's nice and all but you're
00:07:55.039
taking it too far here guys just slow
00:07:56.800
down
00:07:57.440
because that they've they've taken
00:07:59.599
everything away
00:08:00.639
i've just said which model to use and i
00:08:02.960
assume this would be brittle and i
00:08:04.240
assume this will be hard to change
00:08:06.560
so let's change it and see what breaks
00:08:08.560
um if you remember back to our blog post
00:08:10.800
model we had a published field
00:08:12.240
which determined whether or not the post
00:08:14.160
was published so
00:08:15.520
we probably only want our detail view to
00:08:17.440
display published blog posts
00:08:21.360
and that's the code that we need to do
00:08:22.800
it it's not as much as i was expecting i
00:08:24.800
was expecting to have to re-implement a
00:08:26.240
lot of this stuff
00:08:28.160
so we're assigning another attribute on
00:08:29.840
our class which is called query set
00:08:32.080
a query set in django is the equivalent
00:08:34.159
of an active record relation
00:08:35.919
so it's a description of a query and you
00:08:38.399
can add more
00:08:39.360
you can add more filtering to it later
00:08:41.279
or you can ask it for results
00:08:43.279
um and in this case the the query set
00:08:45.680
we're using
00:08:46.320
is the blog post objects filtered on
00:08:49.920
published equals true
00:08:52.320
and the detail view will now take this
00:08:53.839
query set and use it as the basis of its
00:08:55.760
query
00:08:56.480
so it will take this and look inside it
00:08:58.320
for something with the id that we've
00:08:59.680
asked for in the request
00:09:05.360
which is all well and good but we're
00:09:07.200
doing this at class definition time
00:09:09.440
so it's still a little bit brittle
00:09:11.040
because with this is a static query set
00:09:12.800
that we're using it can't change based
00:09:14.399
on the properties of the request
00:09:16.399
so let's add previews to our blog let's
00:09:18.399
say if you add question mark preview
00:09:20.080
equals one to your url
00:09:22.160
that you can see a blog post even if
00:09:24.080
it's not been published
00:09:26.560
well here's the code we've dropped our
00:09:28.880
query set attribute and instead we have
00:09:30.720
the get query set method
00:09:33.760
the parameter self there is just the way
00:09:35.920
that python implements
00:09:37.440
classes you have to have a self
00:09:38.880
parameter on every method
00:09:40.720
some people hate it some people don't
00:09:42.160
notice it but it's that's what that's
00:09:44.320
there for and then we just grab the
00:09:46.880
current request
00:09:47.839
from from self and we look at its get
00:09:50.959
dictionary that's the capital get there
00:09:53.440
at the end of the highlighting
00:09:55.120
the get dictionary is well a dictionary
00:09:57.519
is python speak for hash
00:09:58.880
so it's just a hash with all the stuff
00:10:01.440
from the query string passed out so we
00:10:03.040
can access it
00:10:04.160
and we look inside and see if there was
00:10:05.519
something called preview if there was
00:10:07.360
we return all the blog posts as a query
00:10:09.680
set
00:10:10.480
and otherwise we do the same filtering
00:10:12.160
we were doing before
00:10:13.760
so we've gone from something which is
00:10:15.120
completely conventional and will show us
00:10:16.800
any blog post based on the id we gave it
00:10:19.040
in the url
00:10:20.240
to something which now has some nice
00:10:23.360
dynamic behavior
00:10:25.440
but we're still not loading a blog post
00:10:27.360
here we're still not pulling the id of
00:10:29.040
the url we're still not passing this
00:10:30.560
down to the template
00:10:31.839
we wanted to change the query set and
00:10:33.440
that's all we've had to change
00:10:36.000
and in fact there are many of these
00:10:37.360
methods that we can override so we've
00:10:38.959
overridden get query set
00:10:40.640
but behind the scenes there's a lot more
00:10:43.519
that we could do
00:10:44.720
so the entry point to this the user has
00:10:46.640
made a get request
00:10:48.079
so django's called our view and our view
00:10:50.240
has checked the type of the request seen
00:10:51.680
that it was a get request and called a
00:10:53.040
get method
00:10:54.720
so get is kind of the the the thing that
00:10:57.040
defines
00:10:57.839
what should happen with this request but
00:10:59.680
internally that's calling get template
00:11:01.360
names to figure out the name
00:11:02.640
of the template which will return a
00:11:05.040
template name attribute if we've defined
00:11:06.720
one or we'll just return a sensible
00:11:08.320
default based on conventions
00:11:10.320
get slug field because we can look at
00:11:12.079
blog posts or whatever model by slug
00:11:13.839
rather than by id
00:11:15.279
it has get query set which we've already
00:11:16.800
looked at
00:11:18.320
get object which is responsible for
00:11:20.160
loading the entire
00:11:21.519
model this does the whole thing so if we
00:11:23.440
wanted to stop using the django orm
00:11:25.680
entirely and just
00:11:27.120
use a plain old python object or pull
00:11:29.120
something from somewhere else
00:11:30.800
and we could override this method
00:11:33.920
get context object name which is a bit
00:11:36.079
of a mouthful to say
00:11:37.200
but context is the stuff that gets
00:11:39.760
passed from a view to a template
00:11:41.440
so this is just the name that our blog
00:11:42.959
post should be given in the template
00:11:45.120
and again we can do a static version by
00:11:48.399
setting an attribute on the class or we
00:11:50.079
can override the method for a dynamic
00:11:51.440
version
00:11:52.240
or there's a sensible default by
00:11:54.079
convention
00:11:55.519
and finally get context data returns all
00:11:57.360
of the stuff that gets passed to the
00:11:58.720
template
00:11:59.920
there's a lot of methods being used by
00:12:02.079
get and we can override them all to
00:12:04.560
vary the the algorithm in small ways and
00:12:07.680
if you're a fan of gang of four
00:12:08.959
or you've read sandy metz's practical
00:12:10.800
object-oriented design in ruby
00:12:13.360
you might recognize this as the template
00:12:14.720
method pattern so if you're playing
00:12:16.880
design pattern bingo you can cross that
00:12:18.240
one off on your bingo card now
00:12:21.200
the template method pattern is really
00:12:22.800
useful as a kind of seam
00:12:24.880
between framework code and application
00:12:27.040
code because
00:12:28.399
it says the framework can define a
00:12:31.360
standard algorithm
00:12:33.279
which is just called as one method call
00:12:35.920
you just call the template method
00:12:37.440
and the template method will call lots
00:12:39.279
of small primitive operations
00:12:40.959
and then the subclasses in the
00:12:42.160
application code can say well i like the
00:12:44.399
convention for everything but the query
00:12:46.000
set so i'll just replace that one
00:12:47.440
primitive
00:12:49.600
so this gives us very very flexible code
00:12:52.240
and it gives us very clean code
00:12:53.839
there's no boilerplate in that the only
00:12:55.760
code that you write in a django view
00:12:57.519
is the stuff that goes outside of the
00:12:58.959
conventions you never write any code to
00:13:01.040
implement the conventions
00:13:02.560
which compared to a rails
00:13:05.760
action i mean say you were to use the
00:13:07.519
scaffold the stuff you get out of the
00:13:09.040
scaffold there's some boilerplate in
00:13:10.240
there there's some stuff to read through
00:13:11.600
that's just implementing the conventions
00:13:13.839
so this is a place where i've been i've
00:13:15.440
been surprised and impressed by how far
00:13:17.360
django's taken the idea of convention
00:13:19.200
over configuration
00:13:21.279
because it's not just giving us
00:13:22.560
conventional structure but it's also
00:13:24.079
giving us conventional functionality
00:13:26.079
but in such a way which is still
00:13:29.120
quite simple to change with a small
00:13:30.720
amount of code and still
00:13:32.560
quite flexible when we want to go off
00:13:34.959
the rails or
00:13:36.320
not off the rails off the pony i don't
00:13:37.920
know what would you call it in django
00:13:39.600
um the django logo is a pony i
00:13:43.680
know okay
00:13:47.360
um crashing on uh we so we can display
00:13:50.800
blog posts now but
00:13:52.720
that doesn't really make a blog so far
00:13:54.959
we still have an empty database
00:13:56.399
and we want to create blog posts
00:13:59.440
so this is a single example of an
00:14:02.639
incredibly common pattern in web
00:14:04.079
applications
00:14:04.959
where you have an html form the user
00:14:07.760
submits it which causes a post request
00:14:09.680
to happen
00:14:10.959
some stuff happens in the framework and
00:14:13.360
in the application
00:14:14.560
which results in some sql and the user's
00:14:16.720
data ends up in our database
00:14:18.480
so this is incredibly common most web
00:14:20.160
applications do this in a bunch of
00:14:21.519
different ways in a bunch of different
00:14:22.800
places
00:14:23.519
it's also terrifying there's so much
00:14:26.079
danger in this path
00:14:27.519
there's so much stuff that could go
00:14:28.959
wrong there's so many things that have
00:14:30.320
to happen
00:14:30.959
i mean there's csrf vulnerabilities
00:14:32.800
there's sql injection attacks
00:14:34.720
there's type conversion because html
00:14:37.040
forms only really know about strings
00:14:39.040
but databases know about all kinds of
00:14:40.639
complex data types
00:14:43.279
there's things like the strong
00:14:44.240
parameters problem where the user might
00:14:45.839
have sneakily added an extra field to
00:14:47.680
the form to get that into your database
00:14:49.920
now there's a lot that can go wrong in
00:14:51.440
this path even though it's very common
00:14:53.680
and so understandably frameworks provide
00:14:56.160
a lot of help with this
00:14:58.639
so let's see what django gives us well
00:15:01.360
the first thing
00:15:02.399
we do is create another view remember
00:15:05.279
views were
00:15:05.920
roughly equivalent to rails actions and
00:15:08.480
in this case we're using a create view
00:15:10.320
which is roughly equivalent to both the
00:15:12.000
new and create actions in a rails
00:15:14.240
restful controller
00:15:15.440
so if you send this a get request it
00:15:17.360
will show you a form
00:15:19.199
and if you send a post request it will
00:15:20.639
process that form and then
00:15:22.240
if it's successful redirect you
00:15:23.680
somewhere and if it's failed
00:15:25.440
then show you some nice error messages
00:15:27.440
and things
00:15:29.440
so this is new and create kind of rolled
00:15:31.120
into one
00:15:32.560
and again there's a lot of convention
00:15:34.800
happening here we don't
00:15:35.759
actually have to write very much code to
00:15:37.360
get this working
00:15:38.839
um but behind the scenes in here there's
00:15:41.440
another object
00:15:42.320
in between the view and the model the
00:15:44.079
view and the model aren't interacting
00:15:45.519
directly at any point
00:15:47.600
there's something called a form sitting
00:15:49.120
in the way this is possibly my favorite
00:15:51.120
feature in all of django i get very
00:15:52.800
excited about this
00:15:55.199
so the form itself is a collection of
00:15:58.079
fields
00:15:59.120
and fields understand types they they
00:16:01.839
just know about data types and type
00:16:03.440
conversion
00:16:04.240
so if you give them say it was a date
00:16:05.839
field you could give it a date it would
00:16:07.199
turn it into a string that the form can
00:16:08.880
use
00:16:09.360
you give it a string from the form it
00:16:10.720
turns into a date that the model can use
00:16:14.320
sorry actually sorry there where i said
00:16:16.399
form i meant the html form so you give
00:16:18.240
it
00:16:18.800
a date it turns into a string that the
00:16:20.800
html form can understand not the form
00:16:22.639
object
00:16:24.800
the widget is responsible for user
00:16:26.639
interface so this understands html forms
00:16:28.959
and http and those kind of concerns
00:16:31.360
and it's at the bottom level of the step
00:16:33.680
so concrete example
00:16:36.320
our blog post form needs a date for the
00:16:38.639
uh for the publish date
00:16:40.720
so it's going to use a date field for
00:16:42.320
that and a date select widget which will
00:16:44.880
render this kind of ui
00:16:46.959
so when we send a get request to our
00:16:48.839
view
00:16:50.079
the template is going to say hey form
00:16:51.519
render yourself and the form is going to
00:16:53.440
iterate over its fields and say hey
00:16:54.720
render yourself and the fields are going
00:16:56.000
to say hey widget render yourself
00:16:58.320
and the widgets will spit out the right
00:17:00.639
html
00:17:01.680
and then when you submit the form the
00:17:03.600
data will flow back up in the opposite
00:17:05.360
direction so it'll go through the
00:17:06.400
widgets to the fields to the form
00:17:09.520
um so let's use a custom form just so we
00:17:11.760
can see this happening concretely in
00:17:13.360
code
00:17:14.640
so here's our view again our create view
00:17:16.799
we need to import
00:17:18.240
a blog post form from wherever we're
00:17:20.799
going to define it because
00:17:22.400
explicit imports a blessing in a curse
00:17:24.480
of using python
00:17:26.640
and then we tell our view that it needs
00:17:28.480
to use that particular form class
00:17:31.760
and defining the form class looks like
00:17:34.000
this
00:17:34.799
so there's a lot of code here you can
00:17:36.960
pretty much ignore the top four lines
00:17:38.400
they're just pulling stuff in from other
00:17:39.840
places they're just
00:17:40.799
imports so we're creating a blog post
00:17:44.000
form which inherits from forms.model
00:17:45.919
form
00:17:47.200
a model form is a form that's designed
00:17:49.200
to deal with a model which
00:17:50.720
hopefully is clear from the naming and
00:17:52.880
that gives us three really useful things
00:17:55.520
one is that it can automatically derive
00:17:57.760
its list of fields from the models
00:17:59.280
fields
00:18:00.480
two is that if you give it a model it
00:18:02.720
can populate its fields with the values
00:18:04.480
from that model
00:18:05.280
and three is that it has a save method
00:18:06.960
you can say hey save yourself and it
00:18:08.640
will write
00:18:09.520
the values it has back to the model
00:18:11.039
instance that it's using and then tell
00:18:12.720
the model to save itself to the database
00:18:16.160
so inside this form class we have a meta
00:18:18.160
class which
00:18:19.600
for the purposes of of this code we can
00:18:21.679
just think of as being a collection of
00:18:23.280
configuration options
00:18:24.880
um there's more going on but it's not
00:18:27.280
really important to understand this
00:18:28.559
example
00:18:29.840
so we tell it which model to use and at
00:18:31.600
this point we could stop
00:18:32.880
it could have just derived all of its
00:18:35.200
fields from the model
00:18:36.320
and we're done but we might want to say
00:18:39.200
exclude some fields
00:18:40.960
so this is where django is solving the
00:18:42.720
same problem as strong parameters and
00:18:44.240
actually accessible
00:18:45.200
this is where the filtering is happening
00:18:46.640
where we're saying we want a form that
00:18:48.240
represents a blog post but we don't want
00:18:49.760
the published field
00:18:50.720
we don't want the user who's interacting
00:18:52.160
with this form to be able to publish a
00:18:53.360
post just to create a draft and
00:18:56.880
i think this is a more useful place to
00:18:59.760
have the filtering
00:19:00.960
it's neither in the controller nor in
00:19:02.559
the model it's in a third place in
00:19:04.160
between
00:19:05.200
but because this form object is
00:19:07.280
responsible for both the drawing the
00:19:08.720
user interface
00:19:09.760
and processing the data that comes back
00:19:11.840
it's really hard to screw this up
00:19:15.440
if you accidentally leave a field in the
00:19:18.320
form
00:19:18.960
it's in your user interface someone's
00:19:20.880
probably going to spot that
00:19:22.400
so if the checkbox to turn a user into
00:19:24.160
an admin is in the user edit form
00:19:26.240
someone might notice before a user
00:19:27.760
sneakily manages to to put that in
00:19:29.760
this isn't a separate place where you
00:19:31.200
have to go oh wait i need to do some
00:19:32.559
security filtering this is a different
00:19:34.160
concern this is a separate thing to
00:19:35.520
think about
00:19:36.320
it's just the process of building a form
00:19:38.480
naturally leads you towards
00:19:40.240
including and excluding the right fields
00:19:42.960
and if the form doesn't have a field
00:19:44.720
well the form doesn't really know much
00:19:46.080
about the model it just knows what
00:19:47.440
fields it has
00:19:48.400
so if the model has a field but the form
00:19:50.559
doesn't have that field
00:19:51.679
the form will just ignore it there'll be
00:19:53.440
no exception no breakage it just won't
00:19:55.600
ever get processed
00:19:58.799
so we have a form and the form has a
00:20:00.240
list of fields and the final layer of
00:20:02.159
this is widgets
00:20:04.240
now again we could just stop we could go
00:20:06.480
with the default widgets that come with
00:20:08.000
the fields
00:20:09.039
but the default date widget is actually
00:20:11.520
not that great
00:20:12.559
it's a text field where the user has to
00:20:14.159
type iso 8601
00:20:16.320
formatted dates which i mean i'm fine
00:20:19.039
with that but i don't think it's very
00:20:20.080
good user experience for people who are
00:20:21.520
less nerdy about standards
00:20:23.120
so um let's give the user a better
00:20:26.400
experience by giving them a select date
00:20:27.919
widget
00:20:29.039
so we're just saying like this is a dict
00:20:31.840
again similar to a hash
00:20:33.120
and we're saying for the publish date
00:20:34.559
use a instance of
00:20:36.159
select date widget so the stage is set
00:20:39.520
we have our view
00:20:40.480
it's using this custom form we send it a
00:20:42.799
get request
00:20:43.520
and we see something like this now it
00:20:46.640
needs some
00:20:47.440
it needs some css it needs a submit
00:20:49.440
button it needs a title you know there's
00:20:51.360
some work to do here but it's it's a
00:20:52.640
working interface almost
00:20:54.799
and when the user submits this form
00:20:57.039
here's the process
00:20:58.640
that it runs through um so the html form
00:21:02.480
itself
00:21:04.799
will generate this which is the http
00:21:07.760
encoded version of the fields
00:21:09.919
i'm only i'm only showing the date
00:21:11.600
related fields here because otherwise
00:21:13.039
the examples would be really really long
00:21:14.400
but the other fields would all be in
00:21:15.520
here too
00:21:16.720
this goes through some plumbing and by
00:21:18.240
plumbing i mean insignificant things
00:21:19.679
like the internet
00:21:20.559
and the web server and whiskey which is
00:21:22.559
the python equivalent of rack
00:21:24.159
and the low levels of django and the
00:21:26.159
routing but eventually
00:21:28.080
it comes out looking like this which is
00:21:30.159
already more useful we've already gone
00:21:31.440
from the string that we got
00:21:32.640
from the browser and the http request
00:21:34.559
into addict
00:21:36.159
and we can pull out the individual
00:21:37.360
values but the values are still
00:21:40.159
correlated to the fields in the html
00:21:42.159
form they're not correlated to the
00:21:43.440
fields in our model
00:21:44.559
and they're not in the right types
00:21:45.679
they're all strings
00:21:47.600
so this arrives our view and our view
00:21:50.640
passes the whole thing
00:21:51.840
unmodified to the form it just says hey
00:21:53.679
i got this stuff from the browser
00:21:55.039
see what you make of it um now the form
00:21:57.840
will iterate over all of its fields
00:22:00.159
and one by one it will give the whole
00:22:01.760
dict to the widgets
00:22:03.840
that are associated with those fields
00:22:05.679
because the form doesn't know or care
00:22:07.280
which values came from which widget
00:22:09.440
it leaves that up to the widgets it just
00:22:10.799
says here's all this stuff
00:22:12.640
you know how many fields you created you
00:22:14.400
know what they were called
00:22:15.919
pull out the right values so our select
00:22:18.799
date widget will pull out the right
00:22:20.240
values
00:22:20.880
combine them together into a string and
00:22:23.120
it's a nice
00:22:24.159
iso 8601 formatted string the exact
00:22:26.720
string we didn't want our user to have
00:22:27.919
to type
00:22:28.960
um which hits the field and the field
00:22:31.360
converts it to a python date object
00:22:36.480
now once this has happened for each of
00:22:38.320
the fields they all give their values
00:22:40.559
back
00:22:41.039
to the form and the form combines them
00:22:43.520
into another dict
00:22:44.960
this is a dict that it calls cleaned
00:22:46.559
data because this data is valid it's in
00:22:48.880
the right types
00:22:49.760
it's been combined so multiple fields
00:22:52.000
are smashed down into one field where
00:22:53.520
they need to be
00:22:54.960
and we have just the publish date is a
00:22:58.000
date
00:22:58.960
and only then does it hit the model
00:23:00.240
layer like at this point because we're
00:23:02.159
using a model form
00:23:03.039
it's given to the model and saved and
00:23:06.000
written to our database
00:23:08.000
i think this is brilliant but to explain
00:23:11.200
why let's compare it to
00:23:12.799
the similar flow in rails
00:23:16.880
so we would have a new action and a
00:23:19.200
create action on our blog post
00:23:20.559
controller the new action would use
00:23:22.159
form 4 in the view and there would be a
00:23:24.799
date select somewhere in that form
00:23:27.120
so when the form was submitted we'd get
00:23:28.559
something that looked a bit like this
00:23:30.799
so the keys are slightly more
00:23:31.840
complicated but so far it looks the same
00:23:33.840
as the python example more or less it
00:23:35.919
goes through a very similar set of
00:23:37.440
plumbing there's the internet there's
00:23:38.559
the web server there's rack instead of
00:23:40.080
whiskey
00:23:40.880
there's rails routing instead of django
00:23:42.559
routing but eventually
00:23:44.080
it's converted into a hash and it hits
00:23:47.360
our code
00:23:48.799
it's given as the params params property
00:23:51.679
on our blog post controller
00:23:56.080
so a blog post controller will pull out
00:23:57.919
just the blog post related stuff and
00:23:59.760
hopefully at this point we've remembered
00:24:01.039
to use strong parameters and get the
00:24:02.240
bits we wanted
00:24:05.520
and then that's given wholesale to the
00:24:07.840
blog post
00:24:09.360
to the model now inside the model there
00:24:12.400
is a module mixed in called activerecord
00:24:14.000
attribute assignment this is inherited
00:24:15.440
from activerecord base
00:24:17.600
so we have this and this is responsible
00:24:19.440
for doing when you give
00:24:21.279
a model a hash and it assigns all the
00:24:23.600
things in the hash to the individual
00:24:24.960
fields
00:24:25.520
this is the module that where the magic
00:24:26.960
happens so the first thing it does is it
00:24:29.279
looks at these keys and it notices there
00:24:31.200
are these parentheticals at the end
00:24:33.120
so 1i 2i 3i so it knows that these three
00:24:36.640
publish date values
00:24:38.720
are the first second and third parts of
00:24:40.400
the same values that's the one two three
00:24:42.320
and the i's mean they all need
00:24:43.520
converting to integers
00:24:45.760
so it pulls those out into an array and
00:24:47.360
that gets us as far as here
00:24:49.200
but then there's still this final leap
00:24:50.880
from the array to the date
00:24:53.760
um and how does it make that leap
00:24:56.880
well we're inside the model so we've
00:24:59.200
already checked the database schema to
00:25:00.880
find the types of the fields
00:25:02.320
so it just asks the model hey could you
00:25:04.080
introspect on on your fields and tell me
00:25:06.080
what's going on with this publish date
00:25:07.760
is is this a date is it a time what is
00:25:09.279
it
00:25:09.919
and the model tells it based on what the
00:25:11.520
schema said and um
00:25:14.400
attribute assignment converts this array
00:25:16.400
into a date
00:25:18.000
which definitely works but there's a
00:25:21.200
problem with this we have these two
00:25:22.799
components at opposite ends of our stack
00:25:24.559
we've got a view helper which is the
00:25:26.159
like date select thing over here as
00:25:28.240
close as you can get to the user
00:25:29.760
and then way over here as close as you
00:25:31.279
can get to the database
00:25:32.799
we've got attribute assignment needing
00:25:35.520
to talk to the schema to understand what
00:25:36.880
to do with the stuff that came out of
00:25:38.080
here
00:25:38.960
and then in the middle strong parameters
00:25:40.480
has to also understand this and be able
00:25:42.320
to
00:25:42.720
give back something that this can
00:25:43.840
understand
00:25:45.760
because when you're passing through the
00:25:47.840
publish date three strong parameters
00:25:49.679
strong parameters has to know all the
00:25:50.880
parentheticals i can ignore those and
00:25:52.400
this is all part of the publish date
00:25:53.679
value
00:25:54.400
so we've got two very distant parts of
00:25:57.360
our system that have to cooperate
00:25:59.120
quite closely and we've got three places
00:26:01.279
we would need to change
00:26:02.640
if you would ever um if you ever wanted
00:26:04.960
to change this format which is
00:26:06.720
kind of i guess referred to as shotgun
00:26:08.559
surgery sometimes where you have to
00:26:09.919
change
00:26:10.320
multiple classes to make one change in
00:26:12.240
functionality
00:26:15.840
the django example on the other hand is
00:26:17.919
a really nice example of the single
00:26:19.360
responsibility principle you've got
00:26:20.640
three objects each responsible for one
00:26:23.200
layer of the stack and they just do
00:26:26.720
their job
00:26:28.559
and it's quite clear you can draw quite
00:26:30.240
a clear picture of what's happening in
00:26:31.679
django and you can swap out individual
00:26:33.200
components
00:26:36.799
so why have i been telling you this why
00:26:38.799
have i told you that django has great
00:26:40.240
conventions and why have i told you that
00:26:41.679
django's using single responsibility in
00:26:43.440
places that rails isn't
00:26:44.880
well because django's awesome that's why
00:26:48.320
but rails is also awesome i'm not saying
00:26:51.760
that rails isn't
00:26:53.360
the point of this talk is hopefully not
00:26:55.919
to make you all switch to django or to
00:26:57.679
make you think oh that guy's a jerk he
00:26:59.200
hates rails
00:27:00.240
but to say that there is great work
00:27:02.400
going on in the web that isn't happening
00:27:04.400
in rails
00:27:05.760
there's been loads of cross pollination
00:27:07.600
and collaboration in the past but i
00:27:08.880
think there's still space for more
00:27:10.320
there's still space for the communities
00:27:11.600
to learn from each other and to share
00:27:12.960
ideas
00:27:13.520
and to to kind of gain understanding of
00:27:17.120
different patterns and different
00:27:18.159
solutions by looking at each other's
00:27:19.600
work
00:27:20.640
the the example i gave of date select
00:27:24.559
being reliant on the database schema
00:27:27.520
that means if you're using an active
00:27:28.799
model model
00:27:29.919
you can't use date select with it except
00:27:32.720
i have a pull request
00:27:34.080
in process to make it so that you can
00:27:36.320
which was entirely inspired by me using
00:27:38.320
and understanding django and then
00:27:39.679
looking at rails and seeing that there
00:27:41.120
was
00:27:41.679
something we could be doing better
00:27:43.279
that's not to blow my own trumpet and
00:27:45.039
saying well i'm contributing to rails
00:27:46.480
but
00:27:46.880
it's just to say that there is there is
00:27:48.320
this inspiration out there in other web
00:27:50.000
frameworks and other communities that
00:27:51.200
all of us can draw on
00:27:52.399
and we can use to make our own
00:27:53.600
applications better and rails itself
00:27:55.360
better
00:27:56.399
um and so maybe the next time you see a
00:27:59.279
link to a talk from uh from djangocon
00:28:01.679
or you're starting a side project you
00:28:03.279
know maybe try a different framework try
00:28:04.559
a different language see see what
00:28:05.760
happens
00:28:06.480
the worst that could happen is you think
00:28:07.840
well that was terrible and you get to
00:28:09.039
feel really good about using rails every
00:28:10.480
day
00:28:12.080
and the best that could happen is you
00:28:13.520
learn something and you learn something
00:28:15.120
that you can bring back to the community
00:28:16.320
and share with the rest of us and make
00:28:17.520
rails better for everyone
00:28:19.600
so yeah hopefully that's been a good
00:28:22.399
overview of django
00:28:23.360
and how we can share more any questions
00:28:26.640
feel free to email me or ask now i think
00:28:28.559
we've got a few minutes left
00:28:30.399
yeah we've got about 10 minutes left so
00:28:32.000
if any questions then
00:28:33.600
feel free
00:29:14.799
you