04 мая 2018 25 августа 2022 JavaScript


Архитектура современного веб-приложения выглядит как показано на картинке

Архитектура современного веб-приложения


01 апреля 2018 Python


После миграции на более новую версию Django при изменении любой модели может вылезать вот такая ошибка

django.db.utils.IntegrityError: null value in column "name" violates not-null constraint
DETAIL:  Failing row contains (101, null, history, historymodel).

Это связано с тем что в старой версии для хранения имени модуля использовалось поле name а в новой app_label, при этом поле name является обязательным к заполнению и не учавствует в работе миграции, отсюда и происходит ошибка

Для решения проблемы достаточно удалить поле name из таблицы django_content_type


28 января 2018 25 августа 2022 JavaScript | решать тесты

  • Какое расширение Angular можно использовать для создания "модных" виджетов checkbox?

  • Как сохранять список установленных bower пакетов?


Для начала нужно установить этот модуль

bower install --save angular-switcher

затем подключаем в список зависимостей приложения вот так:

// app.js

angular.module('app', ['ngResource', 'ngRoute', 'ui.bootstrap', 'ui.date', 'ckeditor', 'ngSanitize', 'switcher']) 

используем вот так, заменяем

<input type="checkbox" ng-model="variant.correctly" />

на

<switcher ng-model="variant.correctly" true-label="верно" false-label="не верно"></switcher>

результат будет красив

angular-switcher


23 января 2018 29 января 2018 Python Nginx Angular | решать тесты

  • Как сохранять список установленных bower пакетов?

  • Можно ли использовать декоратор для?


Чтобы сайт работал быстро, необходимо чтобы быстро загружалась статика сайта, для этого используется сжатие ccs и js файлов (Настройка расширения gulp-clean-css для сжати css), а так же кеширование на уровне веб-сервера nginx c помощью опции expires и gzip, например вот так:

    location /static/ {
        alias /home/python/static/;
        expires 30d;
        gzip on;
        gzip_types text/css application/x-javascript application/javascript;
    }

тут задаёт 30 дневный период хранения кеша статики и включается поточное сжатие статики в архив. Таким образом на продакшене образуется ситуация когда настроенный сайт очень быстро получает статику и при повторных запросах использует кеш на уровне веб-серера и веб-браузера. В случае когда присходит редкое обновление фронта требуется чтобы клиенты обновили кеши со статикой сайта. Принудить клиентов обновить кеш можно добавив номер коммита в урлы к статике. Я делаю это так: в моё конфиге я объявил переменную VERSION и инициализирую её при запуске Flask приложения следующим образом:

import os
import subprocess
import datetime
basedir = os.path.abspath(os.path.dirname(__file__))
 
 
class Config(object):
    PROJECT_DIR = basedir
    DEBUG = False
    TESTING = False
    CSRF_ENABLED = not True
    ENABLED_PRODUCTMARKUP = True
    VERSION = '{}.{}'.format(
            str(subprocess.check_output(['git', 'describe', '--tags']),'utf-8').strip(),
            int( subprocess.check_output(['git', 'rev-list', '--all', '--count']) )
        )

И подключить это вот так:

{% extends "base.html" %}

{% block container %}
    <span ng-include="'/static/js/vendor.min.js?v={ { config.VERSION } }'" ng-controller="ProductCtrl"></span>
{% endblock %}

это даст вот такую ссылку

<script src="/static/js/vendor.min.js?v=0.0-235-g1ced510.825" type="text/javascript"></script>

То-есть, после каждого обновления продакшена у клиентов будет обновляться статика, а значит и фронт

Но бывает необходимо чтобы статика закачивалась при каждом новом запросе, это необходимо бывает для верстальщикам. Для решения этой задачи можно подменить значение переменной VERSION текущим значением времени. Я делаю переопределив в DevelopmentConfig аттрибут VERSION следующим образом:

class classproperty(property):
    def __get__(self, cls, owner):
            return classmethod(self.fget).__get__(None, owner)()


class DevelopmentConfig(Config):
    DEVELOPMENT             = True
    DEBUG                   = True
    #~ SQLALCHEMY_ECHO         = True
    UPLOAD_FOLDER           = 'media/'
   
    @classproperty
    def VERSION(self):
        return str(datetime.datetime.now())

тут я создаю специальный класс, который декорирует функцию VERSION, которая возращает каждый раз новую дату и время и представляет эту функцию в виде статического аттрибута класса, таким образом в ссылках на статику при каждой генерации страницы будет новый, уникальный хешь и статика будет всегда самой свежей


