Skip to content

Django - Models

A model in Django is the definitive source of information about your data. It’s a Python class that represents a table (in relational databases) or a collection (in NoSQL databases like MongoDB) in your database. Each attribute of the model class maps to a database field (column).

Models are typically defined in an application’s models.py file (e.g., myapp/models.py). Django uses these model definitions to:

  • Automatically generate database schema (e.g., CREATE TABLE statements) through its migration system.
  • Provide a high-level Python API (the ORM - Object-Relational Mapper) for creating, retrieving, updating, and deleting (CRUD) database records.

Let’s define a simple model to represent companies in an imaginary directory.

myapp/models.py
from django.db import models
class Company(models.Model):
# Fields define the columns in the database table
name = models.CharField(max_length=100, unique=True, help_text="Name of the company")
website = models.URLField(max_length=200, blank=True, help_text="Company website URL (optional)")
email = models.EmailField(max_length=100, help_text="Contact email address")
phone_number = models.CharField(max_length=20, blank=True, help_text="Contact phone number (optional)")
founded_date = models.DateField(null=True, blank=True, help_text="Date the company was founded")
is_active = models.BooleanField(default=True, help_text="Is the company currently active?")
# Automatically added fields (by Django):
# id = models.AutoField(primary_key=True) # Default primary key
class Meta:
db_table = "companies" # Optional: Explicitly name the database table
verbose_name = "Company" # Optional: Human-readable singular name
verbose_name_plural = "Companies" # Optional: Human-readable plural name
ordering = ['name'] # Optional: Default ordering when querying
def __str__(self):
# String representation used in admin and shell
return self.name

Key Points:

  • Every model is a Python class that inherits from django.db.models.Model.
  • Each attribute represents a database field, defined using models.Field types (e.g., CharField, URLField, IntegerField, DateField, BooleanField, ForeignKey, etc.).
  • Common field options include max_length, unique, blank (validation-related, allows empty value in forms), null (database-related, allows NULL in DB), default, help_text.
  • Django automatically adds an auto-incrementing primary key field named id unless you specify primary_key=True on another field.
  • The inner Meta class is optional and allows you to configure model metadata like the database table name (db_table), default ordering (ordering), and human-readable names (verbose_name, verbose_name_plural).
  • The __str__() method defines a human-readable representation of the object, which is very useful in the Django admin and shell.

You can find a comprehensive list of model fields and options in the Django documentation: https://docs.djangoproject.com/en/stable/ref/models/fields/

Defining a model class doesn’t automatically create the corresponding database table. You need to use Django’s migration system:

  1. Ensure your app is in INSTALLED_APPS: Add 'myapp' or 'myapp.apps.MyappConfig' to INSTALLED_APPS in your project’s settings.py.

  2. Create migrations: This command analyzes changes in your models.py (compared to previous migrations) and creates migration files in the myapp/migrations/ directory.

python manage.py makemigrations myapp
  1. Apply migrations: This command reads the migration files and applies the changes to your database schema (e.g., creates the companies table).
python manage.py migrate

You need to run makemigrations and migrate whenever you make changes to your models (add/remove fields, change field types, modify options, etc.).

Django provides a powerful database-abstraction API (the ORM) to interact with your models. You access this API through a Manager attached to your model class, typically called objects.

Let’s see CRUD examples using the Django shell (python manage.py shell) or within a view:

