iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💡

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 server
rails s
Start console python manage.py shell rails console
rails c
  • Unlike Rails, where you work through the rails command, Django uses python manage.py as the starting point for most tasks.

Initial Directory and File Structure

Django
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(Excerpt)
.
└── 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

Django
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}'
Ruby
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)
  1. 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 require statements.



  2. In Django, fields and methods corresponding to DB table columns are basically written in models.py. In Rails, column information is recorded in db/schema.rb when the rails generate model command is executed. Subsequently, it seems the flow is to add validations or methods to app/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.

  3. In Django, since models.py is the starting point, the migration flow is: write in models.pypython manage.py makemigrations & python manage.py migrate. In Rails, the flow seems to be: create the file first with rails generate model, add additional info if necessary, then run rails 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 rails command.

3. Controller (View)

Note: In the following, Rails will be referred to as "Controller" and Django as "View".

Django Pattern 1 (Function-based)
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)})

Django Pattern 2 (Class-based)
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'
Rails
class UserContoller < ApplicationController
    def index
        @users = User.all
    end

    def show
        @user = User.find(params[:id])
    end
end
Item Rails Django
1. ORM Model.all
Model.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
  1. In Django, database query operations are performed through a manager class called objects, so the syntax is Model.objects.method(). In Rails, it seems to be in the form of Model.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.

  2. Nothing in particular.

  3. In Django, templates must be created individually. In Rails, when you generate a controller with rails generate controller, it automatically creates them under app/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.html are 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.

  4. 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 request object 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 of params[: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".

Django
<html>
  <ul>
  {% for user in users %}
    <li>
      <a href="{% url 'detail' user.id %}">{{ user.name }}</a>
    </li>
  {% endfor %}
  </ul>
</html>
Rails
<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
  1. No particular comment.

  2. No particular comment.

  3. 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 parameter user.id to the href attribute in the a tag within the template. In Rails, links are set by passing the model object user to the link_to helper.

    • I felt Rails' approach is much simpler.

5. Routing

Django Pattern 1 (Function-based)
from django.urls import path
from views import user

urlpatterns = [
    path('user', user.index, name='index'),
    path('user/<int:id>', user.detail, name='detail'),
]
Django Pattern 2 (Class-based)
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
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
  1. In Django, it consists of <int : arg>. arg> defines the variable name, and the <int part 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.

  2. No particular comment.

  3. 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
GitHubで編集を提案

Discussion