Debian, Dojo, Django, Python

Делюсь опытом в описанных технологиях. Блог в первую очередь выполняет роль памяток для меня самого.

Об этом блоге

Меня зовут Дунаевский Максим, и я программист-самоучка. Чем я пользуюсь:

  • EMACS
  • Django
  • Django Rest Framework
  • bower
  • Dojo
  • Debian Linux
  • Windows 10

Угораю по литературному программированию, книге "Шаблоны проектирования", по статическим анализаторам, autopep8 и isort, а так же интересным задачам. В этот блог пишу только когда есть время.

Несколько слов о Dojo Framework

Немного о Dojo Framework

Введение

Dojo Dramework - проект, ведущий свою историю с 2004 года. Причиной, побудившей авторов создать его, стала политика фирмы, где они тогда работали: Sencha хотела денег, и в общем-то, цели своей достигла, на их странице можно полюбоваться совершенно нескромными ценами на ExtJS, они же хотели сделать библиотеку бесплатной

Так и не решив проблему мирным путём, группа энтузиастов откололась, чтобы создать свой собственный фреймворк, что им очень даже удалось.

Обзор возможностей

"Из коробки" Dojo содержит практически весь тот функционал, что мы можем найти в jQuery. Правда, вместо $() там используется dojo.query(), но суть не в этом - это лишь малая часть того, что Dojo на самом деле умеет.

Асинхронная подгрузка модулей

Последние 2 года я читаю о том, что в Angular 2 будет реализована асинхронная подгрузка модулей. В Dojo эта фича была реализована в 2009, но не позиционировалась как "серебрянная пуля", в отличие от. Скажу даже больше, это самый что ни на есть базовый функционал. Всё приложение не просто можно, а даже нужно разбивать на маленькие модули. Люди, знакомые с require.js, оценят написанный ниже код:

require([
      "dojo",
      "dojo/domReady!"
], function(
    dojo
){
    dojo.query("div.centered").style({
      color: "orange",
      textWeight: "bold"
    });
});

Классическое наследование

Стоян Стефанов в своей книге "JavaScript. Шаблоны" выразил мысль о том, что для подготовленного программиста наследование через прототип, используемое в JS, является более мощным, чем классическое (через классы). Не могу с ним согласиться, поскольку сама концепция является довольно спорной.

Существует множество способов обойти прототипное наследование JavaScript, и один из них предлагает Dojo. И не просто предлагает, а так же реализует концепцию множественного наследования, когда результирующий класс получает все свойства и методы своих родителей. Для объявления класса в Dojo имеется метод declare(). С его помощью можно очень легко создать новый "класс" (не в том смысле, в каком мы понимаем его говоря о языках с чистым классическим наследованием, например, C++ или Python, а лишь его эмуляцию).

Именно данный метод Dojo предлагает использовать для создания виджетов, о чём сказано ниже.

Dijit и Dojox

Dijit

Dijit представляет собой готовую библиотеку виджетов, входящую в официальную поставку Dojo. В комплекте идёт несколько тем и базовые контролы, необходимые для создания Rich Interface Applications - кнопки, меню, диалоговые окна (опять эмуляция, ясное дело), поля выбора, деревья, списки, панели... Лучше посмотрите сами вот здесь.

Можно создавать свои собственные компоненты (виджеты) на основе имеющихся. Есть даже базовые классы - _BaseWidget, _TemplatedMixin и другие. Да, здесь нет директив из мира Angular, и контроллеров тоже. И сервисов нет, о, Боже, я в аду! Если нам нужно использовать какой-то виджет, мы пишем в свойстве элемента DOM, например, data-dojo-type="dijit/form/Button", и получаем кнопку. Соответственно, нет здесь и проблем с приоритетом директив, и возни с transclude, контролллерами и прочими столь милыми сердцу фанатов Angular надстройками над языком, которые ко второй версии весело отомрут, будучи заменены Web-компонентами.

Dojox

Как я понял, это набор не входящих в официальную поставку виджетов. Авторы могут поддерживать, а могут и не поддерживать их. Исправление ошибок? Новые фичи? Какой-то Road Map? Всё на совести автора, никто ничего не обещает. Однако, библиотека и набор возможностей впечатляют.

Причины непопулярноcти Dojo

Причин множество, я остановлюсь на тех, которые кажутся мне наиболее явными и значительными. Отсортированы в порядке всплытия в памяти.

Скудная документация

Документация к Dojo не просто бедная, а очень бедная. Некоторые статьи были написаны ещё в нулевых, и с тех пор ни разу серьёзно не перерабатывались. Литература? Самое свежее, что я видел - книга 2009 года. От жизни отстала очень сильно.

