BudiBadu Logo
Samplebadu

Django by Example: Foreign Key Constraints

Django 5.0+

Maintaining database integrity is crucial when deleting referenced data. This sample code demonstrates how Django provides several `on_delete` behaviors to control what happens to child records when a parent record is deleted.

Code

from django.db import models

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

class BlogPost(models.Model):
    title = models.CharField(max_length=200)
    
    # CASCADE: When Category is deleted, delete all BlogPosts in it.
    # Use this for strong dependencies.
    category = models.ForeignKey(
        Category, 
        on_delete=models.CASCADE
    )

class User(models.Model):
    username = models.CharField(max_length=50)

class LogEntry(models.Model):
    action = models.CharField(max_length=100)
    
    # SET_NULL: When User is deleted, keep the LogEntry but set user to NULL.
    # Requires null=True on the field.
    user = models.ForeignKey(
        User, 
        on_delete=models.SET_NULL, 
        null=True
    )

class Tag(models.Model):
    name = models.CharField(max_length=50)

class ProtectedItem(models.Model):
    name = models.CharField(max_length=100)
    
    # PROTECT: Prevent deletion of Tag if it is referenced by any ProtectedItem.
    # Raises ProtectedError.
    tag = models.ForeignKey(
        Tag, 
        on_delete=models.PROTECT
    )

Explanation

The on_delete argument is a mandatory parameter for ForeignKey and OneToOneField that dictates how the database should maintain referential integrity when a referenced object is deleted. This behavior is enforced by Django's emulation layer, which often translates to database-level constraints, ensuring that your application does not end up with "orphan records" that point to non-existent data.

Choosing the correct strategy is essential for both data hygiene and business logic:

  • CASCADE: The default and most common behavior. When the parent is deleted, all related child objects are also deleted. Use this for strict dependencies (e.g., deleting a Post deletes its Comments).
  • PROTECT: Prevents the deletion of the parent if any child objects reference it, raising a ProtectedError. This is crucial for preventing accidental data loss in critical systems.
  • SET_NULL: Sets the foreign key to NULL. This requires the field to be defined with null=True and is useful for optional relationships (e.g., an Author is deleted, but their Articles remain with no author).
  • RESTRICT: Similar to PROTECT but allows deletion if the parent is also being deleted via a CASCADE relationship, offering more flexible integrity checks.

It is important to note that while Django emulates these behaviors in Python to support all database backends, mostly standard SQL constraints are used. However, for large bulk deletions, Django's emulation can be slower than native database cascades, so understanding the underlying SQL generation is beneficial for performance tuning.

Code Breakdown

13
models.CASCADE is the most common option. It mimics standard SQL ON DELETE CASCADE behavior, cleaning up related objects automatically.
26-27
models.SET_NULL requires null=True on the field definition. Without it, the database would reject the NULL value, causing an IntegrityError.
39
models.PROTECT is a safe guard. If you try to delete a Tag that is used by a ProtectedItem, Django will throw an exception, forcing you to handle the dependencies manually before deletion.
21
BlogPost model definition. This model represents the child in the relationship, holding the ForeignKey to the parent Category model.