Python3 и MySQL что установить?

Для работы нужна библиотека mysqlclient. Установка библиотеки mysqlclient для работы с MySQL и Python3 происходит следующим образом (в Debian 9)

sudo apt install python3-dev libsqlclient-dev default-libmysqlclient-dev
python3 -m venv venv
source venv/bin/activate
pip install mysqlclient

Для Python2 использовать старый модуль MySQL-python.

Книга CSS для профи — Кит Грант [2019]

CSS для профи — Кит Грант [2019]

Хочу написать отзыв на книгу по CSS. Книга мне понравилась, купил в бумаге. Хорошо написана. В последнее время очень сильно WEB технологии разогнались. Но сейчас не об этом. Мне нужно было самостоятельно приступить к вёрстке крупного проекта. Информация которой я владел была неструктурированная, надёргана тут и там, в добавок устаревшая. Поверхностное владение Flex и Grid. В итоге решил купить книгу, что бы привести знания в порядок и использовать современный подход на сколько позволяет книга (информация в наше время очень быстро устаревает).

Книга написана легко, перевод меня устроил, читать её в удовольствие. Но и как все книги подобного рода, требуют сразу переменить полученные знания на практике. Описаны WebPack и адаптивная вёрстка, Flex и Grid, использование шрифтов и многое другое. Минимум воды, если есть отступления автора то они к месту.
Если CSS нужен по работе, определённо стоит обратить на данную книгу внимание.

Flask и компрессия CSS, JavaScript

Для оптимизации css и JavaScript я использую Flask-Static-Compress.

pip install flask-static-compress
    from flask_static_compress import FlaskStaticCompress
    app = Flask(__name__)
    compress = FlaskStaticCompress(app)
from flask_static_compress import FlaskStaticCompress
#skip code
compress = FlaskStaticCompress()
# skip code
def create_app(config_name):
    app = Flask(__name__)
    # skip code
    compress.init_app(app)
 # skip code

Оно реально стоит того? В моём случаи да. Пусть даже если посещаемость сайта не 10к в минуту, мне нравиться,  что я отдаю css и js минимального размера. Google page speed рад этому.

Важнейший для меня момент: после редактирования файла css (js) он часто продолжает грузиться из кэша, что мешает, и добавляет проблем при обновление сайта, приходилось переименовывать вручную, так же править путь в шаблоне, использование Flask-Static-Compress решает этот вопрос. Короче это удобно когда сайт уже в интернете.
Использовать очень просто:

{% compress 'css' %}
    <link rel="stylesheet" type="text/css" media="all" href="{{ url_for('static', filename='css/style.css') }}"/>
{% endcompress %} 
{% compress 'js' %}
    <script type="text/javascript" src="{{ url_for('static', filename='js/myapp.js') }}"></script>
{% endcompress %} 

В результате будет, что то вроде этого

    <link type="text/css" rel="stylesheet" href="/static/sdist/ed3117165c9910028aec4d167077a78d.css">

Готовые файлы будут в директории /static/sdist/ лучше добавить её в .gitignore

app/static/sdist

Python3 virtualenv

Наконец для всех библиотек  на Python3.6+ появилось всё, что мне нужно, так же свежий Python 3.6+ и 3.7+ доступен на хостинге который я использую. Виртуальное окружение в новых «питонах» создаю так:

python3 -m venv venv

или

python3 -m venv venv -system-site-packages

Использовать точно так же:

 
source venv/bin/activate
pip install --upgrade  pip
pip install -r requirements.txt

Что бы меньше было проблем я всегда ставлю самую новую версию pip.

Динамическая подгруздка select (ComboBox).

Хочу поделится как я считаю хорошим решением динамической подгруздки в select.
Рассмотрю пример на категория + под категория. При выборе категории будут подгружается под категории.
Использовать для этого буду Flask, AJAX, jQuery, SQLite.
Подгружаться данные будут с помощью AJAX и Json.
Для краткости python код будет в app.py, а шаблон в template/index.html
Структура проекта:

app.py
template/index.html

Библиотеки которые должны стоять:

Flask==0.11
Flask-SQLAlchemy==2.1
Flask-WTF==0.12
Jinja2==2.8
MarkupSafe==0.23
SQLAlchemy==1.0.13
WTForm==1.0
WTForms==2.1
Werkzeug==0.11.10

Для начало необходимо создать минимальное приложение которое отображает шаблон.

from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')

if __name__ == '__main__':
app.run(debug=True)

Далее создаём index.html в директории templates:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Flask-select</title>
</head>
<body>
    Hello!
</body>
</html>

Запускам:

$ python app.py
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
...

Если всё завилось то продолжаем.
Необходимо где то хранить список категорий и под категорий. Для этого подойдет база SQLite. Работать с ней будем через SQLAlchemy.
Отредактируем app.py и приведём его к виду:

from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import String
from sqlalchemy import Integer
from sqlalchemy import Column
from sqlalchemy import ForeignKey
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.sqlite'

db = SQLAlchemy(app)


