Skip to main content

UI integration package

Project description

intro abc replication search-nodes append-node push-queue stack-example



Quickstart guide

Инициализация

> pip install 2m

Переходим в рабочий каталог своего приложения:

> cd ../path_to_your_ui

Устанавливаем зависимые пакеты, сверяем соответствие, развёртываем пакет с модулями:

> python -c "from two_m_root.install import install;exec(install.main())"

install

Теперь в вашем текущем покете появился пакет two_m, содержащий несколько модулей, которые необходимо настроить.


Настройка

  • Опишем свои таблицы в models.py

Обращаю ваше внимание на официальную документацию Flask-SQLAlchemy https://flask-sqlalchemy.readthedocs.io/en/stable/legacy-quickstart/#define-models

  • procedures.py

Если нужно работать с хранимыми процедурами, декларируем их в виде экземпляров класса DDL.

Документация SqlAlchemy относительно объектов DDL https://docs.sqlalchemy.org/en/20/core/ddl.html#custom-ddl

  • Настроим .env файл, содержащий константы, которые конфигурируют работу базы данных и локального хранилища

settings

После написания таблиц в models и хранимых процедур в procedures, нужно инициализировать их в базу даных.

> python -c "from two_m.models import create_db;create_db()"

> python -c "from two_m.procedures import init_procedures;init_procedures()"

  • Наконец, можно приступить к использованию! Импортируем класс Tool и начнём работу!
from two_m_root.core import Tool
from two_m.models import SomeModel
...
...
...
class Something:
    def __init__():
        self.tool = Tool()
        self.tool.set_model(SomeModel)

    def ui_action_a():
        self.tool.set_item(...)

    def ui_action_b():
        self.tool.set_item(...)

    def ui_action_b():
        items = self.tool.get_items(...)
        ...

Рассмотрим функционал, который является основной компетенций данного фреймворка

  • orm.set_item(_model=None, _ready=False, _insert=False, _update=False, _delete=False, _where=None, **values)

Установить в очередь запись-кандидата, которая появится в базе данных при первой возможности

    • _model: Таблица из модуля models.py. Можно не указывать, если в текущем контексте (вашего приложения) уже установлена таблица в качестве основной (смотри класс ORM, метод set_model)
    • _ready: Логическое значение. До тех пор, пока это значение False, нода будет находиться в очереди, но commit базы данных не попадёт. Идеальный способ передавать ответ от вашего валидатора
    • _insert, _update, _delete: Логическое значение. DML-SQL
    • _where: Словарь вида column_name:value для выражения where
    • return None

Пример: пользовательская функция-валидатор даёт ответ, готова ли данная запись на транзакцию во "внешний мир":

is_ready

  • orm.get_items(_model=None, _db_only=False, _queue_only=False, **where)

    • _model: Таблица из модуля models.py. Можно не указывать, если в текущем контексте (вашего приложения) уже установлена таблица в качестве основной (смотри класс ORM, метод set_model)
    • _db_only: Получать данные только из базы данных, игнорируя локальные элементы
    • _queue_only: Получать данные только из локальной очереди элементов, игнорируя базу данных
    • return Result

  • orm.join_select(*models, _db_only=False, _queue_only=False, _on=None, **where)

    • models: Таблицы из модуля models.py между которыми существует отношение (PK-FK)
    • _db_only: Получать данные только из базы данных, игнорируя локальные элементы
    • _queue_only: Получать данные только из локальной очереди элементов, игнорируя базу данных
    • _on: Словарь. Выражение ON в JOIN запросах. modelName.column1: modelName2.column2
    • return JoinSelectResult

Несколько слов о принципе работы внутренних механизмов, которые объединяют воедино записи из удалённого расположения (базы данных), и записи из локального хранилища. Рассмотрим 1 строку из таблицы А и 1 строку из таблицы B, чтобы понять, как будет происходить слияние.

  1. Обе записи в отношениях (PRIMARY_KEY - FOREIGN KEY) находятся в локальном расположении Все записи в кеше
  2. Запись из базы данных (PRIMARY_KEY), а запись, которая на неё ссылается (FOREIGN KEY) находится в локальном хранилище Основная запись в бд, зависимая в кеше
  3. Нетипичный случай, когда пользователь установил в столбец (FOREIGN KEY) значение, первичный ключ от этого не нашёлся ни среди элементов из базы данных, ни в локальных. В этом случае будут получены обе записи в отношениях (PRIMARY_KEY - FOREIGN KEY) из базы данных, если таковые имеются; В противном случае этой записи не будет в результатах Ошибка внешнего ключа

