List

Sleeping with the enemy..

Sleeping with the enemy..

by George Brocklehurst

In the video titled "Sleeping with the enemy," George Brocklehurst presents a comparison between the Ruby on Rails framework and Django, highlighting the strengths and unique features of Django in the context of web development. This session was held at Rails Conf 2013 and focuses on learning from different frameworks to enhance understanding and improve applications.

The key points discussed during the session include:

  • Introduction to Django: George shares his background, detailing his experiences transitioning from Rails to Django and emphasizes that his comparisons aim to identify Django’s strengths without denigrating either framework.

  • Project Structure in Django: An overview of how a Django project is organized is provided, pointing out key components such as project settings, URL routing, and application management.

  • Models and Views: George explains how Django handles models similarly to Rails, but with different syntactic approaches. He highlights the use of classes to structure models and the absence of separate schema definitions.

  • Generic Views in Django: The presentation demonstrates Django’s generic views as a powerful feature that reduces boilerplate code by linking URL routing to the appropriate view classes, which automatically handles common functionality like showing model data.

  • Create Operations and Forms: The discussion moves to form handling in Django, illustrating the creation of forms that manage user inputs, data validation, and model interaction fluidly. He contrasts this with Rails' handling of parameters and the potential complexity involved.

  • Convention Over Configuration: George underscores how Django’s design embodies the principle of convention over configuration, allowing for less code to achieve common functionality. He discusses the template method pattern that Django employs, making it adaptable and easy to extend.

  • Learning from Each Other: The talk encourages developers to explore other frameworks like Django to glean insights and practices that can enhance Rails development. George advocates for cross-pollination among programming communities to encourage better solutions and innovations in web development.

  • Conclusion: The main takeaway is an invitation for Rails developers to experiment with Django and draw lessons that could reshape their understanding and usage of Rails. This exploration can lead to improved coding practices and application design.

This comparison is not just to favor Django but to celebrate the unique solutions different frameworks provide, ultimately suggesting that understanding diverse technologies can lead to personal and community growth in web development.

In this session we'll go off the Rails and take a look at what our Pythonista cousins are doing with Django.
I'll start with some live coding: recreating DHH's infamous 15 minute blog demo using Django and explaining the building blocks of a Django app along the way. I'll then take that app and use it to look at some design decisions Django makes, and how they compare to Rails. You'll see convention over configuration in places you didn't expect it, why Django doesn't need attr_accessible or strong parameters, and how the template method pattern could change your life.
Why talk about Python at a Rails conference? Seeing another way of doing things forces us to think about what we're doing, challenges or validates the assumptions we make about our work, and inspires us to try new things.

Help us caption & translate this video!

http://amara.org/v/FGag/

Rails Conf 2013

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