Сегодня решил поразбираться с тем как устроен Google App Engine, а точнее хранилище данных в App Engine и попробовать написать на Python’е небольшое приложение, которое будет получать что-либо от Delphi-приложения и выводить на веб-страницу.
В качестве Delphi-приложения я взял свой Link Compressor и сделал для него небольшую удаленную БД в которой будут храниться все сжатые мной ссылки. При загрузке страницы веб-приложения App Engine будем выводить последние 10 сжатых ссылок с заголовками страниц.
Первое, что нам необходимо — это зарегистрировать новое приложение в своем аккаунте Goole App Engine. Для нового приложения необходимо задать идентификатор, который будет в дальнейшем использоваться в URL и заголовок приложения. После этого Вас попросят ввести номер сотового телефона на который Вам пришлют SMS с кодом активации. SMS отправляют 1 раз за всё время работы с App Engine — подтвердили себя и потом создавайте до 10 бесплатных приложений и спокойно работайте.
Инструментарий
Теперь определимся с инструментами.
Обязательно требуется скачать SDK Google App Engine, который собран в двух вариантах — для Java и Python.
App Engine поддерживает Python 2.5 поэтому скачиваем его с офф.сайта.
Вообще я использую Python 2.7. и пока каих-либо проблем в работе не замечал, а вот с Python 3.х App Engine работать не захотел.
В целом этих инструментов будет достаточно, чтобы начать работу, но после IDE Delphi мне как-то стало тоскливо смотреть на весьма аскетичный «родной» GUI Python’а, поэтому в довесок ко всему я скачал Eclipse и установил для него плагин PyDev — надо сказать работать стало намного удобнее и привычнее. А т.к. последняя версия Eclipse в довесок ко всему без проблем работает с GitHub’ом, то вообще стал доволен как слон :).
В итоге я получил для себя полностью удовлетворяющий моим нынешним потребностям инструмент работы — Eclipse + PyDev. PyDev прекрасно осведомлен о том, что такое App Engine — поэтому выгрузка приложения на сервер осуществляется буквально в два клика, Eclipse помогает не напортачить с написанием приложения — по ходу работы отмечает все ошибки, предупреждает о неиспользуемых переменных, расставляет правильно отступы и, по требованию, опять же в пару кликов, выгружает исходники в репозиторий на GitHub.
Кстати, было бы неплохо увидеть в будущих версиях RAD Studio «фишку» как в Eclipse по выявлению ошибок и предупреждений. Вот, например, как это выглядит:
Слева в редакторе по ходу написания кода появляются метки предупреждений и ошибок. Так в строке 1 мне говорят, что импортирован неиспользуемый модуль — надобно удалить либо что-нибудь из него использовать, на строке 9 я вообще ошибся — упустил пробелы, которые в Python’е играют роль операторных скобок, на 10 строке всё прекрасно, НО вместо 4 пробелов поставил 2 — не критично, но лучше исправить. И так далее. Конечно эта «фишка» не такая уж и важная, но всё-таки хотелось бы такую штуку в Delphi :). Но это всё мечты-мечты…
Инструменты определили — двигаемся дальше.
Пишем первое веб-приложение App Engine
Как я упоминал, использование инфраструктуры webapp в значительной степени облегчает нашу работу по созданию нового приложения, т.к. в том случае отпадает необходимость прописывать весь код приложения и можно сосредоточится на его создании функций приложения, а всю рутину возьмет на себя webapp.
Приложение webapp состоит из трех частей:
* один или несколько классов RequestHandler, обрабатывающих запросы и создающих ответы;
* экземпляр WSGIApplication, направляющий входящие запросы обработчикам в зависимости от URL;
* основная часть, выполняющая WSGIApplication с помощью CGI-адаптера.
Элементтарный пример того, как выглядит простое приложение webapp можно увидеть здесь.
Теперь определим, что будет делать наше приложение.
Приложение должно работать следующим образом:
1. Клиент (Delphi-приложение) отправляет POST-запрос на определенный URL. В запросе содержаться данные о ссылке — URL и анкор
2. Наше приложение берет данные клиента и заносит из в хранилище
3. При заходе на главную страницу веб-приложения пользователю показывается последние 10 ссылок
Все просто. Сразу оговорюсь — веб-приложение никаким образом не проверяет корректность данных клиента. Можно было бы написать соответствующие обработчики, но я пока этим заниматься не буду.
Пойдем по порядку. Вначале импортируем в наше приложение следующие модули и объекты:
from google.appengine.ext import webapp from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.ext import db import os from google.appengine.ext.webapp import template
Теперь пишем первый класс, который будем использовать для записи данных в хранилище, назовем его DataClass:
class DataClass(db.Model): link_url = db.StringProperty(multiline=True) #URL ссылки link_title = db.StringProperty(multiline=True) #Анкор date = db.DateProperty(auto_now_add=True) #Дата добавления в БД
Так, всего в 4 строки мы определили как будут выглядеть наши объекты в хранилище. Здесь:
db.StringProperty — строка длиной до 500 байт, допускающая наличие символов перевода строки.
db.DateProperty(auto_now_add=True) — дата. Значение полю присваивается автоматически при добавлении объекта в хранилище, а также автоматически и обновляется вместе с изменением объекта.
Следующий шаг — работа с хранилищем. Получим последние 10 записей из хранилища и выведем их на странице. Готовый класс выглядит следующим образом:
class MainPage(webapp.RequestHandler): def get(self): lclink_query = DataClass.all().order('-date') lclinks = lclink_query.fetch(10) template_values = {'urls': lclinks,} path = os.path.join(os.path.dirname(__file__), 'index.html') self.response.out.write(template.render(path, template_values))
В двух слова, что здесь делается.
lclink_query = DataClass.all().order('-date')
Получили объект Query со всеми данными, которые соответствуют модели DataClass и отсортированные по дате добавления.
lclinks = lclink_query.fetch(10)
Выполнили запрос, получив в lclinks, говоря привычными терминами, первый 10 записей.
template_values = {'urls': lclinks,}
Создали словарь значений для шаблон страницы результатов
path = os.path.join(os.path.dirname(__file__), 'index.html')
Определили путь до шаблона страницы. В данном случае шаблон находится в корне.
self.response.out.write(template.render(path, template_values))
«Нарисовали» пользователю страничку с результатами.
Заметьте — при работе с данными из хранилища я нигде в явном виде не указывал текст запроса, нигде не выполнял коннект к БД и прочие привычные операции. Все эти операции выполняются в классах и объектах, которые разработаны для нас в Google. Даже при выводе результатов пользователю нам не потребовалось писать слишком много — 1 строка. А шаблон с HTML-кодом лежит отдельно. Кстати шаблон тоже выглядит достаточно просто:
Последние ссылки
-
- {% for link in urls %}
- {{ link.date }}: {{ link.link_title }}
{% endfor %}
Обратите внимание — внутри шаблона присутствует цикл for..in, которые перебирает все значения из словаря (в нашем случае 10 штук) и «рисует» список ссылок.
И наконец, переходим к третьей части нашего приложения — получение данных от клиента и запись их в хранилище. Как уже понятно из представленных выше фрагментов кода — этот также будет достаточно прост и лаконичен. Выглядит он следующим образом:
class AddLinks(webapp.RequestHandler): def post(self): SingleLink = DataClass() SingleLink.link_url = self.request.get('lnk_url') SingleLink.link_title =self.request.get('lnk_text') SingleLink.put() self.redirect('/')
Здесь мы обрабатываем только POST-запросы. При желании можно тут же написать и обработчик GET-ов. Итак, что мы тут наделали.
SingleLink.link_url = self.request.get('lnk_url')
Получили из запроса пользователя данные по URL’у. На следующей строке — также само получили и анкор. А дальше:
SingleLink.put() self.redirect('/')
Записали данные в хранилище и перенаправили клиента на главную страницу. Опять же никакой суеты с текстами запросов и пр. Все достаточно внятно и ясно даже для человека впервые увидевшего код. Кстати, если очень уж хочется написать свой запрос к хранилищу, то нет проблем — можно воспользоваться GQL — язык запросов к хранилищу данных Google, очень напоминающий MySQL.
Теперь нам остается мелочь — определить URL’ы и сопоставить с ними обработчики нашего приложения. Делается это так:
application = webapp.WSGIApplication( [('/', MainPage), ('/sign', AddLinks)], debug=True) def main(): run_wsgi_app(application) if __name__ == "__main__": main()
Здесь мы сопоставили с двумя URL’ами приложения два обработчика. Так, при загрузке главной страницы приложения сработает обработчик MainPage, который выведет пользователю 10 ссылок, а при GET-запросе на /sign ничего не произойдет, так как в AddLinks мы не определяли ничего для GET, а вот POST-запрос инициирует запись данных в хранилище. При этом т.к. мы не использовали никаких проверок данных запроса, то в случае отправки на URL даже пустого документа запись всё равно произойдет — в хранилище попадет объект DataClass с одним определенным значением Date — т.к. оно заполняется автоматически.
Теперь осталась всего одна небольшая, но очень важная для App Engine деталь — создать файл app.yaml — файл конфигурации, который помимо прочей служебной информации определяет также какие сценарии обработчиков для каких URL’ов использовать. Создаем пустой файл и записываем туда следующие строки:
application: myapp version: 1 runtime: python api_version: 1 handlers: - url: /.* script: script.py
Здесь стоит обратить внимание на следующие детали.
Во-первых application в файле должен совпадать с названием Вашего зарегистрированного приложения. Т.е. в данном случае я написал файл конфигурации для веб-приложения доступ к которому осуществляется по URL http://myapp.appspot.com/
Во-вторых, version. С помощью этой опции можно создавать различные версии своего приложения и, в случае необходимости, делать откат на более старые версии.
И, наконец, в представленном файле я «объяснил» серверу, что вне зависимости от того какой URL будет набран всё будет обрабатываться с помощью script.py, в котором и определены все наши классы.
Теперь закачиваем приложение на сервер. Для этого необходимо в SDK Google App Engine нажать кнопку Deploy и ввести логин и пароль к аккаунту. Я же в Eclipse выбираю опцию PyDev : Google App Engine — Upload.
На этом работу с Python можно считать законченной. Переходим в Delphi.
Delphi-приложение для отправки данных
Собственно, наш мини-клиент будет выполнять всего одну функцию — выполнять POST-запрос к URL приложения Google App Engine.
Что у нас есть на входе?
1. Целевой URL, который, судя по данным app.yaml и webapp.WSGIApplication, выглядит как http://myapp.appspot.com/sign
2. Есть необходимые данные для составления запроса, а именно запрос должен содержать два параметра: lnk_url и lnk_text.
Этого нам будет вполне достаточно для работы. Открываем Delphi, создаем новое приложение и укладываем на форму компоненты как показано на рисунке:
В Memo1 будем выводить, в случае успешного запроса, содержание главной страницы.
Теперь пишем обработчик кнопки. Для работы с HTTP я, как уже стало обычным, использую библиотеку Synapse:
procedure TForm6.Button1Click(Sender: TObject); const cParams = 'lnk_url=%s&lnk_text=%s'; var ParamStr: AnsiString; begin ParamStr:=Format(cParams,[EncodeURLElement(AnsiToUTF8(Edit1.Text)), EncodeURLElement(AnsiToUTF8(Edit2.Text))]); with THTTPSend.Create do begin Document.Write(Pointer(ParamStr)^, Length(ParamStr)*SizeOf(AnsiChar)); Document.Seek(0,soBeginning); MimeType:='application/x-www-form-urlencoded'; if HTTPMethod('POST','http://myapp.appspot.com/sign') then begin HTTPMethod('GET','http://myapp.appspot.com/'); Memo1.Lines.LoadFromStream(Document); end else Memo1.Lines.Add(ResultString); end; end;
По большому счёты, если Вы хотя бы 1 раз в жизни работали с веб-формами в Delphi, то этот код для Вас должен выглядеть абсолютно обычно — мы просто отправили данные формы на сервер. Ничего сверхъестественного.
Теперь можно зайти на главную страницу приложения и посмотреть как выглядит наше импровизированное веб-приложение.
Конечно до серьезного этому приложению ещё далеко — это просто небольшая демонстрация того как можно организовать работы с данными.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Понравилась статья. Интересно.
Очень интересно, продолжайте. :)
Продолжу :)
может не туда смотрю, а какой адрес страницы с этими 10 первыми записями?
и еще, сам нулевый в питоне и в яве. Что лучше посоветуете для «освоения» Google App Engine?
Страница http://linkcompressor.appspot.com — нижняя часть отведена под ссылки. В плане совета чего-либо я, видимо, советчик плохой, т.к. уже говорил, что Ява меня очень напрягает :) Мне больше понравился Python — как-то лекго пошло обучение после Delphi. А по поводу App Engine могу сказать, что информации, что по Яве, что по питону в доках — валом.