19 января 2018 Python datetime RFC1123 Last-Modified


Использование даты в этом формате необходимо для обработки HTTP заголовка Last-Modified. Это когда сервер отдаёт дату изменения страницы в заголовке Last-Modified а затем клиет отправляе HEAD запрос с этим заголовком и датой и если страница не менялась, то ваш сервер отвечает 200 и отправляет пустое тело страницы. Это экономит и трафик и ресурсы процессора, а так же ускоряет повторную индексацию сайта и просто более технологично чем просто отдават страницы на каждый запрос.

Итак настроить дату обновления страницы в таком формате очень легко и делается это вот так:

class BaseClass(SQLAModel):
    __abstract__ = True
...
    updated_on =    Column(DateTime, default=func.now(), onupdate=func.now(), doc=_('дата редактирования'))
...

    def get_last_modified(self):
        return self.updated_on.strftime("%a, %d %B %Y %H:%M:%S GMT")

И всё будет прекрасно работать до тех пор пока вам не потребуется локализовать даты (это когда месяцы на русском). Для этого вы сделаете вот так где в __init__.py

...
import locale
...

locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')

Возможно, раз вы используете babel, то у вас будет сменная локаль, это всё круто. Но этот системный вызов изменит поведение вашей get_last_modified и все ваши усилия по обработке Last-Modified приведут к тому, что появится вот такая ошибка

    TypeError: http header must be encodable in latin1

По-этому, формировать дату в RFC формате формате необходимо правильным образом, а именно вот так

from time import mktime
from wsgiref.handlers import format_date_time


class BaseClass(SQLAModel):
    __abstract__ = True
....
    updated_on =    Column(DateTime, default=func.now(), onupdate=func.now(), doc=_('дата редактирования'))

    def get_last_modified(self):
        stamp = mktime(self.updated_on.timetuple())
        return format_date_time(stamp)

Вот так вот всё просто.


17 января 2018 25 августа 2022 JavaScript Gulp


Итак, сжатиt вендорных css исходников необходимо для уменьшения размера общего файла и делается это просто:

var gulp = require("gulp"),
rename = require("gulp-rename"),
sourcemaps = require("gulp-sourcemaps"),
cleancss = require('gulp-clean-css'), // подключаем сжималку
ngAnnotate = require("gulp-ng-annotate"),
gulpif = require("gulp-if"),
concat = require("gulp-concat"),
bower = require('gulp-bower');

/*
....
*/

gulp.task("vendor_css", function () {
    return gulp.src(paths.vendor_css)
        .pipe(cleancss({compatibility: 'ie8'}))  // настраиваем сжималку
        .pipe(concat("vendor.min.css"))
        .pipe(gulp.dest("static/css/"))
});

Вот так всё просто


15 января 2018 Linux Ubuntu git


Для того чтобы смержить два проекта потребуется git начиная с версии >=2.9. Такого нет в репозитариях стабильной версии Ubuntu 16.04. Для установки более новой версии необходимо сделать вот так:

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

вуаля:

ffsdmad@ffsdmad:~/Project$ git --version
git version 2.15.1

 


08 января 2018 30 августа 2021 СуБД Postgresql Mysql | решать тесты

  • Нужно ли перезапускать сервер Mysql?


Нет простого способа перекинуть данные из разных СУБД, даже в OpenSource. Но всё же можно и сделать это можно через промежуточный формат CSV следующим образом в терминале Postgresql выгружаем нужную таблицу в файл /tmp/test.csv

Copy (Select * from region) To '/tmp/test.csv' With CSV DELIMITER '|';

Затем, необходимо разрешить Mysql загружать данные из внешних файлов, для этого нужно временно добавить в файл /etc/mysql/my.cnf в секцию добавить параметр secure-file-priv

[mysqld]
#
# * Basic Settings
#
secure-file-priv = ""

затем нужно сохранить файл и перезапустить Mysql сервер. После того как сервер перезапущен необходимо переложить файл в специальную директориую которая разрешена для чтения в рамках Mysql

mv /tmp/test.csv /var/lib/mysql-files/

после это заходим в терминал mysql и загружаем данные из нашего файла

LOAD DATA INFILE '/var/lib/mysql-files/test.csv' INTO TABLE region FIELDS TERMINATED BY '|' ;