BaseResult

Union[Result, JoinSelectResult]

Он же

Объект результата

Объекты результата, производные от класса BaseResult, возвращаемые методами orm.get_items и orm.join_select соответственно, являются ЛЕНИВЫМИ объектами, то есть не содержат никакого результата явным образом, но хранят в себе все детали запроса. При каждом взаимодействии с итератором, contains, getitem, bool, len и даже str, происходит новый запрос.

: - Что это, зачем, - зачем усложнять? Ведь можно написать запрос и получить ответ: здесь и сейчас :question:


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

  1. Извлекает записи из базы данных
  2. Извлекает записи из локальной базы данных
  3. Реплицирует одно на другое: на записи из базы данных накладываются записи, которые хранятся локально, и, по тем или иным причинам, пока ещё не закоммитились.


В-третьих, эти ленивые экземпляры дают большое количество синтаксического сахара, который, по заветам pythonicway, избавит ваш интерфейс от каждой лишней строчки!

  • has_changes(hash_value=None) -> Optional[bool]

Просто обратимся к экземпляру, чтобы узнать, есть ли изменения в данных: has_changes Если значение хеш-суммы никогда не фигурировало в рамках текущего экземпляра результата, вернёт - None.

  • has_new_entries

Также, можно с лёгкостью узнать, появились ли новые (или стали недоступны те, что получены) записи: has_new_entries

Result

Объект запроса к 1 таблице.

  • items - property - ResultORMCollection
  • iter - ResultORMCollection._ iter _()
  • visible_items - property - ResultORMCollection. Только ноды с атрибутом 'ui_hidden': False в словаре value.
  • bool - True, если есть хотя бы 1 результат, иначе False
  • len - От количества ResultORMItem в ResultORMCollection
  • getitem - Вернёт новый экземпляр ResultORMCollection с одним или несколькими ResultORMItem по:
  1. Индексу (порядковый номер в коллекции, начиная с 0)
  2. Хеш-сумме пары ключ-значение у ResultORMItem. См свойство hash_by_pk
  3. Полной хеш сумме ResultORMItem
  4. Названию таблицы
  • contains - Поддерживается возможность проверки содержания следующих типов:
  1. ResultORMItem
  2. Индекс в коллекции - int
  3. Название таблицы - str
  4. Хеш-сумма от ResultORMItem - int
  5. Хеш-сумма первичного ключа+значения. Можно получить через ResultORMItem.hash_by_pk - int
  • hash - Получить полную хеш-сумму всех значений в словаре значений каждого экземпляра ResultORMItem, в рамках текущего экземпляра контейнера - ResultORMCollection
  • pointer - (property) геттер и сеттер для инициализации Pointer

JoinSelectResult

Объект запроса к нескольким таблицам. В рамках результирующего списка каждый ResultORMCollection представляет внутри себя связку PK-FK.

  • items - property - tuple(ResultORMCollection)
  • iter - tuple(ResultORMCollection)._ _ iter_ _()
  • visible_items - property - кортеж ResultORMCollection. Если в одной из нод, в рамках одной группы нод, имеют 'ui_hidden': True в словаре value, то данная группа будет скрыта из результатов.
  • bool - True, если в списке есть хотя бы 1 результат ResultORMCollection, иначе False
  • len - От количества ResultORMCollection в кортеже результатов
  • getitem - Вернёт новый экземпляр ResultORMCollection по одному из следующих способов:
  1. Индекс в результирующем кортеже - int
  2. Хеш-сумма hash(sum(map(...ResultORMCollection)))
  3. Хеш-сумма первичных ключей+значений внутри ResultORMCollection - int
  • contains - Поддерживается возможность проверки содержания следующих типов:
  1. ResultORMCollection
  2. ResultORMItem
  3. hash_sum - int
  4. Хеш-сумма первичных ключей+значений из всех элементов внутри ResultORMCollection. Можно получить через ResultORMCollection.hash_by_pk - свойство(property) - int
  • hash - сумма всех ResultORMCollection._ hash _() в результирующем списке
  • pointer - (property) геттер и сеттер для инициализации Pointer

Pointer

Ассоциируйте строку, представляющую данные, со ссылкой на получение этих данных. Работает как для запросов к 1 таблице, так и для запросов к нескольким таблицам одновременно.

