Table 1: authors
Structure (columns)
Column name |
Type |
Constraints |
|---|---|---|
id |
BIGSERIAL |
Primary Key |
name |
VARCHAR(200) |
NOT NULL |
VARCHAR(254) |
UNIQUE, NOT NULL |
|
is_active |
BOOLEAN |
DEFAULT TRUE |
created_at |
TIMESTAMPTZ |
DEFAULT NOW() |
Sample data in authors
id |
name |
is_active |
created_at |
|
|---|---|---|---|---|
1 |
George Orwell |
true |
2026-01-21 10:15:00 |
|
2 |
|
true |
2026-01-21 10:20:00 |
|
3 |
Inactive Author |
false |
2026-01-21 10:30:00 |
Table 2: books
Structure (columns)
Column name |
Type |
Constraints |
|---|---|---|
id |
BIGSERIAL |
Primary Key |
author_id |
BIGINT |
FK → authors(id), NOT NULL |
title |
VARCHAR(200) |
NOT NULL |
published_year |
SMALLINT |
NOT NULL |
price |
NUMERIC(8,2) |
NOT NULL |
is_published |
BOOLEAN |
DEFAULT TRUE |
UNIQUE |
(author_id, title) |
One title per author |
Sample data in books
id |
author_id |
title |
published_year |
price |
is_published |
|---|---|---|---|---|---|
1 |
1 |
1984 |
1949 |
9.99 |
true |
2 |
1 |
Animal Farm |
1945 |
7.99 |
true |
3 |
2 |
Harry Potter |
1997 |
19.99 |
true |
4 |
2 |
The Casual Vacancy |
2012 |
14.50 |
true |
Relationship explained visually
authors
+----+-----------------+
| id | name |
+----+-----------------+
| 1 | George Orwell |
| 2 | J. K. Rowling |
+----+-----------------+
↑
│ author_id
│
books
+----+-----------+--------------------+
| id | author_id | title |
+----+-----------+--------------------+
| 1 | 1 | 1984 |
| 2 | 1 | Animal Farm |
| 3 | 2 | Harry Potter |
| 4 | 2 | The Casual Vacancy |
+----+-----------+--------------------+
Relationship summary#
authors
-------
id (PK)
name
email (UNIQUE)
is_active
created_at
1
|
|
|
*
books
-----
id (PK)
author_id (FK → authors.id)
title
published_year
price
is_published
One Author → Many Books
Foreign key lives in books
Reverse access via:
author.books.all()
How Django ORM sees this
Forward (Book → Author)
book.author
Reverse (Author → Book)
author.books.all()
How to see this yourself
Run:
python manage.py sqlmigrate myapp 0001
You’ll see SQL very close to what I showed.
- Or in PostgreSQL:
d authors
d books
Key takeaways
models.py → defines tables
ForeignKey → creates *_id column
unique=True → UNIQUE constraint
Index → CREATE INDEX
validators → Python-level (not DB-level)
Open:
myapp/models.py
Put exactly this:
from django.db import models
from django.core.validators import MinValueValidator
class Author(models.Model):
name = models.CharField(max_length=200)
email = models.EmailField(unique=True)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = "authors"
indexes = [
models.Index(fields=["email"]),
]
def __str__(self):
return self.name
class Book(models.Model):
author = models.ForeignKey(
Author,
on_delete=models.CASCADE,
related_name="books",
)
title = models.CharField(max_length=200)
published_year = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1)]
)
price = models.DecimalField(
max_digits=8,
decimal_places=2
)
is_published = models.BooleanField(default=True)
class Meta:
db_table = "books"
constraints = [
models.UniqueConstraint(
fields=["author", "title"],
name="uniq_book_per_author",
)
]
indexes = [
models.Index(fields=["author"]),
models.Index(fields=["published_year"]),
]
def __str__(self):
return self.title