уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Сегодня решил поразбираться с тем как устроен 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-кодом лежит отдельно. Кстати шаблон тоже выглядит достаточно просто:

Последние ссылки

    1. {% for link in urls %}
    2. {{ 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 и др.
купить книгу delphi на ЛитРес
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
5 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Dmitry
29/08/2010 14:06

Понравилась статья. Интересно.

Yuriy
Yuriy
30/08/2010 20:44

Очень интересно, продолжайте. :)

Oleg
Oleg
14/09/2010 04:33

может не туда смотрю, а какой адрес страницы с этими 10 первыми записями?

и еще, сам нулевый в питоне и в яве. Что лучше посоветуете для «освоения» Google App Engine?