iTranslated by AI
Comparing Django and Rails Syntax #2
Introduction
Purpose of this article
As a first-time learner of web application frameworks, the author chose Django (Python), which follows the MTV model. This article aims to catch up on Ruby on Rails, which follows the MVC model, while comparing it with Django where appropriate.
Prerequisites for reading this article
This is the second article in the series.
For the background on why I started writing these articles, please refer to the previous post.
A Django user tried Rails (+Docker) to catch up (#1 Environment Setup, 2022)
- This time, we will focus on the MTV (MVC) architecture of Django and Rails.
- We will compare the syntax of Django and Rails, sharing features of Rails and my impressions of them.
※ Note the following:
-
These are my impressions at the current stage of early learning.
(Therefore, please be aware that I might be expressing some misconceptions. Since I haven't used it for a long time yet, I haven't fully analyzed everything. If you have any comments, such as "this design exists for this benefit" or "here is the reason for this," I would love to hear them.) -
I will add to and correct this article as I progress in my learning.
1. App Creation to Basic Commands
| Item | Django | Rails |
|---|---|---|
| Project creation | django-admin startproject project |
- |
| App creation | python manage.py startapp app |
rails new rails-app |
| Start development server | python manage.py runserver |
rails serverrails s
|
| Start console | python manage.py shell |
rails consolerails c
|
- Unlike Rails, where you work through the
railscommand, Django usespython manage.pyas the starting point for most tasks.
Initial Directory and File Structure
project/
manage.py
project/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
app/
__init__.py
admin.py
apps.py
migrations
models.py
tests.py
urls.py
views.py
.
└── rails-app
├── app
│ ├── assets
│ ├── channels
│ ├── controllers
│ ├── helpers
│ ├── javascript
│ ├── jobs
│ ├── mailers
│ ├── models
│ └── views
├── bin
├── config
│ ├── environments
│ ├── initializers
│ ├── locales
│ └── webpack
│ ├── application.rb
│ ├── boot.rb
│ ├── cable.yml
│ ├── credentials.yml.enc
│ ├── database.yml
│ ├── environment.rb
│ ├── environments
│ ├── initializers
│ ├── locales
│ ├── master.key
│ ├── puma.rb
│ ├── routes.rb
│ ├── spring.rb
│ ├── storage.yml
│ ├── webpack
│ └── webpacker.yml
├── db
│ └── migrate
├── lib
│ ├── assets
│ └── tasks
├── log
├── node_modules
├── public
│ └── packs
├── storage
├── test
│ ├── channels
│ ├── controllers
│ ├── fixtures
│ ├── helpers
│ ├── integration
│ ├── mailers
│ ├── models
│ └── system
├── tmp
│ ├── cache
│ ├── pids
│ ├── sockets
│ └── storage
└── vendor
-
As shown above, Rails generates many more files than Django at the initial app creation stage.
- Coming from a Django background, it was a bit challenging to grasp what is located where when so many files are generated at once. However, I feel that once I get used to it, I will likely appreciate the convenience of having the entire boilerplate generated from the start.
2. Model
from django.db import models
class User(models.Model):
name = models.CharField(max_length=50)
def display(self):
return f'No{self.id}:{self.name}'
class User < ApplicationRecord
def display
return "No#{self.id}:#{self.name}"
end
end
Below is a summary table of the differences I noticed between Rails and Django.
| Item | Rails | Django |
|---|---|---|
| 1. Loading classes/modules | Files under app/ do not require explicit require | Explicit import required every time |
| 2. Location of column information | db/schema.rb |
model.py |
| 3. Migration | Initiated by rails generate model command(Files created by entering column info during the command) |
Initiated by model.py(Files created using python manage.py makemigrations after writing column design in model.py) |
-
In Django, classes and modules used within a file generally need to be imported explicitly. In Rails, thanks to "autoloading," basic classes that are frequently used do not seem to require explicit
requirestatements.
-
In Django, fields and methods corresponding to DB table columns are basically written in
models.py. In Rails, column information is recorded indb/schema.rbwhen therails generate modelcommand is executed. Subsequently, it seems the flow is to add validations or methods toapp/models/xxx(model_name).rb.- Since I often look at models to check column information in Django, I felt this part would take some getting used to.
- Since I often look at models to check column information in Django, I felt this part would take some getting used to.
-
In Django, since
models.pyis the starting point, the migration flow is: write inmodels.py→python manage.py makemigrations&python manage.py migrate. In Rails, the flow seems to be: create the file first withrails generate model, add additional info if necessary, then runrails db:migrate.- Trying out Rails, this was one of the first differences I noticed compared to Django. I found it convenient that Rails automatically handles basic column definitions and file creation via the
railscommand.
- Trying out Rails, this was one of the first differences I noticed compared to Django. I found it convenient that Rails automatically handles basic column definitions and file creation via the
3. Controller (View)
Note: In the following, Rails will be referred to as "Controller" and Django as "View".
from app.models import User
from django.shortcuts import render
def index(request):
return render(request, 'users/index.html', {'users':User.objects.all()})
def detail(request,id):
return render(request, 'users/detail.html', {'user':User.objects.get(id=id)})
from django.views import generic
from app.models import User
class IndexView(generic.ListView):
model = User
template_name = 'users/index.html'
class DetailView(generic.DetailView):
model = User
template_name = 'users/detail.html'
class UserContoller < ApplicationController
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
end
| Item | Rails | Django |
|---|---|---|
| 1. ORM |
Model.allModel.find(id=1)
|
Model.objects.all()Model.objects.get(id=1)
|
| 2. Passing variables to View (Template) | @arg |
arg |
| 3. Creation of View (Template) files | Created automatically via Rails command | Created separately by hand |
| 4. Method arguments | No explicit arguments | ① Explicitly state request and arguments from the URL dispatcher② No explicit arguments |
-
In Django, database query operations are performed through a manager class called
objects, so the syntax isModel.objects.method(). In Rails, it seems to be in the form ofModel.method().- Since you don't have to write
objects, the code is more concise and looks simpler. Since Django occasionally uses custom manager classes, I need to investigate further which design is easier to handle.
- Since you don't have to write
-
Nothing in particular.
-
In Django, templates must be created individually. In Rails, when you generate a controller with
rails generate controller, it automatically creates them underapp/views.- I think Rails has a clever design that ensures consistent file management without worrying about where to create views or what to name them, as it automatically organizes them into folders named after the controller. In Django's generic class-based views, default file names like
<app name>/<model name>_xxx.htmlare set, but since templates are often grouped in a template folder, the defaults aren't used that much. My impression is that having the decision of template storage and naming left entirely to the developer can lead to cluttered management or extra effort for beginners.
- I think Rails has a clever design that ensures consistent file management without worrying about where to create views or what to name them, as it automatically organizes them into folders named after the controller. In Django's generic class-based views, default file names like
-
In Django, the notation for rendering a template can be written as either a function or a class. When writing as a function, you must explicitly state the
requestobject as the first argument, followed by arguments from URL parameters (though in practice, generic class-based views are the standard when using the Django framework). In Rails, it is basically written as a class without explicit arguments, and parameters from the URL are received in the form ofparams[:xx].- This probably stems from the language specifications of Ruby and Python, but Ruby definitely requires less code.
4. View (Template)
Note: In the following, Rails will be referred to as "View" and Django as "Template".
<html>
<ul>
{% for user in users %}
<li>
<a href="{% url 'detail' user.id %}">{{ user.name }}</a>
</li>
{% endfor %}
</ul>
</html>
<ul>
<% @users.each do |user| %>
<li>
<%= link_to user.name, user %>
</li>
<% end %>
</ul>
| Item | Rails | Django |
|---|---|---|
| 1. Notation |
<% %> and <%= %>
|
{% %} |
| 2. Variable reception from Controller (View) | <% @arg %> |
{% arg %} |
| 3. Link notation to Controller (View) | Conversion from model object to path using URL helper | Namespace using URLconf |
-
No particular comment.
-
No particular comment.
-
In Django, links are set by configuring a namespace corresponding to the URL in the routing (the
name='detail'part) and passing{% url 'detail' %}along with the parameteruser.idto thehrefattribute in theatag within the template. In Rails, links are set by passing the model objectuserto thelink_tohelper.- I felt Rails' approach is much simpler.
5. Routing
from django.urls import path
from views import user
urlpatterns = [
path('user', user.index, name='index'),
path('user/<int:id>', user.detail, name='detail'),
]
from django.urls import path
from views import UserListView, UserDetailView
urlpatterns = [
path('user', UserListView.as_view(), name='list'),
path('user/<int:id>', UserDetailView.as_view(), name='detail'),
]
Rails.applications.routes.draw do
get 'user' => 'user#index'
get 'user/:id' => 'user#detail'
end
| Item | Rails | Django |
|---|---|---|
| 1. Parameter variables | :arg |
<int:arg> |
| 2. Configuration to Controller (View) | controller#method |
view.method |
| 3. Routing by HTTP method | Can branch at the routing level | Branch at the destination View |
-
In Django, it consists of
<int:arg>.arg>defines the variable name, and the<intpart is called a path converter, which determines the pattern that matches this part of the URL path (I haven't fully investigated the URL dispatcher yet...). In Rails, it is written as:arg. -
No particular comment.
-
In Django, the routing part simply maps URLs to Views, and branching based on HTTP methods is implemented within the View. In Rails, you can branch to different Controller actions based on the HTTP method directly in the routing.
- Further verification is needed to see which design is easier to use.
Conclusion
I have roughly checked the MVC architecture of Rails.
My first impressions are:
- The ecosystem for file generation is robust, where the Rails command serves as the starting point and automatically generates files with basic boilerplate information based on the command.
- Combined with Ruby's syntax, the amount of code required for common operations is kept minimal.
These aspects left a positive impression on me.
As for the primary goal—whether it is easy for a Django user to catch up with Rails:
Regarding the basic MVC structure, I feel I was able to grasp it without much issue, though it does require some adjustment.
Future Plans
My plan for the future is to deepen my understanding of framework and language specifications by comparing Django and Rails again once I've become more familiar with Rails, while analyzing the characteristics and pros/cons of both Django/Rails and Python/Ruby.
In the next installment, I hope to cover one of the following:
- Subtle differences between Django and Rails (such as ORM syntax)
- Differences between Django REST Framework and Rails API mode
- Differences in syntax between Python and Ruby
Discussion