Модель в MVC v2 – это слой приложения, который отвечает за связь с предметной областью. В нем находится вся бизнес-логика приложения. Для простоты реализации, сюда часто включают механизмы для работы с базой данных.
Модель, как слой, существует независимо от фреймворка, HTTP и веба в целом. Вся остальная система может обращаться к моделям, но модели не могут знать и не знают ничего про среду, в которой они выполняются.
Это значит, что мы можем работать с моделями через специальный вариант REPL, который работает в контексте фреймворка командой python manage.py shell
.
Создадим новую статью, наполним ее данными и сохраним в базе:
python manage.py shell
from hexlet_django_blog.article.models import Article # Импортируем наш класс модели Article
a = Article()
# Когда объект модели создан, его можно наполнять данными через обычные свойства.
a.name = 'my super article'
a.body = 'my content'
a # <Article: Article object (None)>
a.id
a.name # 'my super article'
a.body # 'my content'
Также можно создать объект методом create
. Этот метод создает объект и автоматически сохраняет его в базу.
from hexlet_django_blog.article.models import Article
a = Article.objects.create(name='my super article', body='my content')
a # <Article: Article object (1)>
a.id # 1
a.name # 'my super article'
a.body # 'my content'
a.created_at # datetime.datetime(2022, 11, 3, 20, 48, 19, 883732, tzinfo=datetime.timezone.utc)
a.updated_at # datetime.datetime(2022, 11, 3, 20, 48, 19, 883732, tzinfo=datetime.timezone.utc)
Этот метод делает запрос INSERT в базу данных. Затем он обновляет сам объект:
- Внутрь записывается идентификатор статьи, взятый из базы данных
- Заполняются поля created_at и updated_at.
Созданный объект можно тут же поменять:
a.name = 'another title'
a.name # 'another title'
Изменение свойств не затрагивает базу данных. Это происходит только на уровне самого объекта. Если в этот момент закрыть консоль, то все изменения потеряются. Для сохранения нужно снова вызвать save()
. Этот метод универсальный, он подходит как для создания новой сущности, так и для ее обновления:
a.save()
a # <Article: Article object (1)>
a.id # 1
a.name # 'another title'
a.body # 'my content'
a.created_at # datetime.datetime(2022, 11, 3, 20, 48, 19, 883732, tzinfo=datetime.timezone.utc)
a.updated_at # datetime.datetime(2022, 11, 3, 20, 48, 19, 883732, tzinfo=datetime.timezone.utc)
Теперь предположим, что у нас нет переменной с текущей статьей. Например, мы перезапустили консоль. Получить нужный объект можно с помощью статических методов, которые появляются у каждой модели. Самый простой способ достать запись, сделать поиск по ее идентификатору:
a = Article.objects.get(id=1)
a # <Article: Article object (1)>
a.id # 1
a.name # 'another title'
a.body # 'my content'
a.created_at # datetime.datetime(2022, 11, 3, 20, 48, 19, 883732, tzinfo=datetime.timezone.utc)
a.updated_at # datetime.datetime(2022, 11, 3, 20, 48, 19, 883732, tzinfo=datetime.timezone.utc)
Article.objects
— это представление всех объектов данной модели. get ищет одну запись по заданному условию и возвращает запись. Если запись не найдена, то вызовется исключение:
a = Article.objects.get(id=2)
# Traceback (most recent call last):
# ...
# DoesNotExist: Question matching query does not exist.
Если нужен поиск по полям, который вернет коллекцию, для этого подходит метод filter:
a = Article.objects.filter(name='another title')
a # <QuerySet [<Article: Article object (1)>]>
Мы можем составить и более сложный запрос. Предположим, что надо вывести body первой записи, у которой это id больше 50 (при условии, что записей в таблице больше 50):
Article.objects.filter(id__gt=50).first().body # 'my content'
Здесь строится целая цепочка вызова методов. И такие цепочки могут быть достаточно длинными. Каждый из шагов уточняет запрос по тому или иному критерию, а в конце всегда стоит вызов какого-то метода. Он определяет, сколько записей мы хотим получить. В данном случае мы хотим получить одну первую попавшуюся. Поэтому запрос заканчивается вызовом метода .first()
. А .body
мы уже получаем у объекта, который является результатом выполнения запроса.
Во фрагменте указано условие, по которому выбираются объекты в запросе — id__gt=50
. Это так называемый lookup. id здесь означает имя поля, а дальше через разделитель "__" (двойное подчеркивание) указывается gt — сокращение от greater than (больше чем) и, наконец, само значение.
Такой синтаксис приходится использовать, так как мы не можем при вызове метода указать аргумент id > 20. Авторы вышли из ситуации и использовали именованные аргументы с такими необычными именами.
Попробуем запросить данные из базы данных и вывести список всех записей модели Article:
a = Article.objects.all()
a # <QuerySet [<Article: Article object (1)>]>
И последнее, что можно сделать с сущностью, это удалить ее:
a = Article.objects.get(id=1)
a.delete() # (1, {'article.Article': 1})
Article.objects.all() # <QuerySet []>
Самостоятельная работа
- Создайте пять статей через репл. Они понадобятся при построении CRUD
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.