Наталья Кайда 29 января 2024

🐍🎸 Курс Django. Часть 2: ORM и основы работы с базами данных

Создаем базу данных на основе моделей, рассматриваем способы наполнения БД с использованием loaddata и скриптов. Передаем данные на фронтенд с помощью функциональных представлений и шаблонов.
🐍🎸 Курс Django. Часть 2: ORM и основы работы с базами данных

Для создания базы данных (и всех взаимодействий с ней) Django использует ORM (объектно-реляционное представление). ORM – это своеобразная прослойка, которая позволяет работать с базой данных, используя классы и методы вместо написания сложных SQL-запросов.

Вот основные возможности и преимущества использования ORM в Django:

  • Моделирование базы данных. Разработчик определяет структуру таблиц, их поля и взаимосвязи между ними с помощью специальных классов – моделей.
  • Отношения и связи. Связывание моделей различными типами отношений (один-к-одному, один-ко-многим и многие-ко-многим) в ORM выполняется очень просто.
  • Простой доступ к данным. Запросы к базе данных выполняются с помощью простого и понятного синтаксиса вместо языка SQL.
  • Гибкое обновление структуры базы данных. С помощью миграций ORM мгновенно изменяет структуру базы данных в соответствии с изменениями в моделях.
  • Автоматическая валидация данных. ORM предусматривает несколько способов автоматической валидации данных в соответствии с определенными правилами и ограничениями.
  • Защита от SQL-инъекций. Код SQL запроса определяется отдельно от параметров запроса.
  • Переносимость – можно легко переключаться между разными базами данных, не меняя код приложения.
  • Кеширование запросов для повышения производительности.
  • Разнообразная дополнительная функциональность – ORM предоставляет готовые решения для работы с данными: создание и изменение объектов, выборки, агрегации, пагинация и т.д. Не нужно все это программировать вручную.

Недостатки у ORM тоже есть, но их гораздо меньше, чем преимуществ:

  • Снижение производительности. ORM добавляет накладные расходы, так как создает дополнительный слой абстракции над базой данных. В некоторых случаях использование ORM может привести к уменьшению производительности из-за неоптимальных запросов.
  • Для написания сложных запросов (и для оптимизации часто повторяющихся, ресурсоемких операций) могут потребоваться Q и F-объекты.
  • В некоторых случаях сложные запросы нужно писать вручную на SQL.
🐍 Библиотека питониста
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»
🐍🎓 Библиотека собеса по Python
Подтянуть свои знания по Python вы можете на нашем телеграм-канале «Библиотека собеса по Python»
🧩🐍 Библиотека задач по Python»
Интересные задачи по Python для практики можно найти на нашем телеграм-канале «Библиотека задач по Python»

Модели в Django

Модели в Django представляют собой классы Python, которые описывают структуру, свойства и взаимосвязи таблиц в базе данных. Модели не только играют ключевую роль в создании и управлении схемой базы данных, но также обеспечивают удобный интерфейс для работы с данными.

Модели содержат поля, которые представляют столбцы в таблице БД. Эти поля определяют тип данных, валидацию и другие свойства для хранения, извлечения и обновления данных. Например, поле типа CharField может описывать строковое значение, поле типа DateField – дату, а поле типа ForeignKey – отношение типа один-ко-многим между таблицами. Кроме полей, для модели можно определять метаданные, методы, менеджеры для выборки и фильтрации данных.

Связи между моделями

Основные типы связей между моделями – один-к-одному, один-ко-многим, многие-ко-многим. Рассмотрим на примерах.

Один-к-одному (OneToOne) – это связь одной записи в одной таблице с одной записью в другой таблице. Например, один пользователь может иметь только один профиль:

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

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', blank=True)
    

Один-ко-многим (ForeignKey) – это связь одной записи в одной таблице со многими записями в другой. Например, один автор может написать несколько книг:

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

class Book(models.Model):
   author = models.ForeignKey(Author, on_delete=models.CASCADE)
   title = models.CharField(max_length=50)
    

