Django: динамическая / программная настройка полей модели - PullRequest
0 голосов
/ 18 октября 2019

Я пытаюсь создать таблицы с полями на основе метаданных, объявленных в других классах. Мой текущий подход выглядит примерно так:

metadata = { ... }
class CustomModel(MyBaseModel):
    field1 = CharField(...)
    field2 = CharField(...)
    ...

for key, info in metadata.items():
    setattr(CustomModel, key, fieldFrom(info))

В настоящее время происходит то, что таблица создается и поля, объявленные в классе, включаются в миграцию.

НО поля, включенные через setattr не включаются в миграцию, даже если они правильно отображаются в классе при проверке с помощью отладчика. Есть ли магия, которая работает только для полей, объявленных «на месте»? Как я могу динамически установить эти поля?

РЕДАКТИРОВАТЬ: модели все еще статичны. Суть здесь в том, что когда я делаю изменения в исходных метаданных, эти изменения будут распространяться (т.е.) на несколько моделей и / или полей. Без этого мне пришлось бы полностью добавить поля к нескольким моделям вручную.

EDIT2:

Я приведу пример, который на самом деле не является моим текущим случаем, но также применим. Представьте, что у меня есть файл openAPI (swagger) где-то внутри моего проекта, и я хотел динамически создавать таблицы на основе его ключа definitions. В этом нет ничего страшного, верно?

Этот файл swagger.json будет статичным. Всякий раз, когда я вносил в него изменения, я запускал makemigrations, и он добавлял необходимые изменения в мою БД.

(Теперь, пожалуйста, не приходите с ", но вы не должны создавать данные изswagger.json ", это не главное в моем вопросе - и это даже вымышленный пример. Я не просил совета по архитектуре, спасибо!)

1 Ответ

0 голосов
/ 18 октября 2019

Модели Django используют функцию "метаклассов" в Python.

Краткое объяснение того, что я понял о метаклассах:

  • Когда вы объявляете класс, это несколько "десугар" длявызов вызываемого. Как правило, type("ClassName", (extends.list,), props), type является корневым метаклассом;
  • Когда вы устанавливаете метакласс, вы (вроде) заменяете type() чем-то другим;
  • Theresтакже дополнительная магия вокруг, так как установлены некоторые дополнительные магические поля, такие как __module__;
  • Вы устанавливаете новый метакласс, записывая class X(metaclass=Y):;
  • Вы можете переопределить объявление класса, установив __new__ на своем метаклассе;__new__ должен вернуть переназначенный класс (не путать с его экземпляром)

Для текущего примера мы получаем цепочку наследования, например:

class MyModel(models.Model)
class Model(metaclass=BaseModel) #django's
class BaseModel(type)            #django's

BaseModel переопределяет __new__ и выполняет логику при объявлении класса. Вы можете увидеть результат, проверив MyModel, в котором будет поле _meta с тоннами метаданных.

Когда я использовал setattr, метаданные модели не обновлялись, поскольку только базовый класс делает это. что в init.

Исправление: При просмотре BaseModel я нашел метод add_to_class, который добился цели. Работает на данный момент. Могут ли быть проблемы? Я надеюсь, что нет.

MyModel.add_to_class('my_field_name', CharField(...))
...