HOW-TO: Программа на Python, Часть 7 Сравнение версий

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Следующая версия
Предыдущая версия
fullcircle:33:python_ч_7 [2010/06/20 17:16]
создано
fullcircle:33:python_ч_7 [2013/10/10 14:07] (текущий)
[Конец.]
Строка 3: Строка 3:
 //​Автор — Грэг Валтерс (Greg Walters)// //​Автор — Грэг Валтерс (Greg Walters)//
 </​style>​ </​style>​
 +
  
   - [[..:​27:​python_ч_1|Программа на Python — часть 1]]   - [[..:​27:​python_ч_1|Программа на Python — часть 1]]
Строка 13: Строка 14:
   - [[..:​34:​python_ч_8|Программа на Python — часть 8]]   - [[..:​34:​python_ч_8|Программа на Python — часть 8]]
   - [[..:​35:​python_ч_9|Программа на Python — часть 9]]   - [[..:​35:​python_ч_9|Программа на Python — часть 9]]
 +  - [[..:​36:​python_ч_10|Программа на Python — часть 10]]
 +  - [[..:​37:​python_ч_11|Программа на Python — часть 11]]
 +  - [[..:​38:​python_ч_12|Программа на Python — часть 12]]
 +  - [[..:​39:​python_ч_13|Программа на Python — часть 13]]
 +  - [[..:​40:​python_ч_14|Программа на Python — часть 14]]
 +
 +Здравствуйте,​ девочки и мальчики. Вот и настало время для сказочки. Усаживайтесь поудобнее и слушайте внимательно. Готовы?​ Ну, поехали!
 +
 +Давным-давно всем миром правила Бумага. Бумаги,​ бумажки,​ бумажечки... Везде бумаги! Для бумаг даже строили особые дома, называли их «шкафы для бумаг». А были ещё и стальные такие штуки, в которых жили Особо Важные Бумаги,​ и они громоздились и громоздились в офисах...*В каждый шкаф помещались такие штуки, называемые «папки»,​ в которые вкладывали бумаги в попытке организовать их по тематике. Но всё равно, со временем бумаги перемеши-вались или рассыпались от вет-хости,​ а то и просто от частого использования.
 +
 +Чтобы научиться правильно использовать шкафы для бумаг нужно было окончить вуз, и всё равно, чтобы найти бумаги,​ хранящиеся в разных шкафах,​ нужно было время. Дела стра-дали жестоко. То были трудные дни (и ночи) в истории челове-чества.
 +
 +И вот однажды с вершины высокой горы (по-моему,​ это вроде была Колорадо) спусти-лась прекрасная фея. Фея была в голубом и серебряном,​ с кра-сивыми крыльями и белоснеж-ными волосами,​ и была она ростом до колена. Звали её — хотите верьте,​ хотите нет — Эскуэль. Прикольное имя, да? Короче,​ Эскуэль сказала,​ что может справиться со всеми Бумагами и шкафами для бумаг, и вернуть потерянное время. И всего-то надо было — поверить в компьютеры и в неё. Эту магию фея назвала «База данных». Она сказала,​ что «База данных» может заменить все-все шкафы для бумаг. Кто-то поверил ей, и вот их жизнь стала свободной и счастливой. Кот-то не поверил,​ и продолжал прозябать под горами Бумаг.
 +
 +Но, разумеется,​ помощь феи не даётся даром. Для того, чтобы воспользоваться магией Эскуэль,​ нужно было изучить иностранный язык, хотя и очень простой. Тем, кто знал английский,​ и вовсе достаточно было научить-ся правильно складывать привычные слова. В общем, просто чуть по-другому назы-вать привычные вещи, да хорошо подумать,​ прежде чем попросить Эскуэль о чём-нибудь,​ чтобы она точно знала, что сделать.
 +
 +Как-то раз к Эскуэль пришёл мальчик,​ которого родители по приколу назвали Пользователь. Его восхитила её красота и он произнёс:​ «Эскуэль,​ научи меня, как использовать твою магию». И Эскуэль согласилась.
 +
 +«Сначала я должна посмотреть,​ как у тебя хранится информация. Покажи-ка мне твои Бумаги».
 +
 +У маленького Пользователя и бумаг было мало. Но Эскуэль произнесла:​ «Пользователь,​ сейчас у тебя не много Бумаг, и все они уместятся в одном шкафу. Однако предвижу я будущее,​ когда Бумаги твои вырастут,​ и, сложи ты их вместе,​ они закроют тебя с головой. Давай используем мою магию».
 +
 +И вот, работая вместе,​ Пользователь и Эскуэль создали «программулину с базой данных» (это такой термин из практической магии). И Пользователь,​ которого с тех пор звали уважительно Юзером,​ жил без Бумаг долго и счастливо.
 +
 +=====Конец.=====
 +
 +Разумеется,​ в этой истории есть немного вымысла. Тем не менее, использование баз данных и SQL может здорово облегчить нам жизнь. На этот раз мы изучим несколько простых запросов SQL и применим их в программе. Кто-то может возразить,​ что это «неправильный» и не лучший путь, но зато этот путь очень практичен. Итак, приступим.
 +
 +Базы данных — это как шкафы для бумаг в рассказанной истории. Таблицы можно представить как папки. Записи в таблицах — это листы бумаг. Ну, а каждая запись на бумаге называется полем. Пока всё гладко,​ не так ли? Запросы SQL (обычно это слово произносят «Эскуэль»,​ хотя англичане произносят что-то вроде «Си-куилл») используются для манипулирования данными. SQL — это сокращение от «Structured Query Language» («Язык Структурированных Запросов»),​ и изначально он был задуман,​ чтобы упростить использование баз данных. Правда,​ на прак-тике запросы могут быть очень сложными,​ но мы для нашего примера будем придерживаться простых запросов.
 +
 +Собираясь что-нибудь создать,​ надо сначала придумать,​ как это создать. Придумайте,​ как долна выглядеть карточка рецепта,​ и давайте создадим базу данных рецептов. Там, где я живу, рецепты выглядят по-разному:​ карточки 3 на 5, бумажки 8 на 10, надписи на платках,​ листовки из магази-нов,​ и даже ещё более стран-ные штуки. Хранят их в книгах,​ коробках,​ скоросшивателях,​ и в других странных местах. Но в одном они все похожи — это формат. Почти всегда вверху вы найдёте название рецепта,​ затем наверняка — количество порций и откуда этот рецепт взят. Дальше идёт список ингредиентов,​ ну, и в конце — инструкции,​ в каком порядке класть ингредиенты,​ сколько готовить,​ и всё такое. Вот этот общий формат и послужит основой для нашего проекта базы данных. Всю работу мы разобьём на два этапа. Сначала мы создадим базу данных,​ а потом — приложение (программу),​ которое будет читать и обновлять эту базу данных.
 +
 +К примеру,​ положим,​ у нас есть такой рецепт,​ как на иллюстрации справа.
 +Обратите внимание на формат,​ мы его уже обсудили. Создавая базу данных,​ мы, конечно,​ можем отвести отдельную запись под каждое поле в рецепте. Однако,​ работать с такой неуклюжей базой данных будет очень неудобно. Мы же возьмём описанную карточку рецепта в качестве шаблона. В одной таблице мы будем хранить заголовок рецепта и прочую общую информацию о рецепте. В другую таблицу мы поместим информацию о составе,​ и в третью - инструкции по приготовлению.
 +
 +Сначала убедимся,​ что у нас установлены SQLite и APSW. SQLite — это неболь-шая система управления базами данных,​ которая не требует выделенного сервера — идеальный выбор для нашего простого приложения. То, чему вы научитесь на этом приме-ре,​ потом можно будет использовать и в более крупных системах управле-ния базами данных,​ таких как MySQL и прочих. Другая хорошая черта SQLite — это то, что в ней мало типов данных — это Text (Текст),​ Numeric (Число),​ Blob (Неформатированные данные),​ и Integer Primary Key (Числовой главный ключ). Как вы уже знаете,​ типом Text может быть всё, что угодно. В нашем случае ингредиенты,​ инструкции и название рецепта будут текстом,​ даже если в них будут цифры. Тип Numeric хранит числа; они могут быть целыми,​ числами с плавающей запятой или рациональными. Blob хранит бинарные данные,​ например,​ картинки или что-нибудь другое. Особый случай — тип Integer Primary Key; система SQLite автоматически помещает в него целое значение и обеспечивает его уникальность,​ в дальнейшем это будет важно.
 +
 +APSW — это аббревиатура от «Another Python SQLite Wrapper» (Ещё одна реализация работы с SQLite в Python). APSW разработан для облегчения работы с SQLite в языке Python. Теперь давайте быстро пробежимся по языку запросов SQL.
 +
 +Чтобы получить запись из базы данных,​ используется запрос типа SELECT. Его формат такой:
 +
 +<​code>​SELECT [Что] ​
 +FROM [Из каких таблиц] ​
 +WHERE [Условия]
 +Прим.перев.:​
 + ​переводя с английского, ​
 +«ВЫБРАТЬ [Что] ИЗ [Откуда] ​
 +ГДЕ [Условия]».</​code>​
 +
 +Итак, чтобы получить все поля всех записей из таблицы рецептов Recipes, нужен запрос:​
 +
 +<​code>​SELECT * FROM Recipes</​code>​
 +
 +Если нужно выбрать только одну запись по уникальному ключу, нужно знать название поля ключа (в примере — pkID) и включить в запрос условие WHERE:
 +
 +<​code>​SELECT * FROM Recipes WHERE pkID = 2</​code>​
 +
 +Пока несложно,​ да? В общем-то,​ сильно упрощённый английский язык. Теперь,​ скажем,​ нам нужно получить по всем рецептам их названия и количество блюд. Как? Очень просто. Нужно включить названия нужных полей в запрос SELECT:
 +
 +
 +<​code>​SELECT name, servings FROM Recipes
 +</​code>​
 +
 +Чтобы добавить запись,​ нужно использовать запрос типа INSERT INTO. Его синтаксис:​
 +
 +<​code>​INSERT INTO [Имя таблицы] ​
 +(перечень полей) ​
 +VALUES (вставляемые значения)
 +(Прим. перев.:​
 + ​ВСТАВИТЬ В [Имя таблицы] ​
 +(перечень полей) ЗНАЧЕНИЯ ​
 +(вставляемые значения))</​code>​
 +
 +Так, чтобы вставить рецепт в таблицу рецептов,​ нужно использовать команду:​
 +
 +<​code>​INSERT INTO Recipes (name,​servings,​source) ​
 +VALUES ("​Лепёшки",​4,"​Грег"​)
 +(Прим.перев.: ​
 +перевод полей таблиы: ​
 +name — название, ​
 +servings — порций, ​
 +source — источник)</​code>​
 +
 +Чтобы удалить запись,​ используем:​
 +
 +<​code>​DELETE FROM Recipes WHERE pkID = 10</​code>​
 +
 +Для обновления записей можно использовать запрос типа UPDATE, но это мы оставим на потом.
 +
 +И снова о SELECT.
 +
 +У нас в базе данных три таблицы (Recipes, Instructions и Ingredients),​ которые связаны друг с другом через поля recipeID, которые указывают на поле pkID таблицы рецептов Recipes. Положим,​ нам нужны все инструкции к определённому рецепту. Можно сделать так:
 +
 +<​code>​SELECT Recipes.name, ​
 +Recipes.servings, ​
 +Recipes.source, ​
 +Instructions.Instructions ​
 +FROM Recipes ​
 +LEFT JOIN instructions ON 
 +(Recipes.pkid = Instructions.recipeid) ​
 +WHERE Recipes.pkid = 1
 +(Прим.перев.: ​
 +ВЫБРАТЬ Recipes.name, ​
 +Recipes.servings, ​
 +Recipes.source, ​
 +Instructions. Instructions ИЗ Recipes ​
 +ЛЕВОЕ СОЕДИНЕНИЕ instructions ​
 +ПО (Recipes.pkid = Instructions. recipeid) ​
 +ГДЕ Recipes.pkid = 1)</​code>​
 +
 +На самом деле, так много печатать необязательно. Можно использовать так называемые псевдонимы. Запрос,​ указанный выше, можно изменить так:
 +
 +SELECT r.name, r.servings, r.source, i.Instructions FROM Recipes r LEFT JOIN instructions i ON (r.pkid = i.recipeid) WHERE r.pkid = 1
 +
 +Так короче и легче читать. Теперь давайте напишем программку,​ которая создаст базу данных,​ создаст в ней таблицы,​ и заполнит их данными для примера,​ чтобы можно было начать работу. Конечно,​ МОЖНО включить эти функции в основную програм-му,​ но мы вынесем их в отдельную программу. Её нужно будет запустить всего один раз; попытка повторного запуска приведёт к ошибке на этапе создания таблиц. Опять же, можно было бы обраба-тывать эту ошибку конструк-цией try...catch,​ но это уж как-нибудь в другой раз.
 +
 +Начнём с импорта APSW.
 +
 +<​code>​import apsw</​code>​
 +
 +Дальше мы должны создать подключение к базе данных. Она будет располагаться в одной папке с нашим приложе-нием. Когда мы создаём соединение,​ SQLite проверяет существование базы данных. В этом случае она открывается,​ иначе просто создаётся пустая база данных. Получив соединение,​ нужно создать то, что называ-ется курсор (cursor) — это механизм работы с базой данных. Итак, запомним:​ нужны и соединение,​ и курсор. Создать их можно так:
 +
 +<​code>#​ Создаём/​открываем базу данных
 +
 +connection=apsw.Connection("​cookbook1.db3"​)
 +cursor=connection.cursor()</​code>​
 +
 +Ну вот, у нас есть и соедине-ние,​ и курсор. Теперь создадим таблицы. У нас будет три табли-цы:​ одна для общей информа-ции о рецепте,​ другая для списка ингредиентов,​ и третья — для инструкций. Разве нельзя объединить это всё в одну таблицу?​ Ну, конечно,​ можно. Только тогда со временем база данных разрастётся неимоверно из-за целой кучи повторяющих-ся данных.
 +
 +Структуру таблиц можно представить так, как на иллюстрации справа. Каждая колонка — это отдельная таблица.
 +
 +В каждой таблице будет поле pkID. Это — уникальный в рамках данной таблицы ключ. Очень важно, чтобы в таблице никогда не появлялось двух совершенно одинаковых записей. Ключ — это целое число, назначаемое системой автоматически. Можно ли без него? Да, но возникает риск создания двух одинаковых записей. Мы же кроме того будем использовать это поле в таблице Recipes в качестве ссылки,​ на которую будут ссылаться таблицы Ingredients и Instructions,​ чтобы определить,​ к какому рецепту относятся ингредиенты или инструкции.
 +
 +Для начала поместим в базу данных информацию;​ в таблице рецептов заполним название,​ источник и количество порций. Поле pkID будет назначено автоматически. Предположим,​ это будет первая наша запись в таблице,​ так что система назначит полю pkID значение 1. Это значение мы и используем как ссылку в других таблицах. Таблица инструкций очень простая:​ в ней хранится длинная текстовая строка для каждого рецепта,​ плюс своё поле pkID и ссылка на ключ в таблице рецептов. Таблица ингредиентов немного сложнее в том смысле,​ что для каждого ингредиента будет своё поле, включающее кроме названия ингредиента свой ключ pkID и ссылку на таблицу рецептов.
 +
 +Чтобы создать таблицу рецептов,​ определим переменную sql и назначим ей команду на создание таблицы:​
 +
 +<​code>​sql = '​CREATE TABLE Recipes  ​
 +(pkiD INTEGER PRIMARY KEY, 
 +name TEXT, servings TEXT, source TEXT)'</​code>​
 +
 +Затем скомандуем ASPW выполнить эту команду:​
 +
 +<​code>​cursor.execute(sql)</​code>​
 +
 +Теперь создадим другие таблицы:​
 +
 +<​code>​sql = '​CREATE TABLE Instructions ​
 +(pkID INTEGER PRIMARY KEY,
 + ​instructions TEXT, recipeID NUMERIC)'​
 +
 +cursor.execute(sql)
 +
 +sql = '​CREATE TABLE Ingredients (pkID INTEGER ​
 +PRIMARY KEY, ingredients TEXT, recipeID NUMERIC)'​
 +
 +cursor.execute(sql)</​code>​
 +
 +Теперь,​ когда таблицы созданы,​ используем запрос INSERT INTO, чтобы заполнить каждое поле нужной таблицы.
 +
 +Так как поле pkID будет назначено автоматически,​ мы не включаем его в список назначаемых полей. А раз уж мы знаем названия полей, их можно указывать в любом порядке,​ не обязательно в том порядке,​ как они создавались. Если мы не перепутаем назва-ния полей, всё будет работать отлично. Итак, команда вставки данных в таблицу рецептов превращается в
 +
 +<​code>​INSERT INTO Recipes (name, servings, source) ​
 +VALUES ("​Рис по-испански",​4,"​Грег Уолтерс"​)</​code>​
 +
 +Теперь нам надо узнать,​ какой же именно pkID только что был назначен новой записи в таблице рецептов. Сделать это очень просто запросом
 +
 +<​code>​SELECT last_insert_rowid()</​code>​
 +
 +Однако в таком виде эта команда не выдаст результата,​ которым можно воспользовать-ся. Нужно использовать сразу серию таких команд. Вот так:
 +
 +<​code>​sql = "​SELECT last_insert_rowid()"​
 +
 +cursor.execute(sql)
 +
 +for x in cursor.execute(sql):​
 + lastid = x[0]</​code>​
 +
 +Почему так? Потому что когда мы получаем данные от ASPW, он возвращает кортеж (tuple). Об этом мы пока не говорили. Если коротко,​ то кортеж — это как список,​ который нельзя изменить. Многие программисты почти не используют кортежи,​ другие используют очень часто, это дело вкуса. В последней строке мы извлекаем из кортежа первое помещённое туда значение. Цикл for используется,​ чтобы заполнить кортеж x возвращаемыми значениями. Пока понятно?​ Ладно, продолжаем...
 +
 +Далее, создадим запрос на вставку данных об инструкциях:​
 +
 +<​code>​sql = '​INSERT INTO Instructions (recipeID,
 +instructions) ​
 +VALUES( %s,"​Поджарить гамбургер. ​
 +Смешать остальные ингредиенты и залить. ​
 +Довести до кипения. Помешать. ​
 +Готовить на медленном огне. ​
 +Накрыть и готовить 20 минут или до впитывания жидкости."​)'​
 + % lastid
 +
 +cursor.execute(sql)</​code>​
 +
 +Обратите внимание,​ мы использовали подстановку переменной (%s), чтобы поместить pkID рецепта в текст запроса. Наконец,​ нам надо поместить каждый из ингредиентов в соответству-ющую таблицу. Я покажу на примере только одной записи:​
 +
 +<​code>​sql = '​INSERT INTO Ingredients (recipeID,​ingredients) ​
 +            VALUES ( %s,"1 чашка обработанного паром риса ​
 +                      (не варёного)"​)'​ % lastid
 +
 +cursor.execute(sql)</​code>​
 +
 +Пока всё было достаточно просто. В следующий раз сделаем кое-что посложнее.
 +
 +Если вам нужен полный исходный код этого примера,​ я выложил его на своём веб-сайте. Скачать его вы можете,​ посетив www.thedesignatedgeek.net(Код 7 части по ссылке Code for Article 7)
 +
 +В следующий раз мы исполь-зуем всё, что изучили в этом цикле статей,​ чтобы создать полноценное приложение с меню из нашей программы для рецептов. Оно сможет выводить список всех рецептов,​ просмат-ривать один выбранный рецепт,​ искать рецепт,​ добавлять и удалять рецепты.
 +
 +Я бы пока посоветовал вам почитать на досуге о языке SQL. В дальнейшем вам это пригодится.
 +
 +---------------------------------------
 +
 +<style center>
 +//​[[..:​33|К содержанию номера]]//​
 +
 +//​[[:​fullcircle|К архиву журналов]]//​
 +</​style>​
 +
 +{{tag>​howto Full_Circle}}