Экземпляр Pointer инкапсулируется в экземпляр результата и предназначен для чтения только по свойству, - не следует пытаться сделать на него ещё одну ссылку :rage:

Инициализация

any_result.pointer = ["список", "совпадающий", "по", "длине", "с", "содержимым"]
Каждый элемент этого списка будет ассоциирован с содержимым внутри результата 1 к 1.

Использование

  • getitem
    any_result.pointer["содержимым"] - получить [-1] элемент
    any_result.pointer["список"] - получить [0] элемент
    any_result.pointer["совпадающий"] - получить [1] элемент
    И так далее...
  • contains
    "длине" in any_result.pointer # True
    "strstr" in any_result.pointer # False
    "с" in any_result.pointer # True
    "совпадающий" in any_result.pointer # True


  • any_result.pointer.has_changes(name: str) -> Optional[bool] - Передаём сюда полную хеш сумму и получаем ответ на вопрос: есть ли какие-нибудь изменения (с момента последнего вопрошания или инициализации).
    Если переданного значения не было в списке при инициализации, вернёт None.

any_result.pointer.is_valid() -> bool. Узнать о текущем состоянии текущего экземпляра Pointer: закрылся он или нет.

  • any_result.pointer.wrap_items -> list. Исходный список строк. Если данный Pointer закрыт, список будет пустым.
  • any_result.pointer.items -> Полный словарь содержимого в виде:
    {"одна_из_строк_wrap": контейнер_с_содержимым}
  • any_result.pointer.replace_wrap(item: str, old_wrapper: Optional[str] = None, hash_: Optional[int] = None, primary_key_hash: Optional[int] = None, index: Optional[int] = None) -> None. Заменить строку указатель на новую.
    • item: Новая строка
    • old_wrapper: Старая строка из wrap_items
    • hash_: Полная хеш сумма результата, у которой следует заменить строку-указатель
    • primary_key_hash: Хеш-сумма первичного ключа и значения результата, у которого следует заменить строку-указатель
    • index: Индекс записи в результате, у которой следует заменить строку-указатель

Инвалидация

И всё было бы хорошо, но как только из базы данных придёт результат количественно другой, или, с записями, которые содержат другие первичные ключи; Если то же самое произойдет и со стороны локальных элементов,- объект сразу же откажется сотрудничать с вами. Всеми своими методами он будет отвечать - None. Всё что можно сделать в этой ситуации - создать новый :)



Содержимое результата

Union[ResultORMCollection, ResultORMItem]

Контейнер ResultORMCollection

Контейнеры с данными, возвращаемый объектом результата - Result или JoinSelectResult. Иммутабелен.

  • ResultORMCollection - композиция из ResultORMItem.

    В коллекции нод результата предусмотрена возможность установить/удалить префикс с названием таблицы в каждый столбец. Это может оказаться полезным при разработке UI.
    • prefix - свойство(property). Возвращает литерал, указывающий на текущую конфигурацию относительно префиксов: "auto", "add", "no-prefix"
    • add_model_name_prefix - Метод(callable). Установить всем столбцам значений, находящихся в каждом ResultORMItem, префикс с названием таблицы
    • remove_model_name_prefix - Метод(callable). Удалить префикс с названием таблицы из каждого значения каждого ResultORMItem
    • auto_model_name_prefix - Метод(callable). Если какой-либо столбец(его название) повторяется в каком-либо ResultORMItem текущего контейнера, то добавить префикс, иначе не добавлять
    • all_nodes - Итератор со всеми ResultORMItem. Он возвращает все ноды, включая те, которые находятся в очереди и должны сделать delete в базе данных.