всё, теперь можно удалить файл и вернуть настройки Mysql сервера на место


08 января 2018 29 января 2018 Python | решать тесты

  • Что такое декоратор?

  • Можно ли использовать декоратор для?


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

# начальная версия кода
def вычисление_значения(a, b):
    return a+b

# код после добавления новых требований
def вычисление_значения(a, b):
    if проверка_пользователя:
        учёт_результатов
    результат = a+b
    учёт_результатов(результат)
    печать_результатов(результат)

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

В программировании декоратор это обёртка функционального кода применяемая относительно другого функционального кода

Можно декорировать любой функциональный код

Можно использовать цепочки декораторов, подобно конфетам которые завёрнуты в фантики, упакованны в коробке и лежат в сумке

Декораторам можно передавать параметры для использования текущего контекста

Использование декораторов позволяет создавать более абстрактный и менее зависящий от контекста код.

Используя декораторы вы можете разделять ваш основной абстрактный код от кода контекста в разных файлах и даже модулях.

Вот так будет выглядеть необходимые нам декораторы

def проверка_пользователя(декорируемая_функция):
    if пользователь.авторизован == True:
        декорируемая_функция()
    генерация исключения авторизации

def печать_результатов(декорируемая_функция):
   печать( "шапка бланка печати" )
   печать( декорируемая_функция() )
   печать( "подвал бланка печати" )

def учёт_результатов(декорируемая_функция):
   try:
      декорируемая_функция()
      журнал("положительное вычисление")
   except:
      журнал("неудачное вычисление")

Схематично использование декораторов можно представить в виде:

@проверка_пользователя
@печать_результатов_вычисление_значения
@учёт_результатов_вычисление_значения
def вычисление_значения(a, b):
    return a+b

Функционально декораторы это простой функциональный код который запускается на этапе интерпретации и в качестве входных аргументов получив имя декорируемой функции (в даннам случае указатель на функцию) манипулирует аргументами и результатами декорируемой функции


07 января 2018 Python variables


Переменная в программе это объект с именем в оперативной памяти компьютера.

Оперативная память компьютера это множество пронумерованных ячеек в которых хранится информация в виде 0 и 1.

Объект в оперативной памяти это участок памяти который интерпретируется процессором в зависимости от типа объекта.

Самый простой тип объекта это бит, бит может принимать только два значений 0(ноль) или 1(единица).

Бит может использоваться в качестве индикаторов состояния включено или выключено.

Если объединить несколько бит то можно интерпретировать состояние бит в виде числа, так например в с помощью 8 бит можно преставить последовательность и 256 цифр (2 значения в 8 степени).

С объединением нескольких значений одного или нескольких типов объектов создаёт составной тип данных, самый простой составной тип данных это байт.

С помощью 1 байта из 8 бит можно представить последовательность из цифр 256 либо последовательность из 256 символов алфавита и технических символов.

Последовательность символов составляет ещё один тип данных — строка символов. Строки могут объединяться в массивы строк.

С помощью последовательности бит можно сформировать активный код выполняеымй процессором, это называет код функции. Код функций это последовательность бит, который выполняется процессором в командном режиме, что приводит к изменению состояния процессора, а следовательно и к изменению состояния объектов в оперативной памяти.

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

Для обращения к объекту необходимо знать адерес объекта в оперативной компьютера, так как работать с адресам для человека не удобно, существую таблицы имён созданых человеком и связанных с именами адресов объектов в оперативной памяти. При этом адрес объекта в оперативной памяти может постоянно меняться но связь с именем всегда позволяет обратиться к объекту.

Так как объекты бывают составными, то существуют иерархически составные названия объектов по аналогии с реальным миром, например: дом, улица, квартира, стул номер 3, ножка стула номер 2, длинна ножки стула.

Таким образом:

Переменная это объект созданный в оперативной памяти компьютера, имещий имя которое используется для обращения к объекту, а так же информацию о типе объекта, а от типа объекта зависит интерпретация процессором оперативной памяти занимаемой объектом.

Типы объектов крайне важны в программировании. Так например, можно изготовить из пластилина молоток на 100% похожий на настоящий молоток, только забивать гвозди им не получится.


04 января 2018 05 января 2018 Linux uuencode mysqldump bash


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

