Работа с SQLALchemy сопряжена с большим количеством моментов. В частности, необходимо научиться добавлять данные, обновлять их, удалять и совершать ряд других операций. Сегодня раскроем то, как это делать правильно.
Добавление данных
Для создания новой записи с использованием SQLAlchemy, необходимо сделать следующее:
- Создать объект.
- Добавить объект в сессию.
- Загрузить сессию.
Сессия – это основной инструмент взаимодействия с базой данных SQLAlchemy. При этом ее создание осуществляется автоматически. Все, что нужно пользователю – это наладить работу с Flask-SQLAlchemy. Чтобы получить доступ к объекту используется db.session. Он используется для работы с базой данных. И он же необходим для осуществления транзакции.
Изначально транзакция запускается и продолжается, пока выполняются коммиты и откаты. Давайте попробуем сгенерировать ряд объектов в этой модели.
(env) gvido@vm:~/flask_app$ python main2.py shell >>> >>> from main2 import db, Post, Tag, Category >>> >>> >>> c1 = Category(name='Python', slug='python') >>> c2 = Category(name='Java', slug='java') >>>
Этот пример кода демонстрирует, как создавать объекты Category. Чтобы работать с отдельными их атрибутами, используется точка.
>>> >>> c1.name, c1.slug ('Python', 'python') >>> >>> c2.name, c2.slug ('Java', 'java') >>> Затем происходит добавление объектов в сессию. >>> >>> db.session.add(c1) >>> db.session.add(c2) >>>
В процессе добавления объектов не происходит их записи в базу данных. Этот процесс предназначен лишь для того, чтобы их сохранять при последующем коммите. Чтобы убедиться в этом, необходимо проверить первичный ключ.
>>> >>> print(c1.id) None >>> >>> print(c2.id) None >>>
Для каждого объекта значение атрибута id – None. Это означает, что в базе данных они не сохраняются.
Вместо того, чтобы каждый раз добавлять по объекту в сессию, можно воспользоваться методом add_all(). С его помощью можно добавить сразу несколько. В качестве аргумента используется список объектов, которые будут использоваться в сессии.
>>> >>> db.session.add_all([c1, c1]) >>>
Можно даже попробовать добавить объект несколько раз в ту же сессию. При этом никаких исключений не возникает.
Чтобы ознакомиться с перечнем объектов сессии, используется db.session.new.
>>> >>> db.session.new IdentitySet([<None:Python>, <None:java>]) >>>
Хорошо, а что делать, когда надо сохранить объект в базе данных? Для этого используется метод commit().
>>> >>> db.session.commit() >>>
Значением атрибута id объекта Category в данной ситуации будет выступать первичный ключ, а не None.
>>> >>> print(c1.id) 1 >>> >>> print(c2.id) 2 >>>
Таблица с категориями будет иметь приблизительно следующий вид.
При этом нет связи новых категорий с постами. Следовательно, c1.posts и c2.posts возвращают список без каких-либо данных в данной ситуации.
>>> >>> c1.posts [] >>> >>> c2.posts [] >>> Теперь попытаемся создать несколько постов. >>> >>> p1 = Post(title='Post 1', slug='post-1', content='Post 1', category=c1) >>> p2 = Post(title='Post 2', slug='post-2', content='Post 2', category=c1) >>> p3 = Post(title='Post 3', slug='post-3', content='Post 3', category=c2) >>>
Чтобы не передавать категорию при создании поста, можно передать следующую инструкцию.
>>> p1.category = c1
Затем в сессию добавляются объекты и делается коммит.
>>> >>> db.session.add_all([p1, p2, p3]) >>> db.session.commit() >>>
И в этом случае атрибут posts вернут список, в котором уже будут содержаться значения.
>>> >>> c1.posts [<1:Post 1>, <2:Post 2>] >>> >>> c2.posts [<3:Post 3>] >>>
Вместе с тем, возможно получение доступа к объекту Category, к которому относится пост, с использованием специального атрибута category объекта Post.
>>> >>> p1.category <1:Python> >>> >>> p2.category <1:Python> >>> >>> p3.category <2:Java> >>>
Необходимо учитывать то, что все это делается с помощью инструкции relationship() в модели Category. На данный момент в базе данных есть три поста, но с тегами нет связи ни у одного из них.
>>> >>> p1.tags, p2.tags, p3.tags ([], [], []) >>>
А чтобы создать теги, необходимо написать код, подобный этому.
>>> >>> t1 = Tag(name="refactoring", slug="refactoring") >>> t2 = Tag(name="snippet", slug="snippet") >>> t3 = Tag(name="analytics", slug="analytics") >>> >>> db.session.add_all([t1, t2, t3]) >>> db.session.commit() >>>
С помощью этого кода выполняется создание трех объектов тегов и делается их коммит в базу данных. Но все еще отсутствует привязка постов к ним. Чтобы их связать, необходимо написать адаптировать под свои задачи следующий код.
>>> >>> p1.tags.append(t1) >>> p1.tags.extend([t2, t3]) >>> p2.tags.append(t2) >>> p3.tags.append(t3) >>> >>> db.session.add_all([p1, p2, p3]) >>> >>> db.session.commit() >>>
С помощью этого коммита мы добавили пять записей в таблицу.
Теперь посты связываются с определенным количеством тегов.
>>> >>> p1.tags [<1:refactoring>, <2:snippet>, <3:analytics>] >>> >>> p2.tags [<2:snippet>] >>> >>> p3.tags [<3:analytics>] >>>
Также можно получить доступ к постам, относящимся в определенному тегу.
>>> >>> t1.posts [<1:Post 1>] >>> >>> t2.posts [<1:Post 1>, <2:Post 2>] >>> >>> t3.posts [<1:Post 1>, <3:Post 3>] >>> >>>
Необходимо учитывать тот факт, что есть еще один вариант реализации коммита тегов и связи их с постами.
>>> >>> t1 = Tag(name="refactoring", slug="refactoring") >>> t2 = Tag(name="snippet", slug="snippet") >>> t3 = Tag(name="analytics", slug="analytics") >>> >>> p1.tags.append(t1) >>> p1.tags.extend([t2, t3]) >>> p2.tags.append(t2) >>> p3.tags.append(t3) >>> >>> db.session.add(p1) >>> db.session.add(p2) >>> db.session.add(p3) >>> >>> db.session.commit() >>>
Также обратите внимание на строки 11-13. Здесь в сессию добавляются объекты Post. Они связаны с тегами таким образом, что добавление поста в сессию приводит к добавлению тегов, которые связанные с ним. Тем не менее, можно добавить их и вручную. Исключения это не вызовет.
Обновление данные
Чтобы обновить содержимое объекта, необходимо его атрибуту передать новое значение, а также сделать несколько других действий.
>>> >>> p1.content # начальное значение 'Post 1' >>> >>> p1.content = "This is content for post 1" # задаем новое значение >>> db.session.add(p1) >>> >>> db.session.commit() >>> >>> p1.content # обновленное значение 'This is content for post 1' >>>
Видим, что здесь мы сначала передали атрибуту объекта новое значение, затем добавили объект и сделали коммит.
Удаление данных
Для выполнения этой задачи используется метод delete() объекта сессии. Ему необходимо передать объект, а далее при последующем коммите будут убраны те данные, которые ему были переданы.
Давайте попробуем создать тег seo, связанный с постами p1 и p2. Его мы потом будем удалять.
>>> >>> tmp = Tag(name='seo', slug='seo') # создание временного объекта Tag >>> >>> p1.tags.append(tmp) >>> p2.tags.append(tmp) >>> >>> db.session.add_all([p1, p2]) >>> db.session.commit() >>>
Этот код выполняет следующие действия:
- Добавляет строку в таблицу table.
- Две строки добавляются в таблицу post_tags.
После этого база данных будет обновлена следующим образом.
А теперь давайте удалим наш временный тег.
>>> >>> db.session.delete(tmp) >>> db.session.commit() >>>
После того, как выполнится этот коммит, все строки, добавленные в предыдущем фрагменте кода, будут удалены. При этом пост, с которым тег был связан, удаляться не будет.
Во время удаления объекта значение связанного с ним объекта в дочерней таблице становится NULL. Чтобы этот момент продемонстрировать наглядно, приведем фрагмент кода.
>>> >>> c4 = Category(name='css', slug='css') >>> p4 = Post(title='Post 4', slug='post-4', content='Post 4', category=c4) >>> >>> db.session.add(c4) >>> >>> db.session.new IdentitySet([<None:css>, <None:Post 4>]) >>> >>> db.session.commit() >>>
Этим коммитом добавляется две дополнительные строки, по одной в каждую таблицу. Одна добавляется в categories, а другая – в posts.
Запрос данных
Для запуска запроса к базе данных, необходимо воспользоваться методом query() объекта session. С помощью этого метода программа получает объект flask_sqlalchemy.BaseQuery, который используется для выполнения запросов к базе.
Основные методы этого класса приводятся в данной таблице:
Метод | Описание |
all() | Возвращает результат запроса (представленный flask_sqlalchemy.BaseQuery) в виде списка. |
count() | Возвращает количество записей в запросе. |
first() | Возвращает первый результат запроса или None, если в нем нет строк. |
first_or_404() | Возвращает первый результат запроса или ошибку 404, если в нем нет строк. |
get(pk) | Возвращает объект, который соответствует данному первичному ключу или None, если объект не найден. |
get_or_404(pk) | Возвращает объект, который соответствует данному первичному ключу или ошибку 404, если объект не найден. |
filter(*criterion) | Возвращает новый экземпляр flask_sqlalchemy.BaseQuery с оператором WHERE. |
limit(limit) | Возвращает новый экземпляр flask_sqlalchemy.BaseQuery с оператором LIMIT. |
offset(offset) | Возвращает новый экземпляр flask_sqlalchemy.BaseQuery с оператором OFFSET. |
order_by(*criterion) | Возвращает новый экземпляр flask_sqlalchemy.BaseQuery с оператором OFFSET. |
join() | Возвращает новый экземпляр flask_sqlalchemy.BaseQuery после создания SQL JOIN. |
Выводы
Мы разобрали основные аспекты, которые касаются ORM SQLAlchemy. Конечно, это далеко не все. Работа с базами данных – отдельная наука. Но этих основ достаточно для того, чтобы получить базовые представления о том, как налаживается взаимодействие с ними.