Notes on Django from a Rubyist
December 7, 2024
In this post I've gathered some notes on setting up a Django project, comparing some of the aspects to Ruby on Rails projects.
Virtual Environments vs Gemfile
While Rails uses Bundler and a Gemfile to manage dependencies, Django projects instead use Python virtual environments and a requirements.txt file. Here's how to set up a virtual environment and generate a requirements.txt:
# Create a virtual environment
python3 -m venv venv
# Activate it (Unix/MacOS)
source venv/bin/activate
# or on Windows
.\venv\Scripts\activate
# Install Django
pip3 install django
# generate a requirements.txt from the current state of the project
pip3 freeze > requirements.txt
# use this to install all project dependencies
pip3 install -r requirements.txt
Creating a New Project
In Django, you first create a project, then create apps within it:
django-admin startproject bookstore
cd bookstore
python manage.py startapp books
The resulting structure is different from Rails:
bookstore/ # Project root
├── bookstore/ # Project configuration
│ ├── __init__.py
│ ├── settings.py # Like config/application.rb
│ ├── urls.py # Like config/routes.rb
│ └── wsgi.py # Production server config
├── books/ # Your app
│ ├── __init__.py
│ ├── admin.py # Admin interface config
│ ├── apps.py # App configuration
│ ├── models.py # Like app/models/
│ ├── views.py # Like app/controllers/
│ └── tests.py # Like test/
└── manage.py # Command-line utility
Remember to add your app to INSTALLED_APPS in settings.py:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'books', # Add this line
]
Models and Migrations
Django models inherit from models.Model:
# books/models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
bio = models.TextField()
def __str__(self):
return self.name
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')
published_date = models.DateField()
price = models.DecimalField(max_digits=6, decimal_places=2)
def __str__(self):
return self.title
Unlike in Rails, in Django model database structure is defined in a models file, from which migrations are generated using the manage.py script. Yes, it's not unusual for Django projects to have all the models defined in one file!
To generate and run migrations, you'd run the following commands:
python manage.py makemigrations
python manage.py migrate
Note, that the default database for Django is SQLite, which would be created when you run the 'migrate' command, if it doesn't exist yet.
Admin Interface
Django includes a complete admin interface out of the box.
# books/admin.py
from django.contrib import admin
from .models import Author, Book
@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name',)
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'published_date', 'price')
list_filter = ('published_date', 'author')
search_fields = ('title',)
Create an admin user:
python manage.py createsuperuser
Routing in Django
The project and the apps have their own urls.py files. The project urls.py will contain the routes for an admin area and references to the apps' url files.
# bookstore/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('books.urls')),
]
# books/urls.py
from django.urls import path
from . import views
app_name = 'books'
urlpatterns = [
path('', views.index, name='index'),
path('books/', views.book_list, name='book_list'),
path('books//', views.book_detail, name='book_detail'),
]
Controllers, Views and Templates
In Django the logic you'd normally have in a Rails Controller, goes into a 'View'. Django views can be functions or classes:
# books/views.py
from django.shortcuts import render
from .models import Book
def index(request):
latest_books = Book.objects.order_by('-published_date')[:5]
return render(request, 'books/index.html', {'latest_books': latest_books})
Templates (no special file type here, just .html files) in Django use a similar syntax to ERB but with different tags:
<!-- books/templates/books/index.html --&rt;
<!DOCTYPE html&rt;
<html&rt;
<head&rt;
<title&rt;Bookstore</title&rt;
</head&rt;
<body&rt;
<h1&rt;Latest Books</h1&rt;
{% for book in latest_books %}
<div class="book"&rt;
<h2&rt;{{ book.title }}</h2&rt;
<p&rt;By {{ book.author.name }}</p&rt;
<p&rt;Price: ${{ book.price }}</p&rt;
</div&rt;
{% endfor %}
</body&rt;
</html&rt;
Key template differences: {% %} for logic (instead of <% %&rt;) {{ }} for output (instead of <%= %&rt;) No more = form_for helpers (Django has its own form system)
Running the Server
python manage.py runserver
By default, Django runs on port 8000 (Rails uses 3000).
Key Philosophical Differences
Explicit vs Implicit: Django favors explicit configuration over Rails' convention over configuration. Apps vs Engines: Django apps are lighter than Rails engines and are meant to be used more frequently. Admin Interface: Django provides a powerful admin interface out of the box. Form Handling: Django's form system is more explicit and separate from models. Template Language: Django's template language is intentionally limited to enforce separation of concerns.
All right, I hope that was useful!