#  категория
class Category(db.Model):
    __tablename__ = 'category'
    id = Column(Integer, primary_key=True)
    name = Column(String(1024))

    def __repr__(self):
        return '<Category %s>' % self.name

    def __unicode__(self):
        return self.name


#  под категория
class SubCategory(db.Model):
    __tablename__ = 'sub_category'
    id = Column(Integer, primary_key=True)
    name = Column(String(1024))
    category_id = db.Column(Integer, ForeignKey('category.id'))
    category = db.relationship("Category", backref="Ctegory.id")

    def __repr__(self):
        return '<SubCategory %s>' % self.name

    def __unicode__(self):
        return self.name

#  создание таблиц
db.create_all()
#  заполнение базы
#  если записи отсутствуют
if len(Category.query.all()) is 0:
    for name in ['фрукты', 'напитки', 'молочные продукты']:
        category = Category(name=name)
        db.session.add(category)
    db.session.commit()

if len(SubCategory.query.all()) is 0:
    for name in ['апельсины', 'яблоки', 'груши']:
        sub_category = SubCategory(category_id=1, name=name)
        db.session.add(sub_category)

    for name in ['сок', 'вода', 'газировка']:
        sub_category = SubCategory(category_id=2, name=name)
        db.session.add(sub_category)

    for name in ['молоко', 'сметана', 'масло']:
        sub_category = SubCategory(category_id=3, name=name)
        db.session.add(sub_category)
    db.session.commit()


@app.route('/')
def index():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

Это только создаст БД и таблицы в ней. Ещё нам понадобится сама форма для отображения выбора. Здесь я решил не использовать Bootstrap и обойтись простой формой.
В тело index.html необходимо добавить следующий код:

    ...
    Пример работы выбора категории и под категории.
    <form method="post" name="form" action="/">
        {{ form.csrf_token }}
        {{ form.category.label }} 
        {{ form.category }}
        {{ form.sub_category.label }} 
        {{ form.sub_category }}
        <input type="submit" value="Отправить">
    </form>
    ...

Для отображения формы необходимо добавить в app.py импорт библиотек и сам класс формы.

...
from flask_wtf import Form
from wtforms import SelectField
...
class FormCategory(Form):
    category = SelectField(u'Категория', coerce=int)
    sub_category = SelectField(u'Под категория', coerce=int)

    def __init__(self, *args, **kwargs):
        super(FormCategory, self).__init__(*args, **kwargs)
        self.category.choices = \
            [(g.id, u"%s" % g.name) for g in Category.query.order_by('name')]
        #  выбранное поле по умолчанию
        self.category.choices.insert(0, (0, u"Не выбрана"))

        self.sub_category.choices = list()
        #  выбранное поле по умолчанию
        self.sub_category.choices.insert(0, (0, u"Не выбрана"))


@app.route('/')
def index():
    form = FormCategory()
    return render_template('index.html',
                           form=form
                           )
...

Страница должна выглядеть примерно так:

Вид страници
Примерный вид страницы

Необходимо заблокировать выбор под категории пока не выбрана категория. Для этого будем менять свойства sub_category через jQuery.
Подключим её добавив между head в файле index.html строку:

...
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
...

После нашей формы необходимо добавить JavaScript который будет менять свойства sub_category, загружать под категории посредствам Ajax.

...
    <script type="text/javascript">
        function choice_category(){
            var tmp_id = parseInt ($("#category").val());
            if(tmp_id == 0)
            {
                $("#sub_category").attr('disabled', 'disabled');
            }
            else
            {
                $("#sub_category").removeAttr('disabled');
                load_subcategory();
            }
        }

        function load_subcategory(){
            $.ajax({
                type: "POST",
                url: "/get_sub_category",
                data: $('form').serialize(),
                success: function(response) {
                    var json = jQuery.parseJSON(response)
                    obj = Object.keys(json)

                    $("#sub_category")
                        .find('option')
                        .remove()
                        .end()
                        .append('<option value="0">Не выбрано</option>')
                        .val('0');

                    var value, key;
                    for(item in obj){
                        value = json[obj[item]];
                        key = obj[item];
                    $("#sub_category").append($("<option></option>")
                            .attr("value",key)
                            .text(value)); 
                    }
                
                },
            error: function(error) {
                console.log(error);
            }
        });
        }

        $(document).ready(function() {
            choice_category();
            $("#category").change(function() {
                choice_category();
            });

            $("#sub_category").change(function() {
            });
        });
    </script>
...

Но и это ещё не всё! Через Ajax мы обращаемся к get_sub_category для которого так же необходимо добавить обработку в app.py и импорт библиотек.

...
from flask import request
from flask import json
...
@app.route('/get_sub_category', methods=('GET', 'POST'))
def get_sub_category():
    category_id = request.form['category']
    item_list = SubCategory.query.filter_by(category_id=category_id).all()
    result_list = dict()
    for item in item_list:
        result_list[item.id] = item.name
    return json.dumps(result_list)
...

Теперь наконец всё должно заработать.
В самом простом случаи можно обойтись вообще без FormCategory из листинга 5 но тогда всё равно придется передавить список категорий, а в самом шаблоне добавится цикл для заполнения категории. Валидацию в таком случаи будет проблематичней сделать, по этому считаю данный вариант оптимальным. Так, что не слушайте людей которые говорят, что форма для 2-х элементов не нужна и всё проще и быстрей сделать просто через input и label.
Вообще для данной формы идеально бы подошел вариант с wtf.quick_form, если ваш сайт использует Bootstrap.

Готовый исходный код можно взять с github

git clone https://github.com/newivan/flask_select.git

Vim переключение раскладки

В Emacs мне нравится, что там есть встроенное переключение раскладки клавиатуры, и проблем с горячими клавишами нет, когда включен русский язык.
В Vim по умолчанию ничего нет и я долго мучился, когда нужно писать то на одном языке то на другом. Решение найдено на просторах интернета, запишу его сюда, что бы не потерять.
В .vimrc нужно добавить:

set keymap=russian-jcukenwin
set iminsert=0
set imsearch=0
highlight lCursor guifg=NONE guibg=Cyan

Теперь при включенной русской раскладки можно использовать сочетания клавиш и всё будет хорошо. Язык же нужно переключать нажатием сочетания Ctrl+^.

AJAX вместе с Flask

В примере я использую Python3, если у вас Python2 добавьте в начало файла app.py строку:

# -*- coding: utf-8 -*- 

Для начала понадобится Flask. Ниже показано как установить его в виртуальное окружение.

$ mkdir flask_simple_ajax
$ virtualenv --system-site-packages --python=/usr/bin/python3 env
$ source env/bin/activate
$ pip install flask

В корне flask_simple_ajax создайте файл app.py с серверным кодом:

from flask import Flask, render_template, json, request
app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')


@app.route('/get_len', methods=['GET', 'POST'])
def get_len():
    name = request.form['name'];
    return json.dumps({'len': len(name)})


if __name__ == '__main__':
    app.run(debug=True)

Далее нужно создать директории template, static/js:

$ mkdir templates
$ mkdir -p static/js

Скачайте с сайта jQuery библиотеку и положите её в static/js.

В template создайте файл index.html со следующим содержимым:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>AJAX вместе с Flask</title>
    <script type=text/javascript src="{{ url_for('static', filename='js/jquery-2.2.2.min.js') }}"></script>
</head>
<body>
    <script>
        function get_len() {
            $.ajax({
                type: "POST",
                url: "/get_len",
                data: $('form').serialize(),
                type: 'POST',
                success: function(response) {
                    var json = jQuery.parseJSON(response)
                    $('#len').html(json.len)
                    console.log(response);
                },
                error: function(error) {
                    console.log(error);
                }
            });
        }
    </script>
    <form action="/get_len" method="post" name="form">
        <label for="name">Введите текст:</label>
        <input id="name" name="name" type="text">
        <input type="button" value="Отправить" onclick="get_len();">
    </form>
    <div id="len"></div>
</body>
</html>

Не забудьте поправить в строке 6 версию jquery на которую скачали ранее. Можно было ничего не качать и вместо 6-й строки написать:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>

Но в некоторых случаях лучше, что бы всё работало автономно.

Запускаем:

$ python3 app.py

Результат можно посмотреть в браузере по адресу http://localhost:5000/

Пример работа AJAX и Flask
Пример работы AJAX и Flask

Данный пример прост для понимания, вы быстро разберётесь и сожмите добавлять AJAX в свои проекты.
Готовые исходные кода можно взять на github

Книга «Путь программиста»

large_49601915
Недавно прочитал эту замечательную книгу. Отзывы в интернете в основном положительные. Но есть и мнение, что в книге банальщина.

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

Перевод очень хорош, электронная версия (я читал epub) прекрасна. Книгу можно приобрести на сайте издательства «Питер» тут. Правда цена мне кажется завышенной. И стоила она раньше (электронная версия), если не ошибаюсь — 300 рублей. Сейчас когда я пишу этот отзыв цена электронной версии составляет 790 рублей, а бумажной 820 рублей.

Рекомендую всем для прочтения, кто хочет работать на себя в сфере ИТ и не только. Автор даёт дельные советы, по этому поводу.
Один из них выглядит примерно так — «Как бы вам не хотелось поскорей уволиться с наёмной работы, и наконец начать всё время уделять работе на себя. Не спешите. Работайте на себя в свободное время, превозмогая усталость и лень. Увольняться нужно только тогда когда ваше дело полетит. Нельзя думать, что основная работа причина нехватки времени и неудач в вашем деле и уволившись у вас всё получиться. Работать на себя, трудно…»

В обще вердикт таков: читать, не нужно не читать 🙂

Flask передача параметра в redirect

Если необходимо передать параметр из одного route в другой, можно воспользоваться следующим способом.


@order.route("/order", methods=['GET', 'POST'])
def view_order():
    #  ....
    return redirect(url_for('.view_ready_order', order_id=order_id))

@order.route("/ready/<int:order_id>", methods=['GET', 'POST'])
def view_ready_order(order_id):
    return render_template('ready.html', title=u'Заказ оформлен', order_id=order_id)

В данном примере я передаю order_id для показа его в шаблоне.