Модели и базы данных
В этой главе вы научитесь использовать Django ORM для определения моделей данных, создания таблиц базы данных и вставки тестовых данных.
Зачем нужны модели?
В предыдущей главе мы жёстко закодировали текст страницы. Настоящему блогу необходимо хранить несколько статей, а содержимое может меняться в любое время.
МодельЭто слой базы данных Django: используйте классы Python для определения структур данных, и Django автоматически создаёт соответствующие таблицы базы данных.
Вам не нужно писать ни одной строки SQL — в этомORM (Object-Relational Mapping)основная ценность.
Определите модель Post
Openblog/models.py, определите модель статьи.
Пример
from django.db import models
from django.utils import timezone
class Category(models.Model):
"""Категория статьи"""
name = models.CharField('название категории', max_length=50, unique=True)
# slug используется для английских идентификаторов в URL (например, /category/django/)
slug = models.SlugField('URL-идентификатор', max_length=50, unique=True)
class Meta:
verbose_name = 'Категории'
verbose_name_plural = 'Категории'
def __str__(self):
return self.name
class Post(models.Model):
"""Запись блога"""
# Заголовок: CharField используется для короткого текста; max_length обязателен.
title = models.CharField('Заголовок', max_length=200)
# slug: английский идентификатор статьи в URL
slug = models.SlugField('URL-идентификатор', max_length=200, unique=True)
# Краткое описание: TextField используется для длинного текста, blank=True означает, что поле необязательно
summary = models.TextField('Краткое описание', blank=True)
# Содержимое: TextField без ограничения длины
content = models.TextField('Содержимое')
# Категория: ForeignKey — связь один-ко-многим, статья принадлежит одной категории
# on_delete=models.CASCADE означает, что при удалении категории все статьи в этой категории также удаляются
category = models.ForeignKey(
Категория,
on_delete=models.CASCADE,
verbose_name='Категории',
related_name='posts' # Обратный запрос: category.posts.all()
)
# auto_now_add: автоматически устанавливает текущее время при создании
created_at = models.DateTimeField('Время создания', auto_now_add=True)
# auto_now: автоматически обновляется до текущего времени при каждом сохранении
updated_at = models.DateTimeField('Время обновления', auto_now=True)
class Meta:
verbose_name = 'Статья'
verbose_name_plural = 'Статьи'
# По умолчанию сортировка по времени создания в порядке убывания
ordering = ['-created_at']
def __str__(self):
# Отображать заголовок при показе объектов в админ-панели и оболочке Shell
return self.title
Краткая справка по распространённым типам полей
| Тип поля | Соответствующие типы баз данных | Сценарий использования |
|---|---|---|
| CharField(max_length=N) | VARCHAR(N) | Короткий текст для заголовков, тегов, имён и т. д. |
| TextField() | TEXT | Тело статьи, введение, комментарии и другой контент |
| IntegerField() | INTEGER | Целые числа для количества, возраста и т. д. |
| DateTimeField() | DATETIME | Время публикации, время обновления |
| ForeignKey() | Ограничение внешнего ключа | Связь один-ко-многим (article → category) |
| ManyToManyField() | связующая таблица | Связь многие-ко-многим (article ←→ tag) |
| SlugField() | VARCHAR | Идентификатор, удобный для URL |
| ImageField() | VARCHAR(path) | Обложка, аватар (требуется библиотека Pillow) |
__str__Метод определяет, как объект отображается в виде строки. Он вызывается в раскрывающихся списках выбора административной панели и выводе Shell для отображения имени объекта. Если вы не напишете этот метод, вы увидитеPost object (1)такой бессмысленный текст.
Миграция: синхронизация моделей с базой данных
После написания классов моделей нужно использоватьмиграцию, чтобы создать таблицы базы данных.
(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
Разделение труда между двумя командами:
- makemigrationsСканирует models.py и генерирует скрипты миграций (файлы Python).
- migrate: Запускает скрипты миграций для создания фактических таблиц в базе данных.
После каждого изменения models.py необходимо запустить
makemigrations+migrateЭти две команды одинаково необходимы: запуск только makemigrations без migrate означает, что таблицы не будут созданы; запуск migrate напрямую без makemigrations означает, что Django не узнает, что вы изменили модели.
Используйте Django Shell, чтобы вставить тестовые данные.
Прежде чем начать писать страницы, сначала с помощью Django Shell добавьте несколько записей в базу данных, чтобы убедиться, что модель работает правильно.
(venv) $ python manage.py shell
Откроется интерактивная оболочка Python Shell, в которой можно работать с базой данных.
Пример
from blog.models import Category, Post
# 1. Создайте категорию
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. Создание статьи
Post.objects.create(
title='Полное руководство по Django для начинающих',
slug='django-beginner-guide',
summary='Изучите Django с нуля, охватывая основные концепции, такие как модели, представления, шаблоны и т. д.',
content='<h2>Почему стоит изучать Django?</h2><p>Django — один из самых популярных веб-фреймворков Python...</p>',
category=cat_django
)
Post.objects.create(
title='Подробное объяснение асинхронного программирования на Python',
slug='python-async',
summary=Разберитесь с asyncio, корутинами и циклами событий в одной статье.,
content=<h2>Что такое корутина?</h2><p>Корутина — это более лёгкое решение для конкурентности, чем потоки...</p>,
категория=cat_python
)
Post.objects.create(
заголовок=Практическое руководство по CSS Grid Layout,
slug='css-grid-layout',
краткое описание=Легко реализуйте сложные адаптивные макеты с помощью CSS Grid.,
содержание=<h2>Начало работы с Grid</h2><p>Grid — это двумерная система компоновки...</p>,
категория=cat_css
)
# 3. Проверка запросов
print(Post.objects.all()) # Вывод всех статей
print(Post.objects.count()) # Всего статей
print(Post.objects.filter(category=cat_django)) # Статьи в категории Django
Базовые запросы Django ORM
ORM позволяет работать с базой данных, используя цепочки методов Python, без написания SQL.
| Методы | Эквивалентный SQL | Возвращаемое значение |
|---|---|---|
Post.objects.all() | SELECT * FROM blog_post | QuerySet (все статьи) |
Post.objects.get(id=1) | SELECT ... WHERE id=1 | Один объект (выбрасывает исключение, если не найден) |
Post.objects.filter(category__slug='django') | WHERE category.slug='django' | QuerySet |
Post.objects.order_by('-created_at') | ORDER BY created_at DESC | QuerySet |
Post.objects.exclude(status='draft') | WHERE status != 'draft' | QuerySet |
QuerySet — этоЛенивое вычислениеЭто означает: запрос не выполняется сразу; SQL отправляется только тогда, когда данные действительно нужны (например, при итерации, печати или преобразовании в список). Поэтому можно объединять несколько вызовов filter, не создавая несколько запросов.
Практика: подготовка данных для последующих глав
В Shell введите ещё несколько статей и категорий, относящихся к трём категориям: Django, Python и CSS, не менее 2 статей на каждую категорию.
Вы могли бы написать команду управления или скрипт для пакетной вставки, но на этом этапе достаточно вставить их по одной с помощью Shell.
Итоги главы
В этой главе вы освоили основной рабочий процесс Django ORM: определение Python-классов (моделей) в models.py, создание файлов миграций с помощью makemigrations, синхронизацию с базой данных с помощью migrate и использование Shell и ORM для запроса и вставки данных.
На данный момент структура статей и категорий уже создана в базе данных.
Другие расширения