Есть ли способ динамически создавать модели (классы) 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)