Django: шаблон в зависимости от режима отладки

Хочу привести рецепт того, как с помощью создания собственного шаблонного тега заставить меняться шаблон в зависимости от режима отладки.

Режимом отладки управляет переменная DEBUG в файле settings.py. Если DEBUG == True, то Django выводит отладочную информацию при возникновении ошибок.

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

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

При написании статьи я использовал Django версии 1.8.

Собственный тег можно определить только в приложении, поэтому создадим приложение app_for_template_tags командой:


python3 manage.py startapp app_for_template_tags

Приложение необходимо добавить в INSTALLED_APPS в файле settings.py:

INSTALLED_APPS = (
    # Другие приложения,
    'app_for_template_tags',
)

Теги должны определяться в пакете templatetags, поэтому надо создать папку templatetags с пустым файлом __init__.py в каталоге app_for_template_tags.

Создадим модуль в котором будет определение тега. Пусть модуль называется debug_mode_tag.py.

Содержимое debug_mode_tag.py:

from django import template
from django.conf import settings

register = template.Library()

@register.assignment_tag
def debug_mode_tag():
     return settings.DEBUG

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

Если разработка ведется на сервере разработчика и включен режим отладки, то функция debug_mode_tag вернет True, иначе False.

В шаблоне тег используется следующим образом:

{% load debug_mode_tag %}

{% debug_mode_tag as debug_mode %}

{% if debug_mode %}
  <!-- Html-код, который будет
  показан, если включена отладка. -->
{% endif %}

В первой строке подключается модуль debug_mode_tag. Во второй строке создается переменная debug_mode, значение которой будет равно значению, которое вернула функция debug_mode_tag.

Итог

Для решения понадобилось создать файловую структуру:

Django тег debug_mode_tag

Создать функцию в модуле debug_mode_tag.py и добавить приложение в settings.py.

Подробное описание создания тегов можно почитать в документации на Django.

Реклама

Переменные в формулах в текстовом документе LibreOffice

LibreOffice Writer позволяет вставлять вычисления прямо в текстовый документ или текстовую таблицу.

В документации LibreOffice об этом есть статья «Вычисления в текстовых документах«. В той же документации есть справка по функциям.

Существует недокументированная возможность создавать переменные в формулах. Делается это следующим образом: задается имя переменной, например A, и ей присваивается значение, например A = 10, затем ставится точка с запятой (;) за которой следует выражение, использующее эту переменную или создающее еще одну переменную. Результатом формулы будет результат самого последнего выражения.

Следующая формула вернет результат 11:

= a = 5; b = a; b + a + 1

Работа с мультиметром Agilent 34410A на Питоне

Мультиметр Agilent 34410A можно подключить к локальной сети.

Зная IP-адрес или сетевое имя, можно открыть сокет на порту 5025 и посылать команды на языке SCPI, а также принимать измерения.

В Питоне работать с сокетами можно благодаря модулю socket.

Пример программы, которая проводит измерение переменного напряжения


# IP-адрес и порт мультиметра
ip_address = '10.42.0.79'
port = 5025

# Модуль для работы с сокетами
import socket

# Создать сокет
s = socket.socket()

# Установить соединение
address = (ip_address, port)
s.connect(address)

# Установить таймаут чтения/записи в секундах
s.settimeout(1.0)

# Команды мультиметру:

# Установить режим измерения: переменное напряжение
s.send(b'CONFigure:VOLTage:AC\n')

# Настроить запуска измерения по команде '*TRG'
s.send(b'TRIGger:SOURce BUS\n')

# Включить ожидание запуска
s.send(b'INITiate\n')

# Запустить измерение
s.send(b'*TRG\n')

# Передать измерение
s.send(b'FETCh?\n')

# Получить и отобразить измерение
value = s.recv(100).decode('utf-8')
print(value)

# Закрыть сокет
s.close()

Ссылки

SQLite: случайные значения первичного ключа

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

Мною были предприняты две попытки, которые дали отрицательный результат.

Первая неудачная попытка

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

create table table1 (
  id integer primary key default (random()),
  field1 integer
);

Таблица создается без ошибок, но при вставке записей значение первичного ключа задается как обычно, то есть условие default игнорируется.

Вторая неудачная попытка

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

create table table1 (
  id integer primary key,
  field1 integer
);

create trigger random_primary_key
instead of insert on table1
when new.id is not null
begin
  insert into table1 values (
    random(),
    new.field1
  );
end;

Но SQLite выдал ошибку:

Error: near line 30: cannot create INSTEAD OF trigger on table: table1

Оказалось, что триггеры, которые заменяют записи, могут работать только с представлениями (view). Что удалось узнать только из исходного кода SQLite. Так как в документации это не разъясняется. В файле src/trigger.c в том месте где генерируется выше названная ошибка сказано:

INSTEAD of triggers are only for views and views only support INSTEAD of triggers.

Перевод: триггер INSTEAD только для представлений и только представления поддерживают этот триггер.

Удачная попытка

заключается в отказе от использования первичного ключа. Поле идентификатора можно сделать уникальным и задать случайное значение по умолчанию:

create table table1 (
  id integer unique not null default (random()),
  field1 integer
);

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

Извлечение древовидной структуры из базы данных

Для всех запросов в статье используется синтаксис СУБД SQLite. Для других СУБД синтаксис может незначительно отличаться.

Чтобы представить древовидную структуру в реляционной базе данных, можно создать следующую таблицу:

create table tree (
  id     integer,
  parent integer,
  name   text,

  primary key (id),
  foreign key (parent) references tree(id)
    on update cascade on delete cascade
);

В поле parent задается родитель для элемента древовидной структуры, причем в качестве значения этого поля используется значение поля id другого элемента. В поле name задается имя элемента.

Для наглядности зададимся древовидной структурой:

  • Овощи
    • Картофель
      • Аспиа
      • Виталот
      • Диво
    • Томаты
      • Томат обыкновенный
      • Томат Перуанский
  • Фрукты
    • Груши
      • Груша обыкновенная
    • Яблоки

Такую структуру можно создать следующими запросами:

insert into tree values ( 1, null, 'Овощи');
insert into tree values ( 2,    1, 'Картофель');
insert into tree values ( 3,    2, 'Аспиа');
insert into tree values ( 4,    2, 'Виталот');
insert into tree values ( 5,    2, 'Диво');
insert into tree values ( 6,    1, 'Томаты');
insert into tree values ( 7,    6, 'Томат обыкновенный');
insert into tree values ( 8,    6, 'Томат перуанский');
insert into tree values ( 9, null, 'Фрукты');
insert into tree values (10,    9, 'Груши');
insert into tree values (11,   10, 'Груша обыкновенная');
insert into tree values (12,    9, 'Яблоко');

Здесь null означает корневую группу.

Предположим, что требуется извлечь данные из таблицы tree в следующем виде:

/Овощи
/Фрукты
/Овощи/Картофель
/Овощи/Томаты
/Фрукты/Груши
/Фрукты/Яблоко
/Овощи/Картофель/Аспиа
/Овощи/Картофель/Виталот
/Овощи/Картофель/Диво
/Овощи/Томаты/Томат обыкновенный
/Овощи/Томаты/Томат перуанский
/Фрукты/Груши/Груша обыкновенная

Для этого понадобится рекурсивный запрос:

with cte as (
  select
    id,
    parent,
    '/' || name as name
  from tree where parent is null
  
  union all
  
  select
    tree.id,
    tree.parent,
    cte.name || '/' || tree.name
  from cte, tree on tree.parent = cte.id
)
select name from cte;

Рекурсивные запросы поддерживаются в SQLite начиная с версии 3.8.3 от 3 февраля 2014 года.

SQL-запросы в коде C/C++

SQL-запросы можно хранить в строковых переменных, например:

const char * query = "SELECT * FROM table1;";

Но если запрос достаточно большой, то его придется разбить на несколько строк, например:

const char * query =
    "SELECT column1, column2, column3"
    " FROM table1 INNER JOIN table2"
    " ON table1.column1 = table2.column2;";

Но такой запрос не скопируешь в какой-нибудь клиент СУБД, чтобы проверить как он исполняется. Мешают кавычки, без которых компилятор ругается.

Есть способ записи запросов, который бы удовлетворил обоим языкам и SQL и C/C++:

const char * query = 
    "SELECT column1, column2, column3 --\n\
     FROM table1 INNER JOIN table2 --\n\
     ON table1.column1 = table2.column2;";

Наклонная черта в конце строк позволяет на C/C++ переносить текст на другую строку, не используя кавычки. А два дефиса и перевод строки (\n) перед наклонной чертой сделают так, чтобы эта черта считалась комментарием на SQL.

Вращающееся изображение на сайте

Пример вращающегося изображения.

С помощью библиотеки jQuery Reel объект на изображении можно заставить вращаться. Вращение можно организовать вокруг вертикальной и горизонтальной оси. Объект может вращаться как сам по себе так и с помощью мыши.

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

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

Затем объединить все изображения в коллаж. Например:

image-reel(485x254)

Порядок изображений в коллаже: слева направо и сверху вниз.

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

Существует простой способ создания коллажей с помощью программы ImageMagick. Файлы с изображением объекта под разными углами лучше именовать следующим образом:

  • image-001.jpg
  • image-002.jpg
  • image-003.jpg
  • и т.д.

Последовательность нумерации должна совпадать с последовательностью вращения.

ImageMagick предоставляет консольную утилиту montage для склейки изображений. Чтобы ей воспользоваться, следует запустить консоль (командную строку), перейти в папку с изображениями и выполнить команду:

montage *.jpg -geometry 485x254 collage.jpg

где:

  • *.jpg — указывает, что требуется объединить все изображения с расширением jpg в данной папке. Вместо этого можно просто перечислить изображения через пробел: image-001.jpg image-002.jpg image-003.jpg и т. д.
  • 485x254 — размер исходных изображений в пикселях (ширина x высота). Если указать другой размер, то каждое изображение будет отмасштабировано до указанного размера. Далее значения ширины и высоты будут использованы в html-коде.
  • collage.jpg — коллаж, итоговое изображение.

Чтобы встроить вращающееся изображение в html-страницу, следует воспользоваться следующим кодом:

<html>
  <head>
    <script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
    <script src="//code.vostrel.cz/jquery.reel.js"></script>
  </head>
  <body>
    <img src="image-001.jpg" width="485" height="254"
      class="reel"
      data-image="collage.jpg"
      data-frames="24"
      data-footage="6"
      data-revolution="800">
  </body>
</html>

В <head> подключается библиотека jQuery и плагин Reel. В <body> изображение добавляется стандартным способом с помощью тега <img>. С помощью атрибута src указывается изображение, которое будет отображаться пока библиотека или коллаж не загрузились, а также если что-то пошло не так и вращение не заработало.

Другие атрибуты:

  • width="485" — ширина одного изображения в коллаже.
  • height="254" — высота одного изображения в коллаже.
  • class="reel" — связь с библиотекой.
  • data-image="collage.jpg" — коллаж.
  • data-frames="24" — количество изображений в коллаже.
  • data-footage="6" — количество изображений по горизонтали.
  • data-revolution="800" — количество пикселей, которое должен пройти указатель мыши для полного оборота.

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

Без коллажа

Можно обойтись без коллажа, воспользовавшись атрибутом data-images, в котором можно задать диапазон изображений.

<html>
  <head>
    <script src="//code.jquery.com/jquery-2.1.3.min.js"></script>
    <script src="//code.vostrel.cz/jquery.reel.js"></script>
  </head>
  <body>
    <img src="image-001.jpg"
      class="reel"
      data-images="image-###.jpg|1..100"
      data-revolution="800">
  </body>
</html>

Вертикальная черта в атрибуте data-images отделяет шаблон имени файла изображения от диапазона чисел, которые задают номер первого и последнего изображения. Алгоритм вращателя заменяет решетки в имени файла на числа из диапазона. Между числами в диапазоне должны быть две точки.

QextSerialPort::read() и таймаут в Виндоус

При использовании библиотеки QextSerialPort версии 1.2rc в Виндоус возникает проблема медленного чтения данных. Дело в том, что данные, даже если они доступны, возвращаются только после истечения таймаута.

Проблема решается, если открывать порт в режиме QIODevice::Unbuffered, например:

port->open( QIODevice::ReadWrite | QIODevice::Unbuffered );

Такое решение нашлось на Stack Overflow.

Но после этого в моей программе стало некорректно работать чтение в Юникс. Читалось меньше данных, чем запрашивалось, даже при достаточно большом таймауте, как-будто таймаут вообще не работает. Проблема решилась использованием разных режимов на разных операционных системах:

const QIODevice::OpenMode openMode =
#if   (defined Q_OS_UNIX)
    QIODevice::ReadWrite;
#elif (defined Q_OS_WIN)
    QIODevice::ReadWrite | QIODevice::Unbuffered;
#else
    #error "Unknown Operating System"
    QIODevice::NotOpen;
#endif

port->open(openMode);

Мне кажется, первая проблема связана с поведением функции «qint64 QIODevice::read(char *data, qint64 maxSize)» из библиотеки Qt. Проблемная часть кода из файла qiodevice.cpp библиотеки Qt версии 5.3.0:

qint64 QIODevice::read(char *data, qint64 maxSize)
{
    ...
            if ((d->openMode & Unbuffered) == 0 && maxSize < QIODEVICE_BUFFERSIZE) {
                ...
                int bytesToBuffer = QIODEVICE_BUFFERSIZE;
                ...
                qint64 readFromDevice = readData(writePointer, bytesToBuffer);
                ...
            }
    ...
}

