I want to dynamically generate a class based on the unit field like this:
def gm_a(unit):
tname = '%s_a' % unit
for c in db.Model._decl_class_registry.values():
if hasattr(c, '__table__') and c.__table__.fullname == tname:
return c
class A(DynamicBase):
__tablename__ = '%s_a' % unit
id = db.Column(db.Integer, primary_key=True)
......# other fields
return A
You can see if I want to use table_a or desk_a table in ORM operations, I can do this:
@app.route('/<unit>/a')
def a(unit):
obj_table = gm_a('table').query.filter_by(xxx).all()
obj_desk = gm_a('desk').query.filter_by(xxx).all()
In this way, we can operate on tables with different table names of the same structure. There is a problem that if we have 3 gm_* functions(gm_a,gm_b,gm_c) and 3 routes (/<unit>/a
,/<unit>/b
,/<unit>/c
), each template like this:
<ul>
<li><a href="a">A</a></li>
<li><a href="b">B</a></li>
<li><a href="c">C</a></li>
</ul>
If we click on these links at random, we expect to generate 3 classes in db.Model._decl_class_registry.values() and 3 tables in db.metadata.tables.
---
[<class '__main__.gm_a.<locals>.A'>, <class '__main__.gm_b.<locals>.B'>, <class '__main__.gm_c.<locals>.C'>]
immutabledict({'table_a': Table('table_a', MetaData(bind=None), Column('id', Integer(), table=<table_a>, primary_key=True, nullable=False), schema=None), 'table_b': Table('table_b', MetaData(bind=None), Column('id', Integer(), table=<table_b>, primary_key=True, nullable=False), schema=None), 'table_c': Table('table_c', MetaData(bind=None), Column('id', Integer(), table=<table_c>, primary_key=True, nullable=False), schema=None)})
127.0.0.1 - - [12/Jul/2018 09:58:03] "GET /table/b HTTP/1.1" 200 -
---
[<class '__main__.gm_a.<locals>.A'>, <class '__main__.gm_b.<locals>.B'>, <class '__main__.gm_c.<locals>.C'>]
immutabledict({'table_a': Table('table_a', MetaData(bind=None), Column('id', Integer(), table=<table_a>, primary_key=True, nullable=False), schema=None), 'table_b': Table('table_b', MetaData(bind=None), Column('id', Integer(), table=<table_b>, primary_key=True, nullable=False), schema=None), 'table_c': Table('table_c', MetaData(bind=None), Column('id', Integer(), table=<table_c>, primary_key=True, nullable=False), schema=None)})
127.0.0.1 - - [12/Jul/2018 09:58:04] "GET /table/c HTTP/1.1" 200 -
---
[<class '__main__.gm_a.<locals>.A'>, <class '__main__.gm_b.<locals>.B'>]
immutabledict({'table_a': Table('table_a', MetaData(bind=None), Column('id', Integer(), table=<table_a>, primary_key=True, nullable=False), schema=None), 'table_b': Table('table_b', MetaData(bind=None), Column('id', Integer(), table=<table_b>, primary_key=True, nullable=False), schema=None), 'table_c': Table('table_c', MetaData(bind=None), Column('id', Integer(), table=<table_c>, primary_key=True, nullable=False), schema=None)})
127.0.0.1 - - [12/Jul/2018 09:58:04] "GET /table/b HTTP/1.1" 200 -
---
[<class '__main__.gm_a.<locals>.A'>, <class '__main__.gm_b.<locals>.B'>]
immutabledict({'table_a': Table('table_a', MetaData(bind=None), Column('id', Integer(), table=<table_a>, primary_key=True, nullable=False), schema=None), 'table_b': Table('table_b', MetaData(bind=None), Column('id', Integer(), table=<table_b>, primary_key=True, nullable=False), schema=None), 'table_c': Table('table_c', MetaData(bind=None), Column('id', Integer(), table=<table_c>, primary_key=True, nullable=False), schema=None)})
127.0.0.1 - - [12/Jul/2018 09:58:04] "GET /table/a HTTP/1.1" 200 -
As you can see in pic or code, We have already clicked a, b, c before, so there are three in class and table. But once we clicked b, there is only a, b in .vales(). Since there is no table_c class, it will be regenerated in our program logic, but table_c does exist in Tables. So it will throw an exception:
sqlalchemy.exc.InvalidRequestError: Table 'table_c' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
I am very confused why the number in db.Model._decl_class_registry.values() will change randomly and the number of _decl_class_registry.values() and db.metadata.tables() will be different. I also use the get_class_by_table function in sqlalchemy-utils, but the principle is consistent with our method and doesn't work.
Does anyone know why?Thx.