Рубрики
Без рубрики

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

Некоторые примеры, которые вы можете найти в Интернете, будут описывать создание моделей для разделов вручную. Что… Tagged с Django, Python, Postgres.

Некоторые примеры, которые вы можете найти в Интернете, будут описывать создание моделей для разделов вручную. Как больно! Это не допускает динамического расширения и использования данных. Это короткая статья, касающаяся решения Django, чтобы динамически создать модели ORM для отдельных разделами таблицы. Бэкэнд базы данных – PostgreSQL для тех, кто заинтересован.

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

Сначала я попытался динамически создать подкласс разделенной таблицы. Здесь будь драконами! Первоначально меня больше всего удивило, так это то, что он работал с точки зрения компиляции и создания объекта класса. То, что было резким, было мое мягкое, так это то, что были созданы динамически созданные поля указателей для суперкласса и вызвали бы свой собственный хаос. Так что делать? Теперь мне пришлось выяснить, как создать необходимые мета-внутренние классы и определения поля со всеми правильными (или, по крайней мере, с учетными) параметрами, установленными. Со всеми множеством вариантов для полей это сложная задача.

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

._meta.fields[].db_type_parameters()

Это возвращает DICT всех параметров, которые были установлены для поле (включая любые производные). Я пытался просто использовать DICT напрямую, но вы получаете ошибки из -за производных полей (те ключи, начинающиеся с подчеркивания, среди других). Были также проблемы, потому что к и on_delete не населены в этом дикте для Иностранный кладж поля. Таким образом, зная минимальное количество параметров, необходимых для правильного создания класса модели ORM для разделения таблицы, я построил набор «допустимых» ключей, чтобы быть минимумом для создания поля модели, и мне пришлось исследовать полевое отношение к Получите к и on_delete Значения для Иностранный кладж поля.

Вот окончательная форма набора функций:

from django.db import transaction
from django.db.models.fields.related import RelatedField

def _create_partition_model(partition_info):
    class _Meta:
        db_table = partition_info.table_name

    model = get_model(partition_info.partitioned_table_name)

    partition_model_name = model.__name__ + _get_partition_model_suffix(partition_instance)

    partition_model_attrs = {
        "Meta": _Meta,
        "__module__": model.__module__,
    }
    allowed_fields = {"null", "primary_key", "default", "editable", "serialize", "db_column", "base_field"}

    for field in model._meta.fields:
        partition_field_attrs = {
            k: v for k, v in field.db_type_parameters(transaction.get_connection()).items() if k in allowed_fields
        }
        if isinstance(field, RelatedField):
            partition_field_attrs["to"] = field.related_model.__name__
            partition_field_attrs["on_delete"] = models.DO_NOTHING
            for rel_obj in field.related_model._meta.related_objects:
                if rel_obj.remote_field == field:
                    partition_field_attrs["on_delete"] = rel_obj.on_delete

        partition_model_attrs[field.name] = type(field)(**partition_field_attrs)

    _model = type(partition_model_name, model.__bases__, partition_model_attrs)

    return _model

Давайте разберем это:

def _create_partition_model(partition_info):
    class _Meta:
        db_table = partition_info.table_name

    model = get_model(partition_info.partitioned_table_name)

    partition_model_name = model.__name__ + _get_partition_model_suffix(partition_instance)

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

partition_info Объект – это просто экземпляр объекта, который содержит информацию о разделе целевой таблицы.

Заменитель get_model () С любыми способами вы бы использовали для динамического получения модельных классов от Django. Это будет какая -то форма django.apps.apps.all_models наверное.

Это предполагает, что разделы названы как (deartitioned_table_name + partition_suffix) В этой функции имя модели раздела (имя класса модели ORM) получено из имени класса разделенного таблицы плюс суффикс имени раздела Table DB. Заменитель _get_partition_model_suffix С любыми способами вы бы использовали для получения суффикса раздела таблицы или с любым именем, которое вы предпочитаете.

    partition_model_attrs = {
        "Meta": _Meta,
        "__module__": model.__module__,
    }
    allowed_fields = {"null", "primary_key", "default", "editable", "serialize", "db_column", "base_field"}

Теперь мы создаем атрибуты класса для нового класса. Ald_fields – это базовый набор необходимых полей (в любом случае, в нашем приложении) для создания поля. Отрегулируйте эти значения по мере необходимости. Ты Уилл нужен base_field для таких вещей, как массив другого типа.

    for field in model._meta.fields:
        partition_field_attrs = {
            k: v for k, v in field.db_type_parameters(transaction.get_connection()).items() if k in allowed_fields
        }
        if isinstance(field, RelatedField):
            partition_field_attrs["to"] = field.related_model.__name__
            partition_field_attrs["on_delete"] = models.DO_NOTHING
            for rel_obj in field.related_model._meta.related_objects:
                if rel_obj.remote_field == field:
                    partition_field_attrs["on_delete"] = rel_obj.on_delete

        partition_model_attrs[field.name] = type(field)(**partition_field_attrs)

Теперь мы строим все поля для нового класса из поля ORM с разделенным таблицей. Обратите внимание, что мы ограничиваем параметры поля для ALLED_FIELDS набор.

Когда мы обнаружили класс типа поля, полученный из Связанный поле Нам нужно установить к и on_delete должным образом. к Атрибут прост. on_delete Атрибут немного сложнее. Решение, которое я обнаружил, что работает хорошо, состоит в том, чтобы пройти через связанные объекты поля, чтобы найти одно отношение, которое указывает на рассматриваемое поле. Там может быть лучший способ сделать это.

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

    _model = type(partition_model_name, model.__bases__, partition_model_attrs)

    return _model

Наконец, мы создаем класс модели, используя type () Анкет Здесь было важно использовать __bases__ Из модели разделенной таблицы. Использование модели напрямую создает проблемный подкласс.

Крутая часть заключается в том, что Django также зарегистрирует новый класс Model в своем all_models Дикт из -за метапреграммы, происходящего под капюшоном.

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

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

Я надеюсь, что эти проекты, использующие разделение в PostgreSQL и использование Django для WebApp, найдут это полезным. Меня действительно удивило бы, если бы не было какого -то способа сделать это с SQLALCHEMY.

Оригинал: “https://dev.to/redhap/dynamically-create-orm-models-for-individual-table-partitions-416n”