Призываю не использовать метод all_nodes, он нужен для служебного пользования :rage:

    • get_node(model, primary_key, value) - Получить ResultORMItem или Exception
    • search_nodes(model, **столбцы_и_значения) - Получить коллекцию ResultORMItem или пустую коллекцию
    • hash - Получить полную хеш-сумму всех значений в словаре значений каждого экземпляра ResultORMItem, в рамках текущего экземпляра контейнера - ResultORMCollection
    • hash_by_pk - свойство(property). Получить хеш-сумму всех первичных ключей и их значений у всех ResultORMItem, в рамках текущего экземпляра контейнера - ResultORMCollection
    • iter - Итератор со всеми ResultORMItem исключая те, под которыми скрываются пустые - которые должны удалить запись из базы данных
    • bool - На основе количества элементов из iter
    • getitem - Вернёт новый экземпляр ResultORMCollection с одним или несколькими ResultORMItem по:
  1. Индексу (порядковый номер в коллекции, начиная с 0)
  2. Хеш-сумме пары ключ-значение у ResultORMItem. См свойство hash_by_pk
  3. Полной хеш сумме ResultORMItem
  4. Названию таблицы
    • contains - Поддерживается возможность проверки содержания следующих типов:
  1. ResultORMItem
  2. Индекс в коллекции - int
  3. Название таблицы - str
  4. Хеш-сумма от ResultORMItem - int
  5. Хеш-сумма первичного ключа+значения. Можно получить через ResultORMItem.hash_by_pk - int

Единица результата ResultORMItem

    • value - свойство(property) - Словарь с содержимым в виде {столбец: значение}
    • hidden - свойство(property) - Скрыт ли элемент из набора результатов
    • model - свойство(property) - Таблица текущего элемента
    • hash_by_pk - свойство(property). Хеш-сумма первичного ключа и значения
    • get(def_value=None) - Тот же getitem, но с возможностью получить значение по умолчанию
    • get_primary_key_and_value(only_key=False, only_val=False) - Словарь. Пара {столбец: значение}. Или что-то одно
    • add_model_name_prefix - Метод(callable). Добавить каждому ключу в словаре value префикс с названием таблицы
    • remove_model_name_prefix - Метод(callable). Удалить префиксы
    • hash - Получить полную хеш-сумму всех значений в словаре значений
    • contains - 2 варианта использования:
  1. строка вида имя_столбца:значение
  2. просто имя столбца
    • getitem - Получить значение столбца по его наименованию value = node["table_column"]
    • bool - От длины словаря value


Тестовый проект

Пример конфигурации с моделями, хранимыми процедурами(триггерами) и тестами!

Postgresql в качестве базы данных

tests



Changelog

1.1

  • Pointer

    Добавлен метод replace_wrap. wrap_items теперь список, а не кортеж. Более мягкое поведение относительно возбуждения WrapperLengthException, если данных нет. pymemcache.RetryingClient вместо pymemcache.Client в свойстве Tool.cache.

    Отказ от идеи наследования класса Tool в пользовательский пакет two_m, модуль main. Напротив, из two_m.main теперь импортируются константы в two_m_root.core.

1.2

Result.order_by(...), JoinSelectResult.order_by(...)

  • Result и JoinSelectResult получили метод order_by от миксина OrderByMixin Сортировка возможна по длине строк, времени добавления, или в алфавитном порядке. По любому столбцу в таблице или просто по первичному ключу.

    Пример использования:

    1. Инициализируем объект Result или JoinSelectResult с интересующими нас параметрами.

    lazy_result = my_tool_instance.get_items(table_name, ...)

    1. Вызываем метод order_by, передавая один или несколько параметров.

    lazy_result.order_by(...)

    1. Теперь, каждый раз, когда вы выполняете итерации по обновлямому результату, будет происходить сортировка.

    Экземплярам класса Result и JoinSelectResult добавлены методы:

    • visible_items (property) - Получить экземпляр ResultORMCollection, содержащем ноды с _delete=True в значениях (они должны удалить запись)
    • hidden_items (property) - Получить экземпляр ResultORMCollection содержащий только скрытые ноды

    BaseResult получил константу ITER_ONLY_VISIBLE_ITEMS_AS_DEFAULT, определяющую поведение относительно скрытых нод. Обратим внимание, что это, в свою очередь, затрагивает работу Pointer.

  • ConnectionManager

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

2m-1.2.5-py3-none-any.whl (71.4 kB view details)

Uploaded Python 3

File details

Details for the file 2m-1.2.5-py3-none-any.whl.

File metadata

  • Download URL: 2m-1.2.5-py3-none-any.whl
  • Upload date:
  • Size: 71.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.10

File hashes

Hashes for 2m-1.2.5-py3-none-any.whl
Algorithm Hash digest
SHA256 18abc0bafa3cd96c4d781ff4bbda9fef623e4c37a834944665bd79708641a903
MD5 d29a1419b2c15fcdcddbb5452184d3d2
BLAKE2b-256 2d2faf560fd074cfd80c3e5cae0be4d0241e1e3b166fda0de8e9392fe27b4ef9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page