Skip to main content

Library for describing data relations in python (Enums, "relational" tables)

Project description

Установка

pip install rels

Введение

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

  • каждый элемент перечисления — отдельный объект со всеми необходимыми методами;

  • наследование перечислений;

  • любое количество дополнительных данных, привязанных к элементу перечисления;

  • автоматическое создание индексов по данным;

  • дополнительные проверки данных;

  • формирование обратных ссылок в связанных друг с другом перечислениях (и других объектах);

Общая идея заключается в использовании реляционной модели данных. При описании нового типа описываются поля отношения (столбцы таблицы) и непосредственно сами данные (в виде таблицы).

За счёт наследования можно заранее объявить необходимые общие (абстрактные) типы, а конкретные перечисления уже наследовать от них.

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

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

from rels import Column, Relation

class Enum(Relation):             # объявляем абстраткное перечисление
    name = Column(primary=True)   # имя
    value = Column(external=True) # значение


class EnumWithText(Enum):  # добавляем дополнительный столбец для описания значений
     text = Column()       # например, для использования в пользовательском интерфейсе


class SOME_CONSTANTS(Enum):       # объявляем конкретное перечисление
     records = ( ('NAME_1', 1),   # и указываем данные для него
                 ('NAME_2', 2))


# Варианты работы с элементами перечислений
SOME_CONSTANTS.NAME_1.name == 'NAME_1'          # True
SOME_CONSTANTS.NAME_1.value == 1                # True

SOME_CONSTANTS(1) == SOME_CONSTANTS.NAME_1      # True

SOME_CONSTANTS.NAME_2 == SOME_CONSTANTS.NAME_2  # True
SOME_CONSTANTS.NAME_2 != SOME_CONSTANTS.NAME_1  # True
SOME_CONSTANTS.NAME_2.is_NAME_2                 # True


class SOME_CONSTANTS_WITH_TEXT(EnumWithText):
    records = ( ('NAME_1', 1, 'constant 1'),
                ('NAME_2', 2, 'constant 2'))


SOME_CONSTANTS.NAME_2 != SOME_CONSTANTS_WITH_TEXT.NAME_2  # True
SOME_CONSTANTS.NAME_1 != SOME_CONSTANTS_WITH_TEXT.NAME_1  # True


# добавление новых элементов перечисления
class EXTENDED_CONSTANTS(SOME_CONSTANTS_WITH_TEXT):
    records = ( ('NAME_3', 3, 'constant 3'), )  # добавляем ещё одно значение

Описание перечисления

При описании столбцов таблицы, можно указать их свойства:

  • name — string — имя столбца (по умолчанию равно имени, которому присваивается объект столбца);

  • unique — boolean — значения в столбце должны быть уникальными, иначе будет брошено исключение (по умолчанию True);

  • primary — boolean — элементы перечисления будут доступны как атрибуты перечисления с именами, указанными в этом столбце (например, ENUM.COLUMN_VALUE) (по умолчанию False);

  • external — boolean — элемент перечисления можно будет получить по значению этого столбца, передав его в конструктор перечисления (например, ENUM(some_value)) (по умолчанию False);

  • single_type — boolean — все значения в столбце должны быть одного типа (по умолчанию True);

  • index_name — string — имя, по которому будет доступен индекс с ключам из этого столбца (по умолчанию равен index_<имя столбца>), см. подробнее в описании индексов;

  • related_name — string | None — имя, по которому элемент перечисления будет доступен в объектах, на которые ссылается этот столбец (по умолчанию None), см. подробнее в описании связывания.

При создании перечисления:

  • Каждая строка данных в таблице превратится в элемент перечисления.

  • В классе перечисления будут установлен атрибут, ссылающиеся на соответствующий элемент перечисления. Имя атрибута будет установлено в значение в столбце, отмеченном как primary.

  • Если primary столбцов больше 1, то будут установлены атрибуты для каждого.

Атрибуты и методы

Атрибуты перечисления:

  • .<имя из primary столбца> — ссылка на элемент перечисления (создаётся для всех значений из primary столбцов);

  • .records — список всех элементов перечисления в порядке их объявления в «сырых» данных;

  • .<имя индекса>— индексы всех столбцов (по умолчанию index_<имя столбца>);

  • .__call__ — принимает значение из столбца с external установленным в True, возвращает элемент перечисления, которому оно соответствует;

  • .select(*<список имён столбцов>) — возвращает таблицу с выборкой данных по указанным столбцам;

  • .get_from_name(<полное имя элемента перечисления>) — принимает строку с именем конкретного элемента перечисления (например, "ENUM.NAME") и возвращает соответствующий элемент перечисления или бросает исключение.

