Difficulty: Beginner
Django models are Python classes that define the structure of your database tables. Each model class inherits from django.db.models.Model, and each class attribute represents a database column. Django's ORM (Object-Relational Mapper) handles the translation between Python objects and SQL database operations, so you write Python code instead of raw SQL.
Django provides a rich set of field types that map to database column types. CharField stores short text and requires a max_length argument. TextField stores unlimited text and is ideal for large content like article bodies or descriptions. IntegerField stores integers, while FloatField and DecimalField store decimal numbers (DecimalField is preferred for financial data because it avoids floating-point precision issues). BooleanField stores True/False values. DateField and DateTimeField store date and date-time values respectively.
Every field accepts common keyword arguments. 'blank=True' means the field can be left empty in forms (validation level). 'null=True' means the database column allows NULL values (database level). For string fields, Django convention is to use blank=True without null=True, because Django stores empty strings rather than NULL for text fields. 'default' sets a default value. 'unique=True' enforces uniqueness at the database level. 'choices' limits the field to a predefined set of values.
The 'choices' parameter is powerful for fields with a fixed set of options. You define choices as a sequence of two-tuples where the first element is the value stored in the database and the second is the human-readable label. Django provides the get_FOO_display() method on model instances to retrieve the human-readable label for a choices field.
Specialized fields include EmailField (validates email format), URLField (validates URL format), SlugField (for URL-friendly strings), FileField and ImageField (for file uploads), UUIDField (for universally unique identifiers), JSONField (for storing JSON data), and IPAddressField/GenericIPAddressField (for IP addresses). These fields provide both database storage and validation.
The inner Meta class configures model-level options. 'ordering' sets the default sort order for QuerySets. 'verbose_name' and 'verbose_name_plural' set human-readable names for the admin. 'db_table' overrides the auto-generated table name. 'unique_together' (or UniqueConstraint) enforces multi-column uniqueness. 'indexes' defines database indexes for query performance.
Every model should define a __str__() method that returns a meaningful string representation. This is used in the admin interface, the shell, and debugging output. Without it, your objects will display as unhelpful strings like 'Product object (1)'.
from django.db import models
class Product(models.Model):
# Text fields
name = models.CharField(max_length=200)
description = models.TextField(blank=True)
slug = models.SlugField(unique=True)
# Numeric fields
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField(default=0)
weight = models.FloatField(null=True, blank=True)
# Boolean field
is_active = models.BooleanField(default=True)
# Date fields
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# File fields
image = models.ImageField(upload_to='products/', blank=True)
def __str__(self):
return self.name
This model demonstrates the most common field types. auto_now_add=True sets the date when the object is first created. auto_now=True updates the date every time the object is saved. DecimalField is used for price to avoid floating-point issues.
from django.db import models
class Order(models.Model):
class Status(models.TextChoices):
PENDING = 'pending', 'Pending'
PROCESSING = 'processing', 'Processing'
SHIPPED = 'shipped', 'Shipped'
DELIVERED = 'delivered', 'Delivered'
CANCELLED = 'cancelled', 'Cancelled'
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.PENDING,
)
total = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'Order #{self.pk} - {self.get_status_display()}'
TextChoices provides an enum-like syntax for defining choices. The first value in each choice is stored in the database, the second is the display label. Use get_status_display() to get the human-readable label.
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200)
content = models.TextField()
author = models.CharField(max_length=100)
published_at = models.DateTimeField()
category = models.CharField(max_length=50)
class Meta:
ordering = ['-published_at'] # Default sort: newest first
verbose_name = 'Article'
verbose_name_plural = 'Articles'
unique_together = ['slug', 'category'] # Composite unique
indexes = [
models.Index(fields=['published_at']),
models.Index(fields=['category', 'published_at']),
]
def __str__(self):
return self.title
The Meta class controls model behavior. ordering sets default QuerySet ordering. unique_together enforces uniqueness across multiple columns. indexes improve query performance for frequently searched fields.
import uuid
from django.db import models
class UserProfile(models.Model):
id = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
)
email = models.EmailField(unique=True)
website = models.URLField(blank=True)
bio = models.TextField(max_length=500, blank=True)
metadata = models.JSONField(default=dict, blank=True)
ip_address = models.GenericIPAddressField(null=True, blank=True)
def __str__(self):
return self.email
UUIDField with primary_key=True replaces the auto-incrementing integer ID. EmailField and URLField add validation. JSONField stores arbitrary JSON data. GenericIPAddressField supports both IPv4 and IPv6.
Model, Fields, CharField, TextField, IntegerField, Meta