Базы данных — это превосходный, безопасный и надежный способ хранения данных. Все основные реляционные базы объединяет SQL, т.е. язык управления данными, их базами и таблицами. SQL — это довольно обширная тема для беседы, особенно если речь заходит о различных создателях этих организованных структур, таких как Microsoft, IBM и Oracle, так что начнем мы с SQLite, самой “легковесной” системы управления базами данных.
Итак, что же такое SQLite? Как хорошо, что вы спросили. Это библиотека, которая предоставляет систему управления реляционными и другими типами баз данных. Термин “Lite” означает “упрощенный” и говорит о том, что среди себе подобных данная библиотека проста в установке и администрировании.
В чем практическая значимость темы? Ответ прост — вы сможете приобрести опыт работы с базой данных, и при этом вам не придется загружать ПО или создавать облачную базу, а потом ломать голову, как же все это подключить к Python. Нельзя сказать, что это самый безопасный вариант, но тем не менее он значительно превосходит файлы CSV и Excel, поскольку ваши данные никто не сможет изменить.
Далее мы рассмотрим основы: создание таблиц, добавление, обновление, удаление, извлечение всех данных или только на основе условия — всё, что нужно для создания приложения.
Постановка задачи
Кто из нас не любит кино?! Вот этому аспекту жизни мы и уделим внимание — создадим базу данных и таблицу для хранения названий фильмов, а также дополнительной информации. Когда таблица будет готова, мы объявим несколько функций для:
- добавления фильмов;
- получения данных обо всех фильмах;
- получения данных об одном фильме;
- обновления информации об одном фильме;
- удаления одного фильма.
Кажется, что объем работы большой, но это обманчивое ощущение. Будем использовать Python для взаимодействия с базой данных SQLite, а фильмы позаимствуем с сайта IMDB.com.
Превосходно! Приступим!
Создание таблиц
Мы не можем напрямую размещать данные в базе — для этого нужны таблицы. Базы данных содержат таблицы, а те в свою очередь — данные. Наша задача — создать таблицу с фильмами только при условии ее отсутствия. Для этого потребуется выполнить 2 запроса SQL: с помощью первого мы проверим, существует ли таблица с данным именем, с помощью второго — создадим таблицу.
Прежде всего, необходимо импортировать библиотеку SQLite, подключиться к базе данных и создать курсор. Всё это сделает для нас следующий фрагмент кода:
import sqlite3
conn = sqlite3.connect('movies.db')
c = conn.cursor()
Если до этого момента база movies.db не существовала, то на данном этапе она будет создана, в противном случае произойдет только подключение.
Далее объявим функцию, проверяющую наличие таблицы, имя которой будет передано в качестве ее параметра. Если таблица существует, функция вернет True
, иначе — False
:
def table_exists(table_name):
c.execute('''SELECT count(name) FROM sqlite_master WHERE TYPE = 'table' AND name = '{}' '''.format(table_name))
if c.fetchone()[0] == 1:
return True
return False
По какой-то причине не работают новые f-strings
, поэтому придется обратиться к устаревшему синтаксису, но это не проблема, просто учтите данное обстоятельство. Далее мы воспользуемся объявленной функцией для проверки наличия таблицы, и в случае ее отсутствия — таковую создадим. Именно это делает следующий фрагмент кода:
if not table_exists('movies'):
c.execute('''
CREATE TABLE movies(
movie_id INTEGER,
name TEXT,
release_year INTEGER,
genre TEXT,
rating REAL
)
''')
Прекрасно! Наша цель достигнута, и мы переходим к решению следующих интересных задач.
Создание, чтение, обновление и удаление
Теперь, когда у нас всё настроено, можно переходить к работе с данными. Начнем с этапа создания, и здесь нам потребуется функция, добавляющая фильм в таблицу. Написать ее не составит труда, поскольку необходимо выполнить лишь одну инструкцию INSERT
и совершить транзакцию. А вот и код:
def insert_movie(movie_id, name, release_year, genre, rating):
c.execute(''' INSERT INTO movies (movie_id, name, release_year, genre, rating) VALUES(?, ?, ?, ?, ?) ''', (movie_id, name, release_year, genre, rating))
conn.commit()
Готово! Чуть позже мы перейдем к тестированию, а пока приступим к этапу чтения.
В этой части объявим 2 функции: первую — для получения данных обо всех фильмах, а вторую — для извлечения информации об одном из них на основании его ID. Вы легко можете уместить всю логику в одну функцию, но я же предпочел этот подход.
Перед вами функция для получения данных обо всех фильмах:
def get_movies():
c.execute('''SELECT * FROM movies''')
data = []
for row in c.fetchall():
data.append(row)
return data
И фрагмент кода для извлечения информации обо одном из них:
def get_movie(movie_id):
c.execute('''SELECT * FROM movies WHERE movie_id = {}'''.format(movie_id))
data = []
for row in c.fetchall():
data.append(row)
return data
Отлично! Переходим к более сложному этапу обновления. Наша задача — скорректировать элементы в соответствии с заданным ID фильма, но что подлежит изменению? Может объявить отдельную функцию для каждого поля? Сомневаюсь. Выполним обновление с помощью словаря.
Сейчас всё объясню. Наша соответствующая функция примет 2 параметра:
movie ID
— это ID фильма, данные о котором нужно обновить;update dictionary
— это пары ключ/значение для обновления.
Ключам в update dictionary
нужно дать имена в соответствии со столбцами таблицы, иначе возникнет исключение. Кроме того, необходимо выполнить обновление числовых и текстовых полей. Эта функция довольно сложная, но мы с ней справимся:
def update_movie(movie_id, update_dict):
valid_keys = ['name', 'release_year', 'genre', 'rating']
for key in update_dict.keys():
if key not in valid_keys:
raise Exception('Invalid field name!')
for key in update_dict.keys():
if type(update_dict[key]) == str:
stmt = '''UPDATE movies SET {} = '{}' WHERE movie_id = {}'''.format(key, update_dict[key], movie_id)
else:
stmt = '''UPDATE movies SET {} = '{}' WHERE movie_id = {}'''.format(key, update_dict[key], movie_id)
c.execute(stmt)
conn.commit()
Понятно? На самом деле не так уж и сложно. В завершении этого раздела — этап удаления. Он намного проще, чем предыдущий, поскольку нам нужно лишь выполнить одну инструкцию и совершить транзакцию. Обратимся к коду:
def delete_movie(movie_id):
c.execute('''DELETE FROM movies WHERE movie_id = {}'''.format(movie_id))
conn.commit()
Далее нам предстоит протестировать все наши функции.
Тестирование
Сначала добавим несколько фильмов:
insert_movie(1, 'Titanic', 1997, 'Drama', 7.8)
insert_movie(2, 'The Day After Tomorrow', 2004, 'Action', 6.4)
insert_movie(3, '2012', 2009, 'Action', 5.8)
insert_movie(4, 'Men in Black', 1997, 'Action', 7.3)
insert_movie(5, 'World War Z', 2013, 'Romance', 10)
Как и ожидалось, выполнение этих 5 строк не приведет к выводу каких-либо данных в консоль — мы просто добавляем данные в таблицу. Затем мы применим нашу предопределенную функцию для получения данных обо всех фильмах:
print(get_movies())
Замечательно! Давайте извлечем информацию об одном фильме:
print(get_movie(2))
Теперь посмотрим, как обновить данные о фильме. Последним в списке добавленных фильмов значился World War Z, ему намеренно был присвоен рейтинг10 и жанр Romance. Сейчас мы это исправим:
update_movie(5, {'genre': 'Horror', 'rating': 7.0})
Теперь все правильно. Осталось только удалить фильм — смотрим, как это сделать:
delete_movie(3)
Как и ожидалось — функция ничего не возвращает, но можно быстро получить данные обо всех фильмах, чтобы подтвердить удаление одного из них.
Всё сработало согласно нашему замыслу.
Заключение
Надеюсь, вам было легко следовать за ходом моей мысли. Мы рассмотрели только основы, но еще много тем ждут нашего внимания. Однако уже полученных знаний достаточно для создания базового приложения или API на основе данных.
Благодарю за внимание!
Читайте также:
- Что такое SQL-атаки и как с ними бороться?
- Использование SQLite с Rust и Actix Web (с тестами)
- NoSQL убивает SQL?
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Dario Radečić: Python has a Built-in Database — Here’s How to use it