Многие-ко-многим (ManyToMany) – это связь между произвольным количеством записей в двух таблицах. Например, одна книга может принадлежать к нескольким жанрам сразу, а к одному жанру относится множество книг:

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

class Book(models.Model):
    title = models.CharField(max_length=50)
    genres = models.ManyToManyField(Genre)
    

Создание базы данных в Django

Для создания базы данных в Django нужно:

  • Создать первичную базу с помощью команды migrate.
  • Создать аккаунт суперпользователя (админа) командой createsuperuser.
  • Написать модели, определяющие нужную структуру БД.
  • Подготовить и выполнить миграции – makemigrations и migrate.

Выполним все эти действия шаг за шагом.

Инициализация базы данных

Django по умолчанию использует SQLite, и для подключения этой базы к приложению (в отличие от других баз типа MySQL и PostgreSQL) не нужно делать никаких специальных настроек. Как только вы создали проект и приложение в нем, можно выполнить команду migrate, что приведет к появлению базы данных db.sqlite3 на одном уровне с директорией приложения и файлом manage.py:

        python -m venv myproject\venv
cd myproject
venv\scripts\activate
pip install django
django-admin startproject config .
python manage.py startapp myapp
python manage.py migrate
python manage.py createsuperuser

    

Структура проекта теперь выглядит так:

        myproject