# Assuming you run 'python manage.py shell'
from myapp.models import Company
import datetime
# === CREATE ===
# Method 1: Create and save in two steps
c1 = Company(name="TechCorp", email="contact@techcorp.com", website="https://techcorp.com")
c1.save() # Executes the INSERT SQL query
# Method 2: Create and save in one step using the manager
c2 = Company.objects.create(
name="Innovate Ltd",
email="info@innovate.com",
phone_number="123-456-7890",
founded_date=datetime.date(2020, 1, 1)
)
print(f"Created company: {c1}")
print(f"Created company: {c2}")
# === RETRIEVE / READ ===
# Get all companies
all_companies = Company.objects.all() # Returns a QuerySet (lazy)
print(f"\nAll Companies ({all_companies.count()}):")
for company in all_companies:
print(f"- {company.name} (ID: {company.id})")
# Get a specific company by primary key (id)
try:
techcorp = Company.objects.get(pk=c1.id) # Or get(id=c1.id)
print(f"\nFound by PK: {techcorp.name}")
except Company.DoesNotExist:
print(f"\nCompany with pk={c1.id} not found.")
# Get using other field lookups (returns one or raises DoesNotExist / MultipleObjectsReturned)
try:
innovate = Company.objects.get(name="Innovate Ltd")
print(f"Found by name: {innovate.name}")
except Company.DoesNotExist:
print("\nInnovate Ltd not found.")
# Filter to get multiple companies (returns a QuerySet)
active_companies = Company.objects.filter(is_active=True)
print(f"\nActive companies ({active_companies.count()}):")
for company in active_companies:
print(f"- {company.name}")
# More complex lookups (field__lookuptype)
recent_companies = Company.objects.filter(founded_date__year__gte=2020) # Founded in or after 2020
print(f"\nRecent companies ({recent_companies.count()}): {list(recent_companies)}")
# Exclude companies
non_tech_companies = Company.objects.exclude(name__icontains="tech") # Case-insensitive contains
print(f"\nNon-tech companies: {list(non_tech_companies)}")
# Ordering results
companies_by_name_desc = Company.objects.order_by('-name') # Descending order
print(f"\nOrdered by name DESC: {list(companies_by_name_desc)}")
# === UPDATE ===
# Get an object, modify attributes, and save
try:
company_to_update = Company.objects.get(name="TechCorp")
company_to_update.website = "https://new-techcorp.com"
company_to_update.is_active = False
company_to_update.save() # Executes the UPDATE SQL query
print(f"\nUpdated TechCorp: Website='{company_to_update.website}', Active={company_to_update.is_active}")
except Company.DoesNotExist:
print("\nTechCorp not found for update.")
# Update multiple objects at once (more efficient, bypasses save() method)
num_updated = Company.objects.filter(founded_date__isnull=True).update(is_active=False)
print(f"\nSet {num_updated} companies with no founding date to inactive.")
# === DELETE ===
# Get an object and delete it
try:
company_to_delete = Company.objects.get(name="Innovate Ltd")
delete_result = company_to_delete.delete() # Executes the DELETE SQL query
print(f"\nDeleted Innovate Ltd. Result: {delete_result}")
except Company.DoesNotExist:
print("\nInnovate Ltd not found for deletion.")
# Delete multiple objects at once (more efficient)
delete_result_multi = Company.objects.filter(is_active=False).delete()
print(f"\nDeleted inactive companies. Result: {delete_result_multi}")

Models can be linked together to represent relationships between data:

  1. ForeignKey (Many-to-One): The most common type. Links one model instance to another. Example: An Employee model could have a ForeignKey to a Company model (many employees belong to one company).

  2. OneToOneField (One-to-One): Similar to ForeignKey, but ensures that each instance on one side of the relationship is linked to at most one instance on the other side. Example: A UserProfile model might have a OneToOneField to Django’s built-in User model.

  3. ManyToManyField (Many-to-Many): Links instances of one model to multiple instances of another (and vice-versa). Example: A Post model and a Tag model could have a ManyToManyField (one post can have many tags, one tag can be applied to many posts). Django automatically creates an intermediate database table for this.

Example with ForeignKey:

myapp/models.py
from django.db import models
# Assuming Company model from above exists
class Employee(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField(unique=True)
# Link to Company (Many employees -> One company)
company = models.ForeignKey(
Company,
on_delete=models.CASCADE, # What happens when the referenced Company is deleted?
related_name='employees' # How to refer back from Company to Employee
)
start_date = models.DateField()
class Meta:
ordering = ['company', 'name']
def __str__(self):
return f"{self.name} ({self.company.name})"
# --- After defining/modifying models, run: ---
# python manage.py makemigrations myapp
# python manage.py migrate
# --- Example Usage (in shell or view) ---
# from myapp.models import Company, Employee
# # Assuming company 'c1' exists (TechCorp)
# c1 = Company.objects.get(name='TechCorp')
# # Create employees for TechCorp
# emp1 = Employee.objects.create(name='Alice', email='alice@techcorp.com', company=c1, start_date='2022-08-15')
# emp2 = Employee.objects.create(name='Bob', email='bob@techcorp.com', company=c1, start_date='2023-01-10')
# # Accessing the related company from an employee
# print(f"{emp1.name}'s company: {emp1.company.name}")
# # Accessing all employees for a company (using related_name)
# print(f"Employees at {c1.name}:")
# for emp in c1.employees.all(): # Uses the related_name 'employees'
# print(f"- {emp.name}")
# # Filtering employees based on company attributes (double underscore lookup)
# techcorp_employees = Employee.objects.filter(company__name='TechCorp')
# print(f"Query for TechCorp employees: {list(techcorp_employees)}")

Key ForeignKey arguments:

  • on_delete: Specifies what happens when the referenced object (the Company) is deleted. Common choices: models.CASCADE (delete employees too), models.PROTECT (prevent deletion if employees exist), models.SET_NULL (set employee’s company field to NULL, requires null=True on the field), models.SET_DEFAULT (set to default value, requires default on the field).
  • related_name: The name used to access the set of related objects from the target model (e.g., company_instance.employees.all()). If omitted, Django uses modelname_set (e.g., employee_set).

Models are a cornerstone of Django development, providing structure to your data and a powerful API for interaction.

Further Reading: