Reputation: 1363
I am attempting to create tables with fields based on metadata declared on other classes. My current approach is a bit like this:
metadata = { ... }
class CustomModel(MyBaseModel):
field1 = CharField(...)
field2 = CharField(...)
...
for key, info in metadata.items():
setattr(CustomModel, key, fieldFrom(info))
What currently happens is that the table is created and the fields declared in class included in the migration.
BUT the fields included through setattr
are not getting included in the migration, even tough they correctly appear in the class when inspecting with the debugger. Is there any magic that only works for fields declared “in-place”? How could I dynamically set those fields?
EDIT: The models are still static. The gist here is that when I make changes to source metadata, those changes would propagate to (i.e.) several models and/or fields. Without this, I'd have to exhaustively add fields to several models manually.
EDIT2:
I'm gonna hand an example that is not really my current case but also applies. Imagine I had an openAPI (swagger) file somewhere inside my project and I wanted to dynamically create tables based on its definitions
key. This should be no big deal, right?
This swagger.json file would be static. Whenever I made changes to it, I'd run makemigrations
and it would add the necessary changes to my DB.
(Now please, don't come with "but you shouldn't be creating data from a swagger.json", this is not the focus of my question -- and this is even a fictional example. I didn't ask for architecture advice, thank you!)
Upvotes: 0
Views: 1277
Reputation: 1363
Django models use the "metaclasses" feature of python.
Short explanation of what I understood of metaclasses:
type("ClassName", (extends.list,), props)
, type
being the "root metaclass";type()
with something else;__module__
are set;class X(metaclass=Y):
;__new__
on its metaclass; __new__
should return the remapped class (not to be confused with its instance)For the current sample, we get an inheritance chain of something like:
class MyModel(models.Model)
class Model(metaclass=BaseModel) #django's
class BaseModel(type) #django's
BaseModel
overrides __new__
and performs logic on class declaration. You can see the result by inspecting MyModel
, which will have a _meta
field with tons of metadata.
When I had used setattr
, the model metadata didnt get updated since the base class only does that on init.
The fix:
When looking into BaseModel
, I found the add_to_class
method which does the trick. Currently working. Might have issues? I hope not.
MyModel.add_to_class('my_field_name', CharField(...))
Upvotes: 2