Models and databases

In this chapter, you will learn to use Django ORM to define data models, create database tables, and insert test data.


Why are models needed?

In the previous chapter, we hardcoded page text. A real blog needs to store multiple articles, and the content can change at any time.

ModelIt is Django's database layer: use Python classes to define data structures, and Django automatically generates the corresponding database tables.

You don't need to write a single line of SQL—this isORM (Object-Relational Mapping)core value.


Define the Post model

Openblog/models.py, define the article model.

Example

# File path: blog/models.py
from django.db import models
from django.utils import timezone

class Category(models.Model):
    """Article category"""
    name = models.CharField('category name', max_length=50, unique=True)
    # slug is used for English identifiers in URLs (e.g., /category/django/)
    slug = models.SlugField('URL identifier', max_length=50, unique=True)

    class Meta:
        verbose_name = 'Categories'
        verbose_name_plural = 'Categories'

    def __str__(self):
        return self.name


class Post(models.Model):
    """Blog Post"""
    # Title: CharField is used for short text; max_length is required.
    title = models.CharField('Title', max_length=200)

    # slug: Article's English identifier in the URL
    slug = models.SlugField('URL identifier', max_length=200, unique=True)

    # Summary: TextField is used for long text, blank=True means optional
    summary = models.TextField('Summary', blank=True)

    # Body: TextField with no length limit
    content = models.TextField('Body')

    # Category: ForeignKey one-to-many relationship, an article belongs to one category
    # on_delete=models.CASCADE means that when deleting a category, all articles under it are also deleted
    category = models.ForeignKey(
        Category,
        on_delete=models.CASCADE,
        verbose_name='Categories',
        related_name='posts'    # Reverse query: category.posts.all()
    )

    # auto_now_add: automatically fill in the current time at creation
    created_at = models.DateTimeField('Creation Time', auto_now_add=True)

    # auto_now: automatically updated to the current time on every save
    updated_at = models.DateTimeField('Update Time', auto_now=True)

    class Meta:
        verbose_name = 'Articles'
        verbose_name_plural = 'Articles'
        # Sorted by creation time in descending order by default
        ordering = ['-created_at']

    def __str__(self):
        # Display the title when showing objects in the Admin backend and Shell
        return self.title

Quick reference for common field types

Field TypeCorresponding database typesUse Case
CharField(max_length=N)VARCHAR(N)Short text for titles, tags, names, etc.
TextField()TEXTArticle body, introduction, comments, and other content
IntegerField()INTEGERIntegers for counts, ages, etc.
DateTimeField()DATETIMEPublish time, update time
ForeignKey()Foreign Key ConstraintOne-to-many relationship (article → category)
ManyToManyField()junction tableMany-to-many relationship (article ←→ tag)
SlugField()VARCHARURL-friendly identifier
ImageField()VARCHAR(path)Cover image, avatar (requires Pillow library)

__str__The method determines how the object is displayed as a string. It is called in the Admin backend's dropdown selection boxes and Shell output to display the object name. If you don't write this method, you will seePost object (1)such meaningless text.


Migration: Sync models to the database

After writing the model classes, you need to useMigrationCreate database tables.

(venv) $ python manage.py makemigrations   # 生成迁移文件(检测模型变化)
Migrations for 'blog':
  blog/migrations/0001_initial.py
    - Create model Category
    - Create model Post

(venv) $ python manage.py migrate           # 执行迁移(创建数据库表)
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying blog.0001_initial... OK

Division of labor between the two commands:

  • makemigrationsScan models.py and generate migration scripts (Python files).
  • migrate: Run the migration scripts to create actual tables in the database.

After each modification to models.py, you need to runmakemigrations + migrateThese two commands are both indispensable: running only makemigrations without migrate means the tables won't be created; running migrate directly without makemigrations means Django won't know you've changed the models.


Use the Django Shell to insert test data.

Before starting to write pages, first use Django Shell to enter a few pieces of data into the database to verify that the model works properly.

(venv) $ python manage.py shell

This opens an interactive Python Shell where you can operate on the database.

Example

# Input line by line in the Django Shell
from blog.models import Category, Post

# 1. Create category
cat_django = Category.objects.create(name='Django', slug='django')
cat_python = Category.objects.create(name='Python', slug='python')
cat_css = Category.objects.create(name='CSS', slug='css')

# 2. Create article
Post.objects.create(
    title='The Complete Django Beginner's Guide',
    slug='django-beginner-guide',
    summary='Learn Django from scratch, covering core concepts such as models, views, templates, etc.',
    content='<h2>is什么学 Django?</h2><p>Django Yes Python 最flowOf the line Web FrameworkOne of...</p>',
    category=cat_django
)

Post.objects.create(
    title='Detailed Explanation of Python Asynchronous Programming',
    slug='python-async',
    summary=Understand asyncio, coroutines, and event loops in one article.,
    content=<h2>What Is a Coroutine?</h2><p>A coroutine is a lighter-weight concurrency solution than threads...</p>,
    category=cat_python
)

Post.objects.create(
    title=Practical CSS Grid Layout,
    slug='css-grid-layout',
    summary=Easily implement complex responsive layouts with CSS Grid.,
    content=<h2>Getting Started with Grid</h2><p>Grid is a two-dimensional layout system...</p>,
    category=cat_css
)

# 3. Verify Queries
print(Post.objects.all())           # Output all articles
print(Post.objects.count())         # Total Articles
print(Post.objects.filter(category=cat_django))  # Articles under the Django category

Django ORM basic queries

ORM lets you operate on the database using Python method chains, without writing SQL.

MethodsEquivalent SQLReturn Value
Post.objects.all()SELECT * FROM blog_postQuerySet (all articles)
Post.objects.get(id=1)SELECT ... WHERE id=1Single object (throws an exception if not found)
Post.objects.filter(category__slug='django')WHERE category.slug='django'QuerySet
Post.objects.order_by('-created_at')ORDER BY created_at DESCQuerySet
Post.objects.exclude(status='draft')WHERE status != 'draft'QuerySet

QuerySet isLazy evaluationThis is: the query is not executed immediately; SQL is only sent when data is actually needed (such as when iterating, printing, or converting to a list). So you can chain multiple filter calls without generating multiple queries.


Hands-on: Prepare data for subsequent chapters

In the Shell, enter several more articles and categories, covering the three categories Django, Python, and CSS, with at least 2 articles per category.

You could write a management command or script for batch insertion, but at this stage it is sufficient to insert them one by one using the Shell.


Chapter summary

In this chapter, you've mastered the core workflow of the Django ORM: defining Python classes (models) in models.py, generating migration files with makemigrations, syncing to the database with migrate, and using the Shell and ORM to query and insert data.

At this point, the structure of articles and categories has been established in the database.

other extensions