Высокий порог вхождения

Angular не может похвастать доброжелательностью к новичкам, однако, даже там, сев вечером с кружкой чая ближе к ночи уже можно сделать что-то более-менее работающее. В случае с Dojo ситуация совершенно иная. Фреймворк требует долгого, настойчивого изучения. Используемые в нём подходы серьёзно отличаются от тех, к которым привыкли пользователи Angular и jQuery.

Слабый пиар

В отличие от Google, пихающего свой Angular буквально везде, IBM - один из основных спонсоров проекта - практически никак его не продвигает. Попробуйте сами найти на YouTube какой-нибудь dojoConf или вебинар по новым возможностям. Может быть, у вас получится найти how-to или разбор сложного примера? Дайте мне ссылку! В основном всё, что я находил, описывается следующим образом: "Вот смотрите, числа обладают свойствами коммутативности. 2+2=4. Зная это, не трудно доказать теорему Ферма". Другими словами, пропущен средний уровень, порой даже складывается впечатление, что спецов, которые могут написать высококачественную статью про Dojo и имеют на это время, свободное от загребания бабла, попросту не существует.

Итоги

Так стоит ли тратить время на этот фреймворк?

Сейчас я не готов ответить на этот вопрос однозначно. Свой новый проект я начал писать на нём, и пока дело не сильно продвинулось. Однако, как мне кажется, Dojo именно тот проект, на который стоит равняться. Многие идеи, которые первыми появились именно в нём, впоследствии были заимствованы другими фреймворками. Исходный код и сама структура проекта (а так же виджетов Dijit) являются хорошим образцом продуманного, профессионального подхода. Стиль, модульность, комментарии - всё это выполнено блестяще.

CSRF-токены и CBV, требующие авторизации

Всё, что нужно знать о CSRF-токенах в Django

Всё Middleware оставлены по-умолчанию, т.е. в settings.py никаких изменений не вносилось.

Ниже идёт код, который позволяет прописать в шаблон Cookies с CSRF-токеном:

from django.core.context_processors import csrf
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.views.generic import View


class Index(View):

    def get(self, request):
        context = {}
        context.update(csrf(request))
        return render_to_response(
            'index.html',
            RequestContext(request, context)
        )

В сам шаблон нужно не забыть включить одну важную строку:

{% csrf_token %}

Без этой строки печенька покрошена на страницу НЕ БУДЕТ. Не знаю, почему, просто вот такой интересный факт.

Миксин для CBV (Class Based Views), которым нужна авторизация:

from os import path

from django.contrib.auth.decorators import login_required
from django.core.context_processors import csrf
from django.shortcuts import render_to_response
from django.template.context import RequestContext
from django.views.generic import View


class LoginRequiredMixin(object):
    """Собственно примесь """

    @classmethod
    def as_view(cls, **initkwargs):
        view = super(LoginRequiredMixin, cls).as_view(**initkwargs)
        return login_required(view, login_url='/login/')


class ProfileView(LoginRequiredMixin, View):

    def get(self, request, *args, **kwargs):
        c = {}
        c.update(csrf(request))

        user = request.user

        template_path = path.join(
            'admin',
            'index.html',
        )

        return render_to_response(
            template_path,
            RequestContext(request, c)
        )

EMACS для Python

Введение

Решил наконец написать статью о том, как настроить EMACS для удобной работы с Python'ом. Ниже список рассматриваемых вопросов:

  • Разбиение конфигурации на части
  • Пакеты
  • Ergoemacs
  • Anaconda-mode

Разбиение конфигурации на части

Долгое время мой .emacs представлял собой всего один файл. Естественно, править его было не очень удобно. С течением времени он рос и становился толще, наконец, в определённый момент поддерживать его стало совершенно невозможно. К счастью, я подсмотрел на StackOverflow, как можно разбить его на несколько маленьких частей.

Для подгрузки параметров из других файлов нужно в .emacs написать следующее:

;;; package --- Summary
;;; Commentary:
;;; Main EMACS settings file, load settings from parts.

;;; Code:

(load "~/.emacs.d/fonts.el")
(load "~/.emacs.d/packages.el")
(load "~/.emacs.d/ergoemacs.el")
(load "~/.emacs.d/personal.el");
(load "~/.emacs.d/keyboard.el")
(load "~/.emacs.d/variables.el")
(load "~/.emacs.d/flycheck.el")
(load "~/.emacs.d/rust.el")
(load "~/.emacs.d/python.el")
(load "~/.emacs.d/web.el")

;;; .emacs ends here