Атрибуты элемента перечисления:

  • .<имя столбца> — получение данных для соответствующего столбца;

  • .is_<имя из primary столбца> — возвращает True, если

Индексы

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

По умолчанию индекс доступен как атрибут перечисления с именем index_<имя столбца>, но оно может быть изменено при описании столбца.

Пример:

from rels import Column, Relation

class ENUM(Relation):
    name = Column(primary=True)
    value = Column(external=True)
    text = Column(unique=False, index_name='by_key')

    records = ( ('NAME_1', 0, 'key_1'),
                ('NAME_2', 1, 'key_2'),
                ('NAME_3', 2, 'key_2'), )

ENUM.index_name # {'NAME_1': ENUM.NAME_1, 'NAME_2': ENUM.NAME_2,  'NAME_3': ENUM.NAME_3}
ENUM.by_key     # {'key_1': [ENUM.NAME_1], 'key_2': [ENUM.NAME_2, ENUM.NAME_3]}

Наследование

Наследование позволяет расширять как количество столбцов, так и добавлять новые элементы перечисления (дополняя таблицу данных).

Добавлять столбцы можно только если в родительском классе не была указана таблица данных.

Пример наследования можно видеть в самом первом листинге.

Связывание

Если для одного из столбцов указано related_name, то во время создания перечисления, для каждого объекта из этого столбца будет вызван метод set_related_name(<имя атрибута>, <ссылка на соответствующий элемент перечисления>), который должен установить объекту атрибут с соответствующим значением.

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

Пример:

from rels import Column, Relation

class DESTINATION_ENUM(Relation):
    name = Column(primary=True)
    val_1 = Column()

    records = ( ('STATE_1', 'value_1'),
                ('STATE_2', 'value_2') )

class SOURCE_ENUM(Relation):
    name = Column(primary=True)
    val_1 = Column()
    rel = Column(related_name='rel_source')

    records = ( ('STATE_1', 'value_1', DESTINATION_ENUM.STATE_1),
                ('STATE_2', 'value_2', DESTINATION_ENUM.STATE_2) )


DESTINATION_ENUM.STATE_1.rel_source == SOURCE_ENUM.STATE_1 # True
DESTINATION_ENUM.STATE_2 == SOURCE_ENUM.STATE_2.rel        # True

Взаимодействие со сторонним кодом

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

Пример использования можно найти в самом первом листинге (SOME_CONSTANTS(1) == SOME_CONSTANTS.NAME_1)

## Использование библиотеки

Все необходимые объекты вынесены в корень модуля:

import rels

# Базовые классы
rels.Column # класс столбца
rels.Record # класс элемента перечисления (обычно использовать нет необходимости)
rels.Relation  # базовый клас перечисления

# Простые перечисления
rels.Enum         # простое перечисление со столбцами name и value
rels.EnumWithText # простое перечисление с дополнительным столбцом text

# Прочее
rels.NullObject   # объект заглушка для отсутствующих значений в external столбцах
rels.exceptions   # модуль с исключениями

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

Django

В модуле rels.django реализован небольшой функционал для взаимодействия с Django.

DjangoEnum

Перечисление, унаследованное от EnumWithText, дополнительные методы:

  • choices — возвращает список [<элемент перечисления, текс>, …]

RelationIntegerField

Наследник models.IntegerField, автоматически конвертирует друг в друга целочисленные значения из базы и элементы перечисления.

Конструктор принимает следующие параметры (кроме стандартных для IntegerField):

  • relation — объект отношения

  • relation_column — имя столбца, который сохраняется в базу (по умолчанию, равен "value")

Django Migrations

Django нормально воспринимает RelationIntegerField, но в случае указания параметра default, понадобится править код миграции, так как django ничего о rels не знает.

Тесты

Все «фичи», за исключением связанных с Django, покрыты тестами.

Project details


Download files

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

Source Distribution

Rels-0.3.1.tar.gz (13.3 kB view hashes)

Uploaded Source

Supported by

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