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 [email protected].
To post to this group, send email to [email protected].
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.