Здесь load - функция, выполняющая загрузку указанного файла. Сам файл находится в каталоге .emacs.d/ и называется fonts.el. Туда я положил настройки шрифтов. Затем идёт подгрузка файла, отвечающего за список пакетов. При необходимости недостающие пакеты будут установлены из MELPA, MELPA-STABLE или другого из указанных репозиториев, впрочем, об этом я уже писал ранее.

Я не буду давать рекомендаций по поводу того, на сколько частей нужно разбивать конфигурацию, как эти части называть и так далее, каждый решает сам, потому что, например, несколько дней назад я не видел необходимости выносить настройки шрифтов отдельно, а сейчас решил, что так будет лучше. Как видите, за настройки для разработки на Python'е отвечает файл .emacs.d/python.el, в него я поместил все нужные параметры EMACS'а.

Пакеты

Как я уже говорил, автоматическую установку пакетов я описывал в другой статье, ниже перечислю лишь те пакеты, которые понадобятся, а так же дам краткое описание того, для чего они нужны.

anaconda-mode

Данный пакет служит для поддержки функциональности IDE при работе с файлами Python'а, как то быстрый переход к декларациям, вывод документации и т.д.

company

Данный пакет рекомендуется как более современная замена пакету auto-complete. Ничего не имею против последнего, но лично мне использовать указанный приятнее.

company-anaconda

Автодополнение в режиме anaconda-mode, без этого подсказки работать не будут.

ergoemacs-mode

В общем-то, не скажу, что этот пакет жизненно необходим, но он даёт необходимые удобства, например, упрощает работу с командами, заменяя стандартные хоткеи EMACS'а на свои, более близкие к тем, которыми пользуются нормальные люди, например, комбинации Ctrl+C и Ctrl+V, а так же многие другие начинают работать так, как это принято в подавляющем большинстве современных программ, т.е. копируют и вставляют текст. Подробнее можно почитать на сайте проекта.

flycheck, flycheck-pos-tip

Стандарт де-факто для проверки синтаксиса. Какой именно синтаксис и чем будет проверяться - настраивается. По умолчанию для Python'а используются flake8, pylint и pycompile. Естественно, чтобы данные средства работали, они должны присутствовать в системе.

helm

Система подсказок. Допустим, нажали Вы Alt+X, начинаете вводить lis, а он сразу выдаёт отфильтрованные команды, в которых присутствует данная строка. Можно стрелками выбрать нужную строку и нажать Enter. Умеет подсказывать не только команды, но так же имена файлов, виртуальные окружения и многое другое, легко расширяется. Практически незаменимое средство.

neotree

Пакет просто выводит дерево файлов слева. Можно забиндить отображение / скрытие на горячую клавишу, работать станет в разы удобнее.

python-mode

Базовый пакет для поддержки EMACS'ом Python'а.

py-autopep8

Пакет позволяет применить autopep8 - средство для автоформатирования кода по стандартам. Можно настроить на автоформат кода при сохранении файла. Удобно, хотя в старых проектах, где код писан ногами, порождает тонны баттхёрта у тех, кто будет делать ревью вашего кода.

py-isort

Сортирует импортированные файлы. Можно передать разные настройки. Лично я предпочитаю импорт 1-1, т.е. одна строка - один модуль или класс. Рекомендую, т.к. в этом случае с помощью flycheck будет проще удалять неиспользованные модули - выделил строку, нажал Backspace, сохранил.

pyvenv, virtualenvwrapper

Позволяет EMACS'у видеть виртуальные окружения и нормально с ними работать.

pip-requirements

Небольшой пакет для удобного редактирования файла зависимостей. Если кто не в курсе - файл REQUIREMENTS создаётся в корне Python-проекта и позволяет в одну команду установить все нужные для работы зависимости:

pip install -r REQUIREMENTS -U

В чём плюс данного пакета? Он выдаёт подсказки по именам, подгружая список с PyPi - главного всемирного рассадника питонячьих пакетов.

Установка через pip (от имени root)

pip install autopep8 flake8 isort pylint -U

Ergoemacs

Данный пакет довольно активно развивается и предоставляет кучу удобств. Вся его конфигурация у меня вынесена в отдельный файл, и чтобы далеко не ходить, публикую его ниже:

;;; Package --- Summary
;;; Comment:
;;; Settings for ergoemacs-mode

;;; Code:

