0

I have a class that acts as a PostgreSQL database interface. It has a number of methods which do things with the MetaData, such as get table names, drop tables, etc. These methods keep calling the same two lines to set up MetaData. I am trying to tidy this up by abstracting this MetaData setup into its own function which is initiated when the class is instantiated, but this isn't working, as the function keeps returning NoneType instead of the MetaData instance.

Here is an example of the class, BEFORE adding the MetaData function:

class Db:

    def __init__(self, config):
        self.engine = create_async_engine(ENGINE, echo=True, future=True)
        self.session = sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)

    def get_table_names(self):
        meta = MetaData()
        meta.reflect(bind=sync_engine)
        meta = self.meta()
        return meta.tables.keys()

This works well, returns a list of table keys:

dict_keys(['user', 'images', 'session'])

When I try to shift the MetaData call into its own function like so:

class Db:

    def __init__(self, config):
        self.engine = create_async_engine(ENGINE, echo=True, future=True)
        self.session = sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)
        self.meta = self.get_metadata()
    
    def get_metadata(self):
        meta = MetaData()
        return meta.reflect(bind=sync_engine)

    def get_table_names(self):
        return self.meta.tables.keys()

It returns this error:

in get_table_names  return self.meta.tables.keys()
AttributeError: 'NoneType' object has no attribute 'tables'

How can I achieve this sort of functionality by calling self.meta() from within the various class methods?

alphaomega
  • 137
  • 1
  • 15

2 Answers2

2

Reflect alters the current metadata in-place. So you can just return the meta variable explicitly.

class Db:
    # ...

    def get_metadata(self):
        meta = MetaData()
        meta.reflect(bind=sync_engine)
        return meta
    
    # ...

Although it might be better to do this in a factory function, like def db_factory(config): and inject these things already prepped in the class constructor, def __init__(self, metadata, engine, session):. Just a thought.

Ian Wilson
  • 6,223
  • 1
  • 16
  • 24
0

Just wanted to post an answer, as with someone else's help I was able to solve this. The code should look like this:

class Db:

def __init__(self, config):
    self.engine = create_async_engine(ENGINE, echo=True, future=True)
    self.session = sessionmaker(self.engine, expire_on_commit=False, class_=AsyncSession)
    self._meta = MetaData()

@property
def meta(self):
    self._meta.reflect(bind=sync_engine)
    return self._meta

def get_table_names(self):
    return self.meta.tables.keys()
alphaomega
  • 137
  • 1
  • 15