Есть ли способ динамически создавать модели (классы) django?



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

. Например, сначала у нас нет моделей, связанных с нашим бизнес-процессом, пользователи администратора могут определять формат формы на наших фронт страницах, которые будут обрабатываться обычными пользователями позже. Затем наш бэкенд получает запрос от фронтэнда и создает модель формы с предоставленными полями. Я обнаружил, что могу создать класс с помощью функции type(), например type(class_name, (models.Model,), attrs), и я получил новый класс с помощью этой функции.

. Но мне интересно, как сохранить этот класс в нашей системе? Например, сохранить этот новый класс в моем models.py и сделать миграцию, чтобы он был и в нашей БД

. Чтобы упростить это, у меня есть псевдокод, который приведен ниже:

form_class_attrs = {
'test_val': models.FloatField(),
'test_name': models.CharField(),
'__module__': __name__,
}

def test(request):
form_list = ['a', 'b']
for form in form_list:
    form_class_name = "form_%s" % form
    attrs = form_class_attrs.copy()
    form_class = type(form_class_name, (models.Model,), attrs)

Здесь я создаю два класса в цикле for, так как же мне сохранить каждый класс формы в цикле for и соответственно сделать миграции?

Ваши динамически созданные классы после вызова test должны быть эквивалентны эквивалентной декларативной Модели - это правильно.

Для того чтобы django мог использовать эти модели, они должны быть переданы туда, где они будут использоваться. Итак, вы создали переменную form_class, которая является локальной для функции фабрики test: вы должны вернуть это значение за пределы функции и сохранить ссылку на каждый созданный класс в масштабах процесса, чтобы Django мог использовать его. (Не получится вызывать функцию и заново создавать модель каждый раз, когда она вам нужна, так как Django создаст новую модель, которая будет конфликтовать с предыдущей, по крайней мере в необходимых ORM-ресурсах).

Кроме того, не следует создавать экземпляры полей только один раз, для всех классов, и пытаться просто вызвать form_class_attrs.copy на них: так вы попытаетесь повторно использовать одни и те же поля в разных классах. В этом случае вам придется использовать deepcopy - но есть огромный шанс, что и это не сработает, из-за внутренних идентификаторов, которые генерируются при создании полей. Вам придется заново создавать экземпляры полей (т.е. делать фактический вызов .CharField() для каждого динамического класса. Самый простой (но не лучший) способ сделать это - просто переместить объявление attr_fields внутрь цикла for.

Вызов функции фабрики один раз и возвращение всех моделей в списке или словаре будет достаточно для использования моделей - но не для целей миграции (включая создание классов) - механизмы миграции в Django, вероятно, зависят от импорта всех модулей и их проверки на наличие существующих моделей: Я не уверен в этом, но думаю, что это зависит от нахождения моделей в пространстве имен модуля, а не просто "созданных". Таким образом, они должны быть добавлены в пространство имен модуля при импорте - даже если они не используются. В Python это можно сделать, обновив словарь, возвращаемый вызовом globals().

В целом, это должно сработать для вас:



def test():
    form_list = ['a', 'b']
    forms = {}
    for form in form_list:
        attrs = {
            'test_val': models.FloatField(),
            'test_name': models.CharField(),
            '__module__': __name__,
        }
        form_class_name = f"form_{form}" 
        form_class = type(form_class_name, (models.Model,), attrs)
        forms[form_class_name] = form_class

all_forms = test()
globals().update(all_forms)
Вернуться на верх