(require 'ergoemacs-mode)

;;; Ergoemacs
(setq ergoemacs-theme nil)
(setq ergoemacs-keyboard-layout "us")
(setq ergoemacs-ini-mode t)
(setq ergoemacs-use-menus t)
(setq ergoemacs-smart-paste nil)
(setq ergoemacs-ctl-c-or-ctl-x-delay 0.3)
(ergoemacs-mode 1)

;;; ergoemacs.el ends here

Anaconda-mode

Тут речь не только об anaconda-mode, ниже приводится весь файл настроек для Python. Надеюсь, комментарии помогут в понимании данных настроек

;;; Package --- Summary
;;; Commentary:
;;; Settings for Python

;;; Code:

;;; Импорт необходимых модулей
(require 'py-autopep8)
(require 'py-isort)
(require 'pip-requirements-mode)

;;; Псевдоним для команды pyvenv-workon, пользователи virtualenv оценят удобство
(defalias 'workon 'pyvenv-workon)

;;; Автоматически загружать модуль python-mode, писать в статус баре "Python-mode"
(autoload 'python-mode "python-mode" "Python mode." t)

;;; Применять python-mode для файлов с расширением .py
;;; Использовать интерпретатор python для python-mode
(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
(add-to-list 'interpreter-mode-alist '("python" . python-mode))

(add-to-list 'auto-mode-alist '("\\REQUIREMENTS" . pip-requirements-mode))
(add-to-list 'auto-mode-alist '("\\REQUIREMENTS.txt" . pip-requirements-mode))
(add-to-list 'auto-mode-alist '("\\requirements" . pip-requirements-mode))
(add-to-list 'auto-mode-alist '("\\requirements.txt" . pip-requirements-mode))

;;; Подсвечивать строки, которые обычно используются при отладке
(defun annotate-pdb()
  (interactive)
  (highlight-lines-matching-regexp "import ipdb")
  (highlight-lines-matching-regexp "import pdb")
  (highlight-lines-matching-regexp "set_trace()")
  (highlight-phrase "TODO")
  (highlight-regexp "FIXME")
  (highlight-regexp "BUG")
  )

;;; В Python-mode автоматически включать anaconda-mode, pdb и
;;; автоматически применять autopep8 при сохранении файла
(add-hook 'python-mode-hook 'anaconda-mode)
(add-hook 'python-mode-hook 'annotate-pdb)
(add-hook 'python-mode-hook 'py-autopep8-enable-on-save)

;;; Перед сохранением так же отсортировать импортированные модули.
;;; Правило сортировки: одна строка - один модуль
(add-hook 'before-save-hook 'py-isort-before-save)
(setq py-isort-options '("-sl"))

;;; python.el ends here

Теперь следует перезапустить EMACS, чтобы он подхватил настройки, и начать работать. Ниже приводится вид моего EMACS'а, когда я пишу на Python.

Конфиг на GitHub

Ошибка "memmove does not exist on this platform"

Который раз вижу вот эту ошибку:

extensions/expat/lib/xmlparse.c:75:2: error: #error memmove does not exist on this platform, nor is a substitute available

Решение очень легко гуглится на StackOverflow, но для себя я решил сохранить, чтобы далеко не ходить. Проблема заключается в том, что в одном из заголовочных файлов Python 2.7 не определена константа HAVE_MEMMOVE

Решение

  • Открыть файл /usr/include/python2.7/pyconfig.h
  • Добавить в его конец строку

    #define HAVE_MEMMOVE 1

Послесловие: хватит уже пользоваться Python 2.7, Гвидо не для того старался с тройкой.

md-autocomplete со значением по умолчанию

Задача

С помощью Angular Material создать autocomplete со значением по-умолчанию. Статья навеяна вопросом со StackOverflow.

Самому как раз сегодня понадобилось решить эту же задачу. Результат можно посмотреть здесь.

Решение

HTML-разметка

<body ng-app="app" flex layout="column" layout-margin ng-controller="Main">
  <md-content layout="column" class="md-whiteframe-z1" layout-margin>
    <md-toolbar>
      <div class="md-toolbar-tools">
        <h3>Form</h3>
      </div>
    </md-toolbar>
    <md-content class="md-whiteframe-z1">
      <div class="md-padding">
        <md-input-container>
          <label for="firstname">First name</label>
          <input type="text" name="firstname" ng-model="user.firstname" />
        </md-input-container>
        <md-input-container>
          <label for="lastname">Last name</label>
          <input type="text" name="lastname" ng-model="user.lastname" />
        </md-input-container>
        <md-autocomplete md-selected-item="user.group" md-items="item in loadGroups(filterText)" md-item-text="item.title" md-search-text="filterText">
          <md-item-template>{{ item.title }}</md-item-template>
          <md-not-found>No items.</md-not-found>
        </md-autocomplete>
      </div>
    </md-content>
  </md-content>
  <md-content class="md-whiteframe-z1" layout-margin>
    <md-toolbar>
      <div class="md-toolbar-tools">
        <h3>Model as JSON</h3>
      </div>
    </md-toolbar>
    <md-content class="md-padding">
      <p>
        {{ user | json }}
      </p>
    </md-content>
  </md-content>
</body>

JavaScript

(function(A) {
  "use strict";

  var app = A.module('app', ['ngMaterial']);

  function main(
    $q,
    $scope,
    $timeout
  ) {
    $timeout(function() {
      $scope.user = {
        firstname: "Maxim",
        lastname: "Dunaevsky",
        group: {
          id: 1,
          title: "Administrator"
        }
      };
    }, 500);

    $scope.loadGroups = function(filterText) {
      var d = $q.defer(),
        allItems = [{
          id: 1,
          title: 'Administrator'
        }, {
          id: 2,
          title: 'Manager'
        }, {
          id: 3,
          title: 'Moderator'
        }, {
          id: 4,
          title: 'VIP-User'
        }, {
          id: 5,
          title: 'Standard user'
        }];

      $timeout(function() {
        var items = [];

        A.forEach(allItems, function(item) {
          if (item.title.indexOf(filterText) > -1) {
            items.push(item);
          }
        });

        d.resolve(items);
      }, 1000);

      return d.promise;
    };
  }

  main.$inject = [
    '$q',
    '$scope',
    '$timeout'
  ];

  app.controller('Main', main);
}(this.angular));

Теперь немного о том, что происходит.

Сначала происходит инициализация модели. В примере задержка ответа эмулируется с помощью $timeout, на практике следует использовать подгрузку данных с помощью $http или $resource. При этом поле, значение которого должно быть связано с полем автодополнения, уже инициализировано, при этом в нём находится целая модель. Вопрос, как получить основную модель, содержащую вложенное поле, излишен. Этим должен заниматься сервер. Например, в Django REST Framework для таких полей используются сериализаторы объектов.

На втором этапе идёт описание способов подгрузки элементов в список. Ну, тут ничего сложного, в общем-то. С помощью того же $timeout эмулируется задержка, во время которой идёт фильтрация элементов списка. Функция возвращает promise, который разрешается списком отфильтрованных записей, когда приходит время.

EMACS: автоустановка пакетов и хоткеи в русской раскладке

Введение

Наткнулся совершенно случайно на две отличных статьи про EMACS.

Если лень читать, то ниже приводятся готовые коды для включения в .emacs

Автоустановка пакетов

Автоустановка пакетов
(require 'cl)
(require 'package)

;; Список пакетов для установки, на самом деле у меня их под 50 штук, но смысл
;; публиковать их здесь?

(defvar cfg-var:packages '(
    anaconda-mode
    company
    company-anaconda
    company-quickhelp
    company-tern
    company-web
    emmet-mode
    ergoemacs-mode
    flycheck
    powerline
    py-autopep8
    py-isort
    web-mode
    yafolding
    yasnippet
    ))

(defun cfg:install-packages ()
    (let ((pkgs (remove-if #'package-installed-p cfg-var:packages)))
        (when pkgs
            (message "%s" "Emacs refresh packages database...")
            (package-refresh-contents)
            (message "%s" " done.")
            (dolist (p cfg-var:packages)
                (package-install p)))))

(add-to-list 'package-archives '("gnu" . "http://elpa.gnu.org/packages/") t)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/") t)
(add-to-list 'package-archives '("org" . "http://orgmode.org/elpa/") t)
(package-initialize)

(cfg:install-packages)

Рабочее решение, которое гораздо лучше тех, что предложены на StackOverflow.

Хоткеи в русской раскладке

Хоткеи в русской раскладке
;; Это надо добавить ближе к началу .emacs
(defun cfg:reverse-input-method (input-method)
  "Build the reverse mapping of single letters from INPUT-METHOD."
  (interactive
   (list (read-input-method-name "Use input method (default current): ")))
  (if (and input-method (symbolp input-method))
      (setq input-method (symbol-name input-method)))
  (let ((current current-input-method)
        (modifiers '(nil (control) (meta) (control meta))))
    (when input-method
      (activate-input-method input-method))
    (when (and current-input-method quail-keyboard-layout)
      (dolist (map (cdr (quail-map)))
        (let* ((to (car map))
               (from (quail-get-translation
                      (cadr map) (char-to-string to) 1)))
          (when (and (characterp from) (characterp to))
            (dolist (mod modifiers)
              (define-key local-function-key-map
                (vector (append mod (list from)))
                (vector (append mod (list to)))))))))
    (when input-method
      (activate-input-method current))))

;; А вот эта строка должна быть в самом конце
(cfg:reverse-input-method 'russian-computer)