Но нам ни кто не мешает поступить следующим образом, полученный дамп базы данных сжимается архиватором (для уменьшения размера), а затем полученный архив отсылается на почту администратора сайта, например на gmail. А для того чтобы не захламлять почтоый ящик регулярными дампами можно настроить ящик так, чтобы он письма с дампом базы данных перекладывал в спам. Таким образом, письмо не мешает работать с почтой и гмайл самостоятельно удаляет слишком старый спам и у вас всегда будет доступна самая актульаня версия базы данных, а устаревшие будут автоматически удаляться. Даже если не настраивать перенос писем в спам, то можно удалять письмо и оно автоматически будет удаленно через n-дней, так уж устроен gmail

У меня на одной базе данных проявилась проблема, так как база данных получалась слишком большая из-за таблицы которая хранила тексты входящей почты. Эти данные не являются актульными и я просто не включал их в дамп базы данных с помощью опции --ignore-table

Таким образом у меня получился вот такой скрипт

#!/bin/bash

site_name="site-name.ru" # переменная с именем проекта

# формирование имения файла дампа с датой
file_base_name="/home/$site_name/backup/$site_name-`date +%F`.sql.bz2"

# создание дампа резервной копии
mysqldump -u логин -pпароль $site_name --ignore-table $site_name.mails | bzip2  > $file_base_name

# отправка дампа на почту
uuencode  $file_base_name  $file_base_name | mail -s "backup $site_name `date '+%Y-%m-%d'`" admin@gmail.com

# удаление резервных копий старше 7 дней
find /home/$site_name/backup/ -ctime +7 -delete

#PS, утилита uuencode входит в состав пакета sharutils

 


04 января 2018 08 января 2018 Python variables


Первое с чем сталкивает человек изучающий программирование это концепция переменных(Что такое переменные). Это сложная тема абстрактных понятий очень часто является барьером в изучении компьютерных наук. У новичков сразу возникают вопросы что такое перменные, какие типы переменных существуют, как правильно и не правильно использовать переменные, почему всё устроенно именно так. Если не разобраться с этим понятием то невозможно продолжить изучение программирования, а если углубиться в изучение концепции переменнных, то очень многие абстракции в программировании станут понятны сами по себе, по крайней мере интуиция будет подсказывать почему всё устроено именно так, а не иначе.

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

  • определение места где болит
  • указать на него каким либо способом

Если родители уже успели заложить в него связи между этими словами и алгоритмами то ребёнок сумеет выполнить команду. Этот пример наглядно показывает, что названия крайне необходимы для выполнения любых разумных и осмысленных действий. Если эти связи не установлены и для ребёнка слова родителей ничего не значат, то не возможно и управлять действиями ребёнка. Тут я ввожу понятие: алгоритм это набор действий приводящий к определённом результату, очевидно что алгоритмы так же должны иметь название и по сути так же являются объектами к которым можно обратиться через название.

Получается, что процесс воспитания ребёнка является программированием сознания ребёнка с помощью выстраивания связей между объектами и явлениями реального мира с названиями объектов и явлений в нашей речи, на нашем языке. В компьютерной среде происходит тоже самое программист работает в среде оперативной памяти компьютера, а в этой памяти располагаться объекты которые создаёт сам программист или которые там были созданы другими программистами. Например, программист создаёт внутри оперативной памяти список учеников и даёт ему некоторое название (можно сказать даёт ему кличку) которое существует в контексте этой программы и для того чтобы повторно обратиться к этому списку программа использует название которое программист присвоил ему при создании. По сути, вся работа программиста заключается в том, что он создаёт объекты в оперативной памяти компьютера, даёт названия объектам и создаёт алгоритмы взаимодействия объектов с помощью названий. От того насколько эффективно он создаёт эти объект и насколько то что он создаёт понятно другим людям зависит профессионализм программиста. Для сравнения можно взять Льва Толстого и его роман "Война и Мир". В этой книге, писатель с помощью правильных связей с названий и удачных конструкций определяющих конекст создал программу которая вызывает практически во всех людях одинаковые эмоции -- именно по этому этот писатель считается Великим, такое могут далеко не все, чаще всего люди собственной речью не могут выразить то что хотят сказать и тем более у них не получается вызывать те же образы что и самого автора.

Я использовал аналогию с обучением ребёнка для того, чтобы стало очевидно то, что для нашей разумной деятельность крайне важно то что мы знаем названия того с чем мы взаимодействуем, без этого невозможно выполнение разумной и упорядоченной деятельности и точно так же в программирование без знания названий сущностей внутри оперативной памяти невозможно делать что либо разумное. Таким образом получается, что на самом деле самос компьютерные "переменные" это всего лишь объекты и названия объектов внутри оперативной памяти. Но очевидно что использование слова "переменные" более удобно и наглядно чем выражение "объекты и названия объектов" для тех понимает о чём речь.

