I have the following model:
def get_my_model(name1, name2):
class MyModel(Model):
class Meta:
table1 = name1
table2 = name2
return MyModel
I am trying to write this without the function and want to pass the arguments name1
and name2
directly to the Meta class but I'm not sure how. I tried using constructors but it somehow doesn't work.
class MyModel(Model):
def __init__(self, name1, name2):
self.name1 = name1
self.name2 = name2
class Meta:
table1 = self.name1
table2 = self.name2
I also tried setting the constructor on the meta class but didn't work either. Anyone an idea?
CodePudding user response:
TL;DR:
- class (
MyModel
) or any higher scope's variable up from the class namedMeta
- modify
ModelBase
's metaclass (patch the file or inherit from)
It's not a metaclass! It's just a class in other class's scope named Meta
. What's happening is that Python has separate contexts/scopes once the execution environment starts building:
- interpreter
- global (the original namespace you're given once you start the interpreter,
globals()
)
then the nested ones within the global namespace:
- module/file
- class
- function
- perhaps some other(s)
You can't pass a parameter to the Meta
class because it's just declared in there. It's not called. Parallel to this would be passing a parameter to a class declaration from the module scope:
# module.py
class MyClass:
value = <how?>
Once you find the place where it's called, then you can inject the parameters by modifying the caller function.
class Main:
class Meta:
def __init__(self, *args, **kwargs):
print("Meta", args, kwargs)
def __init__(self, name):
Main.Meta(name)
print(Main(123))
If I don't explicitly call Main.Meta()
, the Meta
class in this example won't be instantiated.
In case of Django, the Meta
class is pulled via getattr()
for the model class here, therefore you need to target ModelBase.__new__()
with super()
/copy-paste to modify the function so it accepts custom arguments, or, pass them as just class variables (how it's mostly done in Django / DRF).
class Main:
class Meta:
class_var = 123
Judging from the implementation of Model
class in Django you might be able to swap the metaclass
value, but I'm not sure about the inheritance as the __new__()
of a metaclass is executed when you declare the class that uses it, thus inheriting like this breaks:
# the original ModelBase from class Model(metaclass=ModelBase)
class Meta:
def __new__(cls, *_, **__):
print("Hello")
return cls
class MyMeta(Meta):
def __new__(cls, *_, **__):
print("Hi")
# implement your stuff here or copy-paste patch
return cls
class Model(metaclass=Meta):
pass
class CustomModel(metaclass=MyMeta):
pass
class CustomModelWithInheritance(Model, metaclass=MyMeta): # boom
pass
For metaclasses check:
Regarding self
: The naming itself doesn't matter, nor will work where you use it, because the self
is just an implicitly passed instance of a class into a method (a function with a reference to a class instance):
class MyClass:
def func(self_or_other_name):
print(self_or_other_name)
MyClass().func()
The same way behaves a cls
argument in a __new__()
method when creating a class within a metaclass i.e. it's a reference to the metaclass instance (a class declaration in a namespace), for which the "description" is the metaclass that creates it.
cls = type("MyClass", (), {}) # create an instance of "type"
cls # <class '__main__.MyClass'>
# "cls" is class and an instance at the same time
The only "special" variable you can use to refer to the class scope is the class' name anything defined within the class scope as a variable anything in a higher scope be it from a nested class, module or others:
class MyClass:
print(MyClass)
name = 123
print(name)
MyClass()
so for this case it'll be:
class MyModel(Model):
def __init__(self, name1, name2):
MyModel.name1 = name1 # class variable
MyModel.name2 = name2
class Meta:
table1 = MyModel.name1 # class variable
table2 = MyModel.name2