Rails vs Django: an in-depth technical comparison
· by Bernardo Pires · in web frameworks
I’d like to start with a disclaimer. I have been developing websites using Django for 3 years now and it’s no secret that I like Django. I wrote an open-source app for
it and I have started sending patches to Django. I have however written
this article to be as unbiased as possible and there’s plenty of
compliments (and criticism) to both frameworks.
Six months ago I joined a project at my University using Ruby on Rails and have been working with it since then. The first thing I did was to look for for reviews and comparisons between the two frameworks and I remember being frustrated. Most of them were a little shallow and compared them on a higher level while I was looking for answers to questions like ‘how are database migrations handled by both?’, ‘what are the differences on the template syntax?’, ‘how is user authentication done?’. The following review will answer these questions and compare how the model, controller, view and testing is handled by each web framework.
The command above will automatically generate a migration and an empty model file, which looks like this:
Coming from a Django background, one thing that annoyed me was the
fact that I couldn’t know which fields a model has just by looking into
its model file. What I learned was that Rails uses the model files
basically only for business logic and stores how all models looks like
in a file called
Notice how there are two extra fields in the model.
In Django models are defined in a file called
Notice that we had to explicitly add
This would add a new field called
Django only supports migrations by using an third-party library called South, however, I find South’s approach to be both somewhat cleaner and more practical. The equivalent migration above could be done by directly editing the
and then calling
South will automatically recognize that a new field was added to the
Django is finally going to support migrations in its newest version (1.7) by integrating South into it.
This would find all
Django’s way of doing this is not much better, but in my opinion,
more elegant. The equivalent line in Django is looks like this:
Django has two different ways of implementing controllers. You can
use a method to represent each action, very similar to how Rails’ does
it, or you can create a class for each controller action. Django however
doesn’t have separate
The amount of boilerplate in this example in Django is obvious when
compared to RoR. Django seems to have noticed this and developed a
second way of implementing controllers by harnessing inheritance and
mixins. This second method is called class-based views (remember,
Django calls the controller, view) and was introduced in Django 1.5 for
promoting code reuse. Many commons actions such as displaying, listing,
creating and updating a resource already have a class from which you
can inherit from, greatly simplifying the code. Repetitive tasks such as
specifying the view filename to be used, fetching the object and
passing the object to the view are done automatically. The same example
above would only be 4 lines using this pattern.
When the controllers are simple, using class-based views is usually
the best choice, as the code generally ends up very compact and
readable. However, depending on how non-standard your controllers are,
many functions may need to be overridden to achieve the desired
functionality. A common case is when the programmer wants to pass in
additional variables to the view, this is done by overriding the
function
The input fields
Django knows that the
Django will throw in validation for free. By default all fields are required, except when defined otherwise (such as
In the example above a
Django does not use the HTTP verb to route. Instead it is more
verbose and uses regular expressions to match URLs to controllers.
Since regular expressions are used, simple validation is automatically built in. Requesting
All fixtures are automatically loaded and can be accessed directly as local variables on test cases
Django also supports YAML fixtures but developers tend to use JSON-formatted ones.
There’s no magic, so notice how they are more verbose as you have to
explicitly define which model it is and then list the fields under
The same code in Django is very similar.
The code is fairly straightforward to understand what’s happening. The first test line simulates a request to the
There’s also a couple of additional assert helpers such as
Testing the controller in Django is done by using a class called
First we have to initialize the
It’s clear that the Rails magic here is really helpful. Rails/Ruby also has just so many great third-party apps such as factory_girl, RSpec, Mocha and Cucumber that the task of writing tests is a pleasure.
Dependencies can be added by simpling a new line to the Gemfile. All the required gems can be installed by simply calling:
Django strongly recommends the use of virtualenv for having isolated Python environments. pip is then used for managing python packages. A package can be installed by calling:
The project requirements can then be gathered with:
Rake tasks can have prerequisites. The task above, named
Django management commands are less flexible and do not support
prerequisites neither namespaces. Nonetheless they get the job done,
albeit less elegantly.
If we save the contents above in a filed named
Translation is made through a function
I found the process of having to decide the key names to use and
registering them manually on locale files very cumbersome. Django packs
the very convenient gettext. Translation is also done through a helper function,
Django will then examine all source code and automatically collect strings to be translated by calling:
The command above will generate a file for each language that you want to translate to. The files look like this:
Notice that I have already filled in the translation at
This way of localizing projects is a lot more practical as there’s no
need to think about key names and looking them up when needed.
Even though Django has packed an authentication framework since forever, the solution provided wasn’t the most flexible until about a year ago, when version 1.5 came along and brought the configurable user model. Until then, you were forced to take Django’s definition of a “user”, you couldn’t change fields neither add new ones. Finally, that’s not the case anymore, and you can swap out the user model for one that you define yourself.
You can not really go wrong by picking either one of them. My suggestion is always to try both and figure out which one you’re more confortable with. The decision may come down to which language you prefer or which principle you want to follow: convention-over-configuration or explicit is better than implicit. With CoC you get automatic imports, controller instance variables are automatically passed to the view and writing tests is convenient. With explicit is better than implicit, it’s always obvious what the code is doing, even for those not familiar with it.
From my personal experience I prefer Django. I like Python’s explicitness and I love Django’s forms and the fact that the framework is more defensive (limited template language,
Six months ago I joined a project at my University using Ruby on Rails and have been working with it since then. The first thing I did was to look for for reviews and comparisons between the two frameworks and I remember being frustrated. Most of them were a little shallow and compared them on a higher level while I was looking for answers to questions like ‘how are database migrations handled by both?’, ‘what are the differences on the template syntax?’, ‘how is user authentication done?’. The following review will answer these questions and compare how the model, controller, view and testing is handled by each web framework.
A short introduction
Both frameworks were born out of the need of developing web applications faster and organizing the code better. They follow the MVC principle, which means the modelling of the domain (model), the presentation of the application data (view) and the user’s interaction (controller) are all separated from each other. As a side note, Django actually considers the framework to be the controller, so Django addresses itself as a model-template-view framework. Django’s template can be understood as the view and the view as the controller of the typical MVC scheme. I’ll be using the standard MVC nomenclature on this post.Ruby on Rails
Ruby on Rails (RoR) is a web framework written in Ruby and is frequently credited with making Ruby “famous”. Rails puts strong emphasis on convention-over-configuration and testing. Rails CoC means almost no config files, a predefined directory structure and following naming conventions. There’s plenty of magic everywhere: automatic imports, automatically passing controller instance variables to the view, a bunch of things such as template names are inferred automatically and much more. This means a developer only needs to specify unconventional aspects of the application, resulting in cleaner and shorter code.Django
Django is a web framework written in Python and was named after the guitarrist Django Reinhardt. Django’s motivation lies in the “intensive deadlines of a newsroom and the stringent requirements of the experienced Web developers who wrote it”. Django follows explicit is better than implicit (a core Python principle), resulting in code that is very readable even for people that are not familiar with the framework. A project in Django is organized around apps. Each app has its own models, controllers, views and tests and feels like a small project. Django projects are basically a collection of apps, with each app being responsible for a particular subsystem.Model
Let’s start by looking how each framework handles the MVC principle. The model describes how the data looks like and contains the business logic.Creating models
Rails creates models by running a command in terminal.
1
2
| rails generate model Product name:string quantity_in_stock:integer category:references |
1
2
3
| class Product < ActiveRecord::Base end |
schemas.rb
. This file is automatically updated every time a migration is ran. If we take a look at this file we can see how our Product
model looks like.
1
2
3
4
5
6
7
| create_table "products" , :force => true do |t| t.string "name" , t.integer "quantity_in_stock" , t.integer "category_id" , t.datetime "created_at" , :null => false t.datetime "updated_at" , :null => false end |
created_at
and updated_at
are fields that are added to every model in Rails and will be set automatically.In Django models are defined in a file called
models.py
. The same Product
model would look like this
1
2
3
4
5
6
| class Product(models.Model): name = models.CharField() quantity_in_stock = models.IntegerField() category = models.ForeignKey( 'Category' ) created_at = models.DateTimeField(auto_now_add = True ) # set when it's created updated_at = models.DateTimeField(auto_now = True ) # set every time it's updated |
created_at
and updated_at
in Django. We also had to tell Django how these fields behave through the parameters auto_now_add
and auto_now
.Model field defaults and foreign keys
Rails will per default allow fields to be null. You can see on the example above that the three fields we created allowed null. The reference field to aCategory
will also neither create an index nor a foreign key constraint. This means referential integrity is not guaranteed. Django’s default is the exact opposite. No field is allowed to be null unless explicitly set so. Django’s ForeignKey
will also create a foreign key constraint and an index automatically.
Although Rails decision here may be motivated by performance concerns,
I’d side with Django here as I believe this decision avoids (accidental)
bad design and unexpected situations. For example, a previous student
in our project wasn’t aware that all fields he had created allowed null
per default. After a while we noticed that some of our tables contained data that made no sense, such as a poll with null
as the title. Since Rails doesn’t add FKs, following our example we
could have for example deleted a category that is still referenced by
other products and these products would then have invalid references. An
option is to use a third-party app that adds support for automatic creation of foreign keys.Migrations
Migrations allow the database schema to be changed after it has already been created (actually, in Rails everything is a migration — even creating). I have to give props to Rails for supporting this out-of-the-box for a long time. This is done using Rails’ generator
1
| $ rails generate migration AddPartNumberToProducts part_number:string |
part_number
to the Product
model.Django only supports migrations by using an third-party library called South, however, I find South’s approach to be both somewhat cleaner and more practical. The equivalent migration above could be done by directly editing the
Product
model definition and adding a new field
1
2
3
| class Product(models.Model): ... # old fields part_number = models.CharField() |
1
| $ python manage.py schemamigration products --auto |
Product
model and create a migration file. It can then be synced by calling
1
| $ python manage.py migrate products |
Making queries
Thanks to object-relation mapping, you will not have to write a single SQL line in either framework. Thanks to Ruby’s expressiveness you can actually write range queries quite nicely.
1
| Client.where(created_at: ( Time .now.midnight - 1 .day).. Time .now.midnight) |
Client
s created yesterday. Python doesn’t support syntax like 1.day
, which is extremely readable and succinct, neither the ..
range operator. However, sometimes in Rails I feel like I’m writing
prepared statements again. To select all rows where a particular field
is greater than a value, you have to write
1
| Model.where( 'field >= ?' , value) |
1
| Model.objects. filter (field__gt = value) |
Controller
Controllers have the task of making sense of a request and returning an appropriate response. Web applications typically support adding, editing, deleting and showing details of a resource and RoR’s conventions really shine here by making the development of controllers short and sweet. Controllers are divided into methods, each representing one action (show
for fetching the details of a resource, new
for showing the form to create a resource, create
for receiving the POST
data from new
and really creating the resource, etc.) The controllers’ instance variables (prefixed with @
) are automatically passed to the view and Rails knows from the method name which template file to use as a view.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| class ProductsController < ApplicationController # automatically renders views/products/show.html.erb def show # params is a ruby hash that contains the request parameters # instance variables are automatically passed to views @product = Product.find(params[ :id ]) end # returns an empty product, renders views/products/new.html.erb def new @product = Product. new end # Receives POST data the user submitted. Most likely coming from # a form in the 'new' view. def create @product = Product. new (params[ :product ]) if @product .save redirect_to @product else # overrides default behavior of rendering create.html.erb render "new" end end end |
new
and create
methods,
the creation of a resource happens in the same controller where an
empty resource is created. There is also no convention on how to name
your views. View variables need to be passed explicitly from the
controller and the template file to be used needs to be set as well.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # django usually calls the 'show' method 'detail' # the product_id parameter comes from the routing def detail(request, product_id): p = Product.objects.get(pk = product_id) # pk = primary key # renders detail.html with the third parameter passed as context return render(request, 'products/detail.html' , { 'product' : p}) def create(request): # check if form was submitted if request.method = = 'POST' : # similar to RoR' 'create' action form = ProductForm(request.POST) # A form bound to the POST data if form.is_valid(): # All validation rules pass new_product = form.save() return HttpResponseRedirect(new_product.get_absolute_url()) else : # similar to RoR' 'new' action form = ProductForm() # An empty form return render(request, 'products/create.html' , { 'form' : form }) |
1
2
3
4
5
6
7
8
9
10
11
12
13
| # Supposes the routing passed in a parameter called 'pk' # containing the object id and uses it for fetching the object. # Automatically renders the view /products/product_detail.html # and passes product as a context variable to the view. class ProductDetail(DetailView): model = Product # Generates a form for the given model. If data is POSTed, # automatically validates the form and creates the resource. # Automatically renders the view /products/product_create.html # and passes the form as a context variable to the view. class ProductCreate(CreateView): model = Product |
get_context_data
. Do you want to render a
different template depending on a particular field of the current object
(model instance)? You’ll have to override render_to_response
. Do you want to change how the object will be fetched (default is using the primary key field pk
)? You’ll have to override get_object
.
For example, if we wanted to select a particular product by its name
instead of id and to also pass to our view which products are similar to
it, the code would look like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
| class ProductDetail(DetailView): model = Product def get_object( self , queryset = None ): return get_object_or_404(Product, key = self .kwargs.get( 'name' )) def get_context_data( self , * * kwargs): # Call the base implementation first to get a context context = super (ProductDetail, self ).get_context_data( * * kwargs) # Add in the related products context[ 'related_products' ] = self .get_object().related_products return context |
View
Rails views uses the Embedded Ruby template system which allows you to write arbitrary Ruby code inside your templates. This means it is extremely powerful and fast, but with great power comes great responsibility. You have to be very careful to not mix your presentation layer with any other kind of logic. I have another example involving fellow students again. A new student had joined our RoR Project and was working on a new feature. It was time for code review. We started at the controller and the first thing that struck me was how empty his controller looked like. I immediately looked at his views and saw massive ruby blocks intermixed with HTML. Yes, Rails it not to blame for a programmers lack of experience, but my point is that frameworks can protect the developer from some bad practices. Django for instance has a very frugal template language. You can doif
s and iterate through data using for
loops, but there’s no way to select objects that were not passed in
from the controller as it does not execute arbitrary Python expressions.
This is a design decision that I strongly believe that pushes
developers into the right direction. This would have forced the new
student in our project to find the correct way of organizing his code.Assets: CSS, Javascript and images
Rails comes with an excellent built-in asset pipeline. Rails’ asset pipeline is capable of concatenating, minifying and compressing Javascript and CSS files. Not only that, it also supports other languages such as Coffeescript, Sass and ERB. Django’s support of assets it pretty much shameful compared to Rails and leaves everything for the developer to handle. The only thing Django offers is something called static files, which basically collects all static files from each app to a single location. A third-party app called django_compressor offers a solution similar to Rails’ asset pipeline.Forms
Forms in web applications are the interface through which users give input. Forms in Rails consist of helper methods that are used directly in the views.
1
2
3
4
5
6
7
8
9
10
11
| <%= form_tag("/contact", method: "post") do %> <%= label_tag(:subject, "Subject:") %> <%= text_field_tag(:subject) %> <%= label_tag(:message, "Message:") %> <%= text_field_tag(:message) %> <%= label_tag(:subject, "Sender:") %> <%= text_field_tag(:sender) %> <%= label_tag(:subject, "CC myself:") %> <%= check_box_tag(:sender) %> <%= submit_tag("Search") %> <% end %> |
subject
, message
, etc. can than be read at a controller through the ruby hash (a dictionary-like structure) params
, for example params[:subject]
and params[:message]
.
Django on the other hand abstracted the concept of forms. Forms are
normal classes that encapsulate fields and can contain validation rules.
They look a lot like models.
1
2
3
4
5
| class ContactForm(forms.Form): subject = forms.CharField(max_length = 100 ) message = forms.CharField() sender = forms.EmailField() cc_myself = forms.BooleanField(required = False ) |
CharField
counterpart in html are text input boxes and that BooleanField
s are checkboxes. If you wish, you can change which input element will be used through the widget field. Forms in Django are instantiated in the controller.
1
2
3
4
5
6
7
8
9
| def contact(request): if request.method = = 'POST' : form = ContactForm(request.POST) if form.is_valid(): return HttpResponseRedirect( '/thanks/' ) # Redirect after POST else : form = ContactForm() # An unbound form return render(request, 'contact.html' , { 'form' : form }) |
cc_myself
).
Using the snippet above, if the form fails the validation, the same
form will be automatically displayed with an error message and the input
given is shown again. The code below displays a form in a view.
1
2
3
4
| < form action = "/contact/" method = "post" > {{ form.as_p }} <!-- generates a form very similar to rails' --> < input type = "submit" value = "Submit" /> </ form > |
URLs and routing
Routing is the task of matching a particular URL to a controller. Rails makes building REST web services a breeze and routes are expressed in terms of HTTP verbs.
1
| get '/products/:id' , to: 'products#show' |
GET
request to /products/any_id
will be automatically routed to the controller products
and the action show
.
Since it’s such a common task to have all actions (create, show, index,
etc) in a controller and thanks to convention-over-configuration, RoR
created a way of quickly declaring all common routes, called resources
. If you followed Rails’ conventions when naming your controller methods this is very handy.
1
2
3
4
5
6
| # automatically maps GET /products/:id to products#show # GET /products to products#index # POST /products to products#create # DELETE /products/:id to products#destroy # etc. resources :products |
1
2
3
4
5
6
7
8
| urlpatterns = patterns('', # matches the detail method in the products controller url(r '^products/(?P\d+)/$' , products.views.DetailView.as_view(), name = 'detail' ), # matches the index method, you get the gist url(r '^products/$' , products.views.IndexView.as_view(), name = 'index' ), url(r '^products/create/$' , products.views.CreateView.as_view(), name = 'create' ), url(r '^products/(?P\d+)/delete/$' , products.views.DeleteView.as_view(), name = 'delete' ), ) |
/products/test/
will not match any routing rule and a 404 will be raised, as test
is not a valid integer. The difference in philosophy can be seen here
again. Django does have any convention when naming controller actions,
so Django does not have any cool helpers like Rails’ resource
and every route has to be explicitly defined. This results in each controller requiring several routing rules.Testing
Testing is just a breeze in Rails and there’s a much stronger emphasis on it in Rails than in Django.Fixtures
Both frameworks support fixtures (a fancy word for sample data) in a very similar way. I’d however give Rails a bonus point for making it more practical writing them as it infers from the file name for which model you’re writing them. Rails uses YAML-formatted fixtures, which is a human-readable data serialization format.
1
2
3
4
5
6
7
8
9
10
| # users.yml (Rails now knows we're creating user fixtures) john: name: John Smith birthday: 1989 - 04 - 17 profession: Blacksmith bob: name: Bob Costa birthday: 1973 - 08 - 10 profession: Surfer |
1
| users( :john ).name # John Smith |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| [ { "model" : "auth.user" , "fields" : { "name" : "John Smith" , "birthday" : "1989-04-17" , "profession" : "Blacksmith" , } }, { "model" : "auth.user" , "fields" : { "name" : "Bob Costa" , "birthday" : "1973-08-10" , "profession" : "Surfer" , } } ] |
fields
Testing models
Both frameworks are pretty identical in unit testing the models. Checks are made using a bunch of different types of assertions such asassert_equal
, assert_not_equal
, assert_nil
, assert_raises
, etc.
1
2
3
4
5
6
| class AnimalTest < ActiveSupport::TestCase test "Animals that can speak are correctly identified" do assert_equal animals( :lion ).speak(), 'The lion says "roar"' assert_equal animals( :cat ).speak(), 'The cat says "meow"' end end |
1
2
3
4
5
6
7
8
9
| class AnimalTestCase(TestCase): def test_animals_can_speak( self ): """Animals that can speak are correctly identified""" # no way of directly accessing the fixtures, so we have to # manually select the objects lion = Animal.objects.get(name = "lion" ) cat = Animal.objects.get(name = "cat" ) self .assertEqual(lion.speak(), 'The lion says "roar"' ) self .assertEqual(cat.speak(), 'The cat says "meow"' ) |
Testing controllers
Rails really shines thanks to its magic here again. Rails uses the class name to infer which controller is being tested and testing a particular action is as simple as callinghttp_verb :action_name
. Let’s take a look at an example.
1
2
3
4
5
6
7
8
| class UsersControllerTest < ActionController::TestCase test "should get index" do get :index # GET request to the index action assert_response :success # request returned 200 # assigns is a hash containing all instance variables assert_not_nil assigns( :users ) end end |
index
action on the Users
controller. The second line then checks if the request was successful (response code 200-299). assigns
is a hash that contains the instance variables that are passed to the
views. So the third line is testing if an instance variable called users
was set and is not nil
.There’s also a couple of additional assert helpers such as
assert_difference
that are very convenient.
1
2
3
4
5
6
| # assert_difference checks if the number being tested # changed between the start and the end of the block assert_difference( 'Post.count' ) do # creates a post post :create , post: {title: 'Some title' } end |
Client
which acts as a dummy web browser. Here’s how the equivalent code looks like in Django.
1
2
3
4
5
6
7
8
9
| class UsersTest(unittest.TestCase): def setUp( self ): self .client = Client() def test_index( self ): """ should get index """ response = self .client.get(reverse( 'users:index' )) self .assertEqual(response.status_code, 200 ) self .assertIsNotNone(response.context[ 'users' ]) |
Client
at the test setup. The first line of test_index
simulates a GET
request to the controller action index
of User
s. reverse
looks up the URL for the index action. Notice how the code is more verbose and there are no helpers such as assert_response :success
. response.context
contains the variables that were passed to the view.It’s clear that the Rails magic here is really helpful. Rails/Ruby also has just so many great third-party apps such as factory_girl, RSpec, Mocha and Cucumber that the task of writing tests is a pleasure.
Tools and other features
Dependency management
Both frameworks have fantastic dependency management tools available. Rails uses Bundler, which tracks the required gems for running a particular ruby application through a file called Gemfile.
1
2
3
4
| gem 'nokogiri' gem 'rails' , '3.0.0.beta3' gem 'rack' , '>=1.0' gem 'thin' , '~>1.1' |
1
| bundle install |
1
| pip install django-debug-toolbar |
1
| pip freeze > requirements.txt |
Management commands
Almost every project ends up having common administration tasks such as precompiling assets, cleaning logs, etc. Rails uses Rake for handling these tasks. Rake is very flexible and makes developing tasks easy, especially sophisticated ones that depend on other tasks.
1
2
3
4
5
| desc "Eats some food. Cooks it and sets the table before eating." task eat: [ :cook , :set_the_table ] do # Before eating delicious food, :cook and :set_the_table will be done # The code for eating food can be directly written here end |
eat
, runs the tasks cook
and set_the_table
before executing. Rake also support namespaces for grouping common tasks. Tasks can be executed by simply callig its name:
1
| rake eat |
1
2
3
4
5
6
7
| class Command(BaseCommand): help = 'Eats some food' def handle( self , * args, * * options): call_command( 'cook' ) # this is how a management command is called in code set_the_table() # but subtasks should just be regular functions in python # code for eating stuff |
eat.py
we can call it with:
1
| python manage.py eat |
Internationalization and localization
Internationalization in Rails is somewhat rudimentar. Translation strings are defined as ruby hashes in files under the folderconfig/locales
.
1
2
3
4
5
6
7
| # config/locales/en.yml en: # the language identifier greet_username: "Hello, %{user}!" # translation_key: "value" # config/locales/pt.yml pt: greet_username: "Olá, %{user}!" |
t
. The first parameter is the key that identifies which string is to be used (for example greet_username
). Rails will automatically select the correct language.
1
| t( 'greet_username' , user: "Bill" ) # Hi, Bill or Olá, Bill |
ugettext
, but this time the key is the untranslated string itself.
1
| ugettext( 'Hi, %(user)s.' ) % { 'user' : 'Bill' } # Hi, Bill or Olá, Bill |
1
| django-admin.py makemessages -a |
1
2
3
| # locale/pt_BR/LC_MESSAGES/django.po msgid "Hi, %(user)s." # key msgstr "Olá, %(user)s" # value (translation) |
msgstr
(they’re originally empty). Once the translations have been made, they have to be compiled.
1
| django-admin.py compilemessages |
User authentication
I have to say I was somewhat shocked when I learned that RoR does not come bundled with any sort of user authentication. I can’t think of any project that wouldn’t need authentication and user management. The most popular gem for this is devise, which unsurprisingly, is the most popular gem for Rails, having about half the stars Rails has on Github.Even though Django has packed an authentication framework since forever, the solution provided wasn’t the most flexible until about a year ago, when version 1.5 came along and brought the configurable user model. Until then, you were forced to take Django’s definition of a “user”, you couldn’t change fields neither add new ones. Finally, that’s not the case anymore, and you can swap out the user model for one that you define yourself.
Third party libraries
There’s not much to say here. This post has already mentioned plenty of third party libraries for both frameworks and it’s clear that both enjoy a plethora of apps. Django Packages is an excellent website for searching Django Apps. I haven’t found such a website for Rails.Community
I don’t have concrete data to prove it, but I’m pretty sure Rails’ community is bigger. RoR has more than the double amount of stars on Github than Django. It also has the double the amount of questions tagged Rails on Stackoverflow. There also appears to be widely more jobs for RoR developers than Django (241 vs 58 on stackoverflow careers). Rails is huge and has so many fantastic resources for learning such as Rails Casts and Rails for Zombies. Django has Getting Started with Django but it just doesn’t compare. I learned Django using a great resource called The Django Book, but it has been outdated for several years now. Don’t get me wrong, there’s plenty of activity around Django too and if you have any questions, you will most likely find the solution very easily by googling it, but it’s just not as big as Rails.Conclusion
Both Ruby on Rails and Django are outstanding frameworks for web development. They are a big help for developing modularized, clean code and do a great job in reducing time spent on common activities. I can not live anymore without an ORM framework, a templating engine and a session management system. The question is then, how do I pick one?You can not really go wrong by picking either one of them. My suggestion is always to try both and figure out which one you’re more confortable with. The decision may come down to which language you prefer or which principle you want to follow: convention-over-configuration or explicit is better than implicit. With CoC you get automatic imports, controller instance variables are automatically passed to the view and writing tests is convenient. With explicit is better than implicit, it’s always obvious what the code is doing, even for those not familiar with it.
From my personal experience I prefer Django. I like Python’s explicitness and I love Django’s forms and the fact that the framework is more defensive (limited template language,
null
not
enabled by default on model fields). I know however plenty of folks that
can’t live without Rails’ magic and its superior testing environment.
No comments:
Post a Comment