- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF117:google
Материал из Linuxformat.
- Hardcore Linux Проверь себя на крутом проекте для продвинутых пользователей
Содержание |
GAE: Создаем web-приложение
- С Google App Engine легко создавать масштабируемые приложения, не вникая в детали масштабирования. Дэн Фрост закинет программу в облака.
Google App Engine – платформа для разработки приложений в инфраструктуре Google. Как и другие масштабируемые платформы, она дает возможность разместить приложение на «облаке» (LXF108), не тратясь на содержание собственной серверной «фермы».
В отличие от других решений по облачным вычислениям, Google App Engine применяется только для создания web-приложений. Вы можете управлять web-страницами, хранить информацию и взаимодействовать с внешними web-серверами, но не имеете доступа к файлам, дискам и базам данных, как было бы в обычной среде.
Это может заставить вас изменить структуру приложения, но зато в вашем распоряжении будут очень мощные инструменты. Google App Engine поддерживает учетные записи Google, обработку изображений, огромные хранилища данных и взаимодействие с некоторыми службами Google посредством библиотеки Google Data Library.
Пока поддерживаются только приложения на языке Python. Если это не ваш любимый язык программирования – «следите за рекламой»: в будущем появятся и другие.
Работа с App Engine
Сначала поработаем локально, пользуясь сервером разработчика dev_appserver.py, который имитирует реальную среду. Предоставляемая SDK, она включает работающий сервер, хранилище данных, псевдоучетные записи пользователей и все необходимое для создания приложения. Когда ваш новый шедевр Web 2.0 будет готов, разверните его на серверах Google с помощью скрипта appcfg.py, который загрузит программу на вашу учетную запись в App Engine (создайте ее на сайте http://appengine.google.com).
Пора установить среду разработки. Сначала позаботьтесь о наличии установленного Python 2.5, после чего можно загрузить App Engine для вашей ОС с сайта http://code.google.com/appengine/downloads.html. В Linux, распакуйте архив и добавьте App Engine в переменную окружения $PATH:
export PATH=$PATH:/path/to/google_appengine/
Проверьте, что это работает, набрав dev_appserver.py в командной строке – вы должны увидеть обычную страницу справки. Затем создайте каталог для своего приложения:
mkdir ~/myapp/
Теперь создайте файл ключа для App Engine: app.yaml. Он указывает App Engine, в каком каталоге находится приложение и как обращаться с каждым его файлом. Заполните ~/myapp/app.yaml таким содержимым:
application: mydemoapp version: 1 runtime: python api_version: 1 handlers: - url: /.* script: main.py
Этот файл сообщает App Engine, что имя приложения – mydemoapp, а все запросы должны передаваться скрипту main.py. Для обработки любого URL-адреса можно задать любой скрипт и даже применить шаблоны для использования различных файлов:
url: /browse/(.*?)/ script: /listings/\1.py
Приведенная выше конфигурация передает все в скрипт main.py, поэтому создайте этот файл и добавьте в него вывод традиционного сообщения:
print “Hello, World”
Наше скромное приложение готово. Запустите сервер с помощью скрипта dev_appserver.py и откройте в браузере адрес http://localhost:8080.
dev_appserver.py ~/myapp/
В App Engine допустимо большинство стандартных выражений Python; исключения в основном касаются доступа к файловой системе. Попробуйте добавить методы и классы, создайте модуль, и вы увидите, что среда вам знакома.
Применение webapp
В состав App Engine входит MV-каркас webapp, позволяющий создавать хорошо структурированные приложения всего несколькими строками кода. Первая строка импортирует его, затем создается обработчик – это простой класс, унаследованный от webapp.RequestHandler:
from google.appengine.ext import webapp class ExampleApp(webapp.RequestHandler): def get(self): self.response.out.write(‘Hello, well structured world’)
В обработчике есть два важных метода – get() и post(). Первый вызывается для всех запросов HTTP GET, второй – для всех запросов HTTP POST. Следом за самим обработчиком запросов нужно зарегистрировать его в webapp и вызвать метод main() webapp:
application = webapp.WSGIApplication( [(‘/’, ExampleApp)], debug=True) def main(): run_wsgi_app(application) if __name__ == “__main__”: main()
Снова откройте в браузере URL приложения, и вы увидите весьма невыразительную строку текста. Давайте улучшим ее, переместив сообщение в шаблон – создайте файл index.html:
<html> <head><title>Hi there!</title></head> <body><h1>Hello from the template</h1></body> </html>
Затем подключите шаблон, изменив метод get():
def get(self): template_vars = {} self.response.out.write(template.render(path, template_vars))
Если вы хотите включить таблицы стилей, JavaScript, изображения или любые другие статические файлы, нужно предупредить об этом app.yaml. Добавьте в него следующие строки перед обработчиком url: /.*:
- url: /style static_dir: style
Затем создайте каталог style и файл app.css и включите их в index.html:
<link rel=“stylesheet” href=”/style/app.css” type=“text/css”>
Теперь можно добавить в CSS-файл какой-нибудь стиль – сделайте это сами.
Сохранение моделей
Масштабирование баз данных – сложная задача, но Google нашел несколько умных способов ее решения, применив новый подход к традиционным реляционным БД.
BigTable от Google – сердце системы хранения данных App Engine. Это означает, что можно создавать приложения, способные масштабироваться до миллионов пользователей и страниц. BigTable – это распределенная система хранения, созданная для управления «петабайтами данных на тысячах стандартных серверов» (http://labs.google.com/papers/bigtable.html), но начать работать с ней в App Engine очень просто. Все начинается с моделей, создание которых требует несколько строчек кода. Модели в App Engine являются классами-расширениями db.Model. Их свойства аналогичны свойствам полей в таблицах обычных баз данных. Вот пример простой модели:
class MyNote(db.Model): thenote = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True)
В свойство date автоматически записывается текущее время благодаря методу auto_now_add, свойство thenote – просто строка. Типы параметров включают логический (boolean), целые числа (integer), числа с плавающей точкой (float), двоичные данные (blob), почтовые сообщения (emails) и другие.
Подготовка никакая не нужна – просто создайте экземпляр класса, установите его свойства и вызовите метод put():
note = MyNote() note.thenote = ”Just a quick note” note.put()
В нашем примере мы начнем с создания модели для хранения комментариев и назовем ее Comment:
class Comment(db.Model): content = db.StringProperty(multiline=True) date = db.DateTimeProperty(auto_now_add=True) author = db.UserProperty()
Сделайте ведение журнала своей привычкой – добавьте ‘import logging’ в начало приложения и записывайте сообщения в журнал:
logging.info(”Something’s happening...”)
Файлы журналов можно просмотреть через панель инструментов – зайдите в пункт Logs и вникните в каждую подробность.
Теперь добавим в приложение немного данных, просмотрим их и установим им несколько интересных свойств. Чтобы добавить несколько комментариев, нужно создать форму, сохранить данные и затем отобразить их. Начнем с добавления формы в файл index.html:
<form action=”/” method=”post” accept-charset=”utf-8”> <input type=”hidden” name=”parent” value=”{{ comment.key }}” /> <textarea name=”comment”></textarea> <input type=”submit” value=”Add”></div> </form>
Затем добавим метод post() в контроллер.
def post(self): c = Comment() c.content = self.request.get(‘comment’) c.author = users.get_current_user() c.put() self.redirect(‘/’)
Для извлечения данных из хранилища используется GQL, и по большей части он похож на SQL. Вам нужно заменить содержимое метода get() вызовом метода GQL класса Comment, который затем передать в шаблон:
comments = Comment.gql(“ORDER BY date DESC“) template_vars = { ‘comments’: comments } self.response.out.write(template.render(‘index.html’,template_vars))
Наконец, можно пройтись по комментариям и отобразить их:
{% for comment in comments %} <p>{{ comment.content }}</p> {% endfor %}
Через Google Data Services можно получить данные Google для своих приложений. Начните с установки модуля gdata в каталог вашего приложения по инструкциям на http://code.google.com/appengine/docs/usinggdataservi-ces.html и затем импортируйте его командой import gdata.
Для ясности и простоты добавления нового функционала переместите строку с обработкой комментариев в другой файл. Замените вторую строку из последнего примера на { % include ‘comment.html’ %} и создайте файл comment.html со следующим содержимым:
<div class=“comment row1”> <p><strong�Posted on {{ comment.date }}</strong><br /> {{ comment.content }} </p> </div>
Использование пользователей
App Engine позволяет аутентифицировать пользователей по существующим учетным записям Google, и вашим посетителям нет нужды регистрироваться заново ради очередного web-приложения. Для всего этого используется пакет users:
from google.appengine.api import users
Теперь можно получить доступ к имени пользователя и его email-адресу с помощью функции users.get_current_user(). Если пользователь еще не вошел в систему, можно перенаправить его на форму входа:
if users.get_current_user(): user = get_current_user() self.response.out.write(“You are logged in as: “ + user. nickname()) else: self.redirect(users.create_login_url(“/home”))
Параметр /home – это URL, на который вы направляете пользователя после входа в систему.
В приложении обработки комментариев можно заставить пользователей регистрироваться, прежде чем начинать что-либо писать; перенаправим их на страницу входа:
if users.get_current_user(): comments = �omment.gql(“WHERE in_reply_to =:1 ORDER BY date DESC“, None) template_vars = { ‘comments’: comments } self.response.out.write(template.render(‘index.html’, template_vars)) else: message = (“<a href=’ %s’>Please login</a>.” %users. create_login_url(“/rels”)) self.response.out.write(message)
Обновите приложение в браузере, чтобы убедиться, что аутентификация работает.
Теперь, когда пользователи вошли в систему, сохраняйте авторов всех комментариев, добавив следующую строку в метод post() перед командой c.put():
c.author = users.get_current_user()
Чтобы вывести имя и e-mail автора комментария, добавьте следующие строки в файл comment.html:
<p><b>Posted by: <b> {% if comment.author.nickname %} {{ comment.author.nickname }} ({{ comment.author.email }}) {% else %} Anonymous {% endif %} on {{ comment.date }} </p>
Связанные сущности
Отношения между сущностями задаются внутри модели с помощью метода ReferenceProperty, который ссылается на другую модель, или SelfReferenceProperty, который ссылается на текущую. Связать одну модель с другой можно примерно так:
related_thing = db.ReferenceProperty(OtherThing)
Если вы хотите, чтобы пользователи могли отвечать на сообщения, оставленные на доске объявлений, у каждого комментария должен быть родитель _parent_, которого мы назовем in_reply_to. Объявите это свойство, добавив в модель �omment следующую строку:
in_reply_to = db.SelfReferenceProperty()
Теперь можно добавить форму для каждого существующего комментария:
<div> <div id=“comment-{{ comment.key }}” style=“display:none;”> <form action=“/rels” method=“post” acceptcharset=“utf-8”> <input type=“hidden” name=“parent” value=”{{ comment.key }}” /> <textarea name=“comment” rows=“7” cols=”30”></textarea> <input type=“submit” value=“Add”> </form> </div> <a href=“javascript:document.getElementById(‘comment-{{comment.key}}’).style.display=’block’;”>Add comment</a> </div>
…а затем изменить метод post(), чтобы сохранить его:
if self.request.get(‘parent’): key_name = self.request.get(‘parent’) p = db.get(db.Key(key_name)) c.in_reply_to = p else: c.in_reply_to = None
Значение ‘parent’ из HTML-формы передается в метод post(). Затем для поиска объекта в хранилище данных используется метод db.Key(). Если посмотреть исходный код HTML, то вы увидите, что ключи – это длинные строки, а не целые числа.
При попытке ответить на комментарий легко заметить, что ответы появляются внизу страницы, поэтому следующий шаг – это структурировать комментарии иерархически. Вам понадобится обновить GQL, добавить файл comment.html и добавить метод в модель Comment. Назовем его get_replies:
def get_replies(self): comments = Comment.gql(“where in_reply_to =:1 ORDER BY date DESC LIMIT 10”, self) return comments
Как и в любом приложении, работающем с базой данных, при увеличении объемов информации нужно добавить индексы. Для этого пригодится файл index.yaml, который будет создан за нас. Индексы требуются при сортировке, фильтрации по группам сущностей и более сложных запросах. Начните отсюда: http://code.google.com/appengine/docs/python/datastore/queriesandindexes.html.
Этот код возвращает все комментарии, являющиеся ответами на комментарий self. Комментариями верхнего уровня должны быть те, у которых поле in_reply_to не заполнено. Поэтому мы изменим GQL в методе get(), воспользовавшись константой Python None:
comments = Comment.gql(“WHERE in_reply_to =:1 ORDER BY date DESC“, None)
Если обновить страницу сейчас, вы увидите, что отображаются только комментарии верхнего уровня. Последний шаг – сделать comment.html иерархическим:
<p> {% for comment in comment.get_replies %} {% include ‘comment.html’ %} {% endfor %} </p>
Обновите страницу, чтобы увидеть вложенные комментарии. Кликните по ссылке Add Comment, чтобы ответить на любой из комментариев.
Выгрузка вашего приложения
Для выгрузки используется еще одна утилита, appcfg.py. Нужно лишь указать опции команды update и местоположение приложения.
~/appEngineProject/ $ appcfg.py update helloworld/ Loaded authentication cookies from /Users/you/.appcfg_cookies Scanning files on local disk. Initiating update. Email: some_user@gmail.com Password for some_user@gmail.com: Saving authentication cookies to /Users/you/.appcfg_cookies Cloning 21 application files. Uploading 5 files. Closing update. Uploading index definitions
У вас спросят параметры учетной записи Google, после чего приложение загрузится на серверы Google. LXF