Take the following simplified class structure which is used to clean and structure data from a legacy system.
- ProductModels contain fields
- Fields are their own classes and types on which a bunch of cleaning is done.
Overall the approach works like a charm in our use-case. But there is an issue where newly set products hold values from a previously generated product is the field is unused.
The minimal reproducible structure for Field / ProductModel looks like:
class BaseManager:
database_settings = {}
def __init__(self, model_class):
self.model_class = model_class
def build_query(self):
pass
def connection(self):
pass
def run_query(self):
pass
def select(self):
pass
class MetaModel(type):
manager_class = BaseManager
def __new__(mcs, name, bases, attrs):
field_list = []
for k, v in attrs.items():
if isinstance(v, Field):
v.field_name = k
v.table_name = attrs.get('table_name')
field_list.append(k)
cls = type.__new__(mcs, name, bases, attrs)
cls.__field_list__ = field_list
return cls
def _get_manager(cls):
return cls.manager_class(model_class=cls)
@property
def objects(cls):
return cls._get_manager()
class Field:
def __init__(self, field_name, value=None):
self.field_name = field_name
self.value = value
def set_value(self, value):
self.value = value
class ProductModel(metaclass=MetaModel):
sku = Field('sku')
name = Field('name')
table_name = 'my_table'
def __init__(self, **field_data):
for field_name, value in field_data.items():
getattr(self, field_name).set_value(value)
def __str__(self):
return f"{self.sku.value=}, {self.name.value=}"
Now look at the first example:
...: prod = ProductModel(sku='124', name='Name')
...: print(prod)
self.sku.value='124', self.name.value='Name'
The value for the sku = 124, which is correct. The value for the name = "Name", which is also correct.
But now, the second example:
...: prod_two = ProductModel(sku='789')
...: print(prod_two)
self.sku.value='789', self.name.value='Name'
The value for sku has changed to 789, correct. BUT the value for name, has remained "Name" instead of being None
It seem that when I create a new product, the field values are somehow kept from the initial product instead of being re-initialised.
One way of handling it, would be to reset all the field values upon a new ProductModel.init(). But this feels like a poor solution. Instead I would rather understand better how to initialise the classes correctly.
Can you show me the right way?
This defines something known as class attribute. These are supposed to be shared between different instances of the class. In fact, would you check back on your
prodafter creatingprod_two, you will see that it had changed value to789. That shows us that bothprod.skuandprod_two.skuare the same object.In order to define instance attributes, you need to assign them on instance, for example in the
__init__.