Intro: Python Rocks

I recently started working with Python scripts in my Cloud Computing coursework at UT Austin and I’d forgotten how much Python rocks.

Python As I worked on some basic scripting, I realized Python arguably provides more dynamic scripts than Bash. Plus I find it inherently more pleasant to read.

This kicked off a Python renaissance that led me to start tinkering with multiple Python-based web frameworks, including Django and Flask.

def python_power():
    features = ["dynamic", "powerful"]
    return f"Python is {' and '.join(features)}!"

I haven’t mentioned Python’s extensive Data Science and Analytics libraries. I have no doubt these are amazing, I just haven’t worked with them much yet.

For this post, I want to look at the distinct approaches to declaring associations in Django’s ORM and Rails’ ActiveRecord.

First, let’s understand what an ORM is and isn’t.

ORM != SQL

ORM and SQL are two very different approaches to database interaction. Let’s have a look:

Abstraction Level
SQL: Direct, low-level interaction with the database.
ORM: High-level abstraction translating object-oriented code to database operations.

Language
SQL: Uses its own language syntax, specific to database operations.
ORM: Uses the host programming language (e.g., Python, Ruby, Java).

Data Representation
SQL: Works with tables, rows, and columns.
ORM: Represents data as objects and classes.

Queries
SQL: Requires writing explicit SQL queries.
ORM: Generates SQL queries based on method calls and object manipulations.

Database Independence
SQL: Syntax can vary between different database systems.
ORM: Often provides a level of database independence, allowing easier switching between systems.

Learning Curve
SQL: Requires learning SQL syntax and database concepts.
ORM: Requires understanding OOP concepts and the specific ORM’s API.

Django and ActiveRecord

Two popular ORMs, Django’s ORM and Rails’ ActiveRecord, have distinct approaches to declaring associations.

An association in an ORM represents a relationship between two or more database tables, expressed through the object models in your code.

While both ORMs aim to make database relationships intuitive, their syntax and conventions differ significantly. Let’s explore how associations are declared in each framework.

Django’s Approach

Django uses explicit fields to define relationships between models. Here are the main types of associations:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author,
              on_delete=models.CASCADE)
    contributors = models.ManyToManyField(Author,
              related_name='contributions')

class Profile(models.Model):
    user = models.OneToOneField(Author,
            on_delete=models.CASCADE)
    bio = models.TextField()

ActiveRecord’s Approach

Rails’ ActiveRecord uses class methods to declare associations, relying more on convention over configuration:

class Author < ApplicationRecord
  has_many :books
  has_many :contributions
  has_one :profile
end

class Book < ApplicationRecord
  belongs_to :author
  has_many :contributors,
            through: :contributions, source: :author
end

class Profile < ApplicationRecord
  belongs_to :author
end

Differences

Syntax: Django uses field definitions, while ActiveRecord uses class methods.

Explicit vs Implicit: Django requires more explicit configuration (e.g., on_delete), whereas ActiveRecord infers more based on conventions.

Bidirectional Relationships: In Django, you define both sides of the relationship in the model where the field is declared. In ActiveRecord, you typically declare the relationship in both models.

Cascade Behavior: Django requires explicit definition of on_delete behavior. ActiveRecord has default behaviors that can be overridden.

Many-to-Many Relationships: Django uses a ManyToManyField, while ActiveRecord uses a combination of has_many and has_many :through.

Conclusion

Both Django and ActiveRecord offer powerful ways to define and work with database relationships. Django’s approach tends to be more explicit and verbose, offering fine-grained control at the cost of more code.

ActiveRecord, true to Rails’ philosophy, favors convention and offers a more concise syntax, though it may require more familiarity with its conventions.