SQLAlcyemy で repr を自動で
メモ。
SQLAlchemy でモデルクラスを定義していて、__repr__ を毎回定義するのがダルい。
かといって定義しないと、デバッグ時(主にプリントデバッグ)に中身が出ないのも困る。
# -*- coding: utf-8 -*- from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship from db import Base class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50), unique=True) email = Column(String(120), unique=True) def __init__(self, name=None, email=None): self.name = name self.email = email def __repr__(self): # これを毎回書きたくない return 'User(id={0}, name={1}, email={2})'.format( self.id, self.name, self.email )
方法はないかと思ってググったら、以下がヒットした。
http://foobar.lu/wp/2013/07/05/automagic-__repr__-for-sqlalchemy-entities-with-primary-key-columns-with-declarative-base/
試してみたけど、プライマリーキーが取れるだけだった。
次にヒットしたのが、sqlalchemy - Python __repr__ and None - Stack Overflow。
真似してみた。
# -*- coding: utf-8 -*- from sqlalchemy import create_engine from sqlalchemy.orm import scoped_session, sessionmaker from sqlalchemy.ext.declarative import declarative_base class RepresentableBase(object): def __repr__(self): """Dump all columns and value automagically. This code is copied a lot from followings. See also: - https://gist.github.com/exhuma/5935162#file-representable_base-py - http://stackoverflow.com/a/15929677 """ #: Columns. columns = ', '.join([ '{0}={1}'.format(k, repr(self.__dict__[k])) for k in self.__dict__.keys() if k[0] != '_' ]) return '<{0}({1})>'.format( self.__class__.__name__, columns ) engine = create_engine('sqlite:///test.db', convert_unicode=True) db_session = scoped_session( sessionmaker(autocommit=False, autoflush=False, bind=engine) ) Base = declarative_base(cls=RepresentableBase) Base.query = db_session.query_property() def init_db(): Base.metadata.create_all(bind=engine)
モデル。
# -*- coding: utf-8 -*- from sqlalchemy import Column, Integer, String, ForeignKey from sqlalchemy.orm import relationship from db import Base class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String(50), unique=True) email = Column(String(120), unique=True) def __init__(self, name=None, email=None): self.name = name self.email = email class UserImage(Base): __tablename__ = 'images' id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey('users.id')) image = Column(String(120), unique=True) user = relationship('User', backref='images', primaryjoin='User.id==UserImage.user_id', lazy='joined') def __init__(self, user_id=None, image=None): self.user_id = user_id self.image = image
使う側。
from sqlalchemy.orm import joinedload from db import db_session, init_db from models import User, UserImage if __name__ == '__main__': init_db() user = User(name='foo', email='foo@example.com') db_session.add(user) db_session.flush() print(user) image = UserImage(user_id=user.id, image='/path/to/image') image2 = UserImage(user_id=user.id, image='/path/to/image2') db_session.add_all([image, image2]) db_session.flush() db_session.commit() data = db_session.query(User).options(joinedload('images')).all() print(data)
結果。
$ python app.py <User(email='foo@example.com', id=1, name='foo')> [<User(images=[<UserImage(id=1, user_id=1, image='/path/to/image')>, <UserImage(id=2, user_id=1, image='/path/to/image2')>], email='foo@example.com', id=1, name='foo')>]
ちゃんと取れた。
関連付けをしていても関連先のもちゃんと表示してくれる。