Hoc blog est pessimus

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!