So I'm new to this python and sqlalchemy. I need some help with inheritance or maybe a mixin (but rather inheritance).
I have some psudo code but I haven't really made any progress to get anywhere:
Base = declarative_base()
class ModelBase(Base):
"""Base model that only defines last_updated"""
__tablename__ = 'doesnotexistandtheclassshouldnotbeinstantiated'
#all tables inheriting from ModelBase will have this column
last_updated = Column(DateTime)
def __init__(self, last_updated):
self.last_updated = last_updated
class User(ModelBase):
"""Defines the user but should also have the last_updated inherited from ModelBase"""
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
def __init__(self, ....):
ModelBase.__init__(last_updated)
I want all tables inheriting from ModelBase to also have last_updated. How would I do that?
UPDATED CODE:
class BaseUserMixin(object):
"""Base mixin for models using stamped data"""
@declared_attr
def last_updated(cls):
return Column(DateTime)
@declared_attr
def last_updated_by(cls):
return Column(String)
def __init__(self, last_updated, last_updated_by):
self.last_updated = last_updated
self.last_updated_by = last_updated_by
Base = declarative_base(cls=BaseUserMixin)
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String)
fullname = Column(String)
password = Column(String)
enabled = Column(Boolean)
def __init__(self, name, fullname, password, email, last_updated, last_updated_by):
self.name = name
self.fullname = fullname
self.password = password
self.email = email
# goes wrong here
super(User, self).__init__(last_updated, last_updated_by)
def __repr__(self):
return "<User('%', '%', '%', '%', '%', '%')>"\
% (self.name,
self.fullname,
self.password,
self.email,
self.last_updated,
self.last_updated_by
)
The error is:
_declarative_constructor() takes exactly 1 argument (3 given)
What can be the problem? I thought it was working but when re-running the debugger it failed.
The solution is declared_attr
; which will be called and added to instances of DeclarativeMeta anytime they appear:
Edit: the __init__
automagically provided by declarative
cannot call super()
. if you want it, it has to be last, and the only way to do that is to use a regular mixin.
import datetime
from sqlalchemy import Column, DateTime, Integer, String
from sqlalchemy.ext.declarative import declared_attr, declarative_base
class BaseMixin(object):
@declared_attr
def last_updated(cls):
return Column(DateTime)
def __init__(self, last_updated, *args, **kwargs):
super(BaseMixin, self).__init__(last_updated=datetime.datetime.now(), *args, **kwargs)
print "BaseMixin.__init__"
self.last_updated = last_updated
ModelBase = declarative_base()
Note that the mixin must come first!
class User(BaseMixin, ModelBase):
"""Defines the user but should also have the last_updated inherited from ModelBase"""
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
username = Column(String)
def __init__(self, *args, **kwargs):
super(User, self).__init__(last_updated=datetime.datetime.now(), *args, **kwargs)
print "User.__init__"
if __name__ == '__main__':
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///:memory:', echo=True)
ModelBase.metadata.create_all(engine)
user = User(username='alice')
Session = sessionmaker(engine)
session = Session()
session.add(user)
session.commit()
However; Are you sure you want to use __init__
for this in the first place? __init__
is not called when objects are returned from queries; and what you really want is for the column to change to right now when it's modified. That's baked into Column()
already:
from sqlalchemy import func
class BaseMixin(object):
@declared_attr
def created_date(cls):
return Column(DateTime, default=func.now())
@declared_attr
def modified_date(cls):
return Column(DateTime, default=func.now(), onupdate=func.now())
back to using the cls=
argument
ModelBase = declarative_base(cls=BaseMixin)
class User(ModelBase):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
username = Column(String)
if __name__ == '__main__':
engine = create_engine('sqlite:///:memory:', echo=True)
ModelBase.metadata.create_all(engine)
user = User(username='alice')
Session = sessionmaker(engine)
session = Session()
session.add(user)
session.commit()
session = Session()
sameuser = session.query(User).one()
sameuser.username = 'bob'
session.commit()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With