|-- config
|   |-- __init__.py
|   |-- settings.py
|   |-- urls.py
|   `-- wsgi.py
|-- myapp
|   |-- __init__.py
|   |-- admin.py
|   |-- apps.py
|   |-- models.py  
|   |-- tests.py
|   |-- views.py
|   `-- migrations
|-- venv
|-- db.sqlite3  
`-- manage.py
    

Добавьте приложение myapp в config/settings.py:

        INSTALLED_APPS = [
    'myapp.apps.MyappConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
    

Создание структуры базы данных

Сохраните в файле myapp/models.py модели Author, Genre и Book:

        from django.db import models

class Author(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    biography = models.TextField(blank=True)
    
class Genre(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(blank=True)

class Book(models.Model):
    title = models.CharField(max_length=100)    
    description = models.TextField()   
    publication_date = models.DateField()   
    num_pages = models.IntegerField()   
    price = models.DecimalField(max_digits=6, decimal_places=2)   
    genres = models.ManyToManyField(Genre) 
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    is_bestseller = models.BooleanField(default=False)

    

Теперь можно подготовить миграции (изменения в структуре) БД:

        python manage.py makemigrations
    

И применить миграции к структуре БД:

        python manage.py migrate
    

В базе данных появились соответствующие таблицы Author, Genre и Book. Вот какие значения там можно сохранять:

  • CharField – строка ограниченной длины, для небольших текстовых значений вроде имен и названий.
  • TextField – неограниченный текст, для мультистрочных текстов вроде подробных описаний.
  • DateField – дата, хранится в формате ГГГГ-ММ-ДД.
  • IntegerField – целочисленное значение.
  • DecimalField – число с плавающей точкой, для денежных значений. Это более точный формат, чем FloatField.
  • ForeignKey – ссылка на другую модель, связь один-ко-многим.
  • ManyToManyField – связь многие-ко-многим.
  • BooleanField – логическое значение True или False.

У моделей могут быть и другие поля – например, FileField для хранения ссылки на текстовый или аудиофайл книги, ImageField для ссылки на изображение обложки. Надо заметить, что файлы в базе данных обычно не хранят (хотя это возможно) – в соответствующих файлам полях сохраняются только ссылки на файлы, которые, в свою очередь, физически находятся в директории media и других служебных папках.

Регистрация моделей в панели управления Django

Чтобы с моделями можно было работать в админке Django, нужно их зарегистрировать в файле myapp/admin.py:

        from django.contrib import admin
from .models import Author, Genre, Book

admin.site.register(Author)
admin.site.register(Genre)
admin.site.register(Book)

    

Это самый простой способ представления моделей в админке, есть и более удобные. Например, так можно обеспечить вывод полей Book прямо на странице Author:

        from django.contrib import admin
from .models import Author, Genre, Book

class BookInline(admin.TabularInline):
    model = Book

class AuthorAdmin(admin.ModelAdmin):
    inlines = [BookInline]

admin.site.register(Author, AuthorAdmin)
admin.site.register(Genre)
admin.site.register(Book)
    

Теперь можно запустить сервер, зайти в админку и посмотреть на визуальное представление базы данных:

        python manage.py runserver
    

Панель управления Django доступна по адресу http://127.0.0.1:8000/admin:

Панель управления Django доступна по адресу http://127.0.0.1:8000/admin
Панель управления Django доступна по адресу http://127.0.0.1:8000/admin

Класс Meta

Русскоязычные названия для моделей, порядок вывода записей, ограничения по уникальности и многое другое можно определить с помощью класса Meta:

        class Author(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    biography = models.TextField(blank=True)
    class Meta:
        ordering = ['-id']
        verbose_name = 'Автор'
        verbose_name_plural = 'Авторы'

    def __str__(self):
        return f'{self.first_name} {self.last_name}'
    
class Genre(models.Model):
    name = models.CharField(max_length=50)
    description = models.TextField(blank=True)
    class Meta:
        verbose_name = 'Жанр'
        verbose_name_plural = 'Жанры'

    def __str__(self):
        return self.name

class Book(models.Model):
    title = models.CharField(max_length=100)    
    description = models.TextField()   
    publication_date = models.DateField()   
    num_pages = models.IntegerField()   
    price = models.DecimalField(max_digits=6, decimal_places=2)   
    genres = models.ManyToManyField(Genre) 
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    is_bestseller = models.BooleanField(default=False)
    class Meta:
    	ordering = ['-id']
        verbose_name = 'Книга'
        verbose_name_plural = 'Книги'

    def __str__(self):
        return self.title

    

Заполнение базы

Наполнить БД информацией можно несколькими способами:

  • Вручную в админке. Панель управления Django является, по сути, удобным визуальным GUI для CRUD-операций. Но записи придется вводить по одной.
  • В интерактивной оболочке (shell) Django. Так можно ввести сколько угодно записей за раз, однако набирать их придется вручную, а после каждого неверного отступа процесс нужно будет начинать сначала. В shell также можно загрузить данные из файла, ниже мы рассмотрим этот способ.
  • С помощью команды loaddata и данных из заранее подготовленного json-файла, структура которого соответствует схеме БД.
  • С помощью Python-скрипта, который может загрузить данные из любого файла (json, csv, xlsx), придать им нужную структуру и сохранить в базе. Такие скрипты можно выполнять в shell и в cmd.

Самый удобный из этих способов – загрузка с loaddata:

  • Сначала нужно создать директорию fixtures в папке приложения, то есть myapp/fixtures.
  • В директорию fixtures нужно поместить готовый json-файл с данными.
  • После чего надо выполнить команду python manage.py loaddata data.json.
        python manage.py loaddata data.json
Installed 9 object(s) from 1 fixture(s)
    

Запустите сервер, зайдите в админку – теперь там есть 3 жанра, 3 книги и 3 писателя:

В админке появились 3 жанра, 3 книги и 3 писателя
В админке появились 3 жанра, 3 книги и 3 писателя

А так можно добавлять записи в БД в интерактивной оболочке Django:

        python manage.py shell
>>> from myapp.models import Author, Genre, Book
>>> new_author = Author.objects.create(first_name='Дэн', last_name='Симмонс', biography='Современный американский писатель-фантаст')
>>> print(Author.objects.all())
<QuerySet [<Author: Дэн Симмонс>, <Author: Джоан Роулинг>, <Author: Федор Достоевский>, <Author: Лев Толстой>]>
>>> new_genre = Genre.objects.create(name='Триллер', description='В литературных произведениях этого жанра есть загадка и ощущение тревоги или страха.')
>>> print(Genre.objects.all())
<QuerySet [<Genre: Роман>, <Genre: Повесть>, <Genre: Фантастика>, <Genre: Триллер>]>
>>> new_book = Book.objects.create(title='Террор', description='Фантастическая версия трагической гибели арктической экспедиции Джона Франклина.', publication_date='2007-01-09', num_pages=1002, price=970.00, author=new_author, is_bestseller=True)
>>> new_book.genres.add(new_genre)
    

Результат – в базу добавлены записи о писателе Дэне Симмонсе, его книге «Террор», и одном из жанров, к которым относится эта книга – триллер:

В базу добавлены записи о писателе Дэне Симмонсе, его книге «Террор»
В базу добавлены записи о писателе Дэне Симмонсе, его книге «Террор»

Еще один способ – загрузка данных из скрипта, выполненного в shell:

  • Сохраните скрипт add_data.py на одном уровне с manage.py.
  • Запустите оболочку командой python manage.py shell.
  • Выполните команду exec(open("add_data.py", encoding="utf-8").read()).

Готово – в базе данных появились записи о двух новых авторах, книгах и жанрах:

В базе данных появились записи о двух новых авторах, книгах и жанрах
В базе данных появились записи о двух новых авторах, книгах и жанрах

Получение данных из БД

Все основные манипуляции с данными в Django происходят в представлениях, которые находятся в файле views.py. Представления делятся на функциональные и классовые. Представления на основе классов имеют несколько весомых преимуществ по сравнению с функциональными, и в дальнейшем мы подробно их рассмотрим.

Сохраните эти функциональные представления в myapp/views.py:

        from django.shortcuts import render
from .models import Author, Book, Genre

def authors(request):
    authors = Author.objects.all()
    context = {
        'authors': authors
    }
    return render(request, 'authors.html', context)

def books(request):
    books = Book.objects.all()
    context = {
        'books': books
    }
    return render(request, 'books.html', context)

def genres(request):
    genres = Genre.objects.all()
    context = {
        'genres': genres
    }
    return render(request, 'genres.html', context)
    

Представления передают в шаблоны все записи об авторах, книгах и жанрах. В действие эти представления приводят маршруты. Сохраните эти маршруты в файле myapp/urls.py:

        from django.urls import path
from .views import authors, books, genres

urlpatterns = [
    path('authors/', authors, name='authors'),
    path('books/', books, name='books'),
    path('genres/', genres, name='genres')
]
    

И добавьте маршруты myapp в config/urls.py:

        from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('myapp.urls')), 
]

    

Теперь нужно создать директорию templates внутри myapp и сохранить в ней эти шаблоны:

Все готово – запускайте сервер, переходите по ссылкам-маршрутам:

  • http://127.0.0.1:8000/authors/
  • http://127.0.0.1:8000/books/
  • http://127.0.0.1:8000/genres/
http://127.0.0.1:8000/authors/
http://127.0.0.1:8000/authors/
http://127.0.0.1:8000/books/
http://127.0.0.1:8000/books/
http://127.0.0.1:8000/genres/
http://127.0.0.1:8000/genres/

Подведем итоги

Мы рассмотрели:

  • Принцип создания базы данных с помощью моделей ORM.
  • Способы заполнения БД с использованием loaddata, команды create() и скрипта, запускаемого в интерактивной оболочке shell.
  • Простейшие представления на основе функций.
  • Механизм передачи данных из БД на фронтенд.

Представления могут выполнять гораздо более сложные манипуляции с данными, чем передачу в шаблоны всех существующих в базе записей. Со всеми возможностями представлений мы разберемся в последующих главах и курсовых проектах.

Весь код и база данных, использованные в этой статье, находятся здесь.

***

Содержание курса

Комментарии

ВАКАНСИИ

Добавить вакансию
Разработчик C++
Москва, по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