My group at $WORK has decided to start using Django as our web development framework so I am working my way through the tutorial. First off, yeah! there is actually a tutorial! Nice to be using a mature technology with good documentation.
The overview does a nice job of showing off some basics - models (including relationships), routing/urls, views, and templates (including template composition and reuse). Which brings me to my first mental mapping. One basic design pattern for web apps is known as MVC - Model View Controller. So Rails calls three of the directories under ‘app’ models, views, and controllers. There is a fourth directory named helpers, which is an awkward catch-all that tries to take the logic out of the view layer, with variable success. And the urls or routes are not defined within ‘app’ at all and instead are defined in the config directory in ‘config/routes.rb’. Rails reuses the names from MVC but in so doing sometimes ends up with some odd compromises. The Django breakdown, on the other hand, seems to do a nicer job of splitting the controller and view logic into the logic part (named views) and the display part, in templates.
One of the best parts of Ruby is rake and Rails makes extensive use of it for setting up and managing apps. In the Django ecosystem, many similar tasks are performed via the ‘manager.py’ script.
Command | Rails | Django |
---|---|---|
Populate the database | rake db:migrate | manage.py syncdb |
Shell access to models | rails console | manage.py shell |
Global
Django: “reusable apps” Rails: “engines” (and to a lesser degree, plugins)
Rails: built in sessions but no authentication or authorization. Django: sessions + authentication. Not sure how much authorization infrastructure comes built in.
Troubleshooting
rails console - great for models. Now better than it was for controllers, routes, helpers. Not sure about views. Find a recent blog post and read up / link.
Django shell:
manage.py shell
- great for models. To poke around your views layer
(anything that needs the response object), you need to add some power:
Models
Both Rails and Django make handling associations a breeze. In Django, for one to many associations, all you need to do is create declare an attribute on the many side (the side that gets the mapping column) that is a ForeignKey, e.g.:
And Django sets up the mapping for you. Then you can assign a reporter
to an article by saying my_article.reporter = some_reporter
. And you
can chain methods to get to the reporter’s name:
my_article.reporter.full_name
. From the reporter side, you can get
a list of all her articles by asking for
murrow.article_set.all()
. Rails is similar except that you don’t use
foreign key relationships (not my favorite Rails decision) but instead
you declare the relationships explicitly in both models:
The Django database query interface is a bit like the new ActiveModel syntax - but with ‘get’ instead of ‘find’.
Even in the first part of the tutorial, I run up against Python’s
‘explicit is better than implicit’ philosophy. Rails will use the
class name to infer what instances of your model should be called. You
can get to that value using the human_attribute_name method and you
can either override that method in your model (old school) or use
Rails internationalization capabilities to override the value in your
locale config files. ActiveRecord also provides a default display of
each instance which includes all the attributes of an instance. By
contrast, in Django you need to define a __unicode__()
(or in Python 3
a __str__()
) method for each model class so that the shell and the
admin interface know what to call things. There doesn’t appear to be a
default representation that shows all the data values; I suspect you
are expected to use Python’s dir()
function, e.g. print dir(my_poll)
.
Models
In Rails the many side of the relationship is accessed as the plural of it’s name: my_poll.choices. In Django you use type + _set: my_poll.choice_set
Rails scopes (formerly known as named_scopes) let’s you make something that looks like a method that can be used to constructed queries - especially useful if you want to chain several of these conditions together but in a readable way.
Poking around in Django it appears to me that there isn’t such a thing
- unless the people answering questions on StackOverflow are as ignorant as I am about Django’s query interface. The answer appears to be, if it’s simple, just put the logic for the method directly into your filter. If it is hairier, possibly write the SQL yourself. ???
Validations? Cool part of Rails. Is there a Django equivalent? or do you do that validation in the view (aka controller)?
Django admin
Wow! Just wow! Rails scaffold gives you OK CRUD functionality and there are some plugins that help make things somewhat fancier. But the out of the box admin functionality in Django is fabulous: sorting, searching, filtering. The closest thing I know in the Rails ecosystem is ActiveAdmin, which is nice, but deviates somewhat from the Rails standard.
One thing I really like a lot is the “History” tab. I end up wanting to add auditing to a lot of things I write but in several cases I have added acts_as_versioned to tracked the data - but didn’t provide an admin interface for displaying the changes. So, of course, now I want more. Does the Django admin interface provide a way to revert to a specific version? And can it diff within a text area?
Controllers / Views
Similar render and redirect options. Django provides two really handy shortcuts get_object_or_404 and [get_list_or_404]((https://docs.djangoproject.com/en/1.8/topics/http/shortcuts/#get-list-or-404). Handy enough I should create equivalents instead of the controller code I have.
Routing
Rails’ own interpretation of RESTful urls is strongly favored - in code and by the community. URLs all defined in a single routes.rb config file - unless you are using an engine. (For a while engines didn’t really have a mechanism for creating their routes other than by having a generator write something into the routes file at install. But now I think the routing infrastructure will look for routes files in engines that are mounted into your Rails project.
Django’s routing has a master file, urls.py but it is expected that
each of your site’s apps will define their own urls and that you will
just include those urls into your top level urls.py with a line like
url(r'^polls/', include('polls.urls'))
- with or without a
namespace. The point of a namespace is to avoid name conflicts in the
global set of url names. In with the include above, the names in
polls.urls will be global - so the url named details in polls.urls
will be the details url for the entire app. If you say
url(r'^polls/', include('polls.urls', namespace='polls'))
, then they
will not be global and instead can be accessed as polls:details.
Both frameworks encourage you to use the url lookup mechanism for creating links instead of hard coding them. The big win for this is that if your url need to change, you only have to change the appropriate routes.rb or urls.py file. The references to the urls can all stay the same.
In both systems, some people like having even shorter syntax for
defining redirects - redirecting to the object. In Rails it is, as
usual, ‘convention over configuration’. If you have defined your
routes using ‘resources’, then you can just say redirect_to object
In Django, you can get a very similar redirect syntax: return
redirect(object)
But to make it work, you need to define a
get_absolute_url
method on your model class. For example:
Templates
Every web framework provides some way of creating web pages from modular pieces. In fact defining your design and navigation elements in one place is sometimes the major reason for using a framework in the first place. I still need to read ‘The Rails View’ to see if I am doing this right, but I think the Django views may be a bit more flexible than the normal Rails view processing.
Rails - one big layout file - chose which main layout file by setting the name in the controller. ‘yield some_name’ and ‘content_for some_name’. Helpers - methods to munge display variables.
Django general “extends” mechanism. Named blocks. Filters attached to data using unix-style pipe, “|”.
Helpers
One of the fabulous things about Rails is all the useful helper methods available to you. Some were created for internal use but are super handy for display purposes, e.g. humanize, pluralize, etc. Others are specifically for display. Many other frameworks either had their own versions, or borrowed liberally from Rails.
Concept Rails Django Timezone handling django.utils.timezone
Forms
Code generation is nice - but there is a lot less typing involved in declaring the form using the Django admin tool. Field sets are quite nice - esp with the automatic ‘collapse’ option. I think this is fairly similar to Rails’ ActiveAdmin. (Sad to see that AA uses Devise for it’s authenitcation tool.)
Testing
Both frameworks come from a background of TDD and have pretty good tools for writing tests. Ruby actually has an embaarssment of riches since it seems that everyone has written their own testing DSL.
Default task runs all tests, or you can run parts. In Rails there are
tests for each layer. For
Django,
the tests are broken out per app: manage.py test polls
. View tests
can assert against the content of the page with ‘contains’ and if you
don’t find what you are looking for, you can print out the web page
content:
Granular testing. Easy to do in rspec_rails. Possible but not so easy in test_unit/minitest. Easy in Django - just append the name of a specifit test to your command line:
Database for running tests
Djano: Aside from using a separate database, the test runner will otherwise use all of the same database settings you have in your settings file: ENGINE, USER, HOST, etc. The test database is created by the user specified by USER, so you’ll need to make sure that the given user account has sufficient privileges to create a new database on the system.
Both offer fixtures for setting up data. Rails community mainly moved to factories and the Django tutorial has you create a tiny factory as a helper methods. Are there libraries for factories in wide use?
Overriding settings - esp urls for apps that could be mounted anywhere; common option - use a decorator on the test method on or the entire class: @override_settings(LOGIN_URL=’/other/login/’)
Some useful looking Django test methods: SimpleTestCase.assertFieldOutput TestCase.assertFormError
Django integration with Selenium: LiveServerTestCase