Click here to load reader
Upload
jason-myers
View
159
Download
0
Embed Size (px)
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.
Citation preview
SQLAlchemy andAlembic
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_engineengine = 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 ForeignKeyfrom sqlalchemy.orm import ( relationship, backref)
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" )
def __init__(self, name, fullname, balance, group): self.name = name self.fullname = fullname self.balance = balance self.group = group
Base.metadata.create_all(engine)
from sqlalchemy.orm import sessionmakerSession = 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', 'Bobby Jr', 100500.00, 'Mob')user4 = User('Rachael', 'Rachael Rach', 125.50, 'Personal')
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() - datetime.timedelta(30))).order_by('ID DESC').all()
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
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
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=='[email protected]').all()
@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)
Plumbing
from sqlalchemy import create_engineengine = create_engine( ‘dialect+driver://USER:PASS@HOST:PORT/DB’)
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),)
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.execute(ins, id=2, name='wendy', fullname='Wendy McDonalds')
conn.execute(addresses.insert(), [ {'user_id': 1, 'email_address' : '[email protected]'}, {'user_id': 1, 'email_address' : '[email protected]'},])
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)]
InformixMS SQLOraclePostgresSQLiteCustom
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, }
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://mybucket/venue_pipe_'credentials 'aws_access_key_id=ACCESS_KEY; aws_secret_access_key=SECRET_KEY';
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)
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()
pip install alembic
alembic init alembic
# 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
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)
alembic revision -m "initial"
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)
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')
alembic upgrade head
alembic revision --autogenerate -m "Added account table"
Table (adds/removes)Columns (adds/removes)Nullable changes
Optionally: Column Type changescompare_type=TrueNo Name changes on Columns or Table
alembic currentalembic upgrade +2alembic downgrade -1alembic upgrade ae1alembic upgrade 1 --sql > file.sqlalembic history
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
THE END@jasonamyers