Окружение для разработки на aiorest (asyncio) + angular.js
В этой статье мы соберём вместе aiorest + jinja2 + angular.js + gulp.js + bower.js + nginx. В результате мы получим: авто-перезапуск сервера при изменении python-кода и jinja-шаблонов сборка, минификация и автоматическая пересборка при изменении клиентского js-кода Начнём с главного — как пользоваться, а затем я подробнее опишу некоторые моменты реализации. Если вам легче читать код — вот ссылка на репу.Установка $ git clone git@github.com: imbolc/aiorest-angular-template.git $ cd aiorest-angular-template $ sudo npm install -g gulp bower $ npm install $ bower install $ ./bin/buildenv.py $ sudo ./bin/configure_nginx.py Добавляем в /etc/hosts: 127.0.1.1 aio-angular.l.com И запускаем сервер разработчика: $ gulp Готово, теперь можно открывать в браузере: http://aio-angular.l.com/Использование Просто добавляйте обработчики в handlers.py, урлы — в urls.py, а клиентский код раскладывайте в папке client в удобной для вас структуре.Конфигурация aiorest не умеет ничего, кроме как отдавать json, поэтому, для отдачи статики мы будем использовать nginx, шаблон его конфига как и другие настройки находится в папке cfg. Там же находится файлик с питон-зависимостями pipreq.txt и конфиг для питон-части приложения __init__.py + local.py (последний в джанга-стайл перезаписывает специфичную для текущего сервера конфигурацию первого).Хранить конфигурацию лучше в одном месте, поэтому клиентский конфиг мы так же генерируем на сервере, а потом подсовываем ангуляру в виде value (templates/client_config.js):
angular.module ('app').value ('cfg', << config|tojson|safe >>); Сборка js Мы будем пользоваться мудростью этой статьи, которую я всячески рекомендую к прочтению: Real-World Best Practices for Building Angular.js Apps without Browserify or Require.js. Вкратце: require.js для ангуляра не подходит, browserify — подходит, но нам не нужна большая часть его функционала потому, что в ангуляре уже есть модули.Итак, будем использовать gulp.js — современную замену bower.js, он не использует промежуточных файлов, имеет более приятный синтаксис описания задач и уже аналогичную кучу готовых плагинов. Смотрим в gulpfile.js:
gulp.task ('js', function () { gulp.src (['client/app.js', 'client/**/*.js']) .pipe (plumber ()) .pipe (sourcemaps.init ()) .pipe (concat ('app.js')) .pipe (ngAnnotate ()) .pipe (uglify ()) .pipe (sourcemaps.write ()) .pipe (gulp.dest ('./static/build')); }); sourcemaps.init + sourcemaps.write добавляют в сборку source map который позволяет в консоли браузера видеть ошибки и лог-сообщения с нумерацией строк исходных файлов concat — копирует все файлы в один ngAnnotate — нельзя просто так взять и минифицировать ангуляр-код: Dependency Annotation uglify — минификация plumber — перехватывает ошибки в ходе всего этого, чем не даёт падать авто-персборке Авто-перезапуск сервера gulp.task ('dev_server', shell.task ([ ('nodemon ./app.py --exec «var/env/bin/python»' + ' --ext «py html» --ignore «static»') ])); Дада, nodemon умеет перезапускать не только node.js:)Серверный код Приложение начинается в app.py: import asyncio import aiorest import cfg import lib.logging import prepare_static import urls lib.logging.setup (cfg.LOG_FILE) prepare_static.client_config (urls=urls.CLIENT_URLS) prepare_static.render_html ('index.html') server = aiorest.RESTServer (hostname='127.0.0.1') for url in urls.SERVER_URLS: server.add_url (*url) loop = asyncio.get_event_loop () loop.run_until_complete (loop.create_server ( server.make_handler, '127.0.0.1', cfg.PORT)) try: loop.run_forever () except KeyboardInterrupt: pass Здесь мы настраиваем логирование, подготавливаем клиентский конфиг описанный выше. Затем рендерим пока единственный шаблон т.к. в нём есть некоторая логика: <% if cfg.DEBUG %> <% else %> <% endif %> И да, мы переопределили обозначения управляющих конструкций в jinja2 ({{ }} => << >>, {% %} => <% %>), т.к. ангуляр использует такие же.Далее мы создаём aiorest-сервер, добавляем урлы из файлика urls.py:
import handlers SERVER_URLS = [ ('GET', '/hello', handlers.hello), ('GET', '/hello/{name}', handlers.hello), ] CLIENT_URLS = { 'hello': '/api/hello/: name', } CLIENT_URLS — это аякс урлы которые мы будем использовать в браузерном кодe. А хендлеры для серверных урлов находятся в файлике handlers.py: def hello (name='world'): message = 'Hello, {}!'.format (name) return {'message': message} Angular-код Описывать hello world на ангуляре пожалуй не буду :) Единственное, обращу внимание на использование клиентского конфига, который мы готовили выше (client/hello/hello.svc.js): angular.module ('app') .service ('HelloSvc', function ($resource, cfg) { this.fetchGreeting = function () { return $resource (cfg.urls.hello).get.apply (this, arguments); }; });