При этом QIODEVICE_BUFFERSIZE задается равным 16384 в файле qiodevice_p.h:

#define QIODEVICE_BUFFERSIZE Q_INT64_C(16384)

Функция readData() реализуется в библиотеке QextSerialPort и в Виндоус вызывает функцию ReadFile() из WinApi, которая читает заданное количество байт из COM-порта.

Таким образом, если устройство не открыто в режиме Unbuffered, то в Виндоус у COM-порта запрашивается 16384 байт, чтобы заполнить некий буфер. Если нам требуется меньше данных, то ReadFile() не возвращает управление пока не истечет таймаут.

MDK-ARM: замена десятичного разделителя

Чтобы локализовать представление чисел при программировании в среде MDK-ARM требуется выполнить несколько простых шагов:

  1. Скачать ассемблерный файл и добавить в свой проект.
  2. В настройках проекта на вкладке Asm в поле Include Paths указать каталог, в котором расположен файл rt_locale.s. По умолчанию этот файл расположен в «C:\Keil\ARM\ARMCC\include».

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

После этого функции (например printf) преобразующие число в строку и наоборот будут воспринимать запятую в качестве разделителя дробной и целой части.

Подробнее о реализации ассемблерного файла можно почитать в официальной документации на MDK-ARM. Стоит учесть, что в коде, приведенном в документации, есть недочет: отсутствует строчка экспортирующая функцию _get_lc_CATEGORY, в представленном здесь ассемблерном файле эта строчка: «EXPORT _get_lc_numeric». Без неё локализация не заработает.

Привожу код из ассемблерного файла.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; Определение русского десятичного разделителя.
;
; Файл предназначен для программирования в среде Keil MDK-ARM.
;
; В настройках проекта на вкладке Asm в поле Include Paths
; требуется указать каталог, в котором расположен файл rt_locale.s.
; По умолчанию этот файл расположен в "C:\Keil\ARM\ARMCC\include".
;
; В программе не требуется вызывать функцию setlocale().

    GET rt_locale.s ; Подключить файл с макросами

    AREA locales, DATA, READONLY
    LC_NUMERIC_begin russian_numeric_locale, "russian"
    LC_NUMERIC_point ","     ; Десятичный разделитель
    LC_NUMERIC_thousands "." ; Разделитель групп цифр
    LC_NUMERIC_grouping "\3" ; Количество цифр в группе
    LC_NUMERIC_end
    AREA locale_func, CODE, READONLY
    EXPORT _get_lc_numeric
_get_lc_numeric FUNCTION
    LDR r0, =russian_numeric_locale_start
    BX lr
    ENDFUNC

    END ; Конец ассемблерного файла

Поддержка ввода двоичных чисел в Си

Людям, занимающимся низкоуровневым программированием, часто хочется непосредственно ввести двоичную константу, но Си такой возможности не предоставляет. Только некоторые компиляторы поддерживают соответствующее расширение языка. Но если пользоваться таким расширением, то код становится непереносимым.

Чтобы иметь возможность ввода двоичных чисел и сохранить переносимость, вам пригодится этот заголовочный файл: bit.h.

Файл содержит код следующего вида:

#ifndef BIT_H
#define BIT_H

#define b0000_0000 0
#define b0000_0001 1
#define b0000_0010 2
// ...
#define b1111_1110 254
#define b1111_1111 255

#define b0000_0000_1 0
// ...
#define b1111_1111_1 65280

#define b0000_0000_2 0
// ...
#define b1111_1111_2 16711680

#define b0000_0000_3 0
// ...
#define b1111_1111_3 4278190080

#endif

Просто подключаем bit.h:

#include "bit.h"

и вводим двоичные числа, используя префикс b и разделяя тетрады подчеркиванием. Например:

#define BIT_MASK b0001_0101 // 21
const int a = b0011_1110;
...

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

  • _1 — для смещения числа на 1 байт влево, то есть задания второго байта,
  • _2 — для смещения числа на 2 байта влево, то есть задания третьего байта,
  • _3 — для смещения числа на 3 байта влево, то есть задания четвертого байта,

при этом между числами требуется производить операцию или (|).

Например:

// 0xFF00FF00
uint32_t mask = b1111_1111_3 | b0000_0000_2 | b1111_1111_1 | b0000_0000;
...

Можно задавать один какой-то байт или несколько байт идущих не по порядку, например:

uint32_t mask = b1111_1111_3 | b1111_1111_1; // 0xFF00FF00
...