Поняв вышеизложенное становится очевидно, что когда программист говорит, что он создал переменную то он всего лишь создал объект в оперативной памяти компьютера и дал название этому объекту, когда программист говорит что он обратился к  переменной то это означает что он заставил программу обратиться к объекту по его имени. В этом смысле программисты подобны творцам, которые создают объекты, дают им названия, учат эти объекты взаимодействовать друг с другом, создают целые миры из объектов подчняющихся действию алгоримов взаимодействия и даже общению с объектами из миров созданных другими творцами. Так что на самом деле программирование это очень простое и увлекательное творчество, посоревноваться с которым может лишь творчество которое вызывает у людей сильные эмоции, это я сейчас про кино, музыку и всякую прочую живопись, только в программировании гораздо меньше возможностей для субъективизма и больше логики.


... статья в разработке, продолжение следует

Так же с помощью этого примера можно понять что названия не являются постоянными(константыми), например, объект реального мира "яблоко" может иметь название "яблоко" или "apple" так как это зависит от того в какой семье воспитывается ребёнок, это явление назвается контекст, то-есть одно и тоже явление может называться по разному в зависимости от контекста.

И так в нашем собственном сознании присутвтвуют переменные. Среди них можно выделить переменные долгосрочные и короткосрочные. Например, слово мама почти у каждого человека связыватся с одним единственным человеком на свете, то-есть в сознании этого человека есть конкретная связь между этим названием и конкретным объектом реального мира. Связь с составными названиеми "моя машина", "твоя машина" и реальным объектом реального мира показывает, что переменные бывают не постоянными, а с деньгами понимание недолговременности существования переменных становится ещё очевиднее.


03 января 2018 04 января 2018 Linux DNS CNAME host


Когда Яндекс.домены просят подтвердить домен с помощью CNAME записи то этот поддомен прописывается в панели управления доменом у хостера, а затем проверяется с помощью следующей команды

host -t CNAME python.breys.ru ns1.firstvds.ru
Using domain server:
Name: ns1.firstvds.ru
Address: 82.146.43.2#53
Aliases: 

python.breys.ru has no CNAME record

в выхлопе команды будет видно является ли запись типа CNAME, в данном случае не является, в от этот поддомен является CNAME на breys.ru

host -t CNAME static.python.breys.ru ns1.firstvds.ru
Using domain server:
Name: ns1.firstvds.ru
Address: 82.146.43.2#53
Aliases: 

static.python.breys.ru is an alias for breys.ru.

Для проверки MX записи можно воспользоваться вот такой командой

host -t MX breys.ru
breys.ru mail is handled by 20 mail.breys.ru.
breys.ru mail is handled by 10 mail.breys.ru.


host -t MX python.breys.ru
python.breys.ru has no MX recor

Внимательный глаз сразу заметит что можно указывать внешний NS сервер для проверки записи, это необходимо чтобы не дожидаться обновления зоны на локальном DNS сервере и у провайдера.


28 декабря 2017 07 января 2018 Bash Bash mkdir


Когда сервере  необходимо на создать иерархию директорий то можно открыть mc и 23 раз запустить диалог создания директори или ручками создать всё что нужно с помощью команды mkdir, создать 3 родительских директории media/ , media/pages/ и media/products/ а затем ещё 20 раз для каждой директории

либо воспользоваться возможностями интерпретатора bash введя вот такие команды

mkdir -p media/pages/{0,1,2,3,4,5,6,7,8,9}  media/products/{0..9}

тут в одной строке используются два способа задать диапазон и очевидно, что второй лаконичнее и удобнее

(venv3)python@breys ❄  ls -R media/
media/:
pages  products

media/pages:
0  1  2  3  4  5  6  7  8  9

media/pages/0:
media/pages/1:
media/pages/2:
media/pages/3:
media/pages/4:
media/pages/5:
media/pages/6:
media/pages/7:
media/pages/8:
media/pages/9:
media/products:
0  1  2  3  4  5  6  7  8  9

media/products/0:
media/products/1:
media/products/2:
media/products/3:
media/products/4:
media/products/5:
media/products/6:
media/products/7:
media/products/8:
media/products/9: