Hi, I am struggling since some days to extend and implement common class 
inheritance for the SQLAlchemy ORM objects that I define: Here is the to be 
achieved task:
1. In a first step, I want to use sqlalchemy to model my database and the 
ORM, aka define tables, define the ORMs and their relations and some extras 
(association mapper) in Order to make my live easier
2. In a second step I want to take the ORMs and extend the classes with 
some higher level functionality, modeling common operations which will be 
executed in the database.
3. In a third step, I'd like to take the extended ORMs from step 2 and 
extend them again with further convenience functions, which are, however, 
separate from the db-model logic.

To logically isolate and structure the whole thing, I want to wrap each of 
these steps into a dedicated module/package. This I would have solved by 
class Inheritance, which seams to be the natural approach.

However, for all methods which I tried, I cannot figure out how this done 
correctly even in mock scripts. I will post below some example code which 
should demonstrate what I want to achieve.

I tried multiple approaches: 
- with classical mapping (wont work, because i need to explicitly map every 
derived class again, copying all mapper attributes, I would have to double 
paste a lot of code, which is exactly not the point of inheritance)
- with declarative (there is some deep magic to these 
@declarative_attributes, which I do not understand when to use and when 
not, respective at which point they are resolved)
- with hybrid approach, my currently preferred one, as I have at least the 
table definitions done, before messing with the ORM
I could for all of them not find a single solution which would fulfill all 
my needs or which do not throw errors:
Errors include:
- The derived class-names cannot be resolved for the relation to work 
correctly
- The attributes (relation, association_proxy) do not propagate up to the 
derived classes (I have the feeling they bind to the first concrete mapped 
class they are in and inheritance onward is impossible)

If anybody could give me some pointers or make the below code example work 
by some magic alchemistic conjurement I would be very grateful.

Marcel

==================Example Code ===================
import datetime as dt

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import MetaData, Table, Column, Sequence, ForeignKey
from sqlalchemy import Integer, String, DateTime

from sqlalchemy.orm import relationship
from sqlalchemy.ext.associationproxy import association_proxy

from sqlalchemy.orm import Session
from sqlalchemy import create_engine
from sqlalchemy.orm.session import object_session

metadata = MetaData(schema='sandbox')

source_table = Table('source', metadata,
    Column('id', Integer, Sequence("src_id_seq", metadata=metadata), 
primary_key=True),
    Column('name', String(length=32), unique=True, nullable=False, 
index=True),
    Column('created', DateTime(timezone=False), default=dt.datetime.utcnow, 
nullable=False),
    Column('loc_id', ForeignKey(user_table.c.id), nullable=True))

location_table = Table('auth_user_detail', metadata,
    Column('id', Integer,  Sequence("loc_id_seq", metadata=metadata), 
primary_key=True),
    Column('name', String(length=32), unique=True, nullable=False, 
index=True),
    Column('created', DateTime(timezone=False), default=dt.datetime.utcnow, 
nullable=False))
)

# base definition of the Source-class for Mapper
Base = declarative_base(metadata)

class Source_orm(Base):
    __table__ = source_table
    _loc = relationship('Location', uselist=False)
    loc_name = association_proxy('_loc', 'firstname')

    def __init__(self, name):
        self.name = name

# base definition of the Location-class for Mapping
class Location_orm(Base):
    __table__ = location_table

    def __init__(self, name):
        self.name = name


#-------------------
# Higher functions - lvlA : possibly packed into a different module

class Source_lvlA(Source_orm):
    @classmethod
    def get_by_name(cls, session, name):
        return session.query(cls).filter(cls.name == name).one()

    def move_to_loc_by_name(self, loc_name):
        session = object_session(self)
        loc = session.query(Location).filter(Location.name == 
loc_name).one()
        self._loc = loc
        session.commit()

class Location_lvlA(Location_orm):
    @classmethod
    def get_by_name(cls, session, name):
        return session.query(cls).filter(cls.name == name).one()

    def move_src_here(self, src):
        session = object_session(self)
        src.loc_id = self.id
        session.merge(src)
        session.commit()

#-------------------
# Even Higher functions - lvlB : possibly packed into a different module

class Source_lvlB(Source_lvlA):
    def assemble_info(self):
        return f"<Source> {self.name} at <Location> {self.loc_name}"

class Location_lvlB(Location_lvlA):
    def assemble_info(self):
        return f"<Location> {self.name}"


if __name__ == '__main__':
    engine = create_engine("sqlite://", echo=True)
    engine.execute("""DROP SCHEMA IF EXISTS {schema} CASCADE; 
""".format(schema=metadata.schema))
    engine.execute("""CREATE SCHEMA 
{schema};""".format(schema=metadata.schema))

    Base.metadata.create_all(engine)
    session = Session(engine)

    # create low level objects
    s = Source_ormA('MySource')
    s = session.add(a)
    session.flush()
    l = Location_lvlA('MyLocation')
    l = session.add(l)
    session.flush()

    # operate on the db use a higher level function
    s = Source_lvlA.get_by_name(session, 'MySource')
    s.move_to_loc(Location_lvlB.get_by_name('MyLocation'))

    # use highest level functionality
    s = Source_lvlB.get_by_name(session, 'MySource').assemble_info()

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/ed3a4914-2b37-4f2e-b213-3c615b85c367%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to