This error has a bit of a story to give further context: I made this bit of code because if I create a SQLAlchemy Relationship with a lambda function inside, it will only initialize the Relationship after all the models have been created. What it does is basically fill all models in a dictionary (self._all_models
) and the lambda function will then later get the model by accessing (self._all_models[table_name]['model']
). So when I do a session.add(Model)
, this lambda function will be called and try to get the model from the dictionary.
I executed it just fine, but my colleagues had a weird problem: The self
instance was "old", and what I mean by that is that the dictionary self._all_models
had only the models that were created so far, and not all the models. For example, if it had to create 3 models (a
,b
,c
), when the lambda inside b
's Relationship were to be called, the self._all_models
would only have {'a': ...}
instead of having all 3 ({'a': ..., 'b': ..., 'c': ...}
).
The only difference between me and my colleagues is that I run Python 3.9, and they run Python 3.8. And when one of my colleagues – "Jack" – upgraded his Python to 3.9, it ran smoothly (even after changing back to his 3.8 venv); but my other colleague – "Mark" – didn't have as much luck: it still didn't work for him even after upgrading.
Comparing their libs with pip freeze
didn't reveal anything different, and it's very unlikely that the problem lies in SQLAlchemy.
Code:
class ORM:
def __init__(self, source_name: str, ..., address_http_protocol: str):
self._all_models = defaultdict(lambda: {})
...
self._create_all_models()
...
def _create_all_models(self) -> None:
base = declarative_base()
base.metadata.reflect(self._get_engine())
all_tables = base.metadata.tables
for table in all_tables.values():
current_table_name = table.name
relationships = self._get_parents(table)
obj = type(current_table_name, (object,), dict())
mapper(obj, table,
properties={table_name: relationship(lambda table_name=table_name:
self._get_model_by_table_name(table_name), lazy='select')
for table_name in relationships})
self._all_models[current_table_name]['model'] = obj
self._all_models[current_table_name]['table'] = table
@staticmethod
def _get_parents(table: Table) -> set:
parents = set()
if table.foreign_keys:
for fk in table.foreign_keys:
parent_name = fk.column.table.name
parents.add(parent_name) if parent_name != table.name else None
return parents
def _get_model_by_table_name(self, table_name: str) -> type:
if isinstance(table_name, str):
return self._all_models[table_name]['model']
I'm talking about the lambda function inside _create_all_models
: lambda table_name=table_name: self._get_model_by_table_name(table_name)
.