Fields and Relationships
A field in Django represents a column in a database table. Fields are the only mandatory part of a model and each field is defined as a class attribute on a model. It is important to avoid field names that conflict with the Model API, such as clean, save, or delete.
class Book(models.Model):
title = models.CharField(max_length=200)
published_date = models.DateField()Here title and published_date are fields.
Field Types
Each field in your model should be an instance of the appropriate Field class. Django uses these field class types to determine the database column type, the default HTML widget for forms and minimal validation requirements.
Numeric Fields
These fields store numbers, including integers, decimals and floating-point values.
| Field Type | Description |
|---|---|
| AutoField | An IntegerField that automatically increments. Used for auto-created primary keys. |
| BigAutoField | A 64-bit integer, similar to AutoField, but guarantees a larger range (1 to 9 quintillion). |
| SmallAutoField | Like AutoField, but limits values to a smaller range (1 to 32,767). |
| IntegerField | A standard integer. Safe for values from -2,147,483,648 to 2,147,483,647. |
| BigIntegerField | A 64-bit integer guaranteed to fit much larger numbers than IntegerField. |
| SmallIntegerField | Like IntegerField, but for smaller values (usually -32,768 to 32,767). |
| PositiveIntegerField | Like IntegerField, but must be positive or zero. |
| PositiveBigIntegerField | Like BigIntegerField, but must be positive or zero. |
| PositiveSmallIntegerField | Like SmallIntegerField, but must be positive or zero. |
| DecimalField | A fixed-precision decimal number. Requires max_digits and decimal_places. |
| FloatField | A floating-point number represented in Python by a float instance. |
String & Text Fields
These fields handle text data, ranging from short strings and identifiers to large blocks of text.
| Field Type | Description |
|---|---|
| CharField | A string field for small- to large-sized strings. Requires max_length. |
| TextField | A large text field for unlimited text. |
| SlugField | A short label containing only letters, numbers, underscores and hyphens. Useful for creating readable, SEO-friendly URLs. |
| EmailField | A CharField that validates that the value is a valid email address. |
| URLField | A CharField that validates that the value is a valid URL. |
| UUIDField | A field for universally unique identifiers. Uses Python's UUID class. |
| GenericIPAddressField | An IPv4 or IPv6 address in string format (Ex: 192.0.2.30). |
| FilePathField | A CharField restricted to filenames in a specific directory. |
Working with Slugs
A slug is a short, human-readable label containing only letters, numbers, underscores and hyphens. Slugs are incredibly useful for creating SEO-friendly, readable URLs. For example, deriving a slug from the title "My First Blog Post" creates my-first-post, allowing the URL to read /blog/my-first-post/ instead of an opaque ID like /blog/42/.
SlugField is a specialized CharField built for this purpose. Because slugs are primarily used to look up records in the database, SlugField automatically sets db_index=True (which creates a database index for faster lookups). They are also almost always declared with unique=True:
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True)Generating Slugs Automatically
You typically generate a slug from another field using Django's slugify() utility. The best place to do this is inside the model's save() method so it happens automatically before the object is written to the database:
from django.db import models
from django.utils.text import slugify
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True, blank=True)
def save(self, *args, **kwargs):
if not self.slug:
# Generate the initial slug
base_slug = slugify(self.title)
slug = base_slug
counter = 1
# Ensure uniqueness
while Post.objects.filter(slug=slug).exists():
slug = f"{base_slug}-{counter}"
counter += 1
self.slug = slug
super().save(*args, **kwargs)In this example, if two posts are named "My First Blog Post", the first will get my-first-post and the second will get my-first-post-1.
Consuming Slugs in URLs
To use the slug to serve a request, you capture it in your URL configuration and pass it to your view.
In urls.py, use the <slug:slug> path converter:
from django.urls import path
from . import views
urlpatterns = [
path('blog/<slug:slug>/', views.post_detail, name='post_detail'),
]Then, in your views.py, consume the slug to retrieve the object:
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_detail(request, slug):
# Retrieve the post using the slug from the URL
post = get_object_or_404(Post, slug=slug)
return render(request, 'post_detail.html', {'post': post})Date & Time Fields
These fields store temporal data, such as calendar dates, specific times, or durations.
| Field Type | Description |
|---|---|
| DateField | A date, represented in Python by a datetime.date instance. |
| TimeField | A time, represented in Python by a datetime.time instance. |
| DateTimeField | A specific date and time, represented by a datetime.datetime instance. |
| DurationField | A period of time, modeled in Python by datetime.timedelta. |
File & Binary Fields
These fields manage file uploads and raw binary data.
| Field Type | Description |
|---|---|
| FileField | A file-upload field. Requires upload_to to specify the storage directory. |
| ImageField | Inherits from FileField but validates that the uploaded file is an image. |
| BinaryField | Stores raw binary data. Can be assigned bytes, bytearray, or memoryview. |
FileField vs FieldFile
FileField is a model field used to define a file column in the database. It handles file uploads and stores the file path in the database.
document = models.FileField(upload_to="documents/")FieldFile is the File object that you interact with when you access the field on a specific record (instance) of your model. It represents the actual file and provides methods and attributes to work with it.
instance.document.name
instance.document.url
instance.document.size
instance.document.open()In short, FileField defines the field on the model class, while FieldFile is the file object accessed on a model instance.
Miscellaneous Fields
These fields cover specific data types like booleans, structured JSON and composite keys.
| Field Type | Description |
|---|---|
| BooleanField | A true/false field. |
| JSONField | Stores JSON-encoded data. Represented in Python as native dicts or lists. |
| GeneratedField | A field that is always computed by the database based on other fields in the model. Requires an expression and an output_field. |
| CompositePrimaryKey | A virtual field for defining a composite primary key (a key made of multiple fields). |
For detailed information on all field types, refer to the official doc.
Field Options
Django fields support several optional arguments to customize their behavior. The most commonly used options are:
| Option | Description |
|---|---|
null | If True, Django will store empty values as NULL in the database. Default is False. (Note: Avoid using this on string-based fields like CharField unless unique=True is also set). |
blank | If True, the field is allowed to be blank during form validation. Default is False. This is different from null (which is database-related); blank is validation-related. |
choices | A sequence (list or tuple) of 2-tuples to use as choices for this field. If provided, the default form widget becomes a select box instead of a text field. |
default | The default value for the field. This can be a value or a callable object. If it is a callable, it will be called every time a new object is created. |
primary_key | If True, this field is the primary key for the model. |
unique | If True, this field must be unique throughout the table. Enforced at the database level and by model validation. |
verbose_name | A human-readable name for the field. If not given, Django will automatically create one using the field's attribute name (converting underscores to spaces). |
help_text | Extra "help" text to be displayed with the form widget. Useful for documentation. |
editable | If False, the field will not be displayed in the admin or any form and is skipped during model validation. Default is True. |
db_index | If True, a database index will be created for this field. |
validators | A list of validator functions (callables) to run for this field. |
For a complete list of all available field options, refer to the official doc.
Relationship Options
There are also specific options available for relationship fields (ForeignKey, ManyToManyField, OneToOneField).
| Option | Applicable To | Description |
|---|---|---|
on_delete | ForeignKey, OneToOneField | Required. Defines what happens when the related object is deleted (e.g. CASCADE, PROTECT, SET_NULL (requires null=True)). |
related_name | All relationships | Name used to access related objects as an attribute on the related model (e.g. author.books.all()). Defaults to [model_name]_set. |
related_query_name | All relationships | Name used for reverse lookups in queryset filters (e.g. Tag.objects.filter(article__title="News")). Defaults to [related_name] or [model_name]. |
symmetrical | ManyToManyField | Used only with self. If True (default), the relationship is mutual; if False, it is one-way. |
through | ManyToManyField | Specifies a custom intermediate model for the relationship. |
limit_choices_to | All relationships | Limits selectable related objects using a filter (dict or Q). |
to_field | ForeignKey | Specifies which field on the related model is referenced (defaults to the primary key). |
Relationships
Django offers robust support for the three most common types of database relationships, plus a generic form that can point at any model.
Many-to-One
You define a many-to-one relationship using ForeignKey. This field requires a positional argument specifying the model class to which it relates. For example, if a Car model has a Manufacturer, you would add a ForeignKey to the Manufacturer model inside the Car model.
Many-to-Many
You define a many-to-many relationship using ManyToManyField. This also requires the related model class as a positional argument. It does not matter which model contains the field, but it should only appear in one of them.
Standard Use
For simple links where no extra data is needed (Ex: a Person is a member of multiple Groups), Django automatically manages the hidden table connecting the two models.
Extra Data (The through Argument)
If you need to store data about the relationship itself, such as the date a person joined a group or the reason for an invite, you must use an intermediate model. You specify this model using the through argument.
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
# The 'through' argument points to the intermediate model
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
# Extra fields stored on the relationship
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)If you need to associate extra data with the relationship, such as the date a person joined a group, you can specify an intermediate model using the through argument. This intermediate model can hold foreign keys to the related models and any additional fields you need.
One-to-One
You define a one-to-one relationship using OneToOneField. This is useful when an object effectively extends another. For instance, a Restaurant model might have a one-to-one relationship with a Place model. This field works similarly to ForeignKey but ensures that the link is unique.
Generic Relationships
While a standard ForeignKey points to a specific model, a Generic Relationship allows a field to point to any model. This is incredibly useful for features like comments, likes, or tags, which could be attached to a Post, a Photo, or a User.
Generic relationships depend on the django.contrib.contenttypes app (included in INSTALLED_APPS by default).
Defining the Generic Relationship
To create a generic relationship on a model (like Comment), you need three distinct fields:
content_type: A standardForeignKeyto Django'sContentTypemodel. This tells Django which model this comment is attached to (e.g., "Post").object_id: A field to store the primary key of the related object (e.g., Post ID "10").content_object: AGenericForeignKeythat combines the previous two fields into a single, usable Python attribute.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
class Comment(models.Model):
body = models.TextField()
# 1. The model type (e.g. Post)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
# 2. The ID of the specific model instance (e.g. 10)
object_id = models.PositiveIntegerField()
# 3. The virtual field tying them together
content_object = GenericForeignKey('content_type', 'object_id')You can now pass any model instance to content_object when creating a comment:
article = Article.objects.get(pk=1)
# Create a comment attached to the article
Comment.objects.create(body="Great post!", content_object=article)The Reverse Relationship
To access comments from the target model (e.g., getting all comments for an Article), add a GenericRelation field.
Crucially, this ensures that when an Article is deleted, its related Comments are also deleted. Without it, deleting the article leaves orphaned comments in the database.
from django.contrib.contenttypes.fields import GenericRelation
class Article(models.Model):
title = models.CharField(max_length=100)
# Allows article.comments.all() and ensures cascading deletes
comments = GenericRelation(Comment)WARNING
A GenericForeignKey is a virtual field. It is not an actual database column and has no database-level foreign key constraints. This means the database won't prevent a row from pointing to an object that no longer exists. Furthermore, you cannot use it in standard .filter() lookups or with select_related(), though prefetch_related() is supported. Only use generic relationships when absolutely necessary; prefer a standard ForeignKey whenever possible.
Generic Foreign Keys vs Exclusive Arcs
Because of the drawbacks mentioned above, many developers consider the GenericForeignKey to be a database anti-pattern. If you are building a closed application and you know exactly which models need comments, it is often better to use Exclusive Arcs (multiple nullable foreign keys) instead:
class Comment(models.Model):
body = models.TextField()
# Only ONE of these will be populated per row, the rest are NULL
post = models.ForeignKey('Post', on_delete=models.CASCADE, null=True, blank=True)
article = models.ForeignKey('Article', on_delete=models.CASCADE, null=True, blank=True)Trade-offs:
- Exclusive Arcs (Nullable FKs): Provides true database-level referential integrity and allows standard
.select_related()and.filter()operations. However, it requires custom model validation (e.g. inclean()) to ensure exactly one FK is set and requires a database migration every time you want to allow comments on a new model. - Generic Foreign Keys: Provides extreme flexibility and a clean schema, which is perfect for reusable third-party apps where you don't know the target models at design time. However, you sacrifice referential integrity and database-level performance.
Lazy Relationships
When defining relationships, you might encounter a situation where the model you want to link to has not been defined yet. This often happens if the related model is defined lower in the same file or if you have a circular dependency between two apps.
To solve this, Django allows you to refer to a model by its name as a string rather than the class object itself.
There are three types of lazy references:
- Recursive: Used when a model has a relationship to itself, like an "Employee" having a "Manager" who is also an Employee.
- Relative: Used when the related model is in the same
models.pyfile (or app context). - Absolute: Used when the related model is in a different app. This is the most specific and robust format.
Recursive Relationship (Self)
If an object needs to relate to another object of the same class, use self:
class Employee(models.Model):
name = models.CharField(max_length=100)
# 'self' refers to the Employee class itself
manager = models.ForeignKey('self', on_delete=models.SET_NULL, null=True)Order of Definition (Same App)
Python reads code top-to-bottom. If you define Parent before Child, Python raises a NameError because Child does not exist yet. Using 'Child' as a string tells Django to look for it later.
class Parent(models.Model):
name = models.CharField(max_length=50)
# Using 'Child' as a string avoids a NameError
favorite_child = models.ForeignKey('Child', on_delete=models.SET_NULL)
class Child(models.Model):
name = models.CharField(max_length=50)This also applies to circular dependencies within the same file, where one model must be defined later. Lazy relationships resolve this ordering issue.
Circular Imports (Different Apps)
This happens when two different apps depend on each other (Ex: User needs Product and Product needs User). Standard imports create an infinite loop that crashes the application. Using a string path avoids the import entirely.
# In products/models.py
class Product(models.Model):
# Syntax: 'app_label.ModelName'
# No need to import User at the top of the file!
created_by = models.ForeignKey('users.User', on_delete=models.CASCADE)Related Manager (Reverse Relationships)
When you define a relationship (like a ForeignKey), Django automatically creates a "Reverse Relation" on the related model. This “related manager” is used in a one-to-many or many-to-many related context. This allows you to access data from the other side of the relationship.
By default, Django creates an attribute on the related model using the lowercased model name followed by _set.
If a Book model has a ForeignKey to Author:
- Forward:
book.authorgives you the Author object. - Reverse:
author.book_setgives you a "Manager" to access all books by that author.
Field Attributes
Field attributes are API properties available on a field instance. They are primarily used for introspection, allowing you to inspect the properties of a model field programmatically, rather than for defining the field itself.
| Attribute | Description |
|---|---|
auto_created | True if the field was automatically created by Django (like the default id primary key), rather than explicitly defined in your model. |
concrete | True if the field maps to a physical column in the database table. Returns False for fields like many-to-many or reverse relationships, which do not have their own column on the model's table. |
hidden | True if the field is used internally by Django and should not be displayed in forms (Ex: the content_type field in a generic relationship). |
is_relation | True if the field establishes a relationship with another model (Ex: ForeignKey, ManyToManyField, OneToOneField). |
model | Returns the model class where this field is defined. |
related_model | Returns the model class that this field links to. For example, in ForeignKey(Author, ...), this returns the Author class. |
many_to_many | True if the field represents a many-to-many relationship. |
many_to_one | True if the field represents a many-to-one relationship (Ex: a standard ForeignKey). |
one_to_many | True if the field represents a one-to-many relationship (Ex: the reverse side of a ForeignKey). |
one_to_one | True if the field represents a one-to-one relationship (OneToOneField). |
