SQLAlchemy and
Alembic
ORM, Core and Migrations
/Jason Myers @jasonamyers
Architecture
pip install sqlalchemy
pip install flask-sqlalchemy
bin/paster create -t pyramid_alchemy tutorial
from sqlalchemy import create_engine
engine = create_engine(
‘dialect+driver://USER:PASS@HOST:PORT/DB’
)
Porcelain
from sqlalchemy.ext.declarative import (
declarative_base
)
Base = declarative_base()
from sqlalchemy import (
Column,

Integer,
String,
Float
)
from sqlalchemy import ForeignKey
from sqlalchemy.orm import (
...
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String)
fullname = Column(S...
def __init__(self, name, fullname, balance, group):
self.name = name
self.fullname = fullname
self.balance = balance
self....
Base.metadata.create_all(engine)
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
db = Session()
user1 = User('Bob', 'Big Bob', 1000000.00, 'Mob')
user2 = User('Linda', 'Linda Lu', 100.50, 'Diner')
user3 = User('Lil Bob...
db.add(user1)
db.commit()
db.delete(user1)
db.expunge(user1)
db.refresh(user1)
db.expire(user1)
db.rollback()
for user in db.query(User).all():
print user.name, user.balance
Out[1]: Bob 1000000.0
entries = db.session.query(Entries).filter_by(user_id=user.id)
.filter(Entries.entry_time >
(datetime.datetime.utcnow() - ...
SELECT entries.id AS entries_id,
entries.user_id AS entries_user_id,
entries.phone AS entries_phone,
entries.measurement A...
from sqlalchemy import func
from sqlalchemy.sql import label
results = db.query(
User.group,
label('members', func.count(U...
bob.addresses.append(home_address)
bob.addresses
bob.addresses.filter(Address.type='H').one()
query = db.query(User, Address).filter(User.id==Address.user_id)
query = query.filter(Address.email_address=='jack@goog.co...
@hybrid_property
def grand_total(self):
rollup_fields = [
'merchandise_cost',
'tax',
'shipping',
]
total = sum([self.__get...
Plumbing
from sqlalchemy import create_engine
engine = create_engine(
‘dialect+driver://USER:PASS@HOST:PORT/DB’
)
from sqlalchemy import (Table, Column,
Integer, String, MetaData, ForeignKey)
metadata = MetaData()
users = Table('users',...
Base.metadata.create_all(engine)
conn = engine.connect()
ins = users.insert().values(name='jack', fullname='Jack Bell')
result = conn.execute(ins)
ins = users.insert()
conn.execut...
conn.execute(addresses.insert(), [
{'user_id': 1, 'email_address' : 'j@y.com'},
{'user_id': 1, 'email_address' : 'j@m.com'...
def build_table(table_name):
return Table(
table_name,
metadata,
autoload=True,
autoload_with=engine
)
build_table('census')
unavailable_fields = [
c.name for c in t.c if isinstance(c.type, NullType)
]
Informix
MS SQL
Oracle
Postgres
SQLite
Custom
class UnloadFromSelect(Executable, ClauseElement):
def __init__(self, select, bucket, access_key, secret_key):
self.select...
unload = UnloadFromSelect(
select([fields]),
'/'.join(['s3:/', BUCKET, filename]),
ACCESS_KEY,
SECRET_KEY
)
unload (
'select * from venue where venueid in (
select venueid from venue order by venueid desc limit 10)'
)
to 's3://myb...
s = select(
[
t.c.race,
t.c.factor,
func.sum(g.t.c.value).label('summed')
], t.c.race > 0
).where(
and_(
t.c.type == 'POVE...
s = select(
[
table.c.discharge_year,
func.count(1).label(
'patient_discharges'),
table.c.zip_code,
], table.c.discharge_y...
pip install alembic
alembic init alembic
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template use...
from glu import db
target_metadata = db.metadata
def run_migrations_online():
alembic_config = config.get_section(config.c...
alembic revision -m "initial"
def upgrade():
op.create_table('users_to_users',
sa.Column('patient_user_id', sa.Integer(), nullable=False),
sa.Column('pr...
def downgrade():
op.alter_column(u'reminders', u'user_created',
existing_type=mysql.TINYINT(display_width=1), nullable=Fal...
alembic upgrade head
alembic revision --autogenerate -m "Added account table"
Table (adds/removes)
Columns (adds/removes)
Nullable changes
Optionally: Column Type changes
compare_type=True
No Name changes on Columns or Table
alembic current
alembic upgrade +2
alembic downgrade -1
alembic upgrade ae1
alembic upgrade 1 --sql > file.sql
alembic his...
2806761df139 -> 1e9831c8fa7d (head), Adding tags to ...
2806761df139 -> 46a1d4de6e04 (head), Added timezone ...
4f7119855d...
THE END
@jasonamyers
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Introduction to SQLAlchemy and Alembic Migrations
Upcoming SlideShare
Loading in …5
×

Introduction to SQLAlchemy and Alembic Migrations

5,130 views

Published on

In this talk, we'll examine how to use SQLAlchemy ORM and Core in both simple queries and query builder type applications. Next, we'll explore Alembic database migrations and how we can use them to handle database changes.

Published in: Technology, Business
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,130
On SlideShare
0
From Embeds
0
Number of Embeds
31
Actions
Shares
0
Downloads
69
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

Introduction to SQLAlchemy and Alembic Migrations

  1. 1. SQLAlchemy and Alembic ORM, Core and Migrations /Jason Myers @jasonamyers
  2. 2. Architecture
  3. 3. pip install sqlalchemy pip install flask-sqlalchemy bin/paster create -t pyramid_alchemy tutorial
  4. 4. from sqlalchemy import create_engine engine = create_engine( ‘dialect+driver://USER:PASS@HOST:PORT/DB’ )
  5. 5. Porcelain
  6. 6. from sqlalchemy.ext.declarative import ( declarative_base ) Base = declarative_base()
  7. 7. from sqlalchemy import ( Column,
 Integer, String, Float ) from sqlalchemy import ForeignKey from sqlalchemy.orm import ( relationship, backref )
  8. 8. class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) name = Column(String) fullname = Column(String) balance = Column(Float) group = Column(String) addresses = relationship( "Address", order_by="Address.id", backref="user" )
  9. 9. def __init__(self, name, fullname, balance, group): self.name = name self.fullname = fullname self.balance = balance self.group = group
  10. 10. Base.metadata.create_all(engine)
  11. 11. from sqlalchemy.orm import sessionmaker Session = sessionmaker(bind=engine) db = Session()
  12. 12. user1 = User('Bob', 'Big Bob', 1000000.00, 'Mob') user2 = User('Linda', 'Linda Lu', 100.50, 'Diner') user3 = User('Lil Bob', 'Bobby Jr', 100500.00, 'Mob') user4 = User('Rachael', 'Rachael Rach', 125.50, 'Personal')
  13. 13. db.add(user1) db.commit() db.delete(user1)
  14. 14. db.expunge(user1) db.refresh(user1) db.expire(user1) db.rollback()
  15. 15. for user in db.query(User).all(): print user.name, user.balance Out[1]: Bob 1000000.0
  16. 16. entries = db.session.query(Entries).filter_by(user_id=user.id) .filter(Entries.entry_time > (datetime.datetime.utcnow() - datetime.timedelta(30)) ).order_by('ID DESC').all()
  17. 17. SELECT entries.id AS entries_id, entries.user_id AS entries_user_id, entries.phone AS entries_phone, entries.measurement AS entries_measurement, entries.insulin AS entries_insulin, entries.insulin_type AS entries_insulin_type, entries.carbs AS entries_carbs, entries.tag AS entries_tag, entries.three_sixty_id AS entries_three_sixty_id, entries.entry_time AS entries_entry_time, entries.created_time AS entries_created_time FROM entries WHERE entries.user_id = :user_id_1 AND entries.entry_time > :entry_time_1 ORDER BY ID DESC
  18. 18. from sqlalchemy import func from sqlalchemy.sql import label results = db.query( User.group, label('members', func.count(User.id)), label( 'total_balance', func.sum(User.balance) ) ).group_by(User.group).all() for result in results: print result.group, result.members, result.total_balance
  19. 19. bob.addresses.append(home_address) bob.addresses bob.addresses.filter(Address.type='H').one()
  20. 20. query = db.query(User, Address).filter(User.id==Address.user_id) query = query.filter(Address.email_address=='jack@goog.com').all()
  21. 21. @hybrid_property def grand_total(self): rollup_fields = [ 'merchandise_cost', 'tax', 'shipping', ] total = sum([self.__getattribute__(x) for x in rollup_fields]) return round(total, 2)
  22. 22. Plumbing
  23. 23. from sqlalchemy import create_engine engine = create_engine( ‘dialect+driver://USER:PASS@HOST:PORT/DB’ )
  24. 24. from sqlalchemy import (Table, Column, Integer, String, MetaData, ForeignKey) metadata = MetaData() users = Table('users', metadata, Column('id', Integer, primary_key=True), Column('name', String), Column('fullname', String), )
  25. 25. Base.metadata.create_all(engine) conn = engine.connect()
  26. 26. ins = users.insert().values(name='jack', fullname='Jack Bell') result = conn.execute(ins) ins = users.insert() conn.execute(ins, id=2, name='wendy', fullname='Wendy McDonalds')
  27. 27. conn.execute(addresses.insert(), [ {'user_id': 1, 'email_address' : 'j@y.com'}, {'user_id': 1, 'email_address' : 'j@m.com'}, ])
  28. 28. def build_table(table_name): return Table( table_name, metadata, autoload=True, autoload_with=engine )
  29. 29. build_table('census') unavailable_fields = [ c.name for c in t.c if isinstance(c.type, NullType) ]
  30. 30. Informix MS SQL Oracle Postgres SQLite Custom
  31. 31. class UnloadFromSelect(Executable, ClauseElement): def __init__(self, select, bucket, access_key, secret_key): self.select = select self.bucket = bucket self.access_key = access_key self.secret_key = secret_key @compiles(UnloadFromSelect) def visit_unload_from_select(element, compiler, **kw): return "unload ('%(query)s') to '%(bucket)s' credentials 'aws_access_key_id=%(access_key)s; aws_secret_access_key=%(secret_key)s' delimiter ',' addquotes allowoverwrite" % { 'query': compiler.process(element.select, unload_select=True, literal_binds=True), 'bucket': element.bucket, 'access_key': element.access_key, 'secret_key': element.secret_key, }
  32. 32. unload = UnloadFromSelect( select([fields]), '/'.join(['s3:/', BUCKET, filename]), ACCESS_KEY, SECRET_KEY )
  33. 33. unload ( 'select * from venue where venueid in ( select venueid from venue order by venueid desc limit 10)' ) to 's3://mybucket/venue_pipe_' credentials 'aws_access_key_id=ACCESS_KEY; aws_secret_access_key=SECRET_KEY';
  34. 34. s = select( [ t.c.race, t.c.factor, func.sum(g.t.c.value).label('summed') ], t.c.race > 0 ).where( and_( t.c.type == 'POVERTY', t.c.value != 0 ) ).group_by( t.c.race, t.c.factor ).order_by( t.c.race, t.c.factor)
  35. 35. s = select( [ table.c.discharge_year, func.count(1).label( 'patient_discharges'), table.c.zip_code, ], table.c.discharge_year.in_(years) ).group_by(table.c.discharge_year) s = s.where(table.c.hospital_name == provider) if 'total_charges' not in unavailable_fields: s = s.column( func.sum(table.c.total_charges ).label('patient_charges') ) s = s.group_by(table.c.zip_code) s = s.order_by('discharges DESC') cases = conn.execute(s).fetchall()
  36. 36. pip install alembic
  37. 37. alembic init alembic
  38. 38. # A generic, single database configuration. [alembic] # path to migration scripts script_location = alembic # template used to generate migration files # file_template = %%(rev)s_%%(slug)s # set to 'true' to run the environment during # the 'revision' command, regardless of autogenerate # revision_environment = false sqlalchemy.url = driver://user:pass@localhost/dbname
  39. 39. from glu import db target_metadata = db.metadata def run_migrations_online(): alembic_config = config.get_section(config.config_ini_section) from config import SQLALCHEMY_DATABASE_URI alembic_config['sqlalchemy.url'] = SQLALCHEMY_DATABASE_URI engine = engine_from_config( alembic_config, prefix='sqlalchemy.', poolclass=pool.NullPool )
  40. 40. alembic revision -m "initial"
  41. 41. def upgrade(): op.create_table('users_to_users', sa.Column('patient_user_id', sa.Integer(), nullable=False), sa.Column('provider_user_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(['patient_user_id'], ['users.id'],), sa.ForeignKeyConstraint(['provider_user_id'], ['users.id'],), sa.PrimaryKeyConstraint('patient_user_id','provider_user_id') ) op.alter_column(u'reminders', u'user_created', nullable=True)
  42. 42. def downgrade(): op.alter_column(u'reminders', u'user_created', existing_type=mysql.TINYINT(display_width=1), nullable=False ) op.drop_table('users_to_users')
  43. 43. alembic upgrade head
  44. 44. alembic revision --autogenerate -m "Added account table"
  45. 45. Table (adds/removes) Columns (adds/removes) Nullable changes
  46. 46. Optionally: Column Type changes compare_type=True No Name changes on Columns or Table
  47. 47. alembic current alembic upgrade +2 alembic downgrade -1 alembic upgrade ae1 alembic upgrade 1 --sql > file.sql alembic history
  48. 48. 2806761df139 -> 1e9831c8fa7d (head), Adding tags to ... 2806761df139 -> 46a1d4de6e04 (head), Added timezone ... 4f7119855daf -> 2806761df139 (branchpoint), Added Pr... 377addf23edb -> 4f7119855daf, Added user_created to ... 483d9a63fbf5 -> 377addf23edb, Adding claimed to user... 464ba41d7ad8 -> 483d9a63fbf5, Adding username/passwo... 2cfd9dc89267 -> 464ba41d7ad8, Adding Intercom.io d4774a3ce8 -> 2cfd9dc89267, Seperating Roles and Use... None -> d4774a3ce8, Base
  49. 49. THE END @jasonamyers

×