'Column Inheritance for Independent ORM Mapper Classes In SQL Alchemy
I am trying to replicate the following SQLite CREATE TABLE
statements using the declarative ORM mapper classes in SQLALchemy.
CREATE TABLE IF NOT EXISTS question (
id INTEGER NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
body TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS solution (
id INTEGER NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
body TEXT NOT NULL,
source_code TEXT NOT NULL,
runtime_complexity TEXT NOT NULL,
storage_complexity TEXT NOT NULL,
understood TEXT NOT NULL
);
Is there a way to specify inheritance in ORM mapper classes such that the question
and solution
tables remain independent (i.e. Querying against a subclass in the hierarchy should not render as an SQL JOIN along all tables in its inheritance path) while also reducing the amount of repeated code shown below? As indicated in the comments below, these mapper classes have some of the same columns with the same names and datatypes, and have the same __repr__
methods.
from sqlalchemy import Column, Sequence, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Question(Base):
__tablename__ = 'question'
# vvv Both Solution and Question classes have these columns
id = Column(Integer, Sequence('id_seq'), primary_key=True)
title = Column(String, nullable=False)
body = Column(Integer, nullable=False)
# ^^^
# vvv Both Solution and Question classes have this method
def __repr__(self):
class_name = self.__class__.__name__
column_names = tuple(col.name for col in self.__table__.columns)
return f"<{class_name}{column_names}>"
# ^^^
class Solution(Base):
__tablename__ = 'solution'
# vvv same in Question class
id = Column(Integer, Sequence('id_seq'), primary_key=True)
title = Column(String, nullable=False)
body = Column(Integer, nullable=False)
# ^^^
source_code = Column(Integer, nullable=False)
runtime_complexity = Column(Integer, nullable=False)
storage_complexity = Column(Integer, nullable=False)
understood = Column(Integer, nullable=False)
# vvv same in Question class
def __repr__(self):
class_name = self.__class__.__name__
column_names = tuple(col.name for col in self.__table__.columns)
return f"<{class_name}{column_names}>"
# ^^^
From what I read in the Mapping Class Inheritance Hierarchies page in the SQLAlchemy docs, there are three methods of inheritance:
SQLAlchemy supports three forms of inheritance:
- single table inheritance, where several types of classes are represented by a single table.
- concrete table inheritance, where each type of class is represented by independent tables.
- joined table inheritance, where the class hierarchy is broken up among dependent tables, each class represented by its own table that only includes those attributes local to that class..
That section would lead me to assume that concrete table inheritance is what I need. However, under the concreate table inheritance section, it reads:
Concrete inheritance maps each subclass to its own distinct table, each of which contains all columns necessary to produce an instance of that class.
Here's what I'm trying to accomplish (roughly). Is something like this possible in SQLAlchemy?
from sqlalchemy import Column, Sequence, Integer, String, DateTime
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class QS(Base):
# an abstract class that does NOT map to any table in the database.
# just holds column names and column data types,
# along with methods common to question and solution mapper classes
__abstract__ = True
id = Column(Integer, Sequence('id_seq'), primary_key=True)
title = Column(String, nullable=False)
body = Column(Integer, nullable=False)
def __repr__(self):
class_name = self.__class__.__name__
column_names = tuple(col.name for col in self.__table__.columns)
return f"<{class_name}{column_names}>"
class Question(QS):
__tablename__ = 'question'
# actually maps to table called question
# which has columns id, title, body
class Solution(QS):
__tablename__ = 'solution'
# actually maps to table called solution,
# which has columns id, title, body, source_code, runtime_complexity, and storage_complexity
# (has a few more columns than the base class)
source_code = Column(Integer, nullable=False)
runtime_complexity = Column(Integer, nullable=False)
storage_complexity = Column(Integer, nullable=False)
understood = Column(Integer, nullable=False)
Solution 1:[1]
Thanks to @rfkortekaas' comment, which suggested the use of Mixin Classes, I was able to declare the parent class Document
from which Question
and Solution
inherit.
from sqlalchemy import Column, String, Sequence, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import declarative_mixin
from sqlalchemy.orm import declared_attr
Base = declarative_base()
@declarative_mixin
class Document:
def __repr__(self):
return f"<{self.__class__.__name__}{self.__dict__}>"
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
id = Column(Integer, Sequence('id_seq'), primary_key=True)
title = Column(String, nullable=False)
body = Column(String, nullable=False)
class Question(Base, Document):
pass
class Solution(Base, Document):
source_code = Column(String, nullable=False)
runtime_complexity = Column(String, nullable=False)
storage_complexity = Column(String, nullable=False)
understood = Column(String, nullable=False)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |