Introduction to SQLAlchemy and Alembic Migrations

66

Click here to load reader

description

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.

Transcript of Introduction to SQLAlchemy and Alembic Migrations

Page 1: Introduction to SQLAlchemy and Alembic Migrations

SQLAlchemy andAlembic

ORM, Core and Migrations / Jason Myers @jasonamyers

Page 2: Introduction to SQLAlchemy and Alembic Migrations
Page 3: Introduction to SQLAlchemy and Alembic Migrations

Architecture

Page 4: Introduction to SQLAlchemy and Alembic Migrations
Page 5: Introduction to SQLAlchemy and Alembic Migrations

pip install sqlalchemy

pip install flask-sqlalchemy

bin/paster create -t pyramid_alchemy tutorial

Page 6: Introduction to SQLAlchemy and Alembic Migrations

from sqlalchemy import create_engineengine = create_engine( ‘dialect+driver://USER:PASS@HOST:PORT/DB’)

Page 7: Introduction to SQLAlchemy and Alembic Migrations

Porcelain

Page 8: Introduction to SQLAlchemy and Alembic Migrations
Page 9: Introduction to SQLAlchemy and Alembic Migrations

from sqlalchemy.ext.declarative import ( declarative_base)

Base = declarative_base()

Page 10: Introduction to SQLAlchemy and Alembic Migrations
Page 11: Introduction to SQLAlchemy and Alembic Migrations

from sqlalchemy import ( Column, Integer, String, Float)from sqlalchemy import ForeignKeyfrom sqlalchemy.orm import ( relationship, backref)

Page 12: Introduction to SQLAlchemy and Alembic Migrations

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" )

Page 13: Introduction to SQLAlchemy and Alembic Migrations

def __init__(self, name, fullname, balance, group): self.name = name self.fullname = fullname self.balance = balance self.group = group

Page 14: Introduction to SQLAlchemy and Alembic Migrations
Page 15: Introduction to SQLAlchemy and Alembic Migrations

Base.metadata.create_all(engine)

Page 16: Introduction to SQLAlchemy and Alembic Migrations
Page 17: Introduction to SQLAlchemy and Alembic Migrations

from sqlalchemy.orm import sessionmakerSession = sessionmaker(bind=engine)db = Session()

Page 18: Introduction to SQLAlchemy and Alembic Migrations

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')

Page 19: Introduction to SQLAlchemy and Alembic Migrations

db.add(user1)

db.commit()

db.delete(user1)

Page 20: Introduction to SQLAlchemy and Alembic Migrations

db.expunge(user1)

db.refresh(user1)db.expire(user1)

db.rollback()

Page 21: Introduction to SQLAlchemy and Alembic Migrations
Page 22: Introduction to SQLAlchemy and Alembic Migrations

for user in db.query(User).all(): print user.name, user.balance

Out[1]: Bob 1000000.0

Page 23: Introduction to SQLAlchemy and Alembic Migrations

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()

Page 24: Introduction to SQLAlchemy and Alembic Migrations

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_timeFROM entriesWHERE entries.user_id = :user_id_1 AND entries.entry_time > :entry_time_1ORDER BY ID DESC

Page 25: Introduction to SQLAlchemy and Alembic Migrations

from sqlalchemy import funcfrom sqlalchemy.sql import labelresults = 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

Page 26: Introduction to SQLAlchemy and Alembic Migrations
Page 27: Introduction to SQLAlchemy and Alembic Migrations

bob.addresses.append(home_address)

bob.addresses

bob.addresses.filter(Address.type='H').one()

Page 28: Introduction to SQLAlchemy and Alembic Migrations

query = db.query(User, Address).filter(User.id==Address.user_id)query = query.filter(Address.email_address=='[email protected]').all()

Page 29: Introduction to SQLAlchemy and Alembic Migrations
Page 30: Introduction to SQLAlchemy and Alembic Migrations

@hybrid_propertydef grand_total(self): rollup_fields = [ 'merchandise_cost', 'tax', 'shipping', ] total = sum([self.__getattribute__(x) for x in rollup_fields]) return round(total, 2)

Page 31: Introduction to SQLAlchemy and Alembic Migrations
Page 32: Introduction to SQLAlchemy and Alembic Migrations

Plumbing

Page 33: Introduction to SQLAlchemy and Alembic Migrations

from sqlalchemy import create_engineengine = create_engine( ‘dialect+driver://USER:PASS@HOST:PORT/DB’)

Page 34: Introduction to SQLAlchemy and Alembic Migrations

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),)

Page 35: Introduction to SQLAlchemy and Alembic Migrations

Base.metadata.create_all(engine)conn = engine.connect()

Page 36: Introduction to SQLAlchemy and Alembic Migrations

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')

Page 37: Introduction to SQLAlchemy and Alembic Migrations

conn.execute(addresses.insert(), [ {'user_id': 1, 'email_address' : '[email protected]'}, {'user_id': 1, 'email_address' : '[email protected]'},])

Page 38: Introduction to SQLAlchemy and Alembic Migrations

def build_table(table_name):return Table( table_name, metadata, autoload=True, autoload_with=engine)

Page 39: Introduction to SQLAlchemy and Alembic Migrations

build_table('census')unavailable_fields = [ c.name for c in t.c if isinstance(c.type, NullType)]

Page 40: Introduction to SQLAlchemy and Alembic Migrations
Page 41: Introduction to SQLAlchemy and Alembic Migrations

InformixMS SQLOraclePostgresSQLiteCustom

Page 42: Introduction to SQLAlchemy and Alembic Migrations

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, }

Page 43: Introduction to SQLAlchemy and Alembic Migrations

unload = UnloadFromSelect( select([fields]), '/'.join(['s3:/', BUCKET, filename]), ACCESS_KEY, SECRET_KEY)

Page 44: Introduction to SQLAlchemy and Alembic Migrations

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';

Page 45: Introduction to SQLAlchemy and Alembic Migrations

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)

Page 46: Introduction to SQLAlchemy and Alembic Migrations

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()

Page 47: Introduction to SQLAlchemy and Alembic Migrations
Page 48: Introduction to SQLAlchemy and Alembic Migrations

pip install alembic

Page 49: Introduction to SQLAlchemy and Alembic Migrations

alembic init alembic

Page 50: Introduction to SQLAlchemy and Alembic Migrations
Page 51: Introduction to SQLAlchemy and Alembic Migrations
Page 52: Introduction to SQLAlchemy and Alembic Migrations

# A generic, single database configuration.[alembic]# path to migration scriptsscript_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 ofautogenerate# revision_environment = falsesqlalchemy.url = driver://user:pass@localhost/dbname

Page 53: Introduction to SQLAlchemy and Alembic Migrations

from glu import dbtarget_metadata = db.metadatadef 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)

Page 54: Introduction to SQLAlchemy and Alembic Migrations
Page 55: Introduction to SQLAlchemy and Alembic Migrations

alembic revision -m "initial"

Page 56: Introduction to SQLAlchemy and Alembic Migrations

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)

Page 57: Introduction to SQLAlchemy and Alembic Migrations

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')

Page 58: Introduction to SQLAlchemy and Alembic Migrations

alembic upgrade head

Page 59: Introduction to SQLAlchemy and Alembic Migrations

alembic revision --autogenerate -m "Added account table"

Page 60: Introduction to SQLAlchemy and Alembic Migrations

Table (adds/removes)Columns (adds/removes)Nullable changes

Page 61: Introduction to SQLAlchemy and Alembic Migrations
Page 62: Introduction to SQLAlchemy and Alembic Migrations

Optionally: Column Type changescompare_type=TrueNo Name changes on Columns or Table

Page 63: Introduction to SQLAlchemy and Alembic Migrations
Page 64: Introduction to SQLAlchemy and Alembic Migrations

alembic currentalembic upgrade +2alembic downgrade -1alembic upgrade ae1alembic upgrade 1 --sql > file.sqlalembic history

Page 65: Introduction to SQLAlchemy and Alembic Migrations

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.iod4774a3ce8 -> 2cfd9dc89267, Seperating Roles and Use...None -> d4774a3ce8, Base

Page 66: Introduction to SQLAlchemy and Alembic Migrations

THE END@jasonamyers