Download - Introduction to SQLAlchemy and Alembic Migrations

Transcript
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