3. Public Activity
MessagePack-Python
JSON like binary protocol
wsaccel
WebSocket accelerator for Tornado, ws4py
MyKaze
PyMySQL in Tornado (WIP)
Python for PHPer
Quick guide of Python for PHPer (Japanese)
10. ● Dialects -- Various DB-API wrapper
● Engine -- Connection management
● Schema and Types
● SQL expression
● and O/R Mapper
SQLAlchemy features
11. Create Engine
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:',
echo=True)
# Using engine without O/R mapper
con = engine.connect()
with con.begin() as trx:
con.execute('SELECT 1+1')
con.close()
12. Define Object and Schema
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
email = Column(String, nullable=False, unique=True)
password = Column(String, nullable=False)
13. Create table on DB
Base.metadata.create_all(engine)
Output:
CREATE TABLE users (
id INTEGER NOT NULL,
name VARCHAR,
email VARCHAR NOT NULL,
password VARCHAR NOT NULL,
PRIMARY KEY (id),
UNIQUE (email)
)
20. Part 1. The Narratives
3. Mapping to RDB
Part 2. The Patterns
10. Data Source Architectural Patterns
11. O/R Behavioral Patterns
12. O/R Structural Patterns
13. O/R Metadata Mapping Patterns
O/R chapters in the P of EAA
21. Part 1. The Narratives
3. Mapping to RDB
Part 2. The Patterns
10. Data Source Architectural Patterns
11. O/R Behavioral Patterns
12. O/R Structural Patterns
13. O/R Metadata Mapping Patterns
O/R chapters in the P of EAA
23. Unit of Work
Target: Maintains set of objects to save
in transaction.
How:
1. Wrap transaction.
2. Keep track of new, modified and deleted
objects.
3. Save objects when commit
24. Why Unit of Work
Remove boilerplate saving code from domain
logic.
Avoid objects saved too often.
25. Unit of Work in SQLAlchemy
# Create an Unit of Work
session = Session()
# Get connection and start transaction
ed_user = session.query(User).
filter(User.name=='ed').one()
ed_user.name='E.D.'
session.commit() # save ed_user and commit
26. Object states
user = User(...)
# Transient (not saved)
session.add(user)
# pending (saved)
session.flush()
# persistent (saved if modified)
session.expunge(user)
# Detached (not saved)
27. Flushing
Execute insert / update /delete query.
Session flushes automatically when querying
to avoid inconsistent result. (autoflush)
You can stop autoflush.
You can manually flush.
28. expire -- cause reload
session.expire(user)
print(user.name) # Cause reloading
commit() # expires all objects for consistency.
You can avoid this by
expire_on_commit=False.
31. Target:
Avoid two objects for one record.
How:
Keep mapping of (table, pk) -> object.
Check the map when querying.
Bonus: It acts like a easy cache
Identity map
32. Identity map in SQLAlchemy
Session has identity map.
# Looks up id map after querying
session.query(User).filter(User.id==3).one()
# Looks up id map before and after querying.
session.query(User).get(3)
NOTE: Id map is weak reference by default.
44. 12. O/R Structural Pattern
● Identity Field
● Foreign Key Mapping
● Association Table Mapping
● Dependent Mapping
● Embedded Value
● Serialized LOB
● Single Table Inheritance
● Class Table Inheritance
● Concrete Table Inheritance
● Inheritance Mappers
45. Association Table Mapping
Mapping many-to-many association table to
object reference.
In SQLAlchemy:
Pass “secondary” argument to relationship()
http://docs.sqlalchemy.org/en/rel_0_8/orm/tutorial.html#building-a-many-
to-many-relationship
46. 12. O/R Structural Pattern
● Identity Field
● Foreign Key Mapping
● Association Table Mapping
● Dependent Mapping
● Embedded Value
● Serialized LOB
● Single Table Inheritance
● Class Table Inheritance
● Concrete Table Inheritance
● Inheritance Mappers
47. Dependent Mapping
O/R map ownership without identity.
PofEAA says:
I don’t recommend Dependent Mapping
if you’re using Unit of Work.
48. 12. O/R Structural Pattern
● Identity Field
● Foreign Key Mapping
● Association Table Mapping
● Dependent Mapping
● Embedded Value
● Serialized LOB
● Single Table Inheritance
● Class Table Inheritance
● Concrete Table Inheritance
● Inheritance Mappers
49. Embedded Value
Map an object to some columns in a row.
class Duration:
start_date
end_date
class Account:
duration
CREATE TABLE account (
…
start_date DATE,
end_date DATE,
…
);
55. Custom Type
SQLAlchemy provides PickleType for serialized
LOB.
You can define custom type via TypeDecorator:
http://docs.sqlalchemy.org/en/rel_0_8/core/types.
html#marshal-json-strings
56. 12. O/R Structural Pattern
● Identity Field
● Foreign Key Mapping
● Association Table Mapping
● Dependent Mapping
● Embedded Value
● Serialized LOB
● Single Table Inheritance
● Class Table Inheritance
● Concrete Table Inheritance
● Inheritance Mappers
57. Single Table Inheritance
Player
SoccerPlayer BaseballPlayer
CREATE TABLE player(
id INTEGER PRIMARY KEY,
type INTEGER NOT NULL,
position INTEGER,
)
All classes saved
into one table.
58. Single Table Inheritance in SQLA
class Player(Base):
__tablename__ = ‘player’
id = Column(INTEGER, primary_key=True)
position = Column(INTEGER)
type = Column(INTEGER, nullable=False)
__mapper_args__ = {‘polymorphic_on’: type}
SOCCER_PLAYER = 1
BASEBALL_PLAYER = 2
class SoccerPlayer(Player):
__mapper_args__ = {
‘polymorhic_identity’: Player.SOCCER_PLAYER}
59. Defining columns in subclass
Defining columns in subclass may cause
conflict.
SQLA provides way to avoid it. See below.
http://docs.sqlalchemy.
org/en/latest/orm/extensions/declarative.html#resolving-
column-conflicts
60. Class Table Inheritance
Player
SoccerPlayer BaseballPlayer
CREATE TABLE player(
id INTEGER PRIMARY KEY,
type INTEGER NOT NULL
)
CREATE TABLE soccer_player(
id INTEGER PRIMARY KEY,
position INTEGER NOT NULL
)
CREATE TABLE baseball_player(
id INTEGER PRIMARY KEY,
position INTEGER NOT NULL
)
Tables for each
classes.
61. Class table inheritance in SQLA
SQLA call it “Joined table inheritance”
http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative.html#joined-
table-inheritance
62. class Player(Base):
__tablename__ = 'player'
id = Column(INTEGER, primary_key=True)
type = Column(INTEGER, nullable=False)
SOCCER_PLAYER = 1
BASEBALL_PLAYER = 2
__mapper_args__ = {'polymorphic_on': type}
class SoccerPlayer(Player):
__tablename__ = 'soccer_player'
id = Column(ForeignKey('player.id'), primary_key=True)
position = Column(INTEGER, nullable=False)
__mapper_args__ = {
'polymorhic_identity': Player.SOCCER_PLAYER}
63. Concrete Table Inheritance
Player
SoccerPlayer BaseballPlayer
CREATE TABLE soccer_player(
id INTEGER PRIMARY KEY,
name VARCHAR(32) NOT NULL,
position INTEGER NOT NULL
)
CREATE TABLE baseball_player(
id INTEGER PRIMARY KEY,
name VARCHAR(32) NOT NULL,
position INTEGER NOT NULL
)
Tables for each
concrete classes
64. Mix-in
class BasePlayer(object):
id = Column(INTEGER, primary_key=True)
name = Column(VARCHAR(32), nullable=False)
class SoccerPlayer(BasePlayer, Base):
__tablename__ = ‘soccer_player’
position = Column(INTEGER, nullable=False)
Pros) Simple.
Cons) You can’t get support from SQLAlchemy
65. Concrete Table Inheritance in SQLA
http://docs.sqlalchemy.org/en/latest/orm/inheritance.
html#concrete-table-inheritance
66. 10. Data Source Architectural Patterns
● Table Data Gateway
● Row Data Gateway
● Active Record
● Data Mapper
● Architectural Pattern and SQLAlchemy
67. Table Data Gateway
Target: Split querying from your domain logic
How: Create gateway for each table.
class PlayerGateway:
def find_by_id(self, id)
def find_by_name(self, name)
def update(self, id, name=None, age=None)
def insert(self, id, name, age)
68. Row Data Gateway
Target: Split querying from your domain logic
How: Create gateway for each record.
class PlayerGateway:
@classmethod
def find_by_id(cls, id):
…
return cls(id, name, age)
…
def insert(self)
def update(self)
69. Active Record
Row Data Gateway with Domain Logic
class Player:
@classmethod
def find_by_id(cls, id)
...
def birthday(self):
self.age += 1
70. Data Mapper
Target: Split out column/attribute mapping code
from your domain logic.
class PersonMapper(BaseMapper):
def map(self, row):
return Person(id=row.id,
name=row.name,
age=row.age)
72. Querying in SQLAlchemy
Unit of Work handles most of update and insert
queries.
Easy query can be written very easy.
(Building complex query is hard)
73. Table Gateway?
Most tables doesn’t require Table Gateway.
When there are some complex queries,
Table Gateway is good to have.
74. Row Data Gateway?
Active Record?
Separating model object and row data gateway
cause additional complexity.
For example, does your model need identity
map?
Person
Person
PersonRow
75. Active Record drawbacks
Problem: Good table design is not good class
design always.
My answer: Use structural patterns like
Embedded Value.
P: Mixing database access and domain logic is
bad idea.
A: Use Table Gateway to separate them.
77. Simple ActiveRecord & Service
class AccountService:
def register(self, name, email, age,...)
def unregister(self, person_id)
class FriendService:
def request(self,
from_person_id, to_person_id)
78. Finder in Service
When you use service classes, you may be able to
put finders there and don’t use Table Gateway.
class PersonService:
...
def find_by_name(self, name):
return Session.query(Person).
